Am I better off splitting an elaborate mongoose model? - node.js

Assuming the case of a /login API, where, for a matching set of credentials, a user object from the collection should be returned, which approach would be more performant:
1) One model with projection queries:
var UserSchema = new Schema({
name : String,
email : String,
dob : Number,
phone : Number,
gender : String,
location : Object, // GeoJSON format {type: 'Point', geometry: { lat: '23.00', lng: '102.23' }}
followers : [UserSchema],
following : [UserSchema],
posts : [PostSchema],
groups : [GroupSchema]
// ... and so on
});
2) Split models:
var UserMinimalSchema = new Schema({
name : String,
email : String,
phone : Number,
location : Object,
});
var UserDetailSchema = new Schema({
dob : Number,
gender : String,
followers : [UserSchema],
following : [UserSchema],
posts : [PostSchema],
groups : [GroupSchema]
// ... and so on
});
Let's say:
For a logged-in user, only id, name, email, phone and location are to be returned.
The first model will use a projection query to return the properties in (1).
In the second case, only the UserMinimalSchema would be used to query the entire document.
Essentially both queries return exactly the same amount of data as mentioned in (1).
Assume that average user object is ~16MB limit and there are 1 Million records.
If someone performed such a test/links to documentation, it would be of great help to see how much it will matter to split or not.

I would not use split models:
You'll have to perform a population query everytime you want to look all of the user's data
You're increasing your data storage (you now will have to reference the user in your user details schema.
When Mongo will go do a lookup, it will find references to model instances and only extract data that you've specified in your projection query anyways. It will not load the entire object into memory unless you specify that in your query.

Related

what's the ideal way to deal with countries as a property on an object in mongoose (mongodb)

I have a property called country on a jobSchema it is a required string.
Basically just gets the iso-2 letter code (ie: US, NZ or whatever)...and that's what gets saved in the database for the object's country property.
However I have a countries.json file on the filesystem that contains an object containing full name (ie: United States) and lat/long coordinates.
{
"name": "United States",
"iso2": "US",
"lat": "37.09024",
"lng": "-95.712891"
}
I don't really need any crud interface for this. So I've built an API outside of the database to just map the "US" string from the db to the file object.
However, this is proving to be a bit of a PITA.
What is a good way to handle this?
Should I just store this full country object in the db and leave it at that? This seems the easiest route, except of a country definition changes, then I will have all this stale data littered across my mongo db wherever this data was saved.
Consider Mongoose Population:
Population is the process of automatically replacing the specified
paths in the document with document(s) from other collection(s).
You could begin by modifying the "iso2" property of this country object to an _id (since iso codes are unique for each country) and save the modified object in your db. Next is to create a new Mongoose schema for it that you can reference in your jobSchema. For example:
var mongoose = require('mongoose')
, Schema = mongoose.Schema
// Job Schema
var jobSchema = Schema({
name : String,
country : { type: String, ref: 'Country' } // To be populated
});
var countrySchema = Schema({
_id : String,
name : String,
lat : Number,
lng : Number
});
var Country = mongoose.model('Country', countrySchema);
var Job = mongoose.model('Job', jobSchema);
The ref option is what tells Mongoose which model to use during population, in your case the Country model. The country property of the Job model is declared as a String, the same type as the _id used in the countrySchema. It is important to match the type of _id to the type of ref.
To see how population works, just call the populate method on the Job model query and you will get the country field in the documents populated:
Job.find().populate('country').exec(function (error, jobs) {
// ...
});

How to define a single reference schema using mongoose

