Swailem95 Swailem95 - 6 months ago 21
Javascript Question

How can I query a nested document and return attributes from it?

I have a Product collection and every document looks something like this:

"_id" : ObjectId("574393c59afcfdd3763d91b1"),
"name" : "dummy product name",
"price" : 200,

"category" : ObjectId("574393c59afcfdd3763d91ad"),
"images" : [ ],
"ratings" : [
2
],
"reviews" : [ ],
"purchase_count" : 0,
"tags" : [
ObjectId("574393c59afcfdd3763d91a9"),
ObjectId("574393c59afcfdd3763d91ab")
]


Where category and tags are references to their respective collections and they each have a name attribute. Here are all 3 schemas:

var ProductSchema = new mongoose.Schema({
name : {type: String, required: true},
price : { type: Number,required: true },
tags : [{ type: Schema.Types.ObjectId, ref: 'Tag' }],
purchase_count : {type: Number, default: 0},
reviews : [ReviewSchema],
ratings : [{type: Number, min: 0, max: 10}],
category : {type: Schema.Types.ObjectId, ref: 'ProductCategory'},
images : [{type: String}]
});

var ProductCategorySchema = new mongoose.Schema({
name : { type: String, required: true, unique: true, dropDups: true }});

var TagSchema = new mongoose.Schema({
name : { type: String, required: true, unique: true, dropDups: true, lowercase: true }
});


I'm trying to query the product collection and return data of each product like this:

app.models.Product.find({}, function(err, products){
if (err) throw err;
res.json(products);
}).populate('category').populate('tags').exec();


The only the difference is that I want the actual name of the category instead of a document representing the collection with it's ID. I also want the same for the tags , I just want a simple array of strings which represent the tag names. How can I do this in the query?

I tried implicitly defining what I wanted to select by using a select() after the second populate() and writing 'category.name tags.name (and then all the other attributes)' but that didn't work.

This is what the result of the query looks like:

enter image description here

Answer

To select specific field you would

app.models.Product.find({}, function(err, products){
  if (err) throw err;
  res.json(products);
}).populate('category', 'fieldname1 fieldname2').populate('tags', 'fieldname1 fieldname2').exec();

http://mongoosejs.com/docs/populate.html

However i dont see in your schema definition any reference to anything.

Also if you have / need any kind of relation (which is what you seem to need) dont use mongo.

Actually dont use mongo ever unless you absolutely must - try to stay a way from mongo.

update

Something like that.

 var res = app.models.Product.find({}, function(err, products){
  if (err) throw err;
    res.json(products);
 }).populate('category', 'fieldname1 fieldname2').populate('tags', 'fieldname1 fieldname2').exec();

res.category = categoty.fieldname;
res.tags = res.tags.map(function(tag){

  return tag.fieldname
})

Another option might be - to add getter - not sure if this will work because populate might happened after so ..

but :

ProductSchema.virtual('categoryString').get(function () {
  return this.category.fieldname
});

ProductSchema.virtual('tagsArr').get(function () {
  return this.tags.map(function(tag){
     if(tag && tag.name) {
         return tag.fieldname;
     }
     return '';
  });
});
Comments