One to Many mapping in mongoose, How to receive and process? - node.js

The problem I have is a One to many mapping with mongoose(Mongodb). The one is the Order(buyer data) and the many is the Items(price,quantity,etc).
1) How I should create the Schema for the the Order and items, like should I put the items in an array in the order?
2) Would all the data be in one post function?
I herd you can use ObjectId to link the many to one but I am not sure how to.

Since an Order sounds like it will have a relative small number of items, the simplest thing would probably be just a list of item Ids:
var OrderSchema = new mongoose.Schema({
items: [{type: mongoose.Schema.Types.ObjectId, ref: 'Item'}]
});
var ItemSchema = new mongoose.Schema({
price: Number,
quantity: Number
});
Most UIs would not build an entire order in a single POST function, so it's probably best to allow creating an order and then separately adding items to it via order.items.push(itemId).

Related

Using mongoose.Types.ObjectId() with multiple scaled machines (Duplication issue?)

Let's just assume the below code will be executed many times when running a live server. If I scaled the machine to have 10 instances for example, is there a small chance the ObjectId will be duplicated when saving into mongoDb? Or will mongoDb force the _id to be different if there is duplication?
// Taken from mongoose documentation!
const personSchema = Schema({
_id: Schema.Types.ObjectId,
name: String,
age: Number,
});
const Person = mongoose.model('Person', personSchema);
const author = new Person({
_id: new mongoose.Types.ObjectId(),
name: 'Ian Fleming',
age: 50
});
author.save(() => {
console.log('saved!')
});
Basically, what I want to achieve is to create an Id before I save it. I don't want to have to save documents and then collect their _ids.
Scaling your server to multiple instances won't affect the value of generated ObjectIDs such that you now have duplicate ObjectIDs. The uniqueness of the ObjectID generated is not just a function of the time it was generated, the generator(MongoDB node driver in this case) also factors in the unique machine and process properties.
If what you want is to create the _id field yourself before saving documents into the DB, you can easily do that without any trouble, in fact, you don't have to use a hexadecimal string like the one generated, you can make use of string or numbers but then, you would have to ensure the uniqueness yourself.

Mongoose: Difference between referencing "Schema.ObjectId" instead of directly using the schema name?

Suppose I have the following MessageSchema model:
var MessageSchema = new Schema({
userId: String,
username: String,
message: String,
date: Date
});
mongoose.model('Message', MessageSchema)
Can someone tell me the difference between the following two implementations of the Meetings model? Thanks.
var Meetings = new Schema({
_id: ObjectId,
name: String,
messages: [MessageSchema],
...
});
var Meetings2 = new Schema({
_id: ObjectId,
name: String,
messages: [{type: Schema.ObjectId, ref: 'Message'}],
...
});
The main difference is that Meeting model is embedding the MessageSchema (denormalization) whilst the Meeting2 model references it (normalization). The difference in choice boils down to your model design and that depends mostly on how you query and update your data. In general, you would want to use an embedded schema design approach if the subdocuments are small and the data does not change frequently. Also if the Message data grows by a small amount, consider denormalizing your schema. The embedded approach allows you to do optimized reads thus can be faster since you will only execute a single query as all the data resides in the same document.
On the other hand, consider referencing if your Message documents are very large so they are kept in a separate collection that you can then reference. Another factor that determines the referencing approach is if your document grows by a large amount. Another important consideration is how often the data changes (volatility) versus how it's read. If it's updated regularly, then referencing is a good approach. This way enhances fast writes.
You can use a hybrid of embedding and referencing i.e. create an array of subdocuments with the frequently accessed data but with a reference to the actual document for more information.
The general rule of thumb is that if your application's query pattern is well-known and data tends to be accessed only in one way, an embedded approach works well. If your application queries data in many ways or you unable to anticipate the data query patterns, a more normalized document referencing model will be appropriate for such case.
Meetings messages field contains an array of Message object, while Meetings2 messages field contains an array of Message Id's.
var Meetings2 = new Schema({
...
messages: [{type: Schema.ObjectId, ref: 'Message'}],
...
});
can be written as
var Meetings2 = new Schema({
...
messages: [Schema.ObjectId],
...
});
The ref is just a helper function in mongoose, making it easier to populate the messages.
So in summary. In Meetings you embed the messages in an array, while in Meetings2 you reference the messages.

query on many to many relation mongodb database struct

