Can I use padding in front of an aggregate in mongoose?

I have two models, one of them is the user

userSchema = new Schema({ userID: String, age: Number }); 

and the other is a score recorded several times each day for all users.

  ScoreSchema = new Schema({ userID: {type: String, ref: 'User'}, score: Number, created_date = Date, .... }) 

I would like to do some query / calculation on the account for some users that meet specific requirements, say, I would like to calculate the average score for all users more than 20 days a day.

My thought is that first do fill in Scores to fill in the age of users and then do aggregate .

Sort of

 Score. populate('userID','age'). aggregate([ {$match: {'userID.age': {$gt: 20}}}, {$group: ...}, {$group: ...} ], function(err, data){}); 

Can I use padding in front of an aggregate? Or do I first find all userIDs that meet the requirements and store them in an array, and then use $ in to match the evaluation document?

+5
source share
1 answer

No, you cannot call .populate() before .aggregate() , and there is a very good reason why you cannot. But there are different approaches that you can take.

The .populate() method works on the "client side", where the base code actually performs additional requests (or, more precisely, the $in request) to "search" for the specified element (s) from the reference collection.

Unlike .aggregate() , the server-side operation is used, so you can’t basically manipulate the client-side content and then provide this data for subsequent stages of the aggregation pipeline. All this should be present in the collection in which you work.

A more convenient approach can be obtained with MongoDB 3.2 and later using the $lookup operation. It is also probably best to handle from the User collection in this case to narrow down the selection:

 User.aggregate( [ // Filter first { "$match": { "age": { "$gt": 20 } }}, // Then join { "$lookup": { "from": "scores", "localField": "userID", "foriegnField": "userID", "as": "score" }}, // More stages ], function(err,results) { } ) 

Basically, this will include the new “rating” field in the User object as an “array” of elements that match the “search” for another collection:

 { "userID": "abc", "age": 21, "score": [{ "userID": "abc", "score": 42, // other fields }] } 

The result is always an array, since the total expected use is the “left join” of a possible one-to-many relationship. If the result is not consistent, it is simply an empty array.

To use content, just work with the array. For example, you can use the $arrayElemAt operator to simply get the only first element of an array in any future operations. And then you can just use the content like any normal inline field:

  { "$project": { "userID": 1, "age": 1, "score": { "$arrayElemAt": [ "$score", 0 ] } }} 

If you do not have MongoDB 3.2 available, then your other request processing option, limited by the relations of another collection, should first get the results from this collection and then use $in to filter by the second:

 // Match the user collection User.find({ "age": { "$gt": 20 } },function(err,users) { // Get id list userList = users.map(function(user) { return user.userID; }); Score.aggregate( [ // use the id list to select items { "$match": { "userId": { "$in": userList } }}, // more stages ], function(err,results) { } ); }); 

Thus, having received a list of valid users from another collection for the client, and then loading them into another collection in the request, this may be the same as in previous releases.

+8
source

Source: https://habr.com/ru/post/1241948/


All Articles