One thing I started to think about after having looked at MongoDB was how to model things that are somehow connected – without the use of foreign keys and the ability to join stuff.
When working with documents, you generally just embed the data where it belongs. But what if I have the following documents:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
"songs" collection: { "name": "Factory", "artist": "Martha Wainwright" } { "name": "Winter At The Hamptons", "artist": "Josh Rouse" } "users" collection: { "username": "mookid", "name": "Mogens Heller Grabe" } { "username": "duderino", "name": "The Dude" } |
– and I want to constrain access to the songs, allowing me to see both songs, and The Dude to see only Factory?
My first take was to simply add username in an array inside each song, like so:
1 2 3 4 5 6 7 8 9 10 11 12 |
"songs" collection: { "name": "Factory", "artist": "Martha Wainwright", "allowed": ["mookid", "duderino"] } { "name": "Winter At The Hamptons", "artist": "Josh Rouse", "allowed": ["mookid"] } |
– and this will work well with indexing, which can be done like this:
1 2 |
db.songs.ensureIndex({"allowed": 1}) db.songs.find({"allowed": "mookid"}) // will use the index :) |
But here comes the problem: What if each song should be displayed along with the name of who can access that particular song? I need to embed more stuff in the array, e.g. like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
"songs" collection: { "name": "Factory", "artist": "Martha Wainwright", "allowed": [{ "username": "mookid", "name": "Mogens Heller Grabe" }, { "username": "duderino", "name": "The Dude" }] } { "name": "Winter At The Hamptons", "artist": "Josh Rouse", "allowed": [{ "username": "mookid", "name": "Mogens Heller Grabe" }] } |
There’s a challenge now in keeping this extra “denormalized” piece of information up-to-date in case a user changes his name, etc. – but let’s just assume we’ve handled that.
Now here comes the cool thing: It’s cool that MongoDB can index “into” an array, but it can actually index “into” anything! Just tell it where to go, using the Dot Notation.
That means I can perform the same search as I did above like so:
1 2 |
db.songs.ensureIndex({"allowed.username": 1}) db.songs.find({"allowed.username": "mookid"}) // will use the index :) |
How cool is that?? (pretty cool, actually!)