Mongoose Populate Use or Not Use? - node.js

Definitions
I have Post Model in mongoose:
{
sender: ObjectId, // User Id
title : String,
...
}
I want to list my Post with their User's title.
And I have two choice:
1- List Posts > Extract unique Senders > Query for User titles > Replace Ids with Titles in results
One query to list Posts and one query to list unique Users
2- Use mongoose populate method in schema: sender: {type:ObjectId, ref: User},
And use the new populated value for sender in result like: sender.title
Base on how mongoose populate values may has different number of queries
Question!:
When mongoose populate 'sender' property, What does it do?
Because I need to use the best option for my project (And readable one)!
1- Use a new query for each Id
To List 1000 Post we have 1001 queries!! even when we have repeated users!!
2- Or Use a query for each unique Id
To List 1000 Post from 100 Users we have 101 queries!
3- Or even better list unique User ids and query all together (like choice one)
We have only 2 queries!! (the best if possible)

Option 3 - Mongoose will get the posts and then query users exactly once with the $in operator.
Even after doing this, the performance of doing it manually will always be better because mongoose blocks the event emitter for the period of time it takes to complete both the queries whereas your code will block it individually for a shorter time, which has better performance. you can use blocked to benchmark is

Related

How to structure "user friends" collections in MongoDB?

I have a questions about what is the best way to structure "user connections" system in MongoDB, like Linkedin works.
I have the next options but I don't know what is the best way in the future, with millions rows in collections.
Option 1: Two collections, user and user-connect relation
one-to-one in user-connect with this fields: user_one, user_two,
status ("pending", "accepted", "rejected", "bloecked"). When user
request connect to another user, I need just insert a new document in
user-connect collection, but I have a problem: I need to follow all
queries to get "contacts" of a user in two fields, user_one and
user_two because just exists one insert between both.
Option 2: The same structure that option 1 but instead of 1 insert, 2
inserts: The first one will be the request, just a document with
status pending, and the second one will be when this connection is
accepted. Two documents, two insert but when I need to query for user
contacts, just need to get information in one field: user_one
Option 3: one-to-many structure, user-connect will has an user_one
field and contacts field, array of user ids, but reading another
forums, this not is recommended because a lot of "match" system in the
future.

How to model MongoDB User Schema and their corresponding data?

I'm creating a Google Keeper replica where a user can log in and the list of todo list for that user is stored.
I'm new to mongoDB, express, and react, and I was wondering how someone would go about doing this. Would you create a User Schema with the "list objects" or create a User Schema and a separate List schema.
I think the creating one schema would be more efficient, but when I go to update or delete a note, I don't know how I would target a specific note without an ID since the ID would be associated with the entire user schema.
Thank you!
You can assign an unique id to the list entries while inserting them into the database. For example, you can use timestamp. The structure of your list items will be something like-
{ itemId: "1595488458403", value: "Do the laundry" }
As the items will be created one by one, therefore, there timestamps will be different. To create the timestamp of the present time, use-- new Date().getTime()
Here's the roadmap
Bring the value of the list item from your frontend to the backend. (say, "Do the laundry")
Define a variable "itemId" in the backend route:
itemId = new Date().getTime()
While inserting the item to the user's list of to-dos, insert:
{ itemId: itemId, value: "Do the laundry" }

Compare two collections in MongoDb and remove common

I have three collections in MongoDB
achievements
students
student_achievements
achievements is a list of achievements a students can achieve in an academic year while
students collections hold data list of students in the school.
student_achievements holds documents where each documents contains studentId & achievementId.
I have an interface where i use select2 multiselect to allocate one or more achievements from achievements to students from students and save it to their collection student_achievements, right now to do this i populate select2 with available achievements from database. I have also made an arrangement where if a student is being allocated same achievement again the system throws an error.
what i am trying to achieve is if an achievement is allocated to student that shouldn't be available in the list or removed while fetching the list w.r.t student id,
what function in mongodb or its aggregate framework can i use to achieve this i.e to compare to collections and remove out the common.
Perhaps your data-structure could be made different to make the problem easier to solve. MongoDB is a NoSQL schemaless store, don't try to make it be like a relational database.
Perhaps we could do something like this:
var StudentSchmea = new Schema({
name: String,
achievements: [{ type: Schema.Types.ObjectId, ref: 'Achivement' }]
});
Then you can do something like this which will only add the value if it is unique to the achievements array:
db.student.update(
{ _id: 1 },
{ $addToSet: { achievements: <achivement id> } }
)
If you are using something like Mongoose you can also write your own middleware to remove orphaned docs:
AchivementSchema.post('remove', function(next) {
// Remove all references to achievements in Student schema
});
Also, if you need to verify that the achievement exists before adding it to the set, you can do a findOne query before updating/inserting to verify.
Even with the post remove hook in place, there are certain cases where you will end up with orphaned relationships potentially. The best thing to do for those situations is to have a regularly run cron task to to do cleanup when needed. These are some of the tradeoffs you encounter when using a NoSQL store.

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.

Mongoose: Running a second query on the returned documents

Problem:
I have a 'Group' collection. Each group has an embedded document of 'Members'. I need to pull out a specific Member by their 'MemberID' and fetch all their details from the 'Users' collection. I would like to do this using the '.populate()' method but only need to populate that single member's record not all the members records.
So my query looks like this:
DB.model('groups')
.findById(groupID)
.populate('members._user')
.run(function(err, group){
// then loop over every member and return the one that matches
// the member id we require
});
This seems like a very inefficient way of doing things considering I only need the user details for one member in the group! I only have the memberID not the userID so this is the reason I am going to the members collection.
How can I extract a single member from the embedded 'members' document and populate it?
The populate function takes 3 parameters: path, fields and conditions. fields and conditions are applied when the referred document(s) is(are) populated via a separate call to model.find(...). Try passing to populate a valid mongodb condition that will only return members that you are interested in.

Resources