Cannot read property 'push' of undefined when creating data in mongoose - node.js

I am creating a express.js app and I made a database in mongoose for testing purpose. the database is inside a function seedDB() that I export to my app.js file. Creating the seed database has no error but when I add a new "review" inside of that data. it is saying cannot read property "push" of undefined even though my mongoose model is set correctly.
I have two collections inside mongoDB called "tours" and "reviews"
I tried looking up the tours inside mongo shell with db.tours.find() and I find my "review" which is an array that is associated with the review collection is set up correctly. But when I looked up db.reviews.find() . It is also there but it has about x4 result that my expected result.
I tried checking if I just forgot a parenthesis, curly brace but I think that's not the problem.
I also tried looking at my models over and over and changing again but there is also no problem
const tours = require("./models/tours");
const Review = require('./models/reviews');
let tourData = [{
image: "image.jpg",
place: "Place",
name: "name",
description: "this is a description",
price: 1234,
info: "this is a great tour"},
{
image: "image.jpg",
place: "Place",
name: "name",
description: "this is a description",
price: 1234,
info: "this is a great tour"},
{
image: "image.jpg",
place: "Place",
name: "name",
description: "this is a description",
price: 1234,
info: "this is a great tour"},
]
function seedDB(){
tours.deleteMany({}, (err)=>{
if(err){
console.log(err);
}
console.log("removed tours!");
//add a few tours
tourData.forEach(function(seeds){
tours.create(seeds, (err, data)=> {
if(err){
console.log(err)
} else {
console.log('added all tours!');
//create a comment
Review.create(
{
text: "this place is great! ",
author: "Arnold"
}, (err, comment)=> {
if(err){
console.log(err)
} else {
tours.reviews.push(comment); //why is this undefined? I set it up correctly
tours.save();
console.log("created new review")
}
});
}
});
});
});
};
module.exports = seedDB
up until console.log('added all tours!'); it is going well but when I put the Review.create(), it now has error specifically the tours.reviews.push(comment);
//tours.js model
const mongoose = require('mongoose');
var ToursSchema = new mongoose.Schema({
image: String,
place: String,
name: String,
description: String,
price: Number,
info: String,
creator: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
reviews:[
{
type: mongoose.Schema.Types.ObjectId,
ref: "Review"
}
]
});
let Tours = mongoose.model('Tour', ToursSchema);
module.exports = Tours;
reviews.js model
const mongoose = require('mongoose');
var reviewSchema = mongoose.Schema({ //I also tried doing new Mongoose.Schema({
text: String,
author: String
});
module.exports = mongoose.model('Review', reviewSchema);
expected results in console should be
removed tours!
added all tours!
added all tours!
added all tours!
created new review
created new review
created new review
and the actual results in the mongo database is have an array of reviews inside the tours collections.

Multiple things:
tours is your model, not your instance. You want to push into the reviews of one or all of your instances, which in your case is data. So in your case you can do something like data[0].reviews.push(comment). I can see how you this gut mixed up, since tours is lowercase, which makes it look like a instance, not a model.
The second worst variable name after data is data2 :-P
Consider replacing your callback with the far easier to read and maintain async/await syntax
don't directly require your models, but rather register your models and use mongoose.model('tours')

Related

node js, mongodb populate the populated

