Mongoose - get _id of a newly saved inner record - node.js

There is a Mongoose Schema with sub-documents. I want to be able to push sub-document and process newly created inner item after parent saving.
Is there any easy way to get an _id of a newly created inner item? Here is the code:
var trackSchema = mongoose.Schema({
title: String
});
var userSchema = mongoose.Schema({
displayName: String,
tracks: [trackSchema]
});
var Track = mongoose.model('Track', trackSchema);
var User = mongoose.model('User', userSchema);
var track = new Track({
title: 'Sunrise'
});
user.tracks.push(track); // assume 'user' was returned by User.findById()
user.save(function (err, userUpdated) {
// ... is there any way to find _id of pushed 'track' ???
});

Easiest way is to create ObjectId in code and assign it to _id property of inner item on object creation

Related

document must have an _id before saving mongoose error

I am trying to create a schema.
I keep getting the document does not have an _id error, besides the code below I did try to initialize it explicitly, but nothing works.
var UserSchema = new mongoose.Schema({
_id: mongoose.Schema.ObjectId,
username: String,
password: String
});
var User = mongoose.model('user', UserSchema);
http://mongoosejs.com/docs/guide.html#_id reads:
Mongoose assigns each of your schemas an _id field by default if one is not passed into the Schema constructor.
If you explicitly define _id type in the schema, it's your responsibility to set it:
User._id = mongoose.Types.ObjectId('000000000000000000000001');
_id is the primary key for document in a mongoDB. You don't have to specify the _id in your Schema. It will be added automatically once the document is created.
Here is the sample code:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = new Schema({
username: {
type: String
},
password: {
type: String
}
});
module.exports = mongoose.model('User', User);
I think you dont need to define the _id. Try without and see if it works.
Also if that is not the problem try this:
_id: { type: Mongoose.Schema.Types.ObjectId }
if you want to define _id in your schema explicity you should assign a value to "_id" for each insertation. you have two way to solve this problem :
1. remove "_id" from your schema and mongoose generate id automatically.
2. assign a value to _id :
var ObjectId = require('mongodb').ObjectID; // or var ObjectId = require('mongoose').Types.ObjectId; "but the first worked for me"
User._id = objectId('1111111111111111111');
simple remove the line from your code
_id: mongoose.Schema.ObjectId

Mongoose relations design

