Saving an array property on a Mongoose schema - node.js

I have a mongoose object schema that looks similar to the following:
var postSchema = new Schema({
imagePost: {
images: [{
url: String,
text: String
}]
});
I'm trying to create a new post using the following:
var new_post = new Post();
new_post.images = [];
for (var i in req.body.post_content.images) {
var image = req.body.post_content.images[i];
var imageObj = { url: image['url'], text: image['text'] };
new_post.images.push(imageObj);
}
new_post.save();
However, once I save the post, it's created with an empty array for the images property. What am I doing wrong?

You're missing the imagePost object of your schema in your new object. Try this instead:
var new_post = new Post();
new_post.imagePost = { images: [] };
for (var i in req.body.post_content.images) {
var image = req.body.post_content.images[i];
var imageObj = { url: image['url'], text: image['text'] };
new_post.imagePost.images.push(imageObj);
}
new_post.save();

I've just done something similar, in my case appending to an existing collection, please see this question/answer. It may help you:
Mongoose / MongoDB - Simple example of appending to a document object array, with a pre-defined schema
Your problem is that in Mongoose you can't have nested objects, only nested Schemas. So you need to do something like this (for your desired structure):
var imageSchema = new Schema({
url: {type:String},
text: {type:String}
});
var imagesSchema = new Schema({
images : [imageSchema]
});
var postSchema = new Schema({
imagePost: [imagesSchema]
});

Related

Mongoose sub Docs CastError

I am new to MongoDB and MongooseJS. I'm also new to nodeJs.
I have an Angularjs project using Typescript. This project work with a "container" json, which itself contains some properties and a testList, which is a json object containing some properties and a fileList, containing an itemList.
So it's like this :
export class Container{
data:string="";
testList:Test[];
}
export class Test {
moredata:string="";
fileList:File[];
}
export class File {...}
etc.
I send this JSON to my nodejs server. I'm using bodyparser to get the json from the req.body object.
Server side, my mongoose Schema are exactly like my angularjs classes, so they look like this :
/*************** mongoose schemas **************/
// item.js
var mongoose = require('mongoose');
module.exports = mongoose.model('Item', {
Content : {type : Object, default: ''}
});
// file.js
var mongoose = require('mongoose');
var Item = require('./item');
var schema = new mongoose.Schema({
Data : {type : String, default: ''},
ItemList: {type: [Item], default:[]}
});
// test.js
var mongoose = require('mongoose');
var File = require('./file');
var schema = new mongoose.Schema({
Data: {type:String},
FileList: {type:[File], default:[]}
});
// container.js
var mongoose = require('mongoose');
var Test = require('./test');
var schema = new mongoose.Schema({
Name : {type : String, default: '', index:true, unique:true, required: true, dropDups:true},
Test : {type:[Test], default:[]}
});
If I try to create a new Container object (Mongoose object) and assign it the json from req.body, it bugs : CastError.
If I re-create each sub document from JSON and save the main doc, it bugs too : CastError.
I don't know how to achieve this. It worked before but my mongoose schema where using [mongoose.Schema.Types.Mixed] type for sub docs, and not the "real" types. Fact is, with Mixed I had no _id on sub docs, which I want.
Using real sub docs types, I can see in logs that the _id is created, but all the lists are empty.
Here is my nodejs code :
/*************** nodejs **************/
app.post('/api/container', bodyParser.urlencoded({extended:true}), bodyParser({limit:'50mb'}), bodyParser.json(), function(req, res) {
var test = req.body._test;
var testList = [];
var fileList;
var itemList;
var itemObj;
var fileObj;
var testObj;
for(var i in test){
fileList = [];
for(var j in test[i]._fileList){
itemList = [];
for(var k in test[i]._fileList[j]._itemList){
itemObj = new Item({
Content : test[i]._fileList[j]._itemList[k]._content
});
itemList.push(itemObj);
console.log('item pushed : ' + itemObj + ' and length : ' + itemList.length);
// logs gives info OK.
}
fileObj = new File({
Data: locales[i]._fileList[j]._data,
ItemList: itemList
});
fileList.push(fileObj);
console.log('file pushed : ' + fileObj);
// logs gives info NOK. The ItemList is empty : [], instead of a 70+ item list.
}
testObj = new Test({
Data: locales[i]._data,
FileList: fileList
});
testList.push(testObj);
console.log('test pushed : ' + i);
// once again, logs are NOK : the FileList is empty : []
}
// use mongoose to save the project in the database
new Container({
Name : req.body._name.toLowerCase().trim(),
Test: testList
}).save(function(err, container, count){
if(err){
console.log('erreur : ');
console.log(err);
// we enter here as we have this error :
/*
{ [CastError: Cast to undefined failed for value "
{
_id: 5727ebf95a76ff0011374928,
FileList: [],
Data: 'data'
},
{
_id: 5727ebf95a76ff0011374970,
FileList: [],
Data: 'other data'
}" at path "Test"]
message: 'Cast to undefined failed for value "
{ _id: 5727ebf95a76ff0011374928,\n FileList: [],\n Data: \'data\' },
{ _id: 5727ebf95a76ff0011374970,\n FileList: [],\n Data: \'other data\'}"
at path "Test"',
name: 'CastError',
type: undefined,
value: [{"_id":"5727ebf95a76ff0011374928","FileList":[],"Data":"data"},{"_id":"5727ebf95a76ff0
011374970","FileList":[],"Data":"other data"}],
path: 'Test' }
*/
res.status(403).json({error: 'error'});
} else {
console.log('saved ! ');
res.json(container);
}
});
});
I'm not used to post here, I'm more a reader :) Anyway if my post is not appropriated please inform me and I'll move / edit it correctly.
Thanks for your time.
Checked, and working !
So my error was to use the models of my objects in mongoose schema instead of their Schema.
I'm now exporting both models and schema on each object, and using schema for schema definitions, and models for requests. Finally rid of this bug ! :)
/* Mongoose object definitions */
// grab the mongoose module
var mongoose = require('mongoose');
var child = require('./child').schema; // getting the child schema
// define our parent model
var parentSchema = new mongoose.Schema({
Name : {type : String, default: ''},
ChildrenList: {type: [Child], default:[]}
});
var parentModel = mongoose.model('Parent', parentSchema);
// module.exports allows us to pass this to other files when it is called
module.exports = {
model: parentModel, // exporting model for requests
schema: parentSchema // exporting schema for others schema using "Parent"
};
And the code used for requests :
var Child = require('./models/child').model; // here using model
var Parent = require('./models/parent').model; // here using model
new Parent({
Name : req.body._name.toLowerCase().trim(),
ChildrenList : childrenList // variable defined somewhere
}).save(function(err, parent, count){
if(err){
res.status(403).json({error: 'too bad'});
} else {
res.json(parent);
}
});

