Not able to persist array of objects in mongo using mongoose - node.js

I'm trying to persist an array of objects in a document using mongoose. I have tried multiple times but it's not persisting array in document. It places an empty array in document.
Following is my Schema:
var ProfileSchema = new Schema({
name: String,
PagesData: [{
pageAccessToken: {type: String, get: decryptText, set: encryptText},
category: String,
name: String,
id: String,
perms: [String]
}]
});
module.exports = mongoose.model('Profile', ProfileSchema);
I'm trying to save a document with an array of objects using following query:
var newProfile = new Profile();
newProfile.name = "someName";
newProfile.PagesData = [ { pageAccessToken: 'someToken',
category: 'Bags/Luggage',
name: 'someBrandName',
id: '12345',
perms:
[ 'ADMINISTER',
'EDIT_PROFILE',
'CREATE_CONTENT' ] } ];
newProfile.save(function(err, result, numAffected){
if(err) {
console.log(err);
res.send(500, "Error");
}
console.log(result);
res.send(200, "Success");
});
I tried debugging the mongo commands using
require('mongoose').set('debug', true)
On Debug logs it shows, empty array during insert command execution.
Can anyone please tell me how can I store this array of object in my schema ?
Thanks,
Update:
It's been too long and I'm still not able to figure out the root cause of the problem. There is a long thread going on github for this.
https://github.com/Automattic/mongoose/issues/3249
I would like other experts to please take a look and suggest me some way by which I can solve the issue. I'm really stuck at this.
Update 2:
None of the solution worked for me so far, so I decided to modify the schema only to meet my requirements. This resulted in a different problem:
I want to create a map with a objectId as key and an array of string values as its value. The closest that I can get is:
var schema = new Schema({
map: [{myId: {type:mongoose.Schema.Types.ObjectId, ref: 'MyOtherCollection'}, values: [String]}]
});
But somehow this is not working for me. When I perform an update with {upsert: true}, it is not correctly populating the key: value in the map. In fact, I'm not even sure if I have declared the schema correctly.
Can anyone tell me if the schema is correct ? Also, How can I perform an update with {upsert: true} for this schema?
Also, if above is not correct and can;t be achieved then how can I model my requirement by some other way. My use case is I want to keep a list of values for a given objectId. I don't want any duplicates entries with same key, that's why picked map.
Please suggest if the approach is correct or should this be modelled some other way?
Thanks

I tried the exact code you have provided here and it's working for me. I am not sure what is causing the issue for you. Until and unless we get the same issue, it's very difficult to rectify it.
Here are few suggestions which you might try:
Create a simple schema and try storing the object, that way you can
figure it out if it has to do something with the schema.
You can try out your schema in a sample app to find if some
dependency is causing the problem.
Once you know where exactly the problem is, you would be able to figure out a solution too. I hope it helps.

I tested this and the insert works for me using the below:
(I had to remove the get: decryptText, set: encryptText)
var n = { name: "Testing for mongoose", PagesData : [{ pageAccessToken: 'someToken',
category: 'Bags/Luggage',
name: 'someBrandName',
id: '12345',
perms:
[ 'ADMINISTER',
'EDIT_PROFILE',
'CREATE_CONTENT' ] } ] }
Profile.create(n, function (err) {
if (!err) {
return 'records saved successfully';
}
else {
return error on save:' + err;
}
});

To create multiple pageDatas you can use it as an embedded collection instead of using arrays.
The Schema will be as follows:
var PagesDataSchema = new Scheme({
pageAccessToken: {type: String, get: decryptText, set: encryptText},
category: String,
name: String,
id: String,
perms: [String]
})
var ProfileSchema = new Schema({
name: String,
PagesData: [PagesDataSchema]
});
module.exports = mongoose.model('Profile', ProfileSchema);
Reference: http://mongoosejs.com/docs/subdocs.html
For Saving the document you can use like.
exports.save = function(req,res){
var test = new ProfileSchema; // new object for ProfileSchema domain.
test.name= req.body.name;
if(req.body.PagesData){
req.body.PagesData.forEach(function(page){ // For every element of pageData from client.
test.PagesData.push(page) // This pushes each and every pagedata given from the client into PagesData.
})
}
test.save(function (saveErr, saved) { // Saves the new document into db.
if (saveErr) {
console.log(saveErr)
return;
}
res.status(HttpStatus.OK).json(saved);
});
};
Hope this helps.

Have you tried
Profile.create({
name: "someName",
PagesData: [
{
pageAccessToken: 'someToken',
category: 'Bags/Luggage',
name: 'someBrandName',
id: '12345',
perms: [
'ADMINISTER',
'EDIT_PROFILE',
'CREATE_CONTENT'
]
}
]
}, function(err, profile) {
// do your stuff
})
?

