0

I have two collections in MongoDB: one saves post data of blog, the other saves comment data of blog with below schemas. How can I use nodejs and mongoose to query all posts with comment belong to it and respond to single page application?. Thanks!

var PostSchema = mongoose.Schema({
    created: {
        type: Date,
        default: Date.now
    },
    content: {
        type: String,
        default: '',
        trim: true
    },
    user: {
        type: Schema.ObjectId,
        ref: 'user'
    }
 }); 

 var CommentSchema = mongoose.Schema({
    created: {
        type: Date,
        default: Date.now
    },
    content: {
        type: String,
        default: '',
        trim: true
    },
    ofpost: {
        type: Schema.ObjectId, 
        ref: 'post'           //which post this comment belong to
    },
    user: {
        type: Schema.ObjectId,
        ref: 'user'
    }
 }); 




    var Post = mongoose.model('Post', PostSchema);
    var Comment = mongoose.model('Comment', CommentSchema); 

//example:the Comment1 and Comment2 belong to Post1
    var Post1 = new Post({ content: 'good day', user: 'John' });
    var Comment1 = new Comment({content: 'yeah', ofpost: Post1._id, user:'Tom'})
    var Comment2 = new Comment({content: 'agree', ofpost: Post1._id, user:'Tina'})
Vo Thanh Thang
  • 330
  • 1
  • 5
  • 16
  • Can you explain a bit on `ref: 'post'` in `CommentSchema`. – Amol M Kulkarni Sep 24 '13 at 05:52
  • Hi @Amol M Kulkarni, I create two models base on above schemas like this: var Post = mongoose.model('Post', PostSchema); var Comment = mongoose.model('Comment', CommentSchema); And this is two collections: var Post1 = new Post({ content: 'good day', user: 'John' }); var Comment1 = new Comment({content: 'yeah', ofpost: Post1._id, user:'Tom'}) var Comment2 = new Comment({content: 'agree', ofpost: Post1._id, user:'Tina'}) So the Comment1 and Comment2 belong to Post1 – Vo Thanh Thang Sep 24 '13 at 08:03
  • As per your comments `var Post1 = new Post({ content: 'good day', user: 'John' });` has no **`_id`** field; But your are using it as reference, `var Comment1 = new Comment({content: 'yeah', ofpost: ` **`Post1._id`** `, user:'Tom'});`. Fix it in your schema, and update in your question. – Amol M Kulkarni Sep 24 '13 at 12:19
  • After we save it, mongodb will render _id automatically. I have done it and it work. My problem is I can not know how to stick post and comment together to respond to client. – Vo Thanh Thang Sep 24 '13 at 15:35
  • You say 'many to many' but it looks as though each comment belongs to a single post - is that the case? So one post can have many comments, but one comment only belongs to one post. So the 'post to comments' relationship is a 'one to many'. Is that right? – Simon Holmes Sep 24 '13 at 16:22
  • yes, very sorry for my mistake @SimonHolmes – Vo Thanh Thang Sep 25 '13 at 05:18

1 Answers1

1

As mongodb is NoSQL type of database and has no JOIN's or any sort of relationship between documents, you have to take care of such.
There are generally two ways to do so:

Caching
Consider storing comments data within blog document. You can have embedded documents without any problem. In reality it leads to some extra caches, like comments count, array of user id's of comments and other stuff that will make your queries indexed and more easy ways to search through collection.

Multiple Queries
If you still need separate collections, then you need to 'simulate' joins. Most efficient ways is to make temporary indexing arrays and multiple queries to different collections. Usually it should be just 2 queries for one Join (many to many), and small iteration to add second query documents to first array of documents.

Here is the flow that is suitable and performs well still, on example:
Two collections, first is posts, and second is comments which has id of post.

  1. Make query to posts.
  2. Iterate through each post and add its id into postIds array, as well make postMap object where key will be id of post and value will be specific post. - this is so called indexing posts.
  3. Make query to comments collection with $in argument with postIds array of post id's. This collection should have indexing on post id field in order to make this query very efficient. As well this query can include sorting by date (additional compound indexing will speedup it).
  4. Iterate through each comment and using postMap add it to comments array of post.

So we have only 2 queries, and one iteration through all comments to embed data into posts O(n). Without second step, adding to posts will be potentially O(p*c) where p - number of posts and c - number of comments. Which is obviously much slower as well on big queries can be potentially slow.

Summary
Second approach is more manageable approach from data point of view, as well is easier on writes, while is more complicated on reads.
Still will require some caching, like number of comments for blog posts.

moka
  • 22,846
  • 4
  • 51
  • 67
  • Caching: good, but it is unpossible if we have 1000 comment for a post Multiple Queries: This is the way i am doing, but I am using nodejs for server-side, and nodejs process is asynchoronous process, we can not add anything as you say"using postMap add it to comments array of post". Let take a look at my problem: [link](https://github.com/LearnBoost/mongoose/issues/1706) – Vo Thanh Thang Sep 24 '13 at 16:01
  • Another link: [link](http://stackoverflow.com/questions/18950984/return-value-from-asynchronous-function-in-nodejs) – Vo Thanh Thang Sep 24 '13 at 16:02
  • Above links, I use person instead of post, and product instead of comment. One person can buy many kind of products – Vo Thanh Thang Sep 24 '13 at 16:04
  • The problem in link you've posted, is that `i` will be different in callback from when this callback was assigned, that is why you cannot get them properly. You need to get idea about iterators and variable scopes with async callbacks in JS. Check article here: http://tobyho.com/2011/11/02/callbacks-in-loops/ – moka Sep 24 '13 at 16:29