Mongoose populate many objects using 'path array' {path: objectpath,model:'Model'} - node.js

In my webapp, after ordering (SingleOrder) for products, the customer should check for offers. if available, then I should add the order to the ComboOfferOrder.
There, I want to check for the order's payment status. Also, I have to get the entire products list.
I have all the values in my db in backend. But I am not able to populate any of the objects in 'SingleOrder' for my api method.
I have the below schemas.
*User*
{
name : String,
email : Sring,
role : String
}
*BankTransaction*
{
type : String,
transationReference: String,
date : Date,
amount : Number
}
*ComboOfferOrder*
{
customer :{
type :Schema.ObjectId,
ref : 'User'
},
order : {
type :Schema.ObjectId,
ref : 'SingleOrder'
},
productList : [{
type :Schema.ObjectId,
ref : 'Product'
}]
discount : Number,
totalCost : Number,
paymentStatus :String,
deliveryStatus : String
}
*SingleOrder*
{
code: String,
products : {
groceries:[{
type :Schema.ObjectId,
ref : 'Product'
}],
other:[{
type :Schema.ObjectId,
ref : 'Product'
}]
},
billingAddress: String,
deliveryAddress : String,
payment:{
status : String,
transaction :{
type :Schema.ObjectId,
ref : 'BankTransaction'
}
}
}
*Products*
{
name : String,
cost : Number,
expiryDate : Date
}
My api
mongoose.model('ComboOfferOrder')
.findOne({
_id: comboOfferOrderId
})
.select('order')
.exec(function(err, comboOfferOrder) {
var paths = [
{path : "payment.status"},
{path : "payment.trasaction"},
{path : "products.groceries"},
{path : "products.other"}
];
mongoose.model('comboOfferOrder').populate(comboOfferOrder.order,paths,function(err, singleOrder) {
if (err) {
return deferred.reject(err);
}
return deferred.resolve(comboOfferOrder.order);
});
});
In the result, I get only the objectIds of "payment.status","payment.trasaction",products.groceries", "products.other"
Please let me know the solution.Thanks.

You can't populate a nested field with mongoose, and therefore you must nest your callbacks.
You may reread the documentation of populate for usage examples.
This should work (not tested):
mongoose.model('ComboOfferOrder')
.findOne({
_id: comboOfferOrderId
})
.select('order')
.exec(function(err, comboOfferOrder) {
comboOfferOrder.populate('order', function(err, singleOrder) {
singleOrder.populate('products.other products.groceries etc...', function(err, singleOrder) {
if (err) {
return deferred.reject(err);
}
return deferred.resolve(singleOrder);
});
});
});

Populate lets you get a list of a user's friends, but what if you also wanted a user's friends of friends? Specify the populate option to tell mongoose to populate the friends array of all the user's friends:
User.
findOne({ name: 'Val' }).
populate({
path: 'friends',
// Get friends of friends - populate the 'friends' array for every friend
populate: { path: 'friends' }
});

Related

mongoose find by ObjectId

I'm defining a mongoose schema like this
var accountPostSchema = new mongoose.Schema({
account: {
id: { type: mongoose.Schema.Types.ObjectId, ref: 'Account' }
},
post: {
id: { type: mongoose.Schema.Types.ObjectId, ref: 'Post' }
}
});
app.db.model('AccountPost', accountPostSchema);
When a user(account holder) create a post, I save the post in a Post schema and get the 'postId'. Then I save the 'postId' and the 'accountId'
in the above accountPostSchema like this
var fieldsToSet = {
post: {
id: postId
},
account: {
id: accountId
}
};
db.models.AccountPost.create(fieldsToSet, function(err, accountPost) {
if (err) {
// handle error
}
// handle success
});
After entering few postId's and accountId's, I see the following results in the mongo shell
> db.accountposts.find({})
{ "_id" : ObjectId("5835096d63efc04da96eb71e"), "post" : { "id" : ObjectId("5835096d63efc04da96eb71d") }, "account" : { "id" : ObjectId("5833c920c868d7264111da69") }, "__v" : 0 }
{ "_id" : ObjectId("583509e12052c7a2a93c4027"), "post" : { "id" : ObjectId("583509e12052c7a2a93c4026") }, "account" : { "id" : ObjectId("5833c920c868d7264111da69") }, "__v" : 0 }
Now how do I find all the matching 'Posts' given an accountId? (not the postId's)
For example if I the accountId is 583509e12052c7a2a93c4026, I need to find Posts with Post._id=5835096d63efc04da96eb71d and Post._id=583509e12052c7a2a93c4026
What is the query I should run to get the matching Posts?
I think, you should follow this way to get all the posts associated with particular accountid.
db.accountposts.find({'account.id' : accountId})
.populate('post.id')
.exec();
First, I would suggest changing your Schema to the following
var accountPostSchema = new mongoose.Schema({
account: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Account'
},
post: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}
});
This actually makes more sense, especially when you try to populate the subdocuments. Actually, I would say this Schema is useless. Why don't you define your Post schema like the following?
var PostSchema = new mongoose.Schema({
poster: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Account'
},
message: String
});
If you use the latter code, you could execute the following query to get all posts by a particular user:
db.posts.find({poster: accountId}, function(dbErr, userPosts) {
if(dbErr) {
// Handle the error.
}
// Do something with the posts by accountId in the array userPosts.
});
The advantages of removing the id field from poster becomes clear once you try to populate poster. If you defined poster as an object with the field id and try to populate it, you will need to access data about the poster as such:
posterName = retrievedPost.poster.id.name;
Alternatively, by just making the poster field an ObjectId directly, you can access the populated user more directly:
posterName = retrievedPost.poster.name;