Related

mongodb query events done to an item and group them into an array

I know that the title might be a bit vague, but I will elaborate here.
basically what I am trying to achieve is the following:
I have a collection with documents that have the following scheme:
bookId: <uuid>
genre: <string>
isTaken: true
historyIndex: each time something happens i increment this
returnedAt: not required but will be present on documents with historyIndex
takenAt: not required but will be present on documents with historyIndex
there are documents with no historyIndex field because they are representing the book itself without the action that were done to the book.
what i want to do is this:
I want to query the books by their unique uuid and then use the documents with historyIndex and add them to the main documents as in an array as called bookEvents
so the final results will be
bookId:
bookEvents: [] --> an array with all the entries that contain history index
basically everytime the status of the book changes, i am inserting an event with the date it was taken on and the date it was returned on
What would be the best way of achieving such thing ?
Should I query once and iterate in my code ?
Should I query twice (once where the fields exist and once where they don't) ?
Thank you in advance for all the people that will try to help!
You can use the plugin or events to achieve this.
var CounterSchema = new mongoose.Schema({
_id: {type: String, required: true},
seq: { type: Number, default: 0 }
});
var counter = mongoose.model('counter', CounterSchema);
var entitySchema = mongoose.Schema({
sort: {type: String}
});
entitySchema.pre('save', function(next) {
var doc = this;
counter.findByIdAndUpdateAsync({_id: 'entityId'}, {$inc: { seq: 1} }, {new: true, upsert: true}).then(function(count) {
console.log("...count: "+JSON.stringify(count));
doc.sort = count.seq;
next();
})
.catch(function(error) {
console.error("counter error-> : "+error);
throw error;
});
});
refer https://stackoverflow.com/a/40137723/8201020

Schema referencing stores the whole value instead of ObjectId alone

Lately, I've been going through a course named The Web Developer Course.
In which the final project is based on Camps.
In the project, the comment database and the campground database are referenced, that is, the ObjectIds of the comments which are posted in a campground is stored in an array. This is what to be happened actually.
But in my case, the exact scenario's changed..When I try to Add a new comment what actually happens is that the total object gets stored in the comments array instead of the ObjectId of the comment.
I've almost gone through Stackoverflow seeking solution for my problem but failed.
I just wanted the ObjectId to be stored in the comments array instead it stores the whole Object which brings me problem in updating and deleting a comment. When I delete or update a comment the operation does happen in the Comments database but doesn't reflect in the Campgrounds database. Please help me with this issue. If anyone's taking the same course, Please give me solutions if you've experienced something like this already. The Schema are as given below
Campground Schema:
var mongoose = require("mongoose");
var campgroundSchema = mongoose.Schema({
campGroundName: String,
campGroundImage: String,
description: String,
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
],
addedBy: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
module.exports = mongoose.model("Campground", campgroundSchema);
Comment Schema:
var mongoose = require("mongoose");
var commentSchema = mongoose.Schema({
text: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
module.exports = mongoose.model("Comment", commentSchema);
Post request for creating a comment:
router.post("/", middleware.isLoggedIn, function(req, res) {
Comment.create(req.body.comment, function(err, createdComment) {
if(err) {
console.log(err);
} else {
createdComment.author.id = req.user._id;
createdComment.author.username = req.user.username;
createdComment.save(); Campground.findById(req.params.id).populate("comments").exec(function(err, foundCampground){
foundCampground.comments.push(createdComment);
foundCampground.save();
req.flash("success" , "Comment created successfully");
res.redirect("/index/" + req.params.id);
});
}
});
});
The whole source code is below,
https://1drv.ms/u/s!AmISAco3PGaPhQl_Riu8nroCom5h
Please help me fix this issue!
you have this line:
foundCampground.comments.push(createdComment)
which is telling mongodb to store the whole comment in the array.
it should be this instead:
foundCampground.comments.push(createdComment._id)
which will only push the id property of the comment into the array.
The version seemed to have a bug in it.
The problem was fixed when I updated it. Fixed version is 5.0.3

Mongoose MongoDB Objects undefined

I've run into a strange issue. I've started to use MongoDB and it's most likely me doing something terrible wrong.
I have the following models setup:
var cartSchema = mongoose.Schema({
owner: { type: Schema.Types.ObjectId, ref: 'users' },
productline: [{ type: Schema.Types.ObjectId, ref: 'Productline' }]
});
var productlineSchema = mongoose.Schema({
cart: { type: Schema.Types.ObjectId, ref: 'Cart' },
product: { type: Schema.Types.ObjectId, ref: 'products' },
quantity: Number
});
Initially the Cart is setup with an empty array when the User registers, but then we add Productline objects to it later (which works since I can see data in there in my GUI.).
I'm trying to read the name value which is intended to be reached from cart -> productline -> product
for (var i=0; i < cartModel.productline.length; i++) {
console.log(cartModel.productline[i].product.name);
}
But getting TypeError: Cannot read property 'name' of undefined on exactly that line, which means that product == "undefined".
However, when I'm checking in my MongoDB with MongoDB Compass, then I can see that there is infact a connection between them, and the id's looks accurate as far as I can tell, so it should be able to read it.
So either I'm trying to reach the values in the wrong way, cartModel.productline[0].product.name.
Or my code doesn't realize that the object has been updated, which is strange since I even made sure to use Cart.findOne(query, function(err, cartModel) { ... } to be sure I get a fresh one from the database.
Anyone has any ideas? I'll be glad to post some more code if needed, I just tried to find the most relevant parts above, but I might be missing something somewhere else...
I actually managed to solve this by myself. For people who are having problems with nested objects, I recommend you looking into the mongoose-deep-populate plugin: https://github.com/buunguyen/mongoose-deep-populate
It helped me out a lot, and my final query ended up like this:
Cart.findOne({_id: cart}).deepPopulate('productline.product').exec(function (err, docs) {
// docs is JSON data which can be used to reach the nested data.
});
I have also encountered similar error recently
Demo
When I use: .populate('productline')
Cart.findOne({_id: cart}).populate('productline').exec(function(err, docs) {
/* docs.productline items represent the correct model but their
fields are undefined except the `_id` */
})
Solution
When I use: .populate({path: 'productline'})
Cart.findOne({_id: cart}).populate({path: 'productline'}).exec(function(err, docs) {
/* docs.productline items represent the correct model with all properties
fetched with the correct values */
})
Other solution
This example from http://mongoosejs.com/docs/populate.html helped me
Story
.find(...)
.populate({
path: 'fans',
match: { age: { $gte: 21 }},
select: 'name -_id', /* populates only these fields in the `fans` collection */
options: { limit: 5 }
})
.exec()

Unable to insert into subdocument

I have the following schema
var ChildSchema = new Schema({
name: String,
value: String
});
var ParentSchema = new Schema({
children: [ChildSchema]
});
Now, I wish to insert entries into parent.children.
I did the following:
parentRecord.children.push({
name:"foo",
value: "bar"
});
When I use a console.log, I can see here that the record has been successfully added into the parent record's children property. It also has been give an ID.
Now, I attempt to save the record:
parentRecord.save(myCallback);
While the save is a success, I see that the entry was not added into the children property.
I even tried to use parentRecord.markModified("children"); but that also does not help
I have also ensured that I declare the ChildSchema before I declare the ParentSchema.
I am at my wits end trying to figure out what's wrong. I have set the debug property of mongoose to true. I can see that it's identifying the new entry but I just can't see it getting saved in my database.
Following is the output from the debug messages:
Mongoose: parentRecord.update({ _id: ObjectId("5551895182d9adbe1da34d6a") }) { '$inc': { __v: 1 }, '$set': { anotherField: 'anotherValue' }, '$pushAll': { fields: [ { _id: ObjectId("55518c682a0744dc20f1473b"), value: 'bar', name: 'foo' } ] } } {}
Any hints on what else I need to do to get the entry to save?
I am using mongoose 3.8.12
From what I can tell by the change log it looks like this was an issue that was resolved in version 3.8.13. You may want to try upgrading.
https://github.com/Automattic/mongoose/issues/2082

How to find and update subdocument within array based on parent property

I need to find a subdocument which is inside an array, based on its own user_id property and its parent's my_id property
These are the schemas I have for my model (Conversation):
var listSchema = mongoose.Schema({
user_id: Number,
property: Boolean,
});
var conversationSchema = mongoose.Schema({
my_id: Number,
list: [listSchema]
});
After browsing through mongoose's documentation and some similar questions here on SO, I've tried the following (which seems to me like that it should work):
Conversation.findOneAndUpdate(
{ "my_id": 8, "list.user_id": 16 },
{
"$set": {
"list.$.property": false
}
},
function(err, doc) {
console.error("Error:", err);
console.log("Doc:", doc);
}
);
But it gives me this output and the documents are not being updated at all:
Error: null
Doc: null
What am I missing here? Any help is appreciated, thanks in advance!
I've finally figured out what was going on:
I was saving the user_id property as a String to MongoDB so I tried changing this part of the query to:
"list.user_id": "16"
instead of:
"list.user_id": 16
Still didn't work, but then I figured out that I was defining user_id as Number in listSchema so I changed that to String:
var listSchema = mongoose.Schema({
user_id: String,
property: Boolean,
});
And now it works, it doesn't matter if I use "16" or 16 in the query, I think that mongoose will try to find a String now instead of a Number like it was doing before

Resources