Designing mongo 'schema' for RESTful application - mongodb

I'm trying to teach myself mongo through writing an application, and I'm struggling with the best way to design the mongo 'schema' (I know it's schemaless, but that is probably the core issue with my understanding in that I'm coming from a relational background)
Anyway, the application is a Gift List manager, where a user can create a Gift List and can add Gifts they would like to receive to their list. Other users can subscribe to the list, and can mark a Gift from the Gift List as claimed/purchased. (So as to prevent the problem of getting duplicate gifts at Christmas!)
At the moment my GiftLists collection is not 'relational' and is simply a collection of GiftList documents with sub documents for the Gifts, like this:
{
"GiftLists": [
{
"_id": {
"$oid": "55e9924848c4ffd723890b48"
},
"description": "Xmas List for Some User",
"gifts": [{
"description": "Mongo book"
"claimed": false
},
{
"description": "New socks"
"claimed": false
},
{
"description": "New socks"
"claimed": false
}],
"owner": "some.user",
"subscribers": ["some.other.user", "my.friend"]
}
]
}
The idea is that some.user is the owner of the Gift List and has added 3 items he would like to receive. some.other.user has subscribed to the list and can see the Gift List and it's Gifts. He may choose to buy one of the gifts, so needs to mark it as claimed so that my.friend does not also buy it.
At the moment, each Gift in the gifts array is a sub-document without its own id, and I think this is where I'm getting stuck in my understanding/thinking.
I'm trying to provide the app functionality with a RESTful interface.
To POST a new Gift List the url is /giftList/add where the request body is the new Gift List
To GET an individual Gift List including the child Gift's the url is /giftList/<listId> - eg: /giftList/55e9924848c4ffd723890b48
With the above in mind, my natural next step is to be able to mark a Gift as claimed, perhaps with:
PUT to the url /gift/claim/<giftId>
But I don't have any ids on the Gift sub documents
So maybe my url should be:
/giftList/<listId>/claim/<giftId>
But again, I don't have an id on the Gift sub document
Or maybe I try to use the description of the item
/gift/claim/<gift description> eg: /gift/claim/Mongo+book
But what if more than one person had a Gift List containing 'Mongo book', and URL encoding the characters of the description could be messy
Or maybe I reference the Gift List
/giftList/<listId>/claim/<gift description> eg: /giftList/55e9924848c4ffd723890b48/claim/New+socks
But which instance of 'New socks' am I claiming? (after all, everyone needs lots of new socks for Christmas!)
Or maybe I reference the index of the Gift
/giftList/<listId>/claim/<gift index> eg: /giftList/55e9924848c4ffd723890b48/claim/2
But this feels fragile (as it implies that the list must always be presented in the same sequence)
To me what it really feels like is that I need another collection, just for the Gifts, where each Gift document has its own id, which I can then reference in my RESTful url. And either the Gift has a reference to it's parent GiftList, or the GiftList has an array of references to the Gifts
But this is all a very 'relational' way of thinking ... isn't it ?
What's the best way of doing this? Or, if there is no 'best' way, what are my options?

You could solve this with a new collection, or you could add an unique identifier field to each list entry. The MongoDB solution for unique identifiers is generating an ObjectId, just like those used for the _id field of documents. Most MongoDB database drivers should expose functionality for generating ObjectId's. For details, consult the documentation of your database driver.

Related

Mongo: Two collections with pagination (in single list in html)

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.

create mongodb list inside of a table

I want to create a chat app.
I need to decide how my DB is gonna look like.
I a m using mongodb as a DB.
I want to have a collection named "users" which will have every user that is registered for my app.
i.e.
users
- Mike
- David
- Adam
I also want to have a collection of rooms that are in my chat app.
i.e.
rooms
- Game of thrones
- Class of 94'
- sicence fiction
- pet lovers
I also want to have a collection of room with users
i.e.
pet lovers (room name)
- Mike (user1)
- Dave (user2)
.....
I know how to represent the users and rooms in mongodb.
But any idea how to represent the "users in room" collection? it seem I need a list inside a collection (list of users in room) in addition I need to save the room name.
I want it to be easy to pull data out of this collection (i.e. remove or add user to a room / find out which room a user is chatting in...)
also, if someone has a better suggestion to represent this data - feel free...
Thanks
you can add a users array that contains IDs of users in a given room to room document, so the rooms collection will something looks like:
[
{
"_id":"...",
"roomName":"Game of Thrones",
//...
"users":[
"userId1",
"userId2",
//...
"userIdn"
]
}, {
"_id":"...",
"roomName":"the secret zone",
//...
"users":[
"userId2",
//...
]
}
]
then, when a user join a room R, you'll add his ID to R.users array. and remove it when he left.
by the way, I think defining some schema model with mongoose may be helpful

