Monthly Archives: August 2013

MongoDB basics for everyone – Part 7 – Arrays and embedded documents

One of the big things that you will notice when you start working with MongoDB, coming from a relational world, is the lack of joins. Joins are fun and all, but as we all probably know, not very scalable. This means that we usually end up doing “joins” in our applications anyway. MongoDB has no support for joins, although you do have access to so called “dbrefs”, which we usually do not make use of due to their [lack of] speed.

We can create a relation to another document of course, which also makes use of indexes and all the good things that we need in our apps, but this usually requires two

find()

commands to be executed. As an example, we could make a rudimentary “likes” collection:

use likes
db.things.insert({"_id":"thing1", "thing":"ice-cream"});
db.things.insert({"_id":"thing2", "thing":"cookies"});
db.things.insert({"_id":"thing3", "thing":"pumpkin"});

Let us assume we also have a people (users) collection:

db.users.insert({"_id":"user1", "name": "Paul"});
db.users.insert({"_id":"user2", "name": "John"});
db.users.insert({"_id":"user3", "name": "Fred"});

So the problem here is to relate the things that the users like to the users in some way. There are a number of ways to achieve this, without using joins!

Example 1: Using arrays:

We will simply create an array of the things that the users like within the user document. We can either do that as a String of the thing that the user likes, or as an array of the document ID of the thing (which will make maintenance a bit easier, as you only have to change the fields in one place)

db.users.update({"_id":"user1"}, { $set: {"likes":["thing1", "thing2"]}});

You will notice that I have used the OjectID’s of the “things” collection.

OR

db.users.update({"_id":"user2"}, { $set: {"likes":["ice-cream", "cookies"]}});

In the above example, I have simply set an array of things that I like. Please note that this is not as maintainable as the first example, due to the fact that if I update “ice-cream” to “icecream”, for example, I will be forced to to an atomic update across the entire “users” collection, which may take some time if I have a few million users.

A third way of approaching the problem is to embed another document within my document. Remember that MongoDB documents are limited to a size of 16MB, so this may not be the best option for you, but in less data intensive collections (i.e. without images, video, or other GridFS types), it should do just fine!

db.users.update({"_id":"user3"}, { $set: {"likes":{"thing1":"ice-cream", "thing2":"cookies"}}});

Which will give me an embedded JSON document within my “user” document about user “Paul”.

Do a quick

db.users.find().pretty()

to view your handy work!

We would now like to query our shiny new collection to find all the users that like “cookies”

The methods to do so are as below (we are also introducing the $in operator for working with arrays):

db.users.find({"likes":{$in:["thing2"]}})

which will return

{ "_id" : "user1", "likes" : [ "thing1", "thing2" ], "name" : "Paul" }

Next, we work with the array of things that were named (user2):

db.users.find({"likes":{$in:["cookies"]}})

which returns

{ "_id" : "user2", "likes" : [ "ice-cream", "cookies" ], "name" : "John" }

Finally, the embedded document query:

db.users.find({"likes.thing2":"cookies"})

which will return

{ "_id" : "user3", "likes" : { "thing1" : "ice-cream", "thing2" : "cookies" }, "name" : "Fred" }

The key here is that although we have the power of dynamic schema within MongoDB, you still want to think about schema design and plan with that 16MB document size limit in mind!