I am new in node.js and mongodb and as in the below code i define a reference in table employee of department but here when I insert or get data from employee table i always get in array format but i want to define reference as single column not multiple.
var employee = new mongoose.Schema({
name: String,
dept: [department]
});
var department = new mongoose.Schema({
dept_name : String,
dept_code : String
})
I want data in response from employee table in format `{"name":"CS",dept:{"id":_id_of_dept}}
Please guide me the correct way to achieve my objective.
You can only nest using references or arrays, so in this example, you can either create a department record and reference that in the employee document, or have an array of departments for the employee (which I understand is not what you want to do).
To reference the department document from your employee you would use something like:
var department = new mongoose.Schema({
dept_name : String,
dept_code : String
});
mongoose.model('Department', department);
var employee = new mongoose.Schema({
name: String,
dept: { type: Schema.Types.ObjectId, ref: 'Department' }
});
However, then you'll need to populate when querying your employee to gather the department data.
But at a high level, looking at what you're trying to accomplish, I'd suggest just directly storing the department within the employee document. Relational databases may often end up with patterns like you've described above, but in document based databases, there's really not a good reason to separate out the department from the employee. Doing something like what I've put below will help you in the future for queries and general access of the data:
var employee = new mongoose.Schema({
name: String,
dept: {
dept_name : String,
dept_code : String
}
});
This answer might be helpful in understanding what I mean.

How can I query by parent's field in Mongoose?

What I need is, querying the products by its' categories' locale.
Product
.find({
'category.locale' : "en"})
But it is not possible. Where am I wrong?
My Product schema:
var ProductSchema = new Schema({
code: {type : String, default : '', trim : true},
category: {type : Schema.ObjectId, ref : 'Category'}
})
In your Product schema, category contains a reference in the form of an object id.
It's just an _id, so no locale is stored with the category in the product.
There are no joins in MongoDB.
One solution could be to find all categories separately that match locale='en', then use them as a filter on the products query.
This docs page has more details: http://mongoosejs.com/docs/populate.html

self referencing schema in mongoose

I've been through several tutorials. I'm still wondering what the best approach for my problem would be. I got the following Schema:
var userSchema = new Schema({
_id : Number,
first_name : String,
last_name : String,
friends : [ Number ],
messages : [{
from: Number,
body : String,
date : { type : Date, default: Date.now}
}]
}, { collection : "user"});
In friends I want to store the ids of user's friends in an array. In message.from I want to store the sender's id of a message.
Ideally I want those ids in friends and message.from to be only ids of valid user entries.
Unfortunately mongodb doesn't enforce referential integrity.
This functionality must be provided by your application.
So in your case: when a user is deleted your application must also remove references to that user in all other user's friends arrays and message fields.

How can I check if the user already rated the item in NodeJS & Mongoose Application?

I have following schemas in Mongoose:
UserSchema = new Schema({
ratings = [{type : Schema.ObjectId, ref : 'Rating'}] })
ItemSchema = new Schema({
ratings = [{type : Schema.ObjectId, ref : 'Rating'}] })
Rating = new Schema({
user = [{type : Schema.ObjectId, ref : 'User'}],
venue = [{type : Schema.ObjectId, ref : 'Venue'}]
})
Are they right? I should query ratings by users, ratings for items. Also I want to check if the user has already rated an item.
Here are two of the following options you can go with.
You can maintain a separate collection Rating quite similar to what you would have done in SQL.
User: voter (reference to User object),
Item: item_voted (reference to item object),
Rating: what so ever user rated
Time: time_rated,
other fields as per your requirements...
Now maintain index over User and Item to boost up queries to check if user already rated for an item or not.
OR you could maintain an array in User collection for items rated by that user, and index over that array. Here it is what you can have your data model for User like.
items_rated: [item1, item2, item3]
other fields of User as per your requirements...
This second approach has a limitation that it fails if your BSON records exceeds 16MB limit, but in practical usage it very very less probable that you actually would hit that limit. Though nothing can be said. If your Users turn out to be maniac like some top stackoverflow users you will hit that 16MB wall :P
The way you can check if item has been rated or not (if you opt for second choice is)
if (db.user.count({item_rated: item_k, _id:'user-id-1'}) == 0) { ... }

Resources