Relationships in MongoDB - mongodb

On a new project I've decided to go with mongodb for the main database, but my inexperience with it has led to some confusion in how I should structure the relationships between different collections.
In my case I have a users collection, a 'notes' collection and a groups collection. Notes can be open to different users and groups. If a user is part of a group and that group has read access to the note, then so does the user. If the user is removed from the group, then they no longer have access to the note. However, a user may have access to the note on a user basis, and even if they are removed from a group with access, they can still see the note. Hopefully that makes sense.
The problem I'm facing is setting up the permissions so that all of this is possible. Should the permissions be stored in the notes collection? If its stored as follows:
note: {id: xxxx, followers:[{id:user1, permission:write},{id:groupA, permission:read}]}
Then in the case that a user2 is part of groupA, I would need to check to see if they are associated with the node by their id, and if not, I will have to check each group that can see that note and look at their members, and if different groups have different permissions, I have to take the highest permission. This doesn't seem efficient.
The users collection looks like this:
user: {id:user1, projects:[xxxxx, xxxxx], groups:[xxxxx,xxxx...]}
The groups collection:
group: {id:groupA, projects:[xxxxx, xxxx...], users:[user2...]}
I was planning on linking the group documents with the project and user documents so that if the group is removed from a project, the project would be removed from the group. It seems like a dbref is the best way to do this.
I've looked through best practices and I feel like the overall structure I have is messy, but I can't think of another way to do it.

Given your requirements, if groups have more users than users have groups then you should consider storing group_ids within a user.
You could combine users and groups into one collection that had two different schemas and something like an is_group attribute but conceivably there could one day be collection level locking and the below would give you more flexibility.
To figure out if a user has access you would only need to query the project collection and see whether the group ids they are members of are within the write or read arrays after the project document is retrieved.
Project:
{
"_id": ObjectId,
"write": [group_id, group_id, ...],
"read": [group_id, group_id, ...]
}
User:
{
"_id": ObjectId,
"groups": []
}
Group:
{
"_id": ObjectId,
}

Related

MongoDB - should my user document hold a list of project ids?

I have collections of users and projects.
Every project is connected exactly to one user.
My question is: should every user hold the list of project ids?
If i want to retrieve all the projects of a specific user, which option is more efficient and best practice:
Create an index on projects collection on the user id property. than just query on user id property.
Create an index on project collection on the project id property. than, if the user holds its project ids, just query projects collection for those specific ids.
Which option to choose? Maybe there is a third option that is better?
The advantage of the first option, is that i don't need to update the list of project in the user document when deleting/adding projects.
Thanks!
Every project is connected exactly to one user.
A user can have many projects (and a project is associated with one user only). This is a one-to-many relationship.
My question is: should every user hold the list of project ids?
Every user should store the list of his/her projects. For example:
user:
id: <some value>,
name: <some value>,
email: <some value>,
projects: [
{ projectId: <some value>, projectName: <...>, projectDescription: <....>, otherInfo: { fld1: <...>, fld2: <...>, etc. } },
{ projectId: <some value>, projectName: <...>, projectDescription: <....>, otherInfo: { fld1: <...>, fld2: <...>, etc. } },
...
]
Note that each project is a sub-document (object or embedded document) within the projects array. A project has its related details like, projectId, projectName, etc..
If i want to retrieve all the projects of a specific user, which
option is more efficient and best practice:
a. Create an index on projects collection on the user id property.
than just query on user id property.
b. Create an index on project collection on the project id property.
than, if the user holds its project ids, just query projects
collection for those specific ids.
I think, there should be only one collection called as user_projects. Assuming that: (i) a user may have 0 to 100 projects, and (ii) a project's details are not too huge.
This is a model of embedding the 'many' side of the 1-to-N relationship into the 'one' side. This a recommended way, de-normalizing the data. This has the advantage of efficient and fast queries. This simplifies transactions, as writes (inserts, updates and deletes) are going to be atomic with single operation to a document within the same collection.
About retrieving all projects for a specific user:
You will be using the user id or name (with a unique index) to retrieve a document, and it will be very fast query. You can have index on the projects array (indexes on array fields are called as Multikey Indexes) - on the project's fields. For example, index on projectId or/and projectName makes sense.
You can get all projects for a user - its a simple query using the user id / name. Query projection allows what information related to project is displayed. You can use a find or aggregate method to build the query.
You can query a specific project for a user, using the projectId or projectName. Since there are indexes on user and project fields, this will be an efficient query.
So, my recommendation is to have a single collection, user_projects, with a user's information and the projects information embedded in it.

mongodb - proper way of data modeling

I have a collection that represents users, with their names and roles, where a role can be admin, user or guest (lets assume one role per user). Basically I'm new to mongodb. What I would do in a relational database is keep two linked tables, one for users and one for roles. I'm not sure what is the best way to handle this in mongodb though. For instance, should my collection look like so?
{ {"name": "John", role: "admin"}, {"name": "Jack", role: "user"} }
In this case, if I have a typo in one of the roles, then I would have to update all the users with that role. What scheme would you suggest?
Yes, it's the best way to do it and you're right if you want to change a role name you have to update all the item in the collection but it's a single command.
The benefit would be also to have several roles for a single user with requiring to add a table, like this:
{ {"name": "John", roles: ["admin","user"]}, {"name": "Jack", roles: ["user"]} }
But if the role part start to grow, with some access right, having the possibility to share the same right between users, etc.. you can also create a second table with roles, and juste reference the id in the user table, just like a join in SQL
#tomsoft's solution is great.
I give you my piece of advice: when you have a multiple enumerate, traduce "enumerate" like a list. Its much easier for me do this.
Another example:
Vehicles is an enumerate: you can have motorbike, scooter, car, truck...
But you must be wary with atomic enumerates, like favourite fruit:
[Banana, Apple, Peach, Grape] You only must to choose one!

