save mongoose model and submodel after populate - node.js

I'm trying to find a way to simplify my backend logic and optimize my DB, for this it would be great if I could save a document and it's subdocuments all at once so that creating subdocuments becomes easier.
Here is a simple example:
the primary model:
var ItemSchema = new Schema({
name: {type : String},
extrainfo: {type: Schema.Types.ObjectId, ref: 'Extrainfo'},
})
mongoose.model('Item', ItemSchema);
the subdocument:
var ExtrainfoSchema = new Schema({
this: {type : String},
that: {type: String},
})
mongoose.model('Extrainfo', ExtrainfoSchema);
how I hoped to be able to save:
Item.findOne({ name: 'whatever' }).populate('extrainfo').exec(function (err, item) {
item.extrainfo.this = "something diferen" //some random change in subdocument
item.save(function(){console.log("yay")}
});
Is there a way to do this whithout having to save to the document and the subdocument individually ?

Is there a way to do this whithout having to save to the document and the subdocument individually ?
No. mongodb will write to one and only one collection during a given write operation, and mongoose doesn't have any feature as far as I know to provide a single-callback facility to write to multiple collections.

Related

Nested objects are not update

Allora, I'm using mongoose for the first time and I decided to create 2 schemes: the first one represents a user and the second one represents his enquires. Users have an array of enquires like:
var userSchema = new mongoose.Schema({
name: String,
enquires: { type : [Enquire.schema] , "default" : [] },
});
var enquireSchema = new mongoose.Schema({
status: {type: String, 'default': 'pending'},
enquire: String,
});
I see that if I search for an enquire and update its status, it doesn't update the same enquire on the user's array, meaning that they are different object. I don't want to save an array of IDs as it will be the same as a relational database, so I see only 1 solution which is forgetting about the enquire scheme and use only the User scheme. Is it the way mongoose works? For every relationship do I have to insert everything like nested object?
I think you should use references to achieve what you want to achieve.
For more information on mongoose references and populate see Mongoose Populate documentation.
Try this, It may help you.
User Schema :
var userSchema = new mongoose.Schema({
name: String,
enquires: [{ type : mongoose.Schema.Types.ObjectId , ref : 'Enquiry' }]//array of enquiries
});
var User = mongoose.model('User',userSchema );
module.exports = User;
Enquiry Schema :
var enquireSchema = new mongoose.Schema({
status: {type: String, 'default': 'pending'},
enquire: String,
});
var Enquiry = mongoose.model('Enquiry',enquireSchema );
module.exports = Enquiry ;
Working :
create a new Enquiry.
Push it's ID(_id) into user's enquires array.
var enquiry = new Enquiry();
enquiry.enquire = "Dummy enquiry";//set the enquiry
enquiry.save(function(err,result){
if(!err){
//push 'result._id' into users enquires array
}
});
whenever you update an enquiry, it will be automatically updated in
user's document.
use populate to retrieve user's enquiries.
You can embed sub documents (entity) which has id and is like a document or embed native array like a normal property.
And I think the correct definition for yours is :
var enquireSchema = new mongoose.Schema({
status: {type: String, 'default': 'pending'},
enquire: String,
});
var userSchema = new mongoose.Schema({
name: String,
enquires: { type : [enquireSchema] , "default" : [] },
});
If you use refs in embedded link then there are two separate collections and be like relational db's.

Store Country, City and User info using mongoose and nodejs MongoDB

I want to use mongodb on my new project. That is first time, im using mongodb. In relational databases i was saving datas like:
tbl_country > id,name,created_at,etc..
tbl_city > id,country_id,name,created_at,etc...
tbl_user > id,name_surname,city_id,etc...
In this schema i can find a city's country, a user's country, etc... via using foreign keys.
Which way do you suggest to create a schema like this in best performance by nodejs w/ mongodb ?
You can still use foreign keys when you create your schemas. Please check this schemas as examples you have given.
var CountrySchema = new Schema({
name: String,
created_at: Date
});
var CitySchema = new Schema({
countryName: { type: mongoose.Schema.Types.ObjectId, ref: 'Country' },
created_at: Date
});
var UserSchema = new Schema({
name_surname: String,
city: { type: mongoose.Schema.Types.ObjectId, ref: 'City' }
})
mongoose.model('Country', CountrySchema);
mongoose.model('City', CitySchema);
mongoose.model('User', UserSchema);
and when you fetch data, you have to use 'populate' method. For instance:
City.find()
populate('countryName', 'name') // It will bring only name field
.exec(function(err, cities) {
})
Btw. _id s are automatically created by mongo for each data. So you do not need to add id field into your schemas. Hope it helps. Good luck

Mongoose custom schema type date interval

I have many fields in my documents of type date intervals, such as this
{
publishDate:
{
start: {type: Date, required: true},
end: {type: Date, required: true}
}
}
To reduce duplication of the code and make it easier to maintain, how to create custom Mongoose type, for instance DateInterval, containing two fields:
start
end
and containing validator that makes sure both fields are filled out, and start is before end?
You can reuse schemas in mongoose.
var DateIntervalSchema = new Schema({
start: {type: Date, required: true},
end: {type: Date, required: true}
});
var SomeSchema = new Schema({
publishDate: [DateIntervalSchema],
// ... etc
});
You can also reference documents from other collections.
var SomeSchema = new Schema({
publishDate: {type: Schema.ObjectId, ref: 'DateInterval'}
});
//using populate
SomeModel.findOne({ someField: "value" })
.populate('publishDate') // <--
.exec(function (err, doc) {
if (err) ...
})
You'll want to develop a custom schema type. There are a number of plugins that do this already, one of which, for long numbers, can be found here: https://github.com/aheckmann/mongoose-long/blob/master/lib/index.js . This is a good basic example to follow.
For your purposes, then, you can create a DateInterval custom schema, casting it as type Date, and then use a validator to check start and end - http://mongoosejs.com/docs/api.html#schematype_SchemaType-validate.
Since mongoose >=4.4 you can implement your custom schema type.
Documentation is not very clear, but you can follow this example.
You have to:
define your DateInterval custom object with toBSON() / toJSON() and toObject() prototype methods
define the DateIntervalType inherited from mongoose.SchemaType for handle the mongoose integration, and casting to DateInterval.
In this way you can achieve full control on memory (Mongoose model) and mongodb (raw's bson) data representation.

How to declare schematype/datatype of a field in mongoose which is the objetid of an item in another collection?

I am a bit confused by something in MongoDB, when using Mongoose, since I am new to it. I have two collections: authors and posts.
each author in the authors collection have the mongodb inbuilt id associated with them, which as per my understanding is of the schematype/datatype Objectid.
Now in my posts collection, I have a field which has is called author, which should have the value of the author's Objectid, similar to the foreign key concept in SQL.
My question is, which schematype/datatype should I declare the author field in the posts collection? Should I put it as Objectid? If so, would it not auto-increment and not be settable?
Mockup Of Schemas:
var authors = new mongoose.Schema({
name: String,
email: String
});
var posts = new mongoose.Schema({
title: String,
author: **??**
});
Any help would be hugely appreciated!
You can use population for that:
var authors = new mongoose.Schema({
name: String,
email: String
});
var posts = new mongoose.Schema({
title: String,
author: { type: Schema.Types.ObjectId, ref: 'Author' }
// assuming the model will be called 'Author'
});
To use:
// Get 'author' some how (create a new one, query the
// database for existing authors, etc);
...
// Create a new post and associate it with 'author' by referencing its `_id`:
var post = new Post({
...
author : author._id
});
post.save(...);
The documentation linked above also shows how you can get Mongoose to automatically retrieve the author document when you're querying posts.

Use mongoose populate to "join" matching subrecords

Using node.js, mongoose (3.5+), mongodb. Have got two collections in the DB:
var AuthorSchema = new mongoose.Schema({
name: { type: String },
});
var StorySchema = new mongoose.Schema({
title: { type: String },
author: { type: type: Schema.Types.ObjectId },
});
What I would like to do is retrieve an author and populate it with a subcollection (say, "stories") that is looked up from Story and match the author. Yes, much like a SQL join.
All of the examples out there work on the AuthorSchema having an array of objectids that reference StorySchema objects - that works fine. But I want to go the opposite direction; partly due to minimizing insert/updates. If I follow the example, adding a new store requires adding a new Story document and updating the Author. I want to just insert a new Story that references the Author.
I suspect that populate() is the right way to go, but can't get it to work. I'm doing something like this:
Author.find({name: 'Asimov').populate({
path: 'stories',
model: 'Story',
match: {'author': this['_id']},
}).exec(function(err, authors) {
console.log(authors);
})
But this doesn't return any stories member in the returned authors. Is this not a populate() solution? Do I really need to structure the schemas differently? Or is there some other feature of mongoose/mongo that would do what I'm looking for.
In the story schema, do this:
author: { type: type: Schema.Types.ObjectId, ref:'Author' }, //or whatever the model name is
then you can run
Story.find({}).populate('author').exec(function(err,stories) {...});

Resources