Modeling a user-to-item database in MongoDB - mongodb

I've got two tables.
Movies, which lists all the movies in the database.
Users, which has the users.
Usually, I'd create a join table to connect a user to a movie (as in, the user likes a certain movie).
However, since you can't do that in MongoDB, what should I do? I want to be able to find all the movies a certain user likes, as well as all the users that like a certain movie, and movies that a given set of users like.
Embedded documents?
Thanks!

For a many-to-many relationship between movies and users like this, I'd probably have separate collections for each, but denormalise users who like a movie into the movies collection by embedding their _id and name fields into a likes array.
This way, you can retrieve the names of users who like a movie without having to make a separate lookup to the users collection, but still have extra user fields that won't be embedded inside movies.
The trade off is that you'd need to update both collections if a user changed their name, but I think that's a worthwhile cost.
db.movies
{
_id: <objectid>,
name:"Star Wars",
likes: [
{ userid: <user-objectid>, name: "John Smith" },
{ userid: <user-objectid>, name: "Alice Brown" }
]
}
db.users
{
_id: <objectid>,
name: "John Smith",
username: "jsmith",
passwordhash: "d131dd02c5e6eec4693d"
}
Movies a certain user likes
db.movies.find( { "likes.userid": <user-objectid> }, { "name": 1 } );
Users that like a certain movie
db.movies.find( { "_id": <movie-objectid> },
{ "likes.userid": 1, "likes.name": 1 } );
Movies that a given set of users like
db.movies.find( { "likes.userid":
{ $in: [ <user1-objectid>, <user2-objectid> ] } },
{ "name": 1 } );

You can do that in MongoDB, you just can't do the 'join' operation at the database level, you have to do it at the application level.
If you have millions of movies and millions of users you have to do it using a join collection because there is no way you can fit the number of likes for one movie or one user into either document.
Lookup the User and get their _id
Lookup the UserMovie documents with matching _id values
Lookup the Movies as necessary
The denormalization you might do here would be to store the Movie names in the UserMovie collection so you can display the movies a user likes without having to fetch each one from the Movie collection.
A possible optimization
One optimization you can try on this scheme is to create documents in the UserMovie collection which contain multiple relationships instead of using a single document for each relationship (like you would in SQL).
For example, if the most common access pattern is finding what movies a user likes, you could group them by user and put them in one or more documents indexed by that user id. Take a look at the StatementGroups in this blog post for a more complete explanation.

Related

How do you store user data - in one or multiple collections?

I'm trying to store a user details (name, password, etc. ) in mongodb and I also wanted to store data on the books they've read (author, date, title), so this would be like an array of objects. Would it be better to store this in 2 different collections in the same database like this...
collection 1:
{
_id: ObjectID('SOMEid'),
firstName:"x",
lastName:"y",
email:"x#z.com",
username:"xxx",
password:"xyz"
}
collection 2:
{
_id: ObjectID('SOMEOTHERid'),
spend: [objects containing each book they've read],
userID: 'SOMEid'
}
and then reference the user in the collection containing the book data.
or in 1 collection like this...
{
_id: ObjectID('SOMEid'),
firstName:"x",
lastName:"y",
email:"x#z.com",
username:"xxx",
password:"xyz",
books: []
}
or is there another way to do it that's better. thanks
It's good to save it in 2 different collections. You can get all the user meta sata from collection for login page etc. And other collection holds user actions like which all books user has read.
Are you thinking of using other DBs? Or Mongo Db is final?

Search for data in an array in a document in mongo

I have a collection organization with field
users: [
{
"user_id":"1",
"role":"1"
},
{
"user_id":"2",
"role":"2"
}]
and another collection users with fields
{
{"user_id":1},
{"user_id":2},
{"user_id":3},
{"user_id":4}
}
I need to display all users with user id present in the users array in the organizations collection. What is the best way to implement this?
if you just want to display the user_id of the users in the organization you have to unwind users field and do the project on user_id field like below.
collection.aggregate([{"$unwind","$users"},{"$project":{"user_id":"$users.user_id"}}])
db.myDbCollection.find({}, {"user_id": 1});
can use this statement to find data from your collection.

Select documents from one collection based on IDs stored in another in Meteor and MongoDB

I have three collections, Users (Meteor built-in), Movies, and Seen. A document in Seen consists of a movie ID (the movie's ID in the Movies collection) and a user ID (analogous), indicating that that user has seen that movie.
I want to get only those movies (documents) in the Movies collection which the current user has seen.
I know how to select the documents from Seen whose user IDs match the current user's ID. Do I make an array of the movie IDs from this row and then use Mongo's $in operator? What's the best way to do this?
As you already mentioned, you can use the documents from the Seen collection whose user IDs match the current user's ID to make an array of movie IDs that you can then query with on the Movies collection. Something like the following:
var seen = Seen.find({ "userId": Meteor.userId() }).fetch(); // get Seen documents
var movieIds = seen.map(function (s){ return s.movieId; }); // get seen movie IDs list
var seenMovies = Movies.find({"_id": { "$in": movieIds }}).fetch(); // get the seen movies
console.log(JSON.stringify(seenMovies, null, 4));
You've got the basic approach right:
var seenIds = Seen.find({ userId: Meteor.userId() }).map(function(doc){return doc.movieId});
var seenMovies = Movies.find({_id: {$in: seenIds}});

Meteor: How do you populate a field from one collection into another collection with _id field?

In mongo I have a document that stores pending userId's in a collaborators object array and looks like this:
researchThread {
_id: 4374583575745756
pending: {
collaborators: [
{
userId: '13745845754745753'
},
{
userId: '23755845854745731'
},
{
userId: '33755845653741736'
}]
}
}
The userId is the _id field for the user from the users collection. Each user also has a name and an email field.
How can I populate the name and email fields from the user collection, into this document for each user in the researchThread.pending.collaborators object array? And also, will the populated data be reactive when used in the templates?
Loop through each collaborator, find the relevant user document by searching the users collection for the id, and update the researchThread document with that information.
The data will be reactive if the researchThread collection is a Meteor.Collection which you're drawing from in your templates.
However, why do you want to copy the user data? Why not just have Meteor query the users collection based on the researchThread userId when you need the data in the template?

Document References query example

If I choose to use Document References with a structure of Materialized Paths instead of the simple Embedded Documents how can I display the same results?
For example if I had Embedded docs I simply :
db.col.find({'user' : 'foo'})
and return:
{'user' : 'foo',
'posts' : [ {},
{},
{}
]
}
Which command should I use to display posts as an embedded array of that user? Or this can only happen client-side?
If it's document references,
users collection will contain:
{
_id : "foo",
// users details
}
and posts collection:
{
_id: "postid",
author: "foo"
// other fields
}
In this case,
1) First make query to get the user id from users collection.
2) Then send the user id to the posts collection to get all posts
var user = db.users.find({_id : "foo"});
// this is used to get user details or validate user and only after validation if you need to fetch the posts
var posts = db.posts.find({author: user._id });
As the documents are referenced, there will be a roundtrip to the server which is obvious.
I am not sure how you have used materialized path for this scenario, let me know the data structure of it and i would be able to mention the query based on that.