Search code examples
node.jsmongodbmongoosemongoose-schemamongoose-populate

Mongoose: how to do find with populate


I would like to know how I can make a query to the database bringing all the results that meet the 'store' {name} field, but this 'store' is saved with '_id' and I use .populate() to bring me the info

/**example: await product.find({store:{name:"specific store"}})*/

const products = await Product.find({ 'store.name' : "Ejemplo1" })
                .populate('store', 'name')

the schemes are as follows:

const storeSchema = mongoose.Schema({
   _id:"it is added automatically by mongoose",
   name: String
})
export default model('Store', storeSchema);

const productSchema = mongoose.Schema({
   store: {
     type: Schema.Types.ObjectId,
     ref: "Store"
   }
})

Basically what I want to achieve is to extract from the DB all the products with a specific 'store', but I still can't do it, I appreciate any help to solve this, whether it is a reference or an example of a suitable query, thanks in advance

i tried this:

const p = await Product.find({})
  .populate({
    path: 'store',
    match: { name: { $eq: 'Store1' } },
    select: 'name -_id',
  })

but it returns the entire collection and does not do the filtering, I receive something like this:

[{
  _id:1,
  ...
  store:null,
  ...
}, {
  _id:2,
  ...
  store:{name:"Store1"},
  ...
}, {
  _id:3,
  ...
  store:{name:"Store2"},
  ...
}]

Solution

  • As mentioned by @M.CaglarTUFAN you cannot filter the parent document based on the child document with populate. Refactoring your schemas is a good option.

    However, if you are unable to do so you can achieve your desired result with the mongodb aggregate framework using $lookup and $match like so:

    const products = await Product.aggregate([
       { 
          $lookup: { //< Lookup is like a Left Join in SQL
             from: "stores", 
             localField: "store", 
             foreignField: "_id", 
             as: "store_details" //< You are adding this field to the output
          } 
       }, 
       { 
          $match: { //< Now from all the results only give me back the store Ejemplo1
             'store_details.name': 'Ejemplo1' 
          } 
       }
    ]).exec();
    

    If your Product collection is large (millions of documents) then bear in mind this will perform the $lookup on all documents so could be inefficient. But without refactoring your schemas this the best single call to the database.

    Your second option involves 2 database queries.

    1. Get the _id of the store you want from the stores collection.
    2. Then use that result to query the products collection and populate matching records.