Get data from first collection and check if exist on second

Have problem:
userMoments:
userId: {
type: String,
required: true
},
momentId : {
type : String,
required : true
},
pay : {
type : Number
},
momentActive : {
type : Boolean,
default : false
}
and moments :
name: {
type: String,
required: true
},
img : {
type : String
}
I need get all moments from moments collection that not isset in collection userMoments with some userId
To achieve that I would propose the following example:
Algorithm
(1) Get all momentId that are stored into userMoments in relation with some userId
(2) Get all moments that are unrelated with momentId we got
Code
// Get all momentId
UserMoments.find({
$in: arrayThatContainsUserIds,
}, 'momentId')
// Make every momentId unique
.distinct('momentId')
.exec()
// get all moments that do not refer to ids we got before
.then((ret) => find({
$nin: Array.from(ret, x => x.momentId),
}))
.then((ret) => {
// Here you have your moments
})
.catch(err => {});
Documentations
$nin mongoDB documentation
distinct mongoose documentation
Array.from documentation
Promise documentation X.then(...).catch(...)

mongodb mongoose nodejs express4 why insert a _id in Object Array field automatically?

It might be conceptual question about _id in mongodb.
I understand mongodb will insert a _id field automatically if you don't set key field in document.In my case, I defined a field as Object Array, I don't know why it always create a _id in each Object in Array of this field.
I do appreciate if someone could clarify it for me.
Mongoose Model Scheme definition:
module.exports = mongoose.model("Application", {
Name: String,
Description: String,
Dependency: [
{
App_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Application'
},
Priority: Number
}
]
});
This is an Update operation, request data is:
{ _id: '571953e33f33c919d03381b5',
Name: 'A Test Utility (cmd)',
Description: 'A Test Utility (cmd)'
Dependency:
[ { App_id: '571953e33f33c919d03381b6', Priority: true },
{ App_id: '571953e33f33c919d03383da', Priority: 0 } ]
}
I use this code to update it
var id = req.body._id;
Application.findOneAndUpdate({ _id: id }, req.body, function (err, app) {
if (err)
res.send(err);
res.json(app);
});
The update is successful.But the document in mongodb is:
{
"_id" : ObjectId("571953e33f33c919d03381b5"),
"Name" : "A Test Utility (cmd)",
"Description" : "A Test Utility (cmd)",
"Dependency" : [
{
"Priority" : 1,
"App_id" : ObjectId("571953e33f33c919d03381b6"),
"_id" : ObjectId("571a7f552985372426509acb")
},
{
"Priority" : 0,
"App_id" : ObjectId("571953e33f33c919d03383da"),
"_id" : ObjectId("571a7f552985372426509aca")
}
]
}
I just don't understand how come the _id in the "Dependency" Array?
Thanks.
When you use [{..}] that means inside it act as a sub schema and you know that MongoDB insert a _id field automatically if you don't set key field in document. So you need to force to insert document without _id field.
Need use {_id:false} for your Dependency array schema to insert without _id
var ApplicationSchema = new mongoose.Schema({
Name: String,
Description: String,
Dependency: [
{
App_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Application'
},
Priority: Number,
_id: false
}
]
});
module.exports = mongoose.model("Application", ApplicationSchema);

How to find a record using dot notation & update the value in a schema using mongoose

