Currently I'm working on a project where a user can belong to a group, and multiple users can possibly belong to the same group. A user can drop out of a group at anytime, and multiple groups can be named the same way. The problem I'm facing is keeping track of the uniqueness of groups. For instance, if two groups are named the same way, how do I distinguish the two without the concept of an id.
Example of what I mean:
{userName: 'user1', group: 'test' },
{userName: 'user2', group: 'test' },
{userName: 'user3, group: 'test' }
How do I know that all three 'test' groups refer to the same group. In sql, this is easily resolved by a unique Id. It seems much harder to id the groups in this case. This data seems more relational, maybe I'm better off sticking with a sql database?
As I understand you use a document-oriented NoSql DB. So you can try following scheme. Let's "user1" is id for {userName: 'user1', group: 'test' }, "user2" is id for {userName: 'user2', group: 'test' }. You can create document with id "test1", which will contains id's of documents with users. For example: test1: {users:[user1, user2]}. When you insert new user to DB, you should to update document test1.
Related
Currently in our system we have two separate collections, of invites, and users. So we can send an invite to someone, and that invite will have some information attached to it and is stored in the invites collection. If the user registers his account information is stored in the users collection.
Not every user has to have an invite, and not every invite has to have a user. We check if a user has an invite (or visa versa) on the email address, which in those case is stored in both collections.
Originally in our dashboard we have had a user overview, in which there is a page where you can see the current users and paginate between them.
Now we want to have one single page (and single table) in which we can view both the invites and the users and paginate through them.
Lets say our data looks like this:
invites: [
{ _id: "5af42e75583c25300caf5e5b", email: "john#doe.com", name: "John" },
{ _id: "53fbd269bde85f02007023a1", email: "jane#doe.com", name: "Jane" },
...
]
users: [
{ _id: "53fe288be081540200733892", email: "john#doe.com", firstName: "John" },
{ _id: "53fd103de08154020073388d", email: "steve#doe.com", firstName: "Steve" },
...
]
Points to note.
Some users can be matched with an invite based on the email (but that is not required)
Some invites never register
The field names are not always exactly the same
Is it possible to make a paginated list of all emails and sort on them? So if there is an email that starts with an a in collection invites, that is picked before the email that starts with a b in collection users etc. And then use offset / limit to paginate through it.
Basically, I want to "merge" the two collections in something that would be akin to a MySQL view and be able to query on that as if the entire thing was one collection.
I'm preferably looking for a solution without changing the data structure in the collection (as a projected view or something, that is fine). But parts of the code already rely on the given structure. Which in light of this new requirement might not be the best approach.
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!
I have two collections in my mongodb database :- Users , Clubs
User Schema :-
var UserSchema = new Schema({
Name: {
type: String ,
required: true
},
Clubs: [
{type: mongoose.Schema.Types.ObjectId, ref: 'Club'}
]});
Now when a user joins a club , i update the club array . But I also frequently need to fetch all the users for a particular club . Therefore I am creating the club schema as :-
var ClubSchema = new Schema({
clubName : {
type: String ,
unique: true ,
required: true
},
members : [
{type: mongoose.Schema.Types.ObjectId,
ref: 'User' ,
default: []
} ]});
My question is : Is this the right way to do so , or should I maintain this club information at the User Collection only ? Maybe I need to optimize the query related to fetching all the Users belonging to a Club.
It's quite hard to say what's "the right way" to be honest, as this is most likely case by case depending on your application queries and architecture.
I have seen some people do as you designed above; Solving many-to-many relationships by using reference in both collection.
This would work for your case queries above:
db.user.find({"clubs": ObjectId("000000")}); #Find all users belonging to certain club.
db.club.find({"users": ObjectId("111111")}); # Find all clubs where a user belong to.
With two indexes on:
db.user.ensureIndex({"clubs": 1});
db.club.ensureIndex({"users": 1});
Though this may reduce the overall consistency. i.e. when you delete a club, you need to update the other affected documents as well. While in the process of the mentioned update, your documents may not be up-to-date.
If you don't have mongodb on replicas, not accessing the db from distributed systems and you think this problem is not a big issue, then go for this design for the ease of query.
If you think the consistency mentioned above is a deal-breaker,
then go with inserting only clubs reference in users collection.
This is probably the most common design, and the one listed in
mongodb official site.
http://docs.mongodb.org/manual/tutorial/model-referenced-one-to-many-relationships-between-documents/
I would suggest optimising your query first, before choosing to add complexities in your update/insert (Because you have to update all related docs by having ref in Users and Clubs).
Hope this helps.
I'm struggling with a large design choice for my applications' mongo collections and mongoose schemas.
My applications calls for two account types: Students and Teachers.
The only similarity between the two account types is that they both require the fields: firstName, lastName, email, and password. Other than that, they are different (teachers have "assignments", "tests", students have "homework", etc.)
I have pondered my options extensively, and considered the following design choices:
Use mongoose-schema-extend, and create an "abstract" schema for
all accounts. Then, extend this schema to create the Teacher and
Student schemas. This implies two collections, and therefore some
redundant fields. There are also issues with logging in and account creation (checking to see if the email used to log in is a student email or teacher email, etc.)
Create one collection "accounts", and add a type field to
indicate if the account is a "student" or a "teacher". This implies
that entries in the "accounts" collection will be dissimilar. This
also requires that I have two mongoose schemas for a single
collection.
Create an "accounts" collection, have a "type" field and an "accountId" field. In addition to a "student" collection and a "teacher" collection -- the "type" field will indicate which collection the student-specific or teacher-specific fields reside within, and the "accountId" field will indicate exactly which entry the account is matched with.
I appreciate all input, criticism or suggestions.
I've been down a similar road and I eventually landed on a mix of option 1 and 2.
mongoose-schema-extend simply modifies the prototype of Schema with an #extend() method which when invoked performs a deep copy of the passed schema. Most helpful. However, you can control which collection mongoose saves to in MongoDB by adding a collections property to the Schema:
var schema = new Schema({
foo: String,
bar: Boolean
}, { collection: "FooBarBaz" });
Remember: Mongoose understands the concept of a Schema but MongoDB does not. This means you can store dissimilar data and use your custom business logic to control the mess. With that said, you can create a base model called User, force mongoose to use the same collection by using the collection option and then extend off this base model to make your Teachers and Students models.
Make sure you add a type flag in the base model as you suggested in option 2. Not only is this convenient for quick lookups, but it will be critical when working commando with raw MongoDB data.
#jibsales has an excellent solution.
One more solution to consider is using Population with references http://mongoosejs.com/docs/populate.html from the Users collection to the Student and Teacher collections. Some benefits are:
Entries in each of the three collections (Users, Teachers, Students)
are similar in storage.
Allows you to obtain the fields for the "User" independently of
obtaining the fields for the referenced collection.
This would require that the schema is modified before an instance is created (and a model is created from the schema), where refType is the desired collection:
var userSchema = new Schema({
_id : Number,
name : String,
age : Number,
stories : [{ type: Schema.Types.ObjectId, ref: refType}]
});
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,
}