I have two collections in MongoDB: one saves post data of blog, the other saves comment data of blog with below schemas. How can I use nodejs and mongoose to query all posts with comment belong to it and respond to single page application?. Thanks!
var PostSchema = mongoose.Schema({
created: {
type: Date,
default: Date.now
},
content: {
type: String,
default: '',
trim: true
},
user: {
type: Schema.ObjectId,
ref: 'user'
}
});
var CommentSchema = mongoose.Schema({
created: {
type: Date,
default: Date.now
},
content: {
type: String,
default: '',
trim: true
},
ofpost: {
type: Schema.ObjectId,
ref: 'post' //which post this comment belong to
},
user: {
type: Schema.ObjectId,
ref: 'user'
}
});
var Post = mongoose.model('Post', PostSchema);
var Comment = mongoose.model('Comment', CommentSchema);
//example:the Comment1 and Comment2 belong to Post1
var Post1 = new Post({ content: 'good day', user: 'John' });
var Comment1 = new Comment({content: 'yeah', ofpost: Post1._id, user:'Tom'})
var Comment2 = new Comment({content: 'agree', ofpost: Post1._id, user:'Tina'})
As mongodb is NoSQL type of database and has no JOIN's or any sort of relationship between documents, you have to take care of such.
There are generally two ways to do so:
Caching
Consider storing comments data within blog document. You can have embedded documents without any problem. In reality it leads to some extra caches, like comments count, array of user id's of comments and other stuff that will make your queries indexed and more easy ways to search through collection.
Multiple Queries
If you still need separate collections, then you need to 'simulate' joins. Most efficient ways is to make temporary indexing arrays and multiple queries to different collections. Usually it should be just 2 queries for one Join (many to many), and small iteration to add second query documents to first array of documents.
Here is the flow that is suitable and performs well still, on example:
Two collections, first is posts, and second is comments which has id of post.
Make query to posts.
Iterate through each post and add its id into postIds array, as well make postMap object where key will be id of post and value will be specific post. - this is so called indexing posts.
Make query to comments collection with $in argument with postIds array of post id's. This collection should have indexing on post id field in order to make this query very efficient. As well this query can include sorting by date (additional compound indexing will speedup it).
Iterate through each comment and using postMap add it to comments array of post.
So we have only 2 queries, and one iteration through all comments to embed data into posts O(n). Without second step, adding to posts will be potentially O(p*c) where p - number of posts and c - number of comments. Which is obviously much slower as well on big queries can be potentially slow.
Summary
Second approach is more manageable approach from data point of view, as well is easier on writes, while is more complicated on reads.
Still will require some caching, like number of comments for blog posts.

Mongoose – linking objects to each other without duplicating

I have a model "Category". Collection categories contains several objects.
I also a have model "Post". Collection posts may contain a lot of objects with users' posts. "Post" object may relate to 1+ categories. How to link "Post" object to 1+ "Category"-objects without placing "Post"-object inside "Category"-object as subdocument? Certainly, I need to have an option to find all posts related to certain category.
One of the ways I can imagine is to store in "Post"-object obj_id of all categories which it's related to. Smth like this:
var postSchema = mongoose.Schema({
title: String,
description: String,
category: [ObjectId],
created_time: Number,
})
and add category later...
post.category.push(obj_id);
but is it really a mongoose-way? Which way is correct? Thanks.
P.S. I've also read about population methods in mongoose docs, may it be useful in my case? Still not completely clear for me what is this.
Populate is a better tool for this since you are creating a many to many relationship between posts and categories. Subdocuments are appropriate when they belong exclusively to the parent object. You will need to change your postSchema to use a reference:
var postSchema = mongoose.Schema({
title: String,
description: String,
category: [{ type: Schema.Types.ObjectId, ref: 'Category' }],
created_time: Number,
});
You can add categories by pushing documents onto the array:
post.category.push(category1);
post.save(callback);
Then rehydrate them during query using populate:
Post.findOne({ title: 'Test' })
.populate('category')
.exec(function (err, post) {
if (err) return handleError(err);
console.log(post.category);
});

Proper way to create nested objects in mongo

I am trying to create an array of nested objects. I am following an example from a book that does the following:
// Creates the Schema for the Features object (mimics ESRI)
var Phone = new Schema({
number: { type: Number, required: false },
...
personId: {type: Schema.Types.ObjectId}
}
);
// Creates the Schema for the Attachments object
var Person = new Schema({
name: { type: String },
phones: [Phone]
}
);
var Person = mongoose.model('Person', Person);
Which works just fine when storing multiple Phone #'s for a person. However I am not sure if there is a good/fast way to get a Phone object by _id. Since Phone is not a mongoose model you cannot go directly to Phone.findOne({...}); Right now I am stuck with getting a person by _id then looping over that persons phones and seeing if the id matches.
Then I stumbled upon this link:
http://mongoosejs.com/docs/populate.html
Is one way more right than the other? Currently when I delete a person his/her phones go away as well. Not really sure that works with 'populate', seems like I would need to delete Person and Phones.
Anyone want to attempt to explain the differences?
Thanks in advance
The general rule is that if you need to independently query Phones, then you should keep them in a separate collection and use populate to look them up from People when needed. Otherwise, embedding them is typically a better choice as it simplifies updates and deletion.
When using an embedded approach like you are now, note that Mongoose arrays provide an id method you can use to more easily look up an element by its _id value.
var phone = person.phones.id(id);

Resources