I've recently started using Mongoose with Express.js in a Node.js application and I have a question about a proper way to design my schemas.
I have several schemas that have some relationships, i.e. Location schema has an array of Objects (it's not a JS object in this context), and Object schema has its Location property. I've learned that relationships in Mongoose are resolved using population, but when I implemented this approach I noticed that I have to type a lot of duplicate code, i.e. whenever I want to create a new Object I have to also update the Location's array of Objects and then assign the Location to the Object's property. Wouldn't it be more trivial to just manually assemble all the Objects that has a locationId property equal to the Location that I want to get from the database in a separate query?
I have also considered just storing Objects in an array in a Location document (as subdocuments) but I decided that I want to be able to work with Objects (create, remove, update) separately from Locations (without querying a Location) so this approach doesn't fit my needs I guess. But then population has its drawbacks too in my case, so I guess it's really the best to just go with manually collecting Objects of a specific Location in a separate query by that Location's id.
I would like to hear an opinion of some professional or advanced user of this technology on designing Mongoose schemas so that I and others don't get into trouble later maintaining and scaling our applications.
Here are my current schemas in question:
var locationSchema = new mongoose.Schema({
title: String,
objects: [{ type: String, ref: 'object' }]
});
var objectSchema = new mongoose.Schema({
title: String,
location: { type: String, ref: 'location' }
});
Checkout this example
db/schemas.js:
const Schema = mongoose.Schema;
const ObjectSchema = {
title: Schema.Types.String
}
const LocationSchema = new Schema({
title: Schema.Types.String,
objects: [{type: Schema.Types.ObjectId, ref: 'Object'}]
})
module.exports = {
Object: ObjectSchema,
Location: LocationSchema
};
db/model.js:
const
mongoose = require('mongoose'),
schemas = require('./schemas');
module.exports = model => mongoose.model(model, schemas[model+'Schema']);
usage:
const
model = require('./db/model'),
LocationModel = model('Location');
LocationModel
.findOne({_id: 'some id here'})
.populate('objects')
.exec((err, LocationInstance) => {
console.log(LocationInstance.title, ' objects:', LocationInstance.objects);
});
when You create an object and want to relate to location:
const
model = require('./db/model'),
ObjectModel = model('Object'),
LocationModel = model('Location');
let
ObjectInstance = new ObjectModel({title: 'Something'});
ObjectInstance.save((err, result) => {
LocationModel
.findByIdAndUpdate(
'some id here',
{$push: {objects: ObjectInstance._id}},
(err) => {
console.log('Object:', ObjectInstance.title, ' added to location');
});
});
updating object data:
const
model = require('./db/model'),
ObjectModel = model('Object');
let id = 'id of object';
ObjectModel
.findByIdAndUpdate(
id,
{title: 'Something #2'},
(err) => {
console.log('Object title updated');
});
finding location by object:
const
model = require('./db/model'),
LocationModel = model('Object');
let id = 'id of object';
LocationModel
.findOne({objects: id})
.populate('objects')
.exec((err, LocationInstance) => {
console.log('Location objects:', LocationInstance.objects);
});
nothing special findOne({objects: id}) will search inside location documents that has relation by id in objects array
any other question welcome (:

Mongoose - inserting subdocuments

I have a user model, and a log model. The log model is a subdocument of user model. So in my user model I have:
var mongoose = require('mongoose');
var Log = require('../models/log');
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
logsHeld: [
Log
]
});
Then in my 'Log' model I have:
var mongoose = require('mongoose');
var logSchema = new mongoose.Schema({
logComment: {
type: String,
},
});
module.exports = mongoose.model('Log', logSchema);
So upon creation of a 'user', the 'logsHeld' always begins empty. I want to know how to add subdocuments to this user model.
I've tried doing this POST method:
router.post('/createNewLog', function(req, res) {
var user = new User ({
logssHeld: [{
logComment: req.body.logComment
}]
});
user.save(function(err) {
if(err) {
req.flash('error', 'Log was not added due to error');
return res.redirect('/home');
} else {
req.flash('success', 'Log was successfully added!');
return res.redirect('/home');
}
});
});
But this doesn't work. It also includes a 'new User' line, which I don't think I need given this would be for an existing user.
You need to use the logSchema instead of the Log model as your subdocument schema in User model. You can access the schema as follows:
var mongoose = require('mongoose');
/* access the Log schema via its Model.schema property */
var LogSchema = require('../models/log').schema; // <-- access the schema with this
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
logsHeld: [LogSchema]
});
Picking up from your comments in another answer where you are facing another issue
WriteError({"code":11000,"index":0,"errmsg":"E11000 duplicate key
error index: testDB.users.$email_1 dup key:
you are getting this because there's already a document in your users collection that has most probably a null value on the email field. Even though your schema does not explicitly specify an email field, you may have an existing old and unused unique index on users.email.
You can confirm this with
testDB.users.getIndexes()
If that is the case and manually remove the unwanted index with
testDB.users.dropIndex(<index_name_as_specified_above>)
and carry on with the POST to see if that has rectified the error, I bet my $0.02 that there is an old unused unique index in your users collection which is the main issue.
Try using logSchema which references only the subdocument schema, Log refers to the entire contents of ../models/log
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
logsHeld: [
logSchema
]
});
Documentation: http://mongoosejs.com/docs/subdocs.html
Try push to insert item in array in mongoose
var user = new User;
user.logssHeld.push({
logComment: req.body.logComment
});
user.save(function(err, doc) {
//DO whatever you want
});
see the docs here