I've hit a wall in my server when I needed to get data from my server.
The following represents my schemas:
Schema one:{
name: String
}
Schema two:{
code:String,
name_id: refid: schema One
}
Schema three:{
phone:number
code:[refid: Schema two]
}
If I needed data from schema three, and the objects from object ids that are saved in the code array I would use populate and I would get the object referenced by object id.
Question is is it possible to populate the populated data?
If populate schema three
I would get objects such as:
{phone : 000911,
code: :{code:String,
name_id: refid: schema One}
in the previous example I want to populate the name id, is that possible?
With Mongoose, you can populate your schema with dot notation like this:
const One = new Schema({
name: String
})
const Two = new Schema({
code: String,
name: {
type: Schema.ObjectId,
ref: 'One'
}
})
const Three = new Schema({
phone: number
code: [{
type: Schema.ObjectId,
ref: 'Two'
}]
})
Three.find((err, three) => {
if (err) console.log(err)
console.log(three)
// => {
// phone : "the phone number from schema Three",
// code: {
// code: "the code from schema Two",
// name: "the name from schema One"
// }
// }
})
.populate('code.name')

mongoose#populate returns null in nested object inside array

I have a mongoDB database which is generated using a script that uses only the node.js mongoDB driver without mongoose. Later on, in the application, I want to use mongoose to load a document and have a reference be populated automatically; however, this only ever returns null.
Imagine a task which contains sub-items which each have a title and an assigned person. The assigned person, in this case, is the reference I want to have populated, so the reference lives in an object inside an array in the task schema.
The following code (requiring npm install mongodb mongoose) reproduces the problem (watch out, it destroys a local database named test if you have one already):
const mongodb = require('mongodb');
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
(async () => {
// Step 1: Insert data. This is done using the mongodb driver without mongoose.
const db = await mongodb.MongoClient.connect('mongodb://localhost/test');
await db.dropDatabase();
await db.collection('persons').insertOne({ name: 'Joe' });
const joe = await db.collection('persons').findOne({ name: 'Joe' });
await db.collection('tasks').insertOne({ items: [{ title: 'Test', person: joe._id }] });
await db.close();
// ================
// Step 2: Create the schemas and models.
const PersonSchema = new Schema({
name: String,
});
const Person = mongoose.model('Person', PersonSchema);
const TaskSchema = new Schema({
items: [{
title: String,
person: { type: Schema.Types.ObjectId, ref: 'Person' },
}],
});
const Task = mongoose.model('Task', TaskSchema);
// ================
// Step 3: Try to query the task and have it populated.
mongoose.connect('mongodb://localhost/test');
mongoose.Promise = Promise;
const myTask = await Task.findOne({}).populate('items.person');
// :-( Unfortunately this prints only
// { _id: "594283a5957e327d4896d135", items: [ { title: 'Test', person: null } ] }
console.log(JSON.stringify(myTask, null, 4));
mongoose.connection.close();
})();
The expected output would be
{ _id: "594283a5957e327d4896d135", items: [ { title: 'Test', person: { _id: "594283a5957e327d4896d134", name: "Joe" } } ] }
I have verified that the two _ids actually match using mongo shell:
> db.persons.find({})
{ "_id" : ObjectId("594283a5957e327d4896d134"), "name" : "Joe" }
> db.tasks.find({})
{ "_id" : ObjectId("594283a5957e327d4896d135"), "items" : [ { "title" : "Test", "person" : ObjectId("594283a5957e327d4896d134") } ] }
What am I doing wrong when attempting to populate person? I am using mongoose 4.10.6 and mongodb 2.2.28.
The answer to this problem lies in the fact that the collection name mongoose automatically infers from the model Person is people and not persons.
The problem can be solved either by writing to the people collection in the first part or by forcing mongoose to use the collection name persons:
const Person = mongoose.model('Person', PersonSchema, 'persons');
mongoose plans to remove pluralization in the collection name anyway, see #1350 on Github.

Not able to persist array of objects in mongo using mongoose

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
})
?

Why is my mongoose document property not properly setting to another _id property