how use Schema methods in mongoose sub-documents after loading

I have the following scheme in the mongoose
var Schema = mongoose.Schema;
var CellSchema = new Schema({
foo: Number,
});
CellSchema.methods.fooMethod= function(){
return 'hello';
};
var GameSchema = new Schema({
field: [CellSchema]
});
if create new document like:
var cell = new CellModel({foo: 2})
var game = new GameModel();
game.field.push(cell);
game.field[0].fooMethod();
it's correctly work. But if you run this code:
GameModel.findOne({}, function(err, game) {
console.log(game);
game.field[0].fooMethod()
})
i get TypeError: game.field[0].fooMethod is not a function
and console log is
{
field:
[ { foo: 2,
_id: 5675d5474a78f1b40d96226d }
]
}
how correct load sub-document with all schema methods?
You have to define the methods on the embedded schema before defining the parent schema.
Also you have to reference CellSchema instead of 'Cell'
var CellSchema = new Schema({
foo: Number,
});
CellSchema.methods.fooMethod = function() {
return 'hello';
};
var GameSchema = new Schema({
field: [CellSchema]
});

Accessing subdocument properties in mongoose

I am using a MEAN stack to build this application.
Here is my subject.js schema:
var mongoose = require('mongoose');
var schema = mongoose.Schema;
var topics = require('./topic');
var subjectSchema = new schema({
_category : {
type: String,
default: ""
},
topics: [topics.schema]
});
module.exports = mongoose.model('Subject', subjectSchema);
and my topics.js schema:
var mongoose = require('mongoose');
var schema = mongoose.Schema;
var otherstuff = require('./otherstuff');
var otherstuff2 = require('./otherstuff2');
var topicSchema = new schema ({
title: String,
otherstuff: [mongoose.model('otherstuff').schema],
otherstuff2: [mongoose.model('otherstuff2').schema]
});
module.exports = mongoose.model('Topic', topicSchema);
What I am having difficulty with is how to access my topicSchema to populate it with forms from my front end.
I can save information to the subjectSchema, but not the sub documents.
I have tried using this as outlined in another article:
var Subject = mongoose.model('Subject', subjectSchema);
Subject.find({}).populate('subjects[0].topics[0].title').exec(function(err, subjects) {
console.log(subjects[0].topics[0].title);
});
But I continue to get TypeError: Cannot read property 'title' of undefined. How do I access the title property?
populate in mongoose is used to populate referenced documents, that are marked with ref attribute (see more info in the docs). Sub-documents on the other hand are available when do a simple query because they are actually an array of custom objects, so if you remove the populate method your query will work as expected:
Subject.find({}).exec(function(err, subjects) {
console.log(subjects[0].topics[0].title);
});

