According to 3.6 release notes I should be able to populate the categories in my order.items.product docs but no go. Here's the schemas:
var Order = new Schema({
items: [{
product: {
type: Schema.Types.ObjectId,
ref: "Product",
}
...
}]
});
var Product = new Schema({
categories: [{
type: Schema.Types.ObjectId,
ref: "Category",
}]
});
My Query (one of many - I 've tried a few combos - this seems to be what is suggested)
Order.findById(id).populate('items.product').exec(function(err, doc) {
var opts = {
path: 'items.product.categories'
};
console.log(doc.items[0].product.categories) // [ 524f035de9d6178e460001a2, 524f0965e9d6178e460001b6 ] - these docs are in the database under the Category collection
Order.populate(doc, opts, function(err, doc) {
// Returns order with category array blank for each product
console.log(doc.items[0].product.categories // []
});
});
It looks like you accidentally typed Order again in your sub-population call.
The second populate call should be something like:
Category.populate(doc, opts, function(err, doc) {...
instead of
Order.populate(doc, opts, function(err, doc) {...
You're telling Mongoose to populate each Product's Categories array element as an Order instead of as a Category.
Related
I have these two 3 models User, Product and Orders and are also has references to each other.
My Orders Schema:
const orderSchema = Schema({
buyerId:{
type: Schema.Types.ObjectId,
ref: 'User'
},
totalAmount: {
type: Number,
required: [true, "description is required"]
},
createdOn: {
type: Date,
default: new Date()
},
items:[{type:Schema.Types.ObjectId, ref: 'Product'}]})
I'm trying to use populate() like this:
Order.find()
.populate('buyerId')//Reference to User Model
.populate('items')// Reference to Product Model
.exec(function (err, result){
console.log(result);// RETURNS ONLY buyerId populated
console.log(result.buyerId.name);//Successfully references to my User model and prints name
console.log(result.items);//Prints Undefined
})
You can see my console log above and what it returns is only the populated buyerId(only got the reference from my User model)
Seems like my populate('items') doesnt work at all. The items field contains array of IDs, where the IDs are those of products. I need to reference to User and Product both. I'm just following the documentation in mongoose, I don't know what I might be doing wrong.
use aggregate
Order.aggregate([
{ $match:{ user:"sample_id"
}
},
{$lookup:{
from:'users', // users collection name
localField:'buyerId',
foreignField:'_id',
as:'buyerId'
}},
{
$lookup:{
from:'items', //items collection name
localField:'items',
foreignField:'_id',
as:'items'
}
},
])
I'm trying to create a one to many relationship between Category and Services models.
I have two schemas: Category and Service.
Category schema holds services array.
Service schema hold category _id.
I'm trying to retrieve all Categories including services.
Category schema:
import mongoose from 'mongoose';
const categoriesModel = new mongoose.Schema(
{
category: {
type: String,
required: true,
},
services: [{ type: mongoose.SchemaTypes.ObjectId, ref: 'Service' }],
},
{ timestamps: true }
);
export const Category = mongoose.model('Category', categoriesModel);
Service schema:
import mongoose from 'mongoose';
const serviceSchema = new mongoose.Schema(
{
serviceName: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
images: {
type: Array,
image: Buffer,
required: true,
},
category: {
type: mongoose.SchemaTypes.ObjectId,
ref: 'Category',
required: true,
},
},
{ timestamps: true }
);
export const Service = mongoose.model('Service', serviceSchema);
HTTP get request to retrieve all Categories including Services:
export const getAllCategories = async (req, res) => {
try {
const docs = await Category.find({}).populate({
path: 'services',
model: 'Service',
});
console.log(docs);
res.status(200).json({ data: docs });
} catch (err) {
res.status(400).json(err.message);
}
};
This is what I get as a response from the request above:
{
"services": [],
"_id": "60affe06c71901281d3d820d",
"category": "Electricity"
},
I've tried different ways to populate services but none of them worked.
What I've tried so far:
Add additional nesting to services attribute.
Adding object to the populate params:
{
path: 'services',
model: 'Service',
}
Removing existing data ant posting additional one.
Fun fact. Retrieving each Service individually, service data includes the actual category, that it's been assigned to:
export const getAllServices = async (req, res) => {
try {
const doc = await Service.find({}).populate('category');
res.status(200).json({ data: doc });
} catch (err) {
res.status(400).json(err);
}
};
Is there something I'm missing regarding including services within retrieving categories? It's really interesting, because somehow the actual services data is [].
EDIT: Each category in Categories collection has empty services array. The services are added dynamically, they might be null for specific category until user adds a service and assigns a category to it. Is this forbidden? Do I need to find specific category and add a specific service to category while creating new service? If yes, this would require to add additional Category model to the services.controller.js and find the category. Is this really an appropriate solution?
I have schema type Map in my mongoose model. In this map, each element has reference to another model. I know that it's possible to populate attributes in array, but how about Map type? Be cause nesting like "map_type_attribute.some_attribute_to_populate" doesn't work. :)
This is my model:
const Mongoose = require('mongoose');
const parameter = Mongoose.Schema({
definition: {
type: Mongoose.Schema.Types.ObjectId,
ref: 'Definition',
},
value: {},
}, {_id: false});
const schema = Mongoose.Schema({
model: {
type: Mongoose.Schema.Types.ObjectId,
ref: 'Model'
},
name: String,
objectid: Number,
externalId: String,
properties: Mongoose.Schema.Types.Mixed,
parameters: {
type: Map,
of: parameter
}
});
module.exports = Mongoose.model('Element', schema);
This is how i'm trying to populate definition field:
const request = Element.find(query, projection);
request.populate('parameters.definition');
request.exec( (err, docs) => {
...
This functionality was added in Mongoose 5.10.3 (September 2020). You simply denote every element in the map with a $*.
In your example this would be:
const request = Element.find(query, projection);
request.populate('parameters.$*.definition');
request.exec( (err, docs) => {
I also trying to find answer on this question. It seems that deep-populate work, but only if you put keys from the Map to populate method/function. In your case, if you have data like:
{
model: ObjectId("111"),
name: "MyName",
objectid: 111,
externalId: "ExternalId",
properties: ...,
parameters:{
"parameter1":{
"definition":ObjectId("333"),
"value":"value of parameter 1"
},
"parameter2":{
"definition": ObjectId("444"),
"value": "value of parameter 2"
}
}
}
Then you may find and populate like this:
Element.find({}).populate("parameters.parameter1.definiton")
But it's not a good solution. It would be nice if we have something like regexp inside this populate path.
Currently I've managed only to grab all inner collection, and then manually work with Map to substitude collections.
It shouldn't be a huge overhead, since you have only 2 queries to DB. In you case it can be like:
const elements = Element.find({});
const parameters = Parameter.find({});
// go through the elements.parameters and replace it with appropriate value from parameters collection.
I have a very simple mongo scheme I'm accessing with mongoose
I can map the username and firstname to each notification's from field by using populate, the issue is I can't seem to get any sorting to work on the date field
With this code I get an error of
MongooseError: Cannot populate with sort on path notifications.from
because it is a subproperty of a document array
Is it possible to do this a different way, or newer way (deep populate, virtuals)? I'm on Mongoose 5.
I'd rather not use vanilla javascript to sort the object afterwards or create a separate schema
var UserSchema = new Schema({
username: String,
firstname: String,
notifications: [
{
from: { type: Schema.Types.ObjectId, ref: 'User'},
date: Date,
desc: String
}
]
});
app.get('/notifications', function(req, res) {
User.findOne({ _id: req._id }, 'notifications')
.populate({
path: 'notifications.from',
populate: {
path: 'from',
model: 'User',
options: { sort: { 'notifications.date': -1 } }
}
})
.exec(function(err, user) {
if (err) console.log(err)
})
});
That possible duplicate is almost 2 years old about Mongo. I'm asking if there are newer or different ways of doing this in Mongoose as it has changed a bit since 2016 with newer features.
From Mongoose V5.0.12 FAQ : http://mongoosejs.com/docs/faq.html#populate_sort_order
Q. I'm populating a nested property under an array like the below
code:
new Schema({
arr: [{
child: { ref: 'OtherModel', type: Schema.Types.ObjectId }
}] });
.populate({ path: 'arr.child', options: { sort: 'name' } }) won't sort by arr.child.name?
A. See this GitHub issue. It's a known issue but one that's
exceptionally difficult to fix.
So unfortunately, for now, it's not possible,
One way to achieve this is to simply use javascript's native sort to sort the notifications after fetching.
.exec(function(err, user) {
if (err) console.log(err)
user.notifications.sort(function(a, b){
return new Date(b.date) - new Date(a.date);
});
})
It can be achievable using nesting populate like this -
eg - schema - {donationHistory: {campaignRequestId: [ref ids]}}
await user.populate({
path: 'donationHistory.campaignRequestId',
populate: [{
path: 'campaignRequestId',
model: 'CampaignRequest',
options: { sort: { 'createdAt': -1 } },
}],
...deepUserPopulation,
}).execPopulate();
Im coding an app in Node.js which is using MongoDB. I chose MongooseJS to handle my DB queries.
I have two collections that are referenced to each other (Room which is the 'superior' collection and DeviceGroups which is contained within Room collection).
I have a query that gets a list of all of the rooms from Room collection, populates deviceGroups field (which is the Rooms reference to DeviceGroup collection) and inside it there is a map method that goes through every room found in the Room collection and for every room it makes another query - it looks for any deviceGroups in DeviceGroup collection that are referenced to the current room in the map method.
My goal here is to return a list of all of the rooms with deviceGroups field filled in with actual data, not only references.
What I am getting after the queries (inside the then method) is a Mongoose document. The whole algorithm is used as a handler of a GET method, so I need a pure JavaScript object as a response.
Main goal I want to achieve is to get result of all of the queries and population inside them as pure javascript object, so I can create a response object and send it (i dont want to send everything that db returns, because not all of the data is needed for this case)
EDIT:
I am so sorry, I have deleted my code and didnt realize it.
My current code is below:
Schema:
const roomSchema = Schema({
name: {
type: String,
required: [true, 'Room name not provided']
},
deviceGroups: [{
type: Schema.Types.ObjectId,
ref: 'DeviceGroup'
}]
}, { collection: 'rooms' });
const deviceGroupSchema = Schema({
parentRoomId: {
type: Schema.Types.ObjectId,
ref: 'Room'
},
groupType: {
type: String,
enum: ['LIGHTS', 'BLINDS', 'ALARM_SENSORS', 'WEATHER_SENSORS']
},
devices: [
{
type: Schema.Types.ObjectId,
ref: 'LightBulb'
}
]
}, { collection: 'deviceGroups' });
Queries:
app.get('/api/id/rooms', function(req, res) {
Room.find({}).populate('deviceGroups').lean().exec(function(err, parentRoom) {
parentRoom.map(function(currentRoom) {
DeviceGroup.findOne({ parentRoomId: currentRoom._id }, function (err, devices) {
return devices;
});
});
}).then(function(roomList) {
res.send(roomList);
});
});
where are you confusing. here is a simple and effective code snippet
Room.findById(req.params.id)
.select("roomname")
.populate({
path: 'deviceGroup',
select: 'devicename',
model:'DeviceGroups'
populate:{
path: 'device',
select: 'devicename',
model:'Device'
}
})
.lean()
.exec((err, data)=>{
console.log(data);
})