MongoDB - Documents into other documents and update with Nodejs - node.js

I planned to use MongoDB NoSQL database for a video game, but I'm wondering about some things that I don't understand really clearly and I didn't find answer about them so far.
I understood that it was possible to store a document instance (a car for example) into another document instance (a user), but how does that works? Because if it's a copy by value, if I update my car, the user will have a car that is not up-to-date! So I guess it's a copy by reference. Or maybe it's not a copy but directely some kind of weird link such as we used to do with SGBD databases with the ID field.
But, another thing, if I update my schemas (and I will for sure), the new fields or the OLD fields that previously existed won't be updated in the existing data... It looks like it's a know problem and there is some solution, do you have any good links that explain how deal with that? I'm just thinking here, my DB is not wrote and I want to make the best choices about the design. I never used NoSQL stuff before and I'm trying to design it but I still have a lot of misunderstood and "bad" pratice from SGBD DB.
By the way, MongoDb is a security hole (no password by default, etc.), do you have links to protect a database with mongoDb? Thanks.

I am just learning Mongo myself, but I hope I can provide some help. Note the concept of Mongo being a schema-less database, which means that one User may have a car, another have no car, and others have a different car. So, if you want to update the car definition, you need to modify existing user documents accordingly. There is no central definition for the car - i.e. no relationship to a central car table like in an RDBMS.
You can add some structure to Mongo using Mongoose Schemas. This allows some flexibility for changes of schema, for example you can add new properties and apply a default value, meaning you don't need to update existing documents. i.e.:
BEFORE
var Book = new Mongoose.Schema({
title: {type: String}
});
AFTER
var Book = new Mongoose.Schema({
title: String
category: {type: String, default: 'fiction'}
});

Related

Sub documents vs Mongoose population

