What is an equivalent work around to $setOnInsert in mongoose - node.js

I have below collection structure in mongodb. Say
Collection - student - _id,firstname,lastname,class
Now I want to insert 2 extra columns say marks as array{mark1:m1,mark2:m2}when inserting a newrow`.
I did it as below but it inserted record excluding marks values.
var student=new Student({
_id:id,
firstname:result.fname,
lastname:result.lname,
class:result.class,
marks:{
mark1:result.mark.m1,
mark2:result.mark.m2
}
})
Is this possible in Mongoose?
I came across $setOnInsert, but not sure whether this fits here?
So if it fits, is there any equivalent workaround to use MongoDb's $setOnInsert? if not what approach I could use?

Yes, it's possible but that depends on the options set when defining your schema. You don't necessarily need to use the $setOnInsert operator when inserting a new record, the save() method on the model suffices.
The strict option ensures that values added to your model instance that were not specified in the schema gets saved or not to the db.
For example, if you had defined your schema like this:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var studentSchema = new Schema({
firstname: String,
lastname: String,
class: String
})
var Student = mongoose.model('Student', studentSchema);
var student= new Student({
_id: id,
firstname: result.fname,
lastname: result.lname,
class: result.class,
marks: {
mark1: result.mark.m1,
mark2: result.mark.m2
}
});
student.save(); // marks is not saved to the db
But if you set the strict option to false:
var studentSchema = new Schema({..}, { strict: false });
var student= new Student({
_id: id,
firstname: result.fname,
lastname: result.lname,
class: result.class,
marks: {
mark1: result.mark.m1,
mark2: result.mark.m2
}
});
student.save(); // marks is now saved to the db!!
NOTE: The strict option is set to false in mongoose v2 by default for backward compatibility. Set it to true and sleep peacefully. Do not set to false unless you have good reason.

Related

Mongoose schema - nested objects with shared properties

I'm new to both MongoDB and Mongoose and trying to figure out the best way to handle creating my schema. I am more used to relational databases, but am hoping this will work with Mongo.
I am creating objects that "extend" other objects. For example, I have a person object that I want to use as the starting point for a parent or grandparent object. Both the parent and grandparent may have additional values beyond those that a base person has (I'm just including one example in each)...
const personSchema = new mongoose.Schema({
name: String,
birthDate: Date,
deathDate: Date,
});
const parentSchema = new mongoose.Schema({
//all the stuff a person has + below:
children: [personSchemas?] //[array of persons, important... parents can also be children,
multiple parents will share the same child]
parentingStyle: String,
})
const grandParentSchema = new mongoose.Schema({
// stuff that a parent has plus
grandparentingStyle: String,
})
I think the following post includes some answers that could be helpful to you:
Referencing another schema in Mongoose
Other than that, I might also suggest you'd define parents and grandparents as roles perhaps in this way:
var mongoose = require('mongoose');
const PersonSchema = new mongoose.Schema({
person_id: {
type: mongoose.Schema.Types.ObjectId
},
name: String,
birthDate: Date,
deathDate: Date,
//the following is if you want later to fetch persons by role
role: {
type: string,
enum: ["parent", "grandParent","child"],
}
});
//Then you could do it this way
const ParentSchema = new mongoose.Schema({
//all the stuff a person has + below:
children: [personSchemas],
parentingStyle: String
})
//Or this way
const ParentSchema = new mongoose.Schema({
//all the stuff a person has + below:
children: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Person'
}
parentingStyle: String,
})
const GrandParentSchema = new mongoose.Schema({
// same as for parents (ether way)
// stuff that a parent has plus
grandparentingStyle: String
})
In non-relational databases, it really depends on what you'd like to do with the data later on. The "joints" (in comaprison to relational DB) are much simpler to create, either by referencing an id or fetching a whole array (like you did) or simply by making more elaborate queries later on.

Nested Documents in Mongoose

I have two Mongoose schemas, User and Code. Each user can have many codes.
user.js:
var mongoose = require('mongoose');
var codeSchema = require('./code');
var userSchema = mongoose.Schema({
google: {
id: String,
token: String,
email: String,
name: String
},
codes: [codeSchema]
}, {collection : 'users'});
code.js:
var mongoose = require('mongoose');
var codeSchema = mongoose.Schema({
code: String,
name: String,
link: String
}, {collection: 'codes'});
module.exports = codeSchema;
My problem is, whenever I access a user's array of codes by user.codes, I get something like { _id: 56c4c82a37273dc2b756a0ce },{ _id: 56c4c82a37273dc2b756a0cd } rather than the JSON for a code.
What am I missing?
You're missing populate.
By default, Mongoose will only give you the _ids of any references made in a document. populate allows you to fill out nested documents.
userSchema.findOne({}).populate('codes');
More here
please check that you are inserting other values or not this can be a case . Please write how you are inserting in array . I have two other way check out
There are two way to do this
1-->either you save refrence id of codeschema and
2--> is you can insert whole codeschema in array
1. codes: {
type: mongooseSchema.ObjectId,
ref: 'codeSchema',
required: true
},
and when all data is in array 56c4c82a37273dc2b756a0ce,56c4c82a37273dc2b756a0cd
that can be done by this query
domain.User.update({_id:id}
,{$addToSet:{code:codeObjvalue}},
function(err,res){});
and then populate them by this
domain.users.find({},'code')
.populate('code','code color email').
exec(function(err,results){
callback(err, results);
});
2-- and second is to insert whole code schema in userschema
create a codeschema object and add in set like this
var codeobj={};
codeobj.code="xyz";
codeobj.email="xyz#gmail.com"
var codeobject = new domain.code(codeobj);
domain.User.update({_id:id},{$addToSet:{code:codeobject}},function(err,user1){
});
Woops, turns out I was using the wrong dataset, not adding the codes properly (facepalm). Thanks to everyone who answered!

Creating dynamic schema in Mongoose using { strict: true }

I have this 2 schemas in mongoose:
The Booking schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bookingSchema = new Schema({
bookingNO: { type: Number, unique: true},
plateNO: String,
startDate: String,
bookedTime: Number,
creator: {type: Schema.Types.ObjectId, ref: 'User'}
});
var Booking = mongoose.model('Booking', bookingSchema);
The User schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
username: String,
password: String,
balance: Number,
bookings: [{type: Schema.Types.ObjectId, ref: 'Booking'}]
});
var User = mongoose.model('User', userSchema);
The problem with this design is:
Some Bookings were NOT created by a user - in which case the 'creator' field in Booking would be empty. Likewise, some Users do NOT necessarily contain a Booking - they may later on.
I was thinking about deleting the creator and bookings fields from the 2 Schemas, and using the { strict: false } option in Mongoose. Would this be the best option ?
If this is the case, I would have to add the 'creator' property to the Booking model, and 'bookings' property to the User, which would then get saved to the DB.
MOST importantly, due to the fact that I've removed the references from the Schema, how do I go about creating the reference in the case of using { strict: false } ?
Thanks in advance.
Unless a field has require:true flag, it can be left empty.
If you have a field which isn't defined in the Mongoose schema but is present in a document in MongoDB, you'll have to use doc.get('field') instead of just doc.field. Similarly for saving doc.set('field', value) and strict:false will be required otherwise it won't persist.
IMO you should have your schema inclusive rather than exclusive. Mongoose is just a wrapper for your data in MongoDB which at its heart is already schemaless.
In your specific case, you can create a booking without specifying a 'creator', because its of type ObjectId Mongoose will simply create the document and will leave that field empty. The user.bookings is a different matter as it is an Array, in which case Mongoose will always default to an empty array, even if it was left undefined when creating the document. In this case, would it be that bad to just leave the empty array there? It still represents the data accurately, where the user simply has no bookings, but are still able to potentially have them. If you explicitly don't want bookings, then yes, you'll either have to deal with strict: false, or manually $unset/delete the field from the documents.

How to define a nested schema in mongoose?

My mongo record is like this:
{
"_id":{"$oid":"5550b6de437f572112a29f1a"},
"cv_count":177732,
"gender_info": {"male_count": 50, "female_count": 32}
"stability_info_list":[{"ratio":8.802558610369414e-05,"total_count":34081,"years":0},{"ratio":5.868372406912943e-05,"total_count":34081,"years":1}],
"zhineng_id":"IT Manager"
}
I write the schema like this:
var ZhinengGenderSchema = new Schema({
male_count: Number,
female_count: Number
});
var ZhinengStabilitySchema = new Schema({
ratio: Number,
total_count: Number,
years: Number
});
var ZhinengStats = new Schema({
cv_count: Number,
gender_info: ZhinengGenderSchema,
stability_info_list: [ZhinengStabilitySchema],
zhineng_id: String
})
But I got this excetion:
TypeError: Undefined type `undefined` at `gender_info`
Did you try nesting Schemas? You can only nest using refs or arrays.
so mongoose doesn't support nest schemas? But my database has already been there, I cannot change, so how can I define my schema?
Just don't create a new schema for the subdocuments and you should be fine, i.e.:
var ZhinengGenderSchema = {
male_count: Number,
female_count: Number
};
var ZhinengStabilitySchema = {
ratio: Number,
total_count: Number
years: Number
};
var ZhinengStats = new Schema({
cv_count: Number,
gender_info: ZhinengGenderSchema,
stability_info_list: [ZhinengStabilitySchema],
zhineng_id: String
})
With mongoose you can define nesting (embedded) schemas in Array, like this:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var bookSchema = new Schema({
value: { type: String }
});
var authorSchema = new Schema({
books: [bookSchema]
});
Or by reference
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = mongoose.Schema.Types.ObjectId;
var auhtorSchema = new Schema({
book: { type: ObjectId, ref: 'Book'}
});
You may choose what is more appropriate for you
As far as I know, this is due to a current limitation of Mongoose. You cannot
declare a schema field to include a single sub-document: you have to use an array, instead. See this: https://github.com/Automattic/mongoose/pull/585
You can set up later the proper business logic in order to ensure that only one sub-element will be added.
Try this:
var ZhinengStats = new Schema({
cv_count: Number,
gender_info: [ZhinengGenderSchema],
stability_info_list: [ZhinengStabilitySchema],
zhineng_id: String
})
This way, each sub-document has got its own _id in MongoDB (even though it does not lie in a specific collection). See more: http://mongoosejs.com/docs/subdocs.html
You could also prefer something like this:
var ZhinengStats = new Schema({
cv_count: Number,
gender_info: [{male_count: Number, female_count: Number}],
stability_info_list: [ZhinengStabilitySchema],
zhineng_id: String
})
In this case, you nest a schema inside another. The single gender_info element does not have the dignity of a document.

Mongoose: Generate Schema from existing data

For example, I have user collection in db:
{'_id':0, 'name': 'Joe', 'score':[80, 33]}
{'_id':1, 'name': 'Moe', 'score':[90, 81]}
... ...
How can I read this data with existing format, which means, use it's existing schema without create a new one.
I read Mongoose doc and googled for a while but didn't find satisfied answer.
When using mongoose with an existing collection, you have to reference the collection in your mongoose schema. The way to do this is to add the name of the collection to your schema. So if your collection is in 'mongodb://localhost:27017/test' and called 'things', you would use:
~~~
const Schema = mongoose.Schema;
const ThingSchema = new Schema({
name: {
type: String
}
});
const Model = mongoose.model('Thing', ThingSchema, 'things');
module.exports = Model;
~~~
Thanks to http://chrisflx.blogspot.fr/2014/04/nodejs-mongoose-with-preexisting-data.html?m=1
It will work if you make a model with the same schema.
var schema = new mongoose.Schema({ name: 'string', score: [] });
var user = mongoose.model('User', schema);
Edit:
Mongoose is an ODM, so you need a schema to create objects. If you need to run queries and get raw data from the database, I would stick with this library:
https://github.com/mongodb/node-mongodb-native

Resources