Reputation: 32716
So I've got these schemas:
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
/**
* Comment Schema
*/
var CommentSchema = new Schema({
post_id: {
type: Schema.Types.ObjectId,
ref: 'Post',
required: true
},
author:{
type: String,
required: true
},
email:{
type: String,
required: true
},
body: {
type: String,
required: true,
trim: true
},
status: {
type: String,
required: true,
default: 'pending'
},
created: {
type: Date,
required: true,
default: Date.now
},
meta: {
votes: Number
}
});
/**
* Validations
*/
CommentSchema.path('author').validate(function(author) {
return author.length;
}, 'Author cannot be empty');
CommentSchema.path('email').validate(function(email) {
return email.length;
}, 'Email cannot be empty');
CommentSchema.path('email').validate(function(email) {
var emailRegex = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/;
return emailRegex.test(email);
}, 'The email is not a valid email');
CommentSchema.path('body').validate(function(body) {
return body.length;
}, 'Body cannot be empty');
mongoose.model('Comment', CommentSchema);
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
monguurl = require('monguurl'),
Schema = mongoose.Schema;
/**
* Article Schema
*/
var PostSchema = new Schema({
title: {
type: String,
required: true,
trim: true
},
author:{
type: String,
required: true,
default: 'whisher'
},
slug: {
type: String,
index: { unique: true }
},
body: {
type: String,
required: true,
trim: true
},
status: {
type: String,
required: true,
trim: true
},
created: {
type: Date,
required: true,
default: Date.now
},
published: {
type: Date,
required: true
},
categories: {
type: [String],
index: { unique: true }
},
tags: {
type: [String],
required: true,
index: true
},
comment: {
type: Schema.Types.ObjectId,
ref: 'CommentSchema'
},
meta: {
votes: Number
}
});
/**
* Validations
*/
PostSchema.path('title').validate(function(title) {
return title.length;
}, 'Title cannot be empty');
PostSchema.path('body').validate(function(body) {
return body.length;
}, 'Body cannot be empty');
PostSchema.path('status').validate(function(status) {
return /publish|draft/.test(status);
}, 'Is not a valid status');
PostSchema.plugin(monguurl({
source: 'title',
target: 'slug'
}));
mongoose.model('Post', PostSchema);
by an api I query Post like
exports.all = function(req, res) {
Post.find().sort('-created').exec(function(err, posts) {
if (err) {
res.jsonp(500,{ error: err.message });
} else {
res.jsonp(200,posts);
}
});
};
How to retrieve how many comments has the post ? I mean I want an extra propriety in post object like post.ncomments.
The first thing I think of is adding an extra field to the post schema and update it whenever a user add a comment
meta: {
votes: Number,
ncomments:Number
}
but it seems quite ugly I think
Upvotes: 1
Views: 3264
Reputation: 59763
If you want the likely the most efficient solution, then manually adding a field like number_comments
to the Post
schema may be the best way to go, especially if you want to do things like act on multiple posts (like sorting based on comments). Even if you used an index to do the count, it's not likely to be as efficient as having the count pre-calculated (and ultimately, there are just more types of queries you can perform when it has been pre-calculated, if you haven't chosen to embed the comments).
var PostSchema = new Schema({
/* others */
number_comments: {
type: Number
}
});
To update the number:
Post.update({ _id : myPostId}, {$inc: {number_comments: 1}}, /* callback */);
Also, you won't need a comment
field in the PostSchema
unless you're using it as a "most recent" style field (or some other way where there'd only be one). The fact that you have a Post
reference in the Comment
schema would be sufficient to find all Comment
s for a given Post
:
Comments.find().where("post_id", myPostId).exec(/* callback */);
You'd want to make sure that the field is indexed. As you can use populate
with this as you've specified the ref
for the field, you might consider renaming the field to "post"
.
Comments.find().where("post", myPostId).exec(/* callback */);
You'd still only set the post
field to the _id
of the Post
though (and not an actual Post
object instance).
You could also choose to embed the comments in the Post
. There's some good information on the MongoDB web site about these choices. Note that even if you embedded the comments, you'd need to bring back the entire array just to get the count.
Upvotes: 4
Reputation: 6433
It looks like your Post
schema will only allow for a single comment:
// ....
comment: {
type: Schema.Types.ObjectId,
ref: 'CommentSchema'
},
// ....
One consideration is to just store your comments as subdocuments on your posts rather than in their own collection. Will you in general be querying your comments only as they related to their relevant post, or will you frequently be looking at all comments independent of their post?
If you move the comments to subdocuments, then you'll be able to do something like post.comments.length
.
However, if you retain comments as a separate collection (relational structure in a NoSQL DB-- there are sometimes reasons to do this), there isn't an automatic way of doing this. Mongo can't do joins, so you'll have to issue a second query. You have a few options in how to do that. One is an instance method on your post instances. You could also just do a manual CommentSchema.count({postId: <>})
.
Your proposed solution is perfectly valid too. That strategy is used in relational databases that can do joins, because it would have better performances than counting up all the comments each time.
Upvotes: 3