Design MongoDb Schema For My Social

I'm new for MongoDB , I just want to create a simple project to test performance of MongoDB
The project just like a simple CMS
it has users, blogs and comments, users can have friends
so I design my database like that
user
{
_ID:
name:
birth_day:
sex:
friends:[id_1,Id_2]
}
blogs
{
title:
owner:
tags_fiends:
comments:
[
{"_id":"","content":"","date_created":""},
{"_id":"","content":"","date_created":""},
],
"like"={"_id","_id"}
}
And How many collection are needed for this database. Can I use 1 Collection for both user and blog.Thanks in advance.
Due to mongoDB is schema less or schema free DB You can make any kind of structure within a document, which is supported:
individual elements
nested arrays
nested documents
There is a couple of things you have to considare during schema design which for it is useful to have the users and the blogs in separated schema. For example if you storing something in a nested array you can specify index for fastening the search within this array, but you can have only one multykéy index (indexed array content) within one particular collection. so if you store, friends and blogs, and posts, and tags all in arrays you can have index only on one of them.
Also important to know in this case that there is a size limit for each document what is now 16MB.
In your scenario, I would make Users a collection and reference it by _id from the blog collection.
In practise, you could make the Blogs an attribute of User, the only constraint being the max doc size of 16MB - but that's a lot of blogs (text).
To get round that (assuming you need to), a separate Blog collection referencing the user _id would be fine. You may need to denormalise the user name too if that's not your _id. This would mean you can get all the blogs for a user in a single query.

What is a good way to handle "complex" mongodb documents in meteor

I want to store "carpool_debts" which is basically going to hold the number of days owed to other users. It looks like this:
carpool_debts{
_id,
owner,
owner_id,
creditors:[{name,
id,
amount},
{name,
id,
amount}
]}
Does that data structure look reasonable for what I want to store? Also implementing that data structure seemed cumbersome to maintain. I found it cumbersome mainly because there isn't an upsert type of function available in meteor yet. Instead of creditors being a list of sub documents would I be better off storing the creditors as a delimited string? I would like to know if I am on the right path or if I am missing something? Thanks.
You can structure mongo documents just like you would in a relational database, for example, having separate collections for creditors and owners and using carpool_debts as a link table with the amount attached:
carpool_debts{
_id,
owner_id,
creditor_id,
amount}
creditors{
_id,
name}
owners{
_id,
name}
However, this is not using mongodb to its full potential. Especially if this is a database with masses of data, you may want to optimise it for the most used queries, otherwise it'll be slow. For example, to optimise for looking up an owner's debt, you can add the data needed right there in the owners collection, using sub documents for creditors, and sub documents again for individual debts, similar to what you've already done:
owners{
_id,
name,
creditors: {id,
name,
debts: {
amount,
due_date}
}
}
and similarly, add the debt information on the creditors collection if you often look up the outstanding debt of creditors:
creditors{
_id,
name,
debtors: {
owner_id,
owner_name,
debts: {
aount,
due_date
}
}
}
This way, you only need to look up one record to get all the information you need. Of course, there are catches. First of all, this is not very DRY, but that's intentional. But you have to remember to update the other table(s) when something changes. If you change the name of a creditor for example, you'll need to update every owner document that has debts with this creditor (make sure you index that). This of course makes updates much slower (and the database bigger), but if you don't update very often, and look up much more often, this is not going to be a problem.
Also if for example creditors can have thousands of individual outstanding debts, you may have to separate that into a link table, or rather, link collection, like this, so you don't exceed mongodb's maximum document size:
creditors{
_id,
name,
}
debtors: {
owner_id,
creditor_id,
debts: {
amount,
due_date
}
}
Then you have one document for each creditor-owner connection. This means more documents to look up when looking at a creditor, but still just one for looking up an owner.
This looks fine, but you could also consider separating creditors into its own collection and just storing an array of creditor_id's in the debts collection. That would reduce complexity and make finding and filtering information easier. And it would be more DRY since if there are multiple debts with the same creditor, you only have the creditor stored in a single place.
You could also consider just having each document in the debts collection be a single debt by an owner to a single creditor. Then you'd just have id, owner_id and creditor_id - like a link table in a relational database.

Mongoid: retrieving documents whose _id exists in another collection

I am trying to fetch the documents from a collection based on the existence of a reference to these documents in another collection.
Let's say I have two collections Users and Courses and the models look like this:
User: {_id, name}
Course: {_id, name, user_id}
Note: this just a hypothetical example and not actual use case. So let's assume that duplicates are fine in the name field of Course. Let's thin Course as CourseRegistrations.
Here, I am maintaining a reference to User in the Course with the user_id holding the _Id of User. And note that its stored as a string.
Now I want to retrieve all users who are registered to a particular set of courses.
I know that it can be done with two queries. That is first run a query and get the users_id field from the Course collection for the set of courses. Then query the User collection by using $in and the user ids retrieved in the previous query. But this may not be good if the number of documents are in tens of thousands or more.
Is there a better way to do this in just one query?
What you are saying is a typical sql join. But thats not possible in mongodb. As you suggested already you can do that in 2 different queries.
There is one more way to handle it. Its not exactly a solution, but the valid workaround in NonSql databases. That is to store most frequently accessed fields inside the same collection.
You can store the some of the user collection fields, inside the course collection as embedded field.
Course : {
_id : 'xx',
name: 'yy'
user:{
fname : 'r',
lname :'v',
pic: 's'
}
}
This is a good approach if the subset of fields you intend to retrieve from user collection is less. You might be wondering the redundant user data stored in course collection, but that's exactly what makes mongodb powerful. Its a one time insert but your queries will be lot faster.