Why are some of my mongoose models not loading? Apparently they're not there

I'm loading my mongoose models and some of them load, while others apparently don't load at all.
For instance,
var Post = mongoose.model('Post');
var Comment = mongoose.model('Comment');
var Friend = require('../models/Friends.js');
OR var Friend = mongoose.model('Friends');
If I console.log(Friend). I get an empty object. However, if I console log the other models, they exist. I created them EXACTLY the same way. I've been having some issues creating new mongoose models for some reason. Any ideas?
This is my friends.js file, which is exactly the same as the rest.
var mongoose = require('mongoose');
var FriendSchema = new mongoose.Schema({
firstName: String,
lastName: String,
updateFrequency: Number,
email: String,
pastInteraction: [],
lastTime: Date
});
FriendSchema.methods.updateLastAccess = function(cb){
this.lastTime = new Date();
this.save(cb);
}
mongoose.model('Friends', FriendSchema);
You are not exporting anything from friends.js
Export the Friends model from there
var mongoose = require('mongoose');
var FriendSchema = new mongoose.Schema({
firstName: String,
lastName: String,
updateFrequency: Number,
email: String,
pastInteraction: [],
lastTime: Date
});
FriendSchema.methods.updateLastAccess = function(cb){
this.lastTime = new Date();
this.save(cb);
}
module.exports =mongoose.model('Friends', FriendSchema);
After making this change you can console.log() and check
var Friend = require('../models/Friends.js');
console.log(Friend) ;
Why am I able to access the other models in the same way? I haven't
exported yet I'm able to access them.
because you are creating a model at the spot on same file for them like mongoose.model('Post');

Mongoose null checking before saving on nested schema array

I have an Schema model like this:
var propertySchema = new Schema({
name: {type: String, required: true},
surname: String
});
var objSchema = new Schema({
properties: [prepertySchema]
});
var accountSchema = new Schema({
objects: [objSchema]
});
mongoose.model('account', accountSchema);
Then i have the operations:
account.objects.push(null);
account.save(function(error, account) {
//Error checking and response
})
In that case, i'm getting a ValidationError because of the null value. This is expected. But, in the next operations:
var obj = {properties: null}
account.objects.push(obj);
account.save(function(error, account) {
//Error checking and response
})
Here the value is stored on database, and then i have an unexpected null value where it had been an array. Doing that with objects like this,
var obj = {
properties: [{name:'randname'}, null]
}
Also saves null values in the database that are prohibited to the data model.
I've read about validators, and middleware for checking things. Is there anyway to do this directly on the schema, or i have to parse the received object before i save it in the database? What is the best approach for this?
Well you could just use the model definitions for this. Even though you are embedding you can still do this but of course you do not want to actually save the objects to their own collection. Just feed them into the item as embedded:
var async = require("async"),
mongoose = require("mongoose"),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/prop');
var propertySchema = new Schema({
name: { type: String, required: true },
surname: String
});
var objSchema = new Schema({
properties: [propertySchema],
});
var accountSchema = new Schema({
objects: [objSchema]
});
var Account = mongoose.model( 'account', accountSchema );
var ObjMod = mongoose.model( 'ObjMod', objSchema, null, false );
var PropMod = mongoose.model( 'PropMod', propertySchema, null, false );
var account = new Account();
var prop = new PropMod({ "name": null });
var obj = new ObjMod({ properties: prop });
account.objects.push( obj );
account.save(function(err,account) {
if (err) throw err;
console.log( JSON.stringify( account, undefined, 4 ) );
});
So what happens there is the validation will work for each stage, in this case it will fail on the name of the property schema item not being a string or even if not included.

Resources