I have the following senario:
A user can login to a website. A user can add/delete the poll(a question with two options). Any user can give there opinion on the poll by selecting anyone of the options.
Considering the above scenario I have three models - Users Polls Options . They are as follows, in order of dependency:
Option Schema
var optionSchema = new Schema({
optionName : {
type : String,
required : true,
},
optionCount : {
type : Number,
default : 0
}
});
Poll Schema
var pollSchema = new Schema({
question : {
type : String,
required : true
},
options : [optionSchema]
});
User Schema: parent schema
var usersSchema = new Schema({
username : {
type : String,
required : true
},
email : {
type : String,
required : true,
unique : true
},
password : String,
polls : [pollSchema]
});
How do I implement the above relation between those documents. What exaclty is mongoose population? How is it different from subdocuments ? Should I go for subdocuments or should I use Mongoose population.
As MongoDb hasn't got joins as relational databases, so population is a something like hidden join. It just means that when you have that User model and you will populate Poll Model, mongoose will do something like this:
fetch User
fetch related Polls, by ObjectIds which are stored in User document
put fetched Polls documents into User document
And when you will set User as document and Polls as subdocument, it will just mean that you will put whole data in single document. At one side it means that to fetch User Polls, mongoose doesn't need to run two queries(it need to fetch only User document, because Polls data is already there).
But what is better to choose? It just depends of the case.
If your Polls document will refer in another documents (you need access to Polls from documents User, A, B, C - it could be better to populate it, but not for sure. The advantage of populating is fact, that when you will need to change some Polls fields, you don't need to change that data in every document which is referring to that Polls document(as it will be a subdocument) - in that case in document User, A, B, C - you will only update Polls document. As you see it's nice. I told that it's not sure if populating will be better in that case, because I don't know how you need to retrieve your Polls data. If you store you data in wrong way, you will get performance issues or have some problems in easy data fetch.
Subdocuments are the basic way of storing data. It's great when Polls will be only referring to User. There is performance advantage - mongoose need to do one query instead of two as in population and there is no previously reminded update disadvantage, because you store Polls data only in single place, so there is no need to update other documents.
Basically MongoDb was created to mostly use Subdocuments. As the matter of fact, it's just non-relational database. So in most cases I prefer to use subdocuments. I can't answer which way will be better in your case, because I'm not sure how your DB looks like(in a full way) and how you want to retrieve your data.
There is some useful info in official documentation:
http://mongoosejs.com/docs/subdocs.html
http://mongoosejs.com/docs/populate.html
Take a look on that.
Edit
As I prefer to fetch data easily, take care about performance and know that data redundancy in MongoDb is something common, I will choose to store this data as subdocuments.

How to make a subdocument have two parent documents in mongodb?

I'm using mongoose with Node.js for an application. I have a Document class, which has a Review subdocument. I also have a User class.
I want the user to be able to see all the reviews they've done, while I also want the Document to be able to easily get all of its reviews. Searching through all the documents and all their reviews to find ones matching a user seems horribly ineffecient. So, how do I allow the Review to be owned by both a Document and a User?
If this is impossible, how else can I efficiently have two documents know about one subdocument.
If you don't want to deal with consistency issues I don't think there's any way except for normalization to assign two parents for a document. Your issue is a common one for social networks, when developers have to deal with friends, followers, etc. Usually the best solution depends on what queries you are gonna run, what data is volatile and what is not and how many children a document might have. Usually it turns out to be a balance between embedding and referencing. Here's what I would do if I were you:
Let's assume Documents usually have 0-5 Reviews. Which is a few, so we might consider embed Reviews into Documents. Also we would often need to display reviews every time a Document is queried, this is one more reason for embedding. Now we need a way to query all reviews by a User efficiently. Assume we don't run this query as often as the first one but still it is important. Let's also assume that when we query for User's Reviews we just want to display Review titles as links to Review page or even Document page as probably it's hard to read a review without seeing the actual Document. So the best way here would be to store { document_id, review_id, reviewTitle }. ReviewTitle should not be volatile in this case. So now when you have a User object, you can easily query for reviews. Using document_id you will filter out most documents and it will be super fast. Then you can get required Reviews either on the client side or by using MapReduce to turn Reviews into separate list of documents.
This example contains many assumption so it might not be exactly what you need by my goal was to show you the most important things to consider while designing your collections and the logic you should follow. So just to sum up, consider QUERIES, HOW VOLATILE SOME DATA IS and HOW MANY CHILDREN A DOCUMENT IS GONNA HAVE, and find a balance between embeding and referencing
Hope it helps!
This is an old question, but here is a solution that, I think, works well:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var DocumentSchema = new Schema({
title: String,
...
});
mongoose.model('Document', DocumentSchema);
var UserSchema = new Schema({
name: String,
...
});
mongoose.model('User', UserSchema);
var ReviewSchema = new Schema({
created: {
type: Date,
default: Date.now
},
document: {
type: Schema.ObjectId,
ref: 'Document'
},
reviewer: {
type: Schema.ObjectId,
ref: 'User'
},
title: String,
body: String
});
Then, you could efficiently query the Reviews to get all reviews for a User, or for a Document.
Hope this helps someone.

Effective mongodb + mongoose. Schema design

I'm new to mongodb and nosql databases. I would really appreciate some input/help with my schema design so I don't shoot myself in the foot.
Data: I need to model Quotes. A Quote contains many Ttems. Each Item contains many Orders. Each Order is tied to a specific fiscal quarter. Ex. I have a Quote containing an Item which has Orders in Q3-14, Q4-14, Q1-15. Orders only go max 12 quarters (3 years) into the future. Specifically, I'm having trouble with modelling the Order-quarter binding. I'm trying to denormalize the data and embed Quote <- Items <- Orders for performance.
Attempts/Ideas:
Have an Order schema containing year and qNum fields. Embed an array of Orders in every Item. Could also create virtual qKey field for setting/getting via string like Q1-14
Create a hash that embeds a Orders into an Item using keys like Q1-14. This would be nice, but isn't supported natively in Mongoose.
Store the current (base) quarter in each Quote, and have each Item contain an array of Orders, but have them indexed by #quarters offset from the base quarter. I.e. if It's currently Q1-14, and an order comes in for Q4-14, store it in array position 2.
Am I totally off the marker? Any advice is appreciated as I struggle to use Mongo effectively. Thank you
Disclaimer: I've embarked on this simply as a challenge to myself. See the <rant> below for an explanation as to why I disagree with your approach.
First step to getting a solid grasp on No-SQL is throwing out terms like "denormalize" – they simply do not apply in a document based data store. Another important concept to understand is there are no JOINS in MongoDB, so you have to change the way you think about your data completely to adjust.
The best way to solve your problem with mongoose is to setup collections for Quotes and Items separately. Then we can set up references between these collections to "link" the documents together.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var quoteSchema = new Schema({
items: [{ type: Schema.Types.ObjectId, ref: 'Item' }]
});
var itemSchema = new Schema({});
That handles your Quotes -> Items "relationship". To get the Orders setup, you could use an array of embedded documents like you've indicated, but if you ever decided to start querying/indexing Orders, you'd be up a certain creek without a paddle. Again, we can solve this with references:
var itemSchema = new Schema({
orders: [{ type: Schema.Types.ObjectId, ref: 'Order' }]
});
var orderSchema = new Schema({
quarter: String
});
Now you can use population to get what you need:
Item
.findById(id)
.populate({
path: 'orders',
match: { quarter: 'Q1-14' }
})
.exec(function (err, item) {
console.log(item.orders); // logs an array of orders from Q1-14
});
Trouble with references is that you are actually hitting the DB with a read instruction twice, once to find the parent document, and then once to populate its references.
You can read more about refs and population here: http://mongoosejs.com/docs/populate.html
<rant>
I could go on for hours why you should stick to an RDBMS for this kind of data. Especially when the defense for the choice is a lack of an ORM and Mongo being "all the rage." Engineers pick the best tech for the solution, not because a certain tech is trending. Its the difference between weekend hackery and creating Enterprise level products. Don't get me wrong, this is not to trash No-SQL – the largest codebase I maintain is built on NodeJS and MongoDB. However, I chose those technologies because they were the right technologies for my document based problem. If my data had been a relational ordering system like yours, I'd ditch Mongo in a heartbeat.
</rant>

Mongoose - flexible field

I am new to NOSQL and MongoDB, I am building an app with NodeJS and Mongoose, and I am building a mongoose schema for a new collection.
The documents of this collection will have some standard fields (id, creation date, user etc...) but then I need to store other stuff, which is a "data" field which will have to contain different data depending on the document. The value will sometimes be simple text and other times it will have lots of key/values pairs.
I am wondering what would be the best solution for this kind of storing needs :
-Create only one "data" field with a String type and then put different types of data into it (text for simple values, stringified objects for more complex data)
-Create in the model all the possible fields that my "more complex data" could have and use only the ones I need in each document
-Something else
What is the best practice for this kind of thing ?
Try to use the Mixed schemaType, I think that's what you are looking for:
http://mongoosejs.com/docs/schematypes.html
This one:
-Create in the model all the possible fields that my "more complex data" could have and use only the ones I need in each document
Mark the standard fields of the schema as required: true and leave the rest optional. That way you get the flexibility you want without losing the Mongoose benefits of validation, casting, and change detection.

Designing MongoDB model?

i have one conceptual question about designing data model in mongo db.
I have some record for "lists". Each list is "one row" record with id, descr, createdBy etc. fiels. But now i have a requirement that each "list" can be shared with other users. What is the best way to design/re-design the model so when user login to see "lists" that he created and "lists" that was shared with him? I was thinking of creating new field "sharedWith" in which all usernames with which "list" is shared to be put separated with comma(or something else). And then when request is made to search in this field. But somehow this approach doesn't seem to me very useful. Can you give me some advice or guidelines ?
I'm using nodejs with mongoose.
Thanks!
User: {
...
myLists: Array<ListObject>
sharedLists: Array<ListObject>
};
ListObject: {
...
owner: User,
sharedWith: Array<User>
};
That would seem a sensible design to me.

Resources