I am using MongoDb for one of the mobile app that we are developing. It has a feature of sync contacts.
I wanted to know the ideal way of storing the relationships(friends relationship and not RDBMS kind of relationship) in mongodb. I want to know the architecture for the same.
I have thought of the following user collection structure:
{
_id: ObjectID(abc),
name: "abc",
contacts: ["def", "ghi"]
}
In the above collection I am considering "def" and "ghi" as object ids of friends of user abc. Is this the correct way of doing it or can someone suggest me a better and right way that they might have implemented?
All I am concerned about is I should not get stuck or hit the performance when retrieving data specific the user's friends in future.
Consider If I want to get all the activities from collection Activities done by my friends.
I think you could use advantage of noSql structure and save/serve some more info about friend
{
_id: ObjectID(abc),
name: "abc",
contacts: [{id:"def" name:"John"}, {id:"ghi", name:"Sari"} ]
}
To display basic list you will need just one get query, and then having name (or other important related details) - check for activities.
The extra overhead with this structure is a need to update name (and other details) every time when user updates it's name - but this is not a hammer - who changes its name frequently?
Related
I'm building an API for a small social network and I came across a design decision that I have to make. I'm working with Express and MongoDB with mongoose to deal with the database.
I have two Documents: Users and Posts. I want the Users to be able to mark Posts as their favorites. I came up with two different ways for the Implementation:
Option A: Saving the favorites in the User Document. It makes it easy to show all favorite posts of an user. But how would I query the users, that have favorited a specific Post?
UserSchema:
favorite_posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "posts"
}
]
Option B: Saving the Users, that hit the favorite button in the Post Document. The benefit would be, that you can easily display all Users, that have favorited a Post. But how do I list all Posts that one specific User has marked as favorites.
PostSchema:
users_favorited: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "users"
}
]
Can somebody explain me how to query such things? I'm not getting smarter from the documentation... :(
As already mentioned in the comments your best bet would be a join-table to make a n:m relation work. Mongoose does emulate the sql inner-join functionality through the populate() functionality in regular queries or the $lookup-step in an aggregation. So basically create a table called "likes" that only holds refs to the user and the post. Using the aggregation framework, you can then easily query for all likes of a user or all likes on a post by first using the $match operator, then $group by either the user or the post and $push to create an array of all likes of a user or vice versa and then join the needed data on it using the $lookup step.
However, you could, as you've decribed, put all the favorites in a array on either the user- or the post-documents, but unless you know for sure that these arrays won't grow large, I'd recommend against it, as mongoDb is not designed for this kind of usage and you'll very quickly run into performance problems. See http://www.askasya.com/post/largeembeddedarrays/ for more.
If you are gonna query a lot by userid, you can just add a userid column on the favorites document. This would save queries/joins/aggregations
I am relatively new to No-SQL databases. I am designing a data structure for an e-learning web app. There would be X quantity of courses and Y quantity of users.
Every user will be able to take any number of courses.
Every course will be compound of many sections (each section may be a video or a quiz).
I will need to keep track of every section a user takes, so I think the whole course should be part of the user set (for each user), like so:
{
_id: "ed",
name: "Eduardo Ibarra",
courses: [
{
name: "Node JS",
progress: "100%",
section: [
{name: "Introdiction", passed:"100%", field3:"x", field4:""},
{name: "Quiz 1", passed:"75%", questions:[...], field3:"x", field4:""},
]
},
{
name: "MongoDB",
progress: "65%",
...
}
]
}
Is this the best way to do it?
I would say that design your database depending upon your queries. One thing is for sure.. You will have to do some embedding.
If you are going to perform more queries on what a user is doing, then make user as the primary entity and embed the courses within it. You don't need to embed the entire course info. The info about a course is static. For ex: the data about Node JS course - i.e. the content, author of the course, exercise files etc - will not change. So you can keep the courses' info separately in another collection. But how much of the course a user has completed is dependent on the individual user. So you should only keep the id of the course (which is stored in the separate 'course' collection) and for each user you can store the information that is related to that (User, Course) pair embedded in the user collection itself.
Now the most important question - what to do if you have to perform queries which require 'join' of user and course collections? For this you can use javascript to first get the courses (and maybe store them in an array or list etc) and then fetch the user for each of those courses from the courses collection or vice-versa. There are a few drivers available online to help you accomplish this. One is UnityJDBC which is available here.
From my experience, I understand that knowing what you are going to query from MongoDB is very helpful in designing your database because the NoSQL nature of MongoDB implies that you have no correct way for designing. Every way is incorrect if it does not allow you in accomplishing your task. So clearly, knowing beforehand what you will do (i.e. what you will query) with the database is the only guide.
I'm quite new to nosql world.
If I have a very simple webapp with users authenticating & publishing posts, what's the mongodb(nosql) way to store users & posts on the nosql db?
Do I have (like in relationnal databases) to store users & posts each one in his own collection? Or store them in the same collection, on different documents? Or, finally with a redondant user infos (credentials) on each post he has published?
A way you could do it is to use two collection, a posts collection and a authors collection. They could look like the following:
Posts
{
title: "Post title",
body: "Content of the post",
author: "author_id",
date: "...",
comments: [
{
name: "name of the commenter",
email: "...",
comment: "..."
}],
tags: [
"tag1", "tag2, "tag3
]
}
Authors
{
"_id": "author_id",
"password": "..."
}
Of course, you can put it in a single collection, but #jcrade mentioned a reason why you would/should use two collections. Remember, that's NoSQL. You should design your database from an application point of you, that means ask yourself what data is consumed and how.
This post says it all:
https://www.mongodb.com/blog/post/6-rules-of-thumb-for-mongodb-schema-design-part-1
It really depends on your application, and how many posts you expect your users to have: if it's a one-to-few relationship, then probably using embedded documents (inside your users model) is the way to go. If it's one to many (up to a couple of thousands) then just embed an array of IDs in your users model. If it's more than that, then use the answer provided by Horizon_Net.
Read the post, and you get a pretty good idea of what you will have to do. Good luck!
When you are modeling nosql database you should think in 3 basic ideas
Desnormalization
Copy same data on multiple documents. in order to simplify/optimize query processing or to fit the user’s data into a particular data model
Aggregation
Embed data into documents for example (blog post and coments) in order to impact updates both in performance and consistency because mongo has one document consistency at time
Application level Joins
Create applicaciton level joins when its not good idea to agregate information (for example each post as idependent document will be really bad because we need to accces to the same resource)
to answer your question
Create two document one is blogPost with all the comments, and tags on it and user ui. Second User with all user information.
I'm building a RESTful web application using node.js and MongoDB.
I have a Person model
Person
id: '12345'
name: 'John'
likes: [ {id: '54321', name: 'Mary'} ]
isLikeydBy: []
Person
id: '54321'
name: 'Mary'
likes: []
isLikeydBy: [ {id: '12345', name: 'John'} ]
What is the best way to model "likes" and "isLikedBy" relationships? Since I am using MongoDB I thought that this is a good way to model the relationships, as there is only one access to database needed to get all data about one person.
How to create a REST api for this relationship? What if 'John' doesn't like 'Mary' anymore. The server only receives the following put request.
Person
id: '12345'
name: 'John'
likes: []
isLikeydBy: []
But the server should also update 'Mary', because now she is not liked by 'John' anymore. (I know that MongoDB does not directly support transactions and that I have to implement them myself.)
My ideas:
1. On each update of Person (of it's 'likes' and 'isLikedBy' fields) get this person from database and compare their 'likes' and 'isLikedBy' fields with the request. There is some overhead with this approach and also I do not know if it is in the spirit of RESTful APIs.
2. Make the client send both the original 'likes' and 'isLikedBy' fields and also the new updated ones (or only the diff). This seems even further away from RESTful design, since the client must now be aware of which data was last successfully saved to the server.
3. Create a separate object which would contain the relationship information (3 fields: id, me, whoILike). But this means that each time I would want to get data about a person I would need 2 queries, one for the person and one for relationships and then combine the data into single object.
What should I do?
We've wrestled with this same problem at my company when using Mongo and tracking likes.
After much discussion we decided to store the counts of likes with the entities - in this case storing likes with people.
Opinions on your options:
The overhead in doing additional queries is probably a bad idea. Especially since "liking" stuff is seen by the users as a lightweight operation. In other words, you might find users liking a ton of stuff, which means a lot of writes - and in this case every write has an additional read or two with it.
This is a lot of work for the developer to do and it's easy to get wrong.
I think it's ok, but I still prefer to store likes with the person. Mongo isn't good at joins like you mentioned.
I think you should store the like/liked by fields with the person document. The only thing I would change is the REST call being made.
Maybe something like:
PUT http://www.rest.com/person/123/likes/456
This would say "Person 123 likes 456." Then your REST call makes sure the data is updated. It would update the Person 123 object and the Person 456 object.
To remove something like:
DELETE http://www.rest.com/person/123/likes/456
Keep in mind every time someone makes a REST call, the update doesn't have to update the entire document. You can do partial updates on the Person document with the modified likes. You can also easily add/remove an array in a document.
I'm trying to figure out how to best design Mongo DB schemas. The Mongo DB documentation recommends relying heavily on embedded documents for improved querying, but I'm wondering if my use case actually justifies referenced documents.
A very basic version of my current schema is basically:
(Apologies for the psuedo-format, I'm not sure how to express Mongo schemas)
users {
email (string)
}
games {
user (reference user document)
date_started (timestamp)
date_finished (timestamp)
mode (string)
score: {
total_points (integer)
time_elapsed (integer)
}
}
Games are short (about 60 seconds long) and I expect a lot of concurrent writes to be taking place.
At some point, I'm going to want to calculate a high score list, and possibly in a segregated fashion (e.g., high score list for a particular game.mode or date)
Is embedded documents the best approach here? Or is this truly a problem that relations solves better? How would these use cases best be solved in Mongo DB?
... is this truly a problem that relations solves better?
The key here is less about "is this a relation?" and more about "how am I going to access this?"
MongoDB is not "anti-reference". MongoDB does not have the benefits of joins, but it does have the benefit of embedded documents.
As long as you understand these trade-offs then it's perfectly fair to use references in MongoDB. It's really about how you plan to query these objects.
Is embedded documents the best approach here?
Maybe. Some things to consider.
Do games have value outside of the context of the user?
How many games will a single user have?
Is games transactional in nature?
How are you going to access games? Do you always need all of a user's games?
If you're planning to build leaderboards and a user can generate hundreds of game documents, then it's probably fair to have games in their own collection. Storing ten thousand instances of "game" inside of each users isn't particularly useful.
But depending on your answers to the above, you could really go either way. As the litmus test, I would try running some Map / Reduce jobs (i.e. build a simple leaderboard) to see how you feel about the structure of your data.
Why would you use a relation here? If the 'email' is the only user property than denormalization and using an embedded document would be perfectly fine. If the user object contains other information I would go for a reference.
I think that you should to use "entity-object" and "object-value" definitions from DDD. For entity use reference,but for "object-value" use embed document.
Also you can use denormalization of your object. i mean that you can duplicate your data. e.g.
// root document
game
{
//duplicate part that you need of root user
user: { FirstName: "Some name", Id: "some ID"}
}
// root document
user
{
Id:"ID",
FirstName:"someName",
LastName:"last name",
...
}