Improve performance of query of database design sechema in mongodb - mongodb

I have implemented mongodb schema design for groups where i have a query to get the groups of a specific user. I am thinking to store data as given schema.
For Example
var GroupSchema = new Schema({
name:String,
mememers: [{
userId: { Schema.ObjectId, ref: 'User'}
}]
});
and
var UserSchema = new Schema({
name:String,
email:String,
groups:[{ type:Schema.ObjectId, ref:'Group'}]
});
But i am still confused that what query will be efficient for the query.
var group = Group.find({ "memebers.userId": "***UserId***"});
group.exec(function(err, groups){ });
or
var user = User.findOne({_id:"***userId**"}).populate("groups");
user.exec(function(err, user){ });
Please give me the pro and cons of the both the queries.

Related

MongoDB (Mongoose) data structure question

I'm curious about the best way to represent this kind of situation in Mongo. I have my own idea, but I'm curious on what the general consensus/best practice actually would be.
Imagine I have two collections:-
Employees
--> _id
--> FirstName
--> Surname
--> Email
Comments
--> _id
--> PersonReference
--> CommentDate
--> Comment
Now imagine that Employees can come and go and the 'Employees' collection is always up-to-date. However, in the event that an employee has ever made a comment, the full information on the comment including who made it must be available.
The way I would propose to tackle this problem, is to organise the structure like this instead:-
Employees
--> _id: _id
--> FirstName: string
--> Surname: string
--> Email: string
Comments
--> _id: _id
--> CommentDate: date
--> Comment: string
[-] --> PersonReference
[+] --> Employee: object { _id: id, FirstName: string, Surname: string, Email:string }
So essentially, I would have a list of 'Active Employees' and at a time where a comment is made, I would duplicate the employee information into the Comments collection document (rather than use a reference).
From a high level perspective, is this considered best practise?
Many thanks
Duplicating the employee info in the comments collection is really a bad idea.
When an employee info needs to be changed, it will also needs to be updated in the comments.
You have a few options:
1-) Embedding the comments inside the Employee schema:
In this method we have no separate Comments collection.
If you have no need to independently query comments, this method makes sense.
This way we can access a user and his/her comments in one db access and without needing any join (populate or lookup).
The schema for this can be like this:
const mongoose = require("mongoose");
const employeeSchema = new mongoose.Schema({
firstName: String,
username: String,
email: String,
comments: [
new mongoose.Schema({
commentDate: Date,
comment: String
})
]
});
module.exports = mongoose.model("Employee", employeeSchema);
2-) Parent referencing:
In this method we keep the reference of the comments in the Employee schema.
If you don't need to access to employee from a comment, this can an option.
Employee Schema:
const mongoose = require("mongoose");
const employeeSchema = new mongoose.Schema({
firstName: String,
username: String,
email: String,
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});
module.exports = mongoose.model("Employee", employeeSchema);
Comment Schema:
const mongoose = require("mongoose");
const commentSchema = new mongoose.Schema({
commentDate: Date,
comment: String
});
module.exports = mongoose.model("Comment", commentSchema);
3-) Child referencing
In this method we keep reference of the employee in the comments.
So if you need to access the comments from an employee we need to use Populate Virtual feature of mongoose. Becase in employee schema we don't have a reference to the comments.
Employee Schema:
const mongoose = require("mongoose");
const employeeSchema = new mongoose.Schema(
{
firstName: String,
username: String,
email: String
},
{
toJSON: { virtuals: true } // required to use populate virtual
}
);
// Populate virtual
employeeSchema.virtual("comments", {
ref: "Comment",
foreignField: "employee",
localField: "_id"
});
module.exports = mongoose.model("Employee", employeeSchema);
Comment Schema:
const mongoose = require("mongoose");
const commentSchema = new mongoose.Schema({
commentDate: Date,
comment: String,
employee: {
type: mongoose.Schema.Types.ObjectId,
ref: "Employee"
}
});
module.exports = mongoose.model("Comment", commentSchema);
4-) Both parent and child referencing:
With this method, it is possible to select comments from employee, and employee from comments. But here we have some kind of data duplication, and also when a comment is deleted, it needs to be done in both of the collections.
const mongoose = require("mongoose");
const employeeSchema = new mongoose.Schema({
firstName: String,
username: String,
email: String,
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});
module.exports = mongoose.model("Employee", employeeSchema);
Comment Schema:
const mongoose = require("mongoose");
const commentSchema = new mongoose.Schema({
commentDate: Date,
comment: String,
employee: {
type: mongoose.Schema.Types.ObjectId,
ref: "Employee"
}
});
module.exports = mongoose.model("Comment", commentSchema);
Many database implement kind of no-delete collections, implementing a delete/active flag for each document.
For example, Employees collection would become :
Employees
--> _id: _id
--> FirstName: string
--> Surname: string
--> Email: string
--> Active: boolean
This way, you keep track on employees data that has been deleted, and prevent documents duplication if you have database size restrictions.
PS: nowadays you can be tackled keeping user data if they ask deletion (RGPD)
EDIT: This solution with boolean may not work if Employees document is updated and you want to keep employees firstname,name,mail,etc at the time he made the Comment.

Mongoose - populate multiple ids

