How can I use aggregate and find
together in Mongoose?
i.e I have the following schema:
const schema = new Mongoose.Schema({ created: { type: Date, default: Date.now() }, name: { type: String, default: 'development' } followers: [{ type: Mongoose.Schema.ObjectId, ref: 'Users'}] ... }) export default Mongoose.model('Locations', schema)
How can I query the users with only the fields name
and followers_count
.followers_count
: the length of followers
.
There, I know we can use select to get only the field name
.
How can we get the count of followers
?
To create an aggregation pipeline, you can use can use MongoDB's aggregate() method. This method uses a syntax that is fairly similar to the find() method used to query data in a collection, but aggregate() accepts one or more stage names as arguments. This step focuses on how to use the $match aggregation stage.
Mongoose comes with aggregate() function by which you can use MongoDB's aggregation framework with Mongoose. Basically, it is a thin wrapper and you can work any aggregation query in mongoose that works in mongoDB shell without making any changes.
Without seeing your data and your query it is difficult to answer why aggregate+sort is faster than find+sort. A well indexed(Indexing that suits your query) data will always yield faster results on your find query.
For MongoDB 3.6 and greater, use the $expr
operator which allows the use of aggregation expressions within the query language:
var followers_count = 30; db.locations.find({ "$expr": { "$and": [ { "$eq": ["$name", "development"] }, { "$gte": [{ "$size": "$followers" }, followers_count ]} ] } });
For non-compatible versions, you can use both the $match
and $redact
pipelines to query your collection. For example, if you want to query the locations
collection where the name is 'development' and followers_count
is greater than 30, run the following aggregate operation:
const followers_count = 30; Locations.aggregate([ { "$match": { "name": "development" } }, { "$redact": { "$cond": [ { "$gte": [ { "$size": "$followers" }, followers_count ] }, "$$KEEP", "$$PRUNE" ] } } ]).exec((err, locations) => { if (err) throw err; console.log(locations); })
or within a single pipeline as
Locations.aggregate([ { "$redact": { "$cond": [ { "$and": [ { "$eq": ["$name", "development"] }, { "$gte": [ { "$size": "$followers" }, followers_count ] } ] }, "$$KEEP", "$$PRUNE" ] } } ]).exec((err, locations) => { if (err) throw err; console.log(locations); })
The above will return the locations with just the _id
references from the users. To return the users documents as means to "populate" the followers array, you can then append the $lookup
pipeline.
If the underlying Mongo server version is 3.4 and newer, you can run the pipeline as
let followers_count = 30; Locations.aggregate([ { "$match": { "name": "development" } }, { "$redact": { "$cond": [ { "$gte": [ { "$size": "$followers" }, followers_count ] }, "$$KEEP", "$$PRUNE" ] } }, { "$lookup": { "from": "users", "localField": "followers", "foreignField": "_id", "as": "followers" } } ]).exec((err, locations) => { if (err) throw err; console.log(locations); })
else you would need to $unwind
the followers array before applying $lookup
and then regroup with $group
pipeline after that:
let followers_count = 30; Locations.aggregate([ { "$match": { "name": "development" } }, { "$redact": { "$cond": [ { "$gte": [ { "$size": "$followers" }, followers_count ] }, "$$KEEP", "$$PRUNE" ] } }, { "$unwind": "$followers" }, { "$lookup": { "from": "users", "localField": "followers", "foreignField": "_id", "as": "follower" } }, { "$unwind": "$follower" }, { "$group": { "_id": "$_id", "created": { "$first": "$created" }, "name": { "$first": "$name" }, "followers": { "$push": "$follower" } } } ]).exec((err, locations) => { if (err) throw err; console.log(locations); })
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With