How to store a user record on another collection in Meteor

I'm trying to find how to associate a user with another collection document in Meteor and am unsure about how to do this.
My objective is to find the most efficient and future proof method for storing this information, although I am now aware this can be seen as somewhat subjective.
In my example, I am using a "Message" collection and am storing user ids on this document as both "sender" and "recipients", recipients being an array of user ids.
When I want to display information about the sender/recipients of this message, should I use helpers to output certain data? Or add things like senderName and senderAvatar onto the document itself when it gets created? Or am I missing another way of associating a user with another object that is perhaps more efficient?
Here's a JSON example:
Option 1 - Simply storing user ids on the other object (Message)
{
"_id": "boDNs36xzLw7eLLhx",
"sender": "8jpS96b4T65g5ARug",
"recipient": "4Pa5i5vQ2gDtYQBDP",
"message": "A new message.",
"createdAt": "2015-10-22T21:18:18.291Z"
}
Option 2 - Storing more information on the document itself
{
"_id": "boDNs36xzLw7eLLhx",
"sender": "8jpS96b4T65g5ARug",
"senderName": "Joe Bloggs",
"senderAvatar": "http://myimg.com",
"recipient": "4Pa5i5vQ2gDtYQBDP",
"recipientName": "Bill Bloggs",
"recipientAvatar": "http://myotherimg.com",
"message": "A new message.",
"createdAt": "2015-10-22T21:18:18.291Z"
}
In my opinion you should stick with the 1st option and then get user data from users collection by user id.
Two main arguments:
Second option duplicates data. If users exchange 100 messages, there are 101 "Joe Bloggs", "Bill Bloggs" etc identical strings in database.
If one of the users changes name, it either doesn't change in messages, or you have to update each message sent and received by this this user, which means a massive and unnecessary database load.

mongodb: Embedded only id or both id and name

I'm new to mongodb, please suggest me how to correct design schema for situation like below:
I have User collection and Product collection. Product contain info like id, title, description, price... User can bookmark or like Product. Currently, in User collection, I'm store 1 array for liked products, and 1 array for bookmarked products. So when I need to view info about 1 user, I have to read out these 2 array, then search in Product collection to get title of liked and bookmarked products.
//User collection
{
_id : 12345,
name: "John",
liked: [123, 456, 789],
bkmark: [123, 125]
}
//Product collection
{
_id : 123,
title: "computer",
desc: "awesome computer",
price: 12
}
Now I think I can speed up this process by embedded both product id and title in User collection, so that I don't have to search in Product collection, just read it out and display. But if I choose this way, whenever Product's title get updated, I have to search and update in User collection too. I can't evaluate update cost in 2nd way, so I don't know which way is correct. Please help me to choose between them.
Thanks & Regards.
You should consider what happens more often: A product gets renamed or the information of a user is requested.
You should also consider what's a bigger problem: Some time lag in which users see an outdated product name (we are talking about seconds, maybe minutes when you have a really large number of users) or always a longer response time when requesting a user profile.
Without knowing your actual usage patterns and requirements, I would guess that it's the latter in both cases, so you should rather optimize for this situation.
In general it is not recommended to normalize a MongoDB as radical as you would normalize a relational database. The reason is that MongoDB can not perform JOINs. So it's usually not such a bad idea to duplicate some relevant information in multiple documents, while accepting a higher cost for updates and a potential risk of inconsistencies.

Mongo DB chained query

I am new to MongoDB and as I wonder if a chained query like the following is possible(somewhat like a join):
db.places.insert({
"_id": original_id
"place_name": "Broadway Center"
"url": "bc.example.net"})
db.people.insert({
"name": "Erin"
"places_id": original_id
"url": "bc.example.net/Erin"})
So given a place name string, I want to select the people associated with that place.
But the people collection only reference the place id, not the place name.
You cannot use joins in MongoDB.
The idiomatic solution is retrieve all place_ids for that place_name from your places collection and then use the place_ids to query in your people collection.
Another option is keeping, for example, places in people collection (this makes more sense to me than people inside places collection but, of course, it depends on your domain). But then you have to take into account that in case that only one place changes, you have to change all people documents sharing a specific place. If people and places are in separate collections this doesn't happen so it depends on if we have static data or not and on if we want to optimize searches or updates.