How to populate the User object with Mongoose and Node

I am trying to add a couple of attributes to the scaffolded MEAN.js User entity.
locationName: {
type: String,
trim: true
}
I also have created another entity Book connected with User. Unfortunately, I think I do not quite grasp the concept behind the populate method because I am not able to "populate" the User entity with the locationName attribute.
I tried the following:
/**
* List of Books
*/
exports.list = function(req, res) {
Book.find().sort('-created').populate('user', 'displayName', 'locationName').exec(function(err, books) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(books);
}
});
};
Unfortunately, I get the following error:
/home/maurizio/Workspace/sbr-v1/node_modules/mongoose/lib/connection.js:625
throw new MongooseError.MissingSchemaError(name);
^
MissingSchemaError: Schema hasn't been registered for model "locationName".
Any suggestion?
Thanks
Cheers
The error is clear, you should have a schema for the locationName.
If your location is just a string property in your user model and does not refer to separate model, you don't need and shouldn't use populate with it, it will simply be returned as a property of the returned user object from mongoose find() method.
If your want to make your location a stand alone entity (different mongodb document), you should have a mongoose model that defines your location object, aka have a file in your app\models name for example: location.server.model.js that contains something like:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var LocationSchema = new Schema({
_id: String,
name: String
//, add any additional properties
});
mongoose.model('Location', LocationSchema);
Note that the _id here replaces the auto generated objectId, so this has to be unique, and this the property you should refer to in your User object, meaning if you have a location like this:
var mongoose = require('mongoose'),
Location = mongoose.model('Location');
var _location = new Location({_id:'de', name:'Deutschland'});
you should refer to it in your User object like this:
var _user=new User({location:'de'});
//or:
var _user=new User();
_user.location='de';
then you should be able to populate your location object with your user, like this:
User.find().populate('location').exec(function(err, _user) {
if (err) {
//handle error
} else {
//found user
console.log(_user);
//user is populated with location object, makes you able to do:
console.log(_user.location.name);
}
});
I suggest you to further read in mongodb data modeling and mongoose Schemas, Models, Population.

Reference documents with ObjectId when saving in mongoose

I have the following schemas:
// ingredient
var ingredSchema = new Schema({
name: String,
cost: Number
});
// order
var orderSchema = new Schema({
cusName: String,
ingredients: [{type: Schema.Types.ObjectId, ref: 'Ingredient'}]
});
// create model
var Ingredient = mongoose.model('Ingredient', ingredSchema);
var Order = mongoose.model('Order', orderSchema);
I have already saved a bunch ingredients in a collection ingredients and have a UI where users choose a set of ingredients for their burgers. I then try to save an order for a burger in another collection orders within the same database burgers like this:
// get order info from the form
var newOrder = new Order({ cusName: req.body.name,
ingredients: req.body.ingredients });
newOrder.save(function(err) {
if (err)
return console.log('Could not save your new order', err);
res.redirect('/order');
});
The call to save an order generates the following error:
{ message: Cast to ObjectId failed for value xxx at path 'ingredients',
name: 'CastError',
type: ObjectId,
value: xxx,
path: 'ingredients' }
I use mongoose version 3.6.11. Please help me hack this.
PS: req.body.ingredients is an array built from checkboxes.
There are 2 possible problems with your code right now:
1. req.body.ingredients will not be an array of ObjectIds, and mongoose wants it alright (I doubt of this one).
You should cast every ingredient to ObjectId first. Supposing req.body.ingredients is array, then you would do something like this:
var casted = req.body.ingredients.map(function( ingredient ) {
return mongoose.Types.ObjectId(ingredient);
});
I did not tested this, see if it'll work for you.
2. Mongoose is trying to cast your ingredients, but one of them is not a valid ObjectId
ObjectId should be composed of 24 hex chars, check whether you're passing values like this to Mongoose.
Please, post the result if one of them work for you :)

Resources