I am trying to set up a simple mongoose test file, and am getting some pretty confusing results. When I run this following code:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/myapp');
var personSchema = Schema({
name : String,
age : Number,
stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
var storySchema = Schema({
creator : { type: Schema.Types.ObjectId, ref: 'Person' },
title : String,
fans : [{ type: Schema.Types.ObjectId, ref: 'Person' }]
});
var Story = mongoose.model('Story', storySchema);
var Person = mongoose.model('Person', personSchema);
var aaron = new Person({name: 'Aaron', age: 100 });
aaron.save(function (err) {
if (err) console.log("something didnt work!");
var story1 = new Story({
title: "Once upon a timex.",
creator: aaron._id // assign the _id from the person to creator
});
console.log(aaron._id);
story1.save();
});
Story.findOne({ title: 'Once upon a timex.' },function(err,story){
console.log(story); // printing here
});
I get this output:
{
_id: 54b9e08ed983b41d432473e4,
title: 'Once upon a timex.',
_creator: 0,
__v: 0,
fans: []
}
54bcacb4c812ec812382b6b2
there are many things that don't really make sense in this. As you can see from my code I only console.log(); 2 things:
arron._id
the story document created in the save callback of aaron
Question 1:
When we print out the story object, we see that the creator field is set to 0 (we'll get to this later), and underscore has been added for some reason (I assume it's because it links to an ObjectId). I also tried to add an underscore to creator, like it showed in the documentation, and this caused the creator property to simply not get saved to the document. Could anybody explain how _ interacts with mongoose?
Question 2:
When we try to set creator:aaron_id it gets set to 0, we know that arron._id is not 0, because we print it successfully in the same scope. What am I doing wrong?
Question 1: The issue is that that the find query executes before your save query does (remember that they execute asynchronously).
Try:
var aaron = new Person({name: 'Aaron', age: 100 });
aaron.save(function (err) {
if (err) console.log("something didnt work!");
var story1 = new Story({
title: "Once upon a timex.",
creator: aaron._id // assign the _id from the person to creator
});
console.log(aaron._id);
story1.save(function(err){
Story.findOne({ title: 'Once upon a timex.' },function(err,story){
console.log(story); // printing here
});
});
});
Question 2: Are you sure you're not looking up older objects that you attempted to save? I have a feeling that at some point story had the property _creator and you changed it to 'creator', but its finding your older document because you are not querying by id. Try changing your story query to:
Story.findOne({ title: 'Once upon a timex.', creator: aaron._id },function(err,story){
console.log(story); // printing here
});

Mongoose populate return undefined

I'm currently trying to develop an app using mongo and node.js.
I am facing a problem when I want to build a query who use the populate option.
Here are my Schemas :
// Schema used by mongoose
var userSchema = new mongoose.Schema(
{
_id: mongoose.Schema.Types.ObjectId,
login: String,
password: String,
movies: [ { type: mongoose.Schema.Types.ObjectId, ref: movieModel} ],
admin: Boolean
},{ collection: "user" });
var movieSchema = new mongoose.Schema(
{
_id: mongoose.Schema.Types.ObjectId,
title: String,
}, { collection: "movie" });
As you can see, each user have an array of movies, this array contains valid ids of movies. What I want is to have the movies of an user. This is how I build my query :
var query = userModel.findOne({ login: req.session.user["login"] })
.populate("movies");
query.exec(function(err, user)
{
if (err)
throw err;
console.log(user.movies[0].title);
});
The query is executed successfully, but when I try to display the title of the first movie at the console.log line I got an error "TypeError: Cannot read property 'title' of undefined". I checked the documentation of mongoose and don't understand why I'm getting this error.
I would like to specify that my database contains valid data.
I put mongoose in debug mode, and this is the query that is executed :
Mongoose: user.findOne({ login: 'user' }) { fields: undefined }
Mongoose: user.find({ _id: { '$in': [ ObjectId("52e2a28949ad409834473e71"), ObjectId("52e2a28949ad409834473e79") ] } }) { fields: undefined }
The two ids on the second line are valid ids of movies. I would like to display their name.
Thanks a lot for your help.
What is the value of this: ref: movieModel?
movieModel would need to be set to the string like "Movie". See here for more information. It will need to match the identifier provided when you create the Movie model.
var Movie = mongoose.model('Movie', movieSchema);
So, you might have in a schema:
var userSchema = mongoose.Schema({
name: String,
favorite_movies: { type: Schema.Types.ObjectId, ref: 'Movie' }
});
var User = mongoose.model('User', userSchema);
I've used the string Movie in both the Schema definition and when creating the Movie type. They need to be exactly the same.
MongooseJs uses the string name of the Model to determine where to fetch the documents from when using ref and populate.
In the debug output, you can see how Mongoose is actually querying the wrong collection, as I'd expect it to be using movies.find to find the relevant Movie documents.

Resources