I am new to mongoose and I was strugling whole day trying to understand populate. I managed to do simple examples but now I created two schemas:
First which is UserSchema with some user details:
const UserSchema: mongoose.Schema = new mongoose.Schema ({
name: String,
email: String
});
And second which is MatchSchema witch I want to be populated with user details but I am not sure if something like this will work:
const MatchSchema: mongoose.Schema = new mongoose.Schema ({
player_one: {
id: String,
score: Number,
player_details: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
},
player_two: {
id: String,
score: Number,
player_details: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
},
winner: String
},{timestamps: true});
Probably I used something which wont work and any help will be appriciated.
You need to create a Mongoose model using the UserSchema and name it 'User'. You can then create a Match model using the MatchSchema. Assuming the UserSchema and MatchSchema are in the same file, you can add the following:
const User = mongoose.model('User', UserSchema)
const Match = mongoose.model('Match', MatchSchema)
Then when you want to populate the Match model with User data:
let data = Match.find({})
.populate('player_one.player_details')
.populate('player_two.player_details')

class registration schema recommendation

I am trying to design a database of a class registration relationship. Is this the best way for this situation?
Registration schema
const mongoose = require("mongoose");
//Course Schema
//Faculty can add courses to the DB
const registrationSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
courseID: { type: mongoose.Schema.Types.ObjectId, ref: "Course" },
studentID:{ type: mongoose.Schema.Types.ObjectId, ref: "User" }
});
module.exports = mongoose.model("Registration", registrationSchema);
Instead of a seperate registration collection, why not try modelling using 'Two-way embedding' approach? refer
So your Course collection will have array of students reference and Student collection will have an array of courses reference, for easy retrieval.

Mongo Reference Schema Collections

I’m looking to confirm that my collection structures will correctly allow for the reference I’m trying to make between my collections. Ideally I have a parent collection labeled category, where the child collection is labeled images. I plan on having around 4 documents for my “category” collection and 30 documents for my “images”. All of the documents were going to be uploaded from the command line with a csv file format. If I set up my schemas like I do below and upload the documents with headers matching the fields in the schemas, will I not have an issue with the reference points?
Category
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var categorySchema = new Schema({
name: String,
description: String
});
var Category = mongoose.model(‘Category’, categorySchema);
module.exports = Category;
Images
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var imageSchema = new Schema({
imageUrl: String,
category_id: { type: sechema.ObjectId, ref:"categorySchema"}
});
var Images = mongoose.model('Images', imageSchema);
module.exports = Images;
Here is the layout of my .csv files
category.csv
**name** **description**
Drama Stories about...
images.csv
**imageUrl** **category_id**
www.site.com/image.jpg Drama
Hopefully there can be only one category belonging to an image. One category having many images and each image belongs to only one category.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var categorySchema = new Schema({
name: String,
description: String
images: [{type: mongoose.Schema.Types.ObjectId, ref: 'Images'}]
});
var Category = mongoose.model(‘Category’, categorySchema);
module.exports = Category;
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var imageSchema = new Schema({
imageUrl: String,
category_id: {type: mongoose.Schema.Types.ObjectId, ref:"Category"}
});
var Images = mongoose.model('Images', imageSchema);
module.exports = Images;
Note: You would reference your model not Schema.
Then on you can use mongoose middleware to update your references while saving.
imageSchema.pre('save', function(next){
var Category = require("../models/Category");
var image = this;
Category.findOneAndUpdate(
{_id: {$in: {this.category_id}},
{$push: {images: this._id}},
next
);
});

Mongoose populate() returning empty array

so I've been at it for like 4 hours, read the documentation several times, and still couldn't figure out my problem. I'm trying to do a simple populate() to my model.
I have a User model and Store model. The User has a favoriteStores array which contains the _id of stores. What I'm looking for is that this array will be populated with the Store details.
user.model
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var UserSchema = new Schema({
username: String,
name: {first: String, last: String},
favoriteStores: [{type: Schema.Types.ObjectId, ref: 'Store'}],
modifiedOn: {type: Date, default: Date.now},
createdOn: Date,
lastLogin: Date
});
UserSchema.statics.getFavoriteStores = function (userId, callback) {
this
.findById(userId)
.populate('favoriteStores')
.exec(function (err, stores) {
callback(err, stores);
});
}
And another file:
store.model
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var StoreSchema = new Schema({
name: String,
route: String,
tagline: String,
logo: String
});
module.exports = mongoose.model('Store', StoreSchema);
After running this what I get is:
{
"_id": "556dc40b44f14c0c252c5604",
"username": "adiv.rulez",
"__v": 0,
"modifiedOn": "2015-06-02T14:56:11.074Z",
"favoriteStores": [],
"name": {
"first": "Adiv",
"last": "Ohayon"
}
}
The favoriteStores is empty, even though when I just do a get of the stores without the populate it does display the _id of the store.
Any help is greatly appreciated! Thanks ;)
UPDATE
After using the deepPopulate plugin it magically fixed it. I guess the problem was with the nesting of the userSchema. Still not sure what the problem was exactly, but at least it's fixed.
I think this issue happens when schemas are defined across multiple files. To solve this, try call populate this way:
.populate({path: 'favoriteStores', model: 'Store'})