I am using mongoose to perform CRUD operation on my db. This is how my model looks.
var EmployeeSchema = new Schema({
name: String,
description: {
type: String,
default: 'No description'
},
department: [],
lastUpdated: {
type: Date,
default: Date.now
}
});
The department can contains array of object like this.
[
{
"id" : "55ba28f680dec4383eeebf97",
"text" : "Sales",
"topParentId" : "55ba28f680dec4383eeebf8b",
"topParentText" : "XYZ"
},
{
"id" : "55ba28f680dec4383eeebf98",
"text" : "IT",
"topParentId" : "55ba28f680dec4383eeebf8b",
"topParentText" : "XYZ"
},
{
"id" : "55ba28f680dec4383eeebf94",
"text" : "Marketing",
"topParentId" : "55ba28f680dec4383eeebccc",
"topParentText" : "ABC"
}
]
Now I need to find all the employee where department.id = '55ba28f680dec4383eeebf94' and then I need to update the text of the object.
Employee.find({'department.id': '55ba28f680dec4383eeebf94'}, function(err, Employees) {
_.each(Employees, function (emp) {
_.each(emp.department, function (dept) {
if(dept.id === '55ba28f680dec4383eeebf94'){
dept.text = 'XXXXX'; // How to update the employee to save the updated text
}
});
});
});
What is the right way to save the employee with updated text for that department?
Iterating is code is not a "sharp" way to do this. It is better to use the MongoDB update operators, especially since there is no schema defined for the array items here, so no rules to worry about:
Employee.update(
{'department.id': '55ba28f680dec4383eeebf94'},
{ "$set": { "department.$.text": "XXXXX" },
function(err,numAffected) {
// handling in here
}
);
The $set is the important part, otherwise you overwrite the whole object. As is the positional $ operator in the statement, so only the matched ( queried item in the array ) index is updated.
Also see .find**AndUpdate() variants for a way to return the modified object.
I think you can use the update model:
Employee.update({department.id: '55ba28f680dec4383eeebf94'}, {department.text: 'XXXXX'}, {multi: true},
function(err, num) {
console.log("updated "+num);
}
);
First object is the query, what to find: {department.id: '55ba28f680dec4383eeebf94'}, the second one is the update, what to update: {department.text: 'XXXXX'} and the third one is the options to pass to the update, multi means update every records you find: {multi: true}

Using mongoose, how do I filter and then group by?

I am using mongoose and so far the query I use gets me all of the critiques based on a docId. I would like to group this result by distinct editors now. Except, my editors is an object.
This is what my critique query looks like:
Critique.find({docId:req.params.docId}).populate('editor', 'name username').exec(function(err, critiques){
if(err){
console.error("Cannot find critiques with docId: " + critiques.docId);
}
console.log(critiques);
res.jsonp(critiques);
});
This is my model I am querying:
var CritiqueSchema = new Schema({
className : String,
content: String,
eleId: Number,
type: String,
comments: String,
isAccepted: Boolean,
classes: String,
docId:{
type: Schema.ObjectId,
ref: 'Composition'
},
created: {
type: Date,
default: Date.now
},
editor: {
type: Schema.ObjectId,
ref: 'User'
},
});
UPDATE new query:
Critique.aggregate(
[ {$match : {docId : mongoose.Types.ObjectId(req.params.docId)}},
{$group : { _id : "$editor", critiques: { $push: "$$ROOT" } } }
]).exec(function(error, result){
if(!error)console.log(result);
else console.log(error);
});
What you need is $group in the aggregation framework. But aggregation and population don't go along. So you have two options populate and group the results by yourself by writing a loop or you can use $group to group them and then query each editor manually. The second is better as there will no duplication in editor queries whereas in population there will be significant duplication going on.
Critique.aggregate(
[{
$match:
{
docId: ObjectId(req.params.docid)
}
},
{ $group : { _id : "$editor", critiques: { $push: "$$ROOT" } } }
],
function(err,result){
if(!err){
/* result will be of the form:
[{_id:<an editor's objectid>,critiques:[{<critique1 document>},{<critique2 document>}...]}...]
*/
//you will have to manually query for each distinct editor(result[i]._id) which sucks
//because the call will be asynchronous in the loop and you can't send your response without using async library
//another option would be to use the $in operator on an array of the distinct critiques:
var editors = result.map(function(x) { return x._id } );
User.find({_id:{$in:editors}},{'username':1},function(err,editorDocs){
var editor_ids=editorDocs.map(function(x){return x._id})
var index;
for(var i=0;i<result.length;i++){
index=editor_ids.indexOf(result[i]._id);
result[i].editor=editorDocs[index].username;
}
//result is your final result. In the editor field of each object you will have the username of the editor
})
}
})
Check the docs for $$ROOT.

Resources