Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoose: how to use aggregate and find together

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?

like image 358
Colin Wang Avatar asked Feb 22 '17 14:02

Colin Wang


People also ask

Can you use Find and aggregate in MongoDB?

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.

Can we use aggregate with mongoose?

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.

Is aggregate faster than find?

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.


1 Answers

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); }) 
like image 101
chridam Avatar answered Oct 04 '22 23:10

chridam