One-To-Many relation in MongoDB - node.js

At the moment I am looking at mongoDB. I try to implement a simple one-to-many relation using nodejs and mongoose:
Model/Schema User:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var UserSchema = new Schema({
name: String
});
module.exports = mongoose.model('User', UserSchema);
Model/Schema Article:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ArticleSchema = new Schema({
name: {
type: String,
required: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
module.exports = mongoose.model('Article', ArticleSchema);
So my question, now:
How can I get all Users including its Articles?
Do I really have to add a ref to my UserScheme, too? What is the best-practice in an one-to-many relation like this? Is there something like a join in mongodb?
One User has many Articles - an Article belongs to one User.
Calling something like /user/:user_id , I want to receive the user with _id=user_id containing all of his articles.

That is the most horrible idea, for various reasons.
First, there is a 16MB BSON document size limit. You simply can not put more into a document. Embedding documents is rather suited for "One-to-(VERY-)Few" relationships than for a "One-to-Many".
As for the use case: What is your question here? It is
For a given user, what are the articles?
REST wise, you should only return the articles when /users/:id/articles is GETed and the articles (and only the articles) should be returned as a JSON array.
So, your model seems to be natural. As for the user:
{
_id: theObjectId,
username: someString
…
}
and an article should look like this:
{
_id: articleIdOrSlugOrWhatever,
authors: [theObjectId],
// or author: theObjectId
retention: someISODate,
published: someOtherISODate
}
So when your REST service is called for /users/:id you'd simply look up
var user = db.users.findOne({_id:id})
And when /users/:id/articles is called, you'd lookup
var articles = db.articles.find({author:id})
Problem solved in a scalable way, adhering to REST principles.

Related

Schema design suitable for big system

I am designing a follow feature for my online books reading which had about 100k books. So I wonder which is a better way to use MongoDB.
Push all books followed into array and store as 1 document. When reach max document we split into another document
When the user followed a book we create a need documents
Please help me point out the Pros and Cons of each way or a new way better than both above way to reach this situation
const mongoose = require('mongoose');
const {comicConnection} = require('../db');
const Schema = mongoose.Schema;
const UserFollowsSchema = new Schema(
{
userId: { type: Schema.Types.ObjectId, ref: 'Users' },
follow: [{ type: Schema.Types.ObjectId, ref: 'Books' }],
},
{
timestamps: true,
}
);
module.exports = comicConnection.model(
'UserFollows',
UserFollowsSchema,
'UserFollows'
);
It is better to use the second method, you have better access and you can do calculations more easily.
Whenever a user follows a book, create a document that the user follows the book, and so on.
Then you can use this to find out how many books you have followed:
User.countDocuments({user:{USER-ID}});

How to show relationship between schemas in mongodb like in sql database?

I have got two models one is "Posts" and another one is "Comments". I want to show relationship between these two models which is a single post has may comments, but i am stuck here. I know how to show these kind of relationship in sql database. Currently i am using mongoose-schema and node js.
To make relation in between Posts and Comments schema
// Comments Schema
var CommentSchema = new Schema({
// your fields
});
module.exports = mongoose.model('Comments', CommentSchema);
// Post schema
var PostSchema = new Schema({
// your fields
comments:[{
type: Schema.Types.ObjectId,
ref:'Comments'
}],
});
// so in comments you will store comments._id values
module.exports = mongoose.model('Posts', PostSchema);

Mongoose nested schema vs nested models

What is the difference between nesting schema in schema (subdocuments) vs creating two separate models and referring to them, What about their performance?
subdocuments:
const postSchema = new Schema({
title: String,
content: String
});
const userSchema = new Schema({
name: String,
posts: [postSchema]
});
module.export = mongoose.model('User', userSchema);
nested models (Populating by reference):
const postSchema = new Schema({
title: String,
content: String,
author: { type: String, ref: 'User' }
});
module.export = mongoose.model('Post', postSchema);
const userSchema = new Schema({
name: String,
posts: [{ type: Schema.Types.ObjectId, ref: 'Post'}]
});
module.export = mongoose.model('User', userSchema);
Edit: This is not a duplicate question.
In this question: Mongoose subdocuments vs nested schema - mongoose subdocuments and nested schema is exactly the same.
BUT nested models creating a separate collection in database.
My question is what is diffrence in nested schema vs nested models, not subdocuments vs nested schema.
When using subdocuments, you actually have a copy of the data within your parent-document, wich allows you to get all the document + sub-document-data in a single query.
When using "nested models" you're not really nesting them, but referencing from the parent-model to the child-model. In this case you have to use population, which means you can't get all the data in a single query.
In short: subdocuments actually nest the data, and your "nested models" only reference them via their id

Structuring a list of favourites with MongoDB & Mongoose?

I am starting to use mongo, and I would like to create a schema for items that a user has 'favourited'. My current code, using mongoose and node.js looks at follows:
// load the things we need
var mongoose = require('mongoose');
// define the schema for our favourites model
var favouritedItemsSchema = mongoose.Schema({
userId : Number,
item : [{
itemId : Number,
addedDate : Date
}]
});
// create the model for favourites and expose it to our app
module.exports = mongoose.model('Favourites', favouritedItemsSchema);
Coming from a relational DB background, I am wondering whether the above approach would represent a suitable NoSQL DB design approach? If not, can someone show me what would be something that fits the design philosophy?
Yes, you are right, the relational and the NoSQL design approach are totally different.
Where you have 10 tables for example in RDBMS, you could have only 2 or 3 collections in mongo. That's because the way we create relations between objects is far more interesting in NoSQL (subdocument, arrays, etc..).
Here is one solution for your problem, reusing an existing User collection.
// load the things we need
var mongoose = require('mongoose');
// define the schema for our model
var userSchema = mongoose.Schema({
username: string,
favourites: [{
id: Schema.Types.ObjectId,
addedDate: Date
}]
});
// export model
module.exports = mongoose.model('User', userSchema);

How would I develop a relationship User belongs to Groups in Mongoose & Node.js?

I am trying to teach myself node.js, coming from Rails, and I want to establish this relationship:
A group has_many users, a user belongs_to group
How would I go about doing this in my models?
//for user
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
name: String
});
module.exports = mongoose.model('User', UserSchema);
//for group
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var GroupSchema = new Schema({
name: String
});
module.exports = mongoose.model('Group', GroupSchema);
Also I would like to allow a moderator to kick someone out of a group if the person were obnoxious. How could I target a user belonging to a group and then delete the user. It seems more complicated than the traditional CRUD methods. Do I need nested resources like in rails, or the equivalent?
Mongoose has a feature called population that you can use to set up "relationships" (MongoDB only has limited support for relationships).
Your schema would look something like this:
var UserSchema = new Schema({
name : String,
group: { type: Schema.Types.ObjectId, ref: 'Group' }
});
var GroupSchema = new Schema({
name : String,
users : [ { type: Schema.Types.ObjectId, ref: 'User' } ]
});
In other words: the User model has a group property which points to the group the user belongs to, and the Group model has a users property (an array) that contains references to the users that belong to the group.
To associate a user to a group (assuming that group is a variable that is a Group instance):
user.group = group;
user.save(...);
And to add the user to a group:
group.users.push(user);
group.save(...);
However, you should be aware that array properties in MongoDB documents have limited scalability. I think that a few dozen items in an array will work just fine, but when the number of users in a group are expected to be in the thousands, this probably won't be a viable solution. In that situation, you probably should consider a "junction collection" (similar to SQL's "junction table").
To remove a user from a particular group, I think you need to use .update():
Group.update({ _id: group._id }, { $pull : { users : user._id }})

Resources