Reputation: 4022
I'm trying to just count subdocuments in Mongo for each document.
It's easy to get documents in a collection with db.users.find().length()
. I want to do something similar like db.users.projects.find().length()
. How can I do this?
Edit:
The result I want is to know how many projects each user has... So something like:
{_id: 123, projects: 4}
Example user document:
{
_id:{$oid: 123},
username: johnsmith,
projects: [{$oid: 456}, {$oid: 789}]
}
Upvotes: 13
Views: 12391
Reputation: 193
A better approach for counting sub-document using mongoose using VirtualType() Let us understand this by a simple example using Blogpost and Comment schema design
const Blogpost = new Schema({
title: { type: String, required: true },
body: { type: String, required: true },
userId: { type: Schema.Types.ObjectId, ref: 'User', required: true },
commentId: [{ type: Schema.Types.ObjectId, ref: 'Comment' }],
lastModifiedAt: { type: Date, default: Date.now },
createdAt: { type: Date, default: Date.now },
})
const Comment = new Schema({
postId: { type: Schema.Types.ObjectId, ref: 'Post', required: true },
userId: { type: Schema.Types.ObjectId, ref: 'User', required: true },
text: { type: String, required: true },
lastModified: { type: Date, default: Date.now },
createdAt: { type: Date, default: Date.now }
})
Now, if we have to get the totalComments for every blog post then we have to create a VirtualType in Blogpost schema design
Blogpost.virtual('totalComments', {
ref: 'Comment', // model to use for matching
localField: '_id', // from `localField` i.e., Blogpost
foreignField: 'postId', // is equal to `foreignField` of Comment schema
count: true //only get the number of docs
});
Blogpost.set('toObject', { virtuals: true });
Blogpost.set('toJSON', { virtuals: true });
As we have postId in Comment schema that holds the Blogpost._id which is associated with that particular Blogpost then only we are able to match and get the totalComments for every Blogpost
Now using population we can get the totalComments for every Blogpost
let blogpost = await Blogpost.model
.find({})
.populate({ path: 'totalComments', count: true })
Upvotes: 0
Reputation: 7621
It seems like you may have multiple username docs in your design, e.g.
{ username: "buzz", projects: [ list of 2 things ] }
{ username: "dan". projects: [ list of 3 things ] }
{ username: "buzz", projects: [ list of 4 things ] }
To arrive at the "grant total" of projects for each username
try this:
c = db.foo.aggregate([
{$project: {
"username": 1,
"n": {$size: "$projects"}
}
}
,
{$group: {
"_id": "$username",
"alln": {$sum: "$n"}
}
}
]);
to yield
{ "_id" : "buzz", "alln" : 6 }
{ "_id" : "dan", "alln" : 3 }
Upvotes: 1
Reputation: 7464
Per @n9code, you will need the Aggregation Framework. However, you can very easily count the sub-documents with $size
:
db.users.aggregate([{
$project: {
_id: '$_id',
totalProjects: { $size: "$projects" }
}
}]);
Which should return something like this:
{ "_id" : ObjectID(...), "totalProjects" : 89 }, ...
$size will return the length of the projects
array for each document and $project changes the format of the documents to include totalProjects
as the size of the projects
array.
Upvotes: 13
Reputation: 7458
You need MongoDB's Aggregation Framework
here:
db.users.aggregate({$unwind: '$projects'}).count()
This will unwind the projects
array field in each document of your collection, which will be the total number of projects
subdocuments.
Upvotes: -1