A Quick, Vital Tip for New MongoDB'ers
This is aimed at people interested in the MEAN stack and have a need to sprinkle some relational functionality onto Mongo. If you need to do this USE MONGOOSE.JS.
So this is pretty simple, but at first (especially if you haven't done a lot with databases) it can be counter intuitive.
Don't Nest Collections.
For example, if you have a user document and they have "books" don't do it like this:
> db.users.find({ email: "me@jcolemorrison.com"})
{
_id: ObjectId("asdfn1238nakljx"),
firstname: "Cole",
lastname: "Morrison",
email: "me@jcolemorrison.com",
books: [
{
title: "Ender's Game",
author: "Orson Scott Card",
thoughts: "Awesome book, bad movie."
},
{
title: "The Way of Kings",
author: "Brandom Sanderson",
thoughts: "Storm you all!"
}
]
}
I know. I know. I know. This looks like it makes sense. And it does. BUT. MongoDB gives no easy way to sort, modify, and traverse through nested collections. So let's say you want to change the "thoughts" on "Ender's Game." Here's the fun query you have to type out:
> db.users.update(
{
"email": "me@jcolemorrison.com",
"book.title": "Ender's Game"
},
{
$set: {
'books.$.thoughts': "My thoughts exactly" }
}
);
Oh what? You forgot to add a unique ID to each book? Too bad. This is how you'd do it. Okay okay, this isn't TOO bad. But I'll show you what is.
Client: Can we add a "Date Added" field to all of the books that users have?
Mwahahaha. Try it. Oh that's right. YOU CAN'T. Not without writing a script to handle it or doing it manually.
Client: Well, could we rename the "thoughts" field to "description" ?
BAHAHAHAHA. TRY IT. OH YOU CAN'T. Mongo won't let you update all items in an array simultaneously.
Let's pretend that you HAD added a date field to the books before you'd inserted them. And then let's pretend that you want to change all books in a user's collection that we're added AFTER 4/1/14. You can't.
How You Should Do it
When you need to give a user "books" just make a whole new collection. So just like you have a "users" collection (db.users.find()
) you'd have a "books" collection (db.books.find()
). Now let's revisit the above. So a document would look like this:
> db.books.findOne()
{
_id: ObjectId("somerandomthingofids"),
userId: "idOfUserWhoOwnsThisBook",
title: "Some Title",
author: "Some Author",
thoughts: "Some Thoughts about book"
}
Client: Can we add a "Date Added" field to all of the books that users have?
> db.books.update({},
{ $set: { "dateAdded": 1 }},
{ multi: true })
Client: Well, could we rename the "thoughts" field to "description" ?
> db.books.update({},
{ $rename: { "thoughts": "description" }},
{ multi: true })
And done.
Hopefully this helps some other developer from wasting hours upon hours trying to push and pull nested documents.
J Cole Morrison
http://start.jcolemorrison.comDeveloper Advocate @HashiCorp, DevOps Enthusiast, Startup Lover, Teaching at awsdevops.io