Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moongoose aggregate $match does not match id's

I want to show products by ids (56e641d4864e5b780bb992c6 and 56e65504a323ee0812e511f2) and show price after subtracted by discount if available.

I can count the final price using aggregate, but this return all document in a collection, how to make it return only the matches ids

"_id" : ObjectId("56e641d4864e5b780bb992c6"),  "title" : "Keyboard",  "discount" : NumberInt(10), "price" : NumberInt(1000)  "_id" : ObjectId("56e65504a323ee0812e511f2"),  "title" : "Mouse",  "discount" : NumberInt(0), "price" : NumberInt(1000)  "_id" : ObjectId("56d90714a48d2eb40cc601a5"),  "title" : "Speaker",  "discount" : NumberInt(10), "price" : NumberInt(1000) 

this is my query

productModel.aggregate([         {             $project: {                 title   : 1,                 price: {                     $cond: {                         if: {$gt: ["$discount", 0]}, then: {$subtract: ["$price", {$divide: [{$multiply: ["$price", "$discount"]}, 100]}]}, else: "$price"                     }                  }             }         }     ], function(err, docs){         if (err){             console.log(err)         }else{             console.log(docs)         }     }) 

and if i add this $in query, it returns empty array

productModel.aggregate([             {                 $match: {_id: {$in: ids}}             },             {                 $project: {                     title   : 1,                     price: {                         $cond: {                             if: {$gt: ["$discount", 0]}, then: {$subtract: ["$price", {$divide: [{$multiply: ["$price", "$discount"]}, 100]}]}, else: "$price"                     }                  }             }         }     ], function(err, docs){         if (err){             console.log(err)         }else{             console.log(docs)         }     }) 
like image 204
Muhammad Fasalir Rahman Avatar asked Mar 24 '16 04:03

Muhammad Fasalir Rahman


People also ask

What does $match do in MongoDB?

The MongoDB $match operator filters the documents to pass only those documents that match the specified condition(s) to the next pipeline stage.

What can the $match aggregation stage be used for?

The $match stage of the pipeline can be used to filter documents so that only ones meeting certain criteria move on to the next stage. In this article, we'll discuss the $match stage in more detail and provide examples that illustrate how to perform match aggregation in MongoDB.

What does Mongoose aggregate return?

Returns: A cursor to the documents produced by the final stage of the aggregation pipeline operation, or if you include the explain option, the document that provides details on the processing of the aggregation operation. If the pipeline includes the $out operator, aggregate() returns an empty cursor.


1 Answers

Your ids variable will be constructed of "strings", and not ObjectId values.

Mongoose "autocasts" string values for ObjectId into their correct type in regular queries, but this does not happen in the aggregation pipeline, as in described in issue #1399.

Instead you must do the correct casting to type manually:

ids = ids.map(function(el) { return mongoose.Types.ObjectId(el) }) 

Then you can use them in your pipeline stage:

{ "$match": { "_id": { "$in": ids } } } 

The reason is because aggregation pipelines "typically" alter the document structure, and therefore mongoose makes no presumption that the "schema" applies to the document in any given pipeline stage.

It is arguable that the "first" pipeline stage when it is a $match stage should do this, since indeed the document is not altered. But right now this is not how it happens.

Any values that may possibly be "strings" or at least not the correct BSON type need to be manually cast in order to match.

like image 143
Blakes Seven Avatar answered Sep 28 '22 17:09

Blakes Seven