Home

Noppanit

23 Jan 2014

Merge documents from two collections together in MongoDb

The problem is I have a lot of documents in one collection and even a lot more documents in another collection. I want to merge those documents together but I don’t want to write a lot of code to merge those documents together. It also might take a lot of time to do it.

MongoDb comes with map/reduce which is a cool feature you can use to achieve something like this. If you don’t know what map/reduce is I wrote an article like this sometime ago.

Let’s say you have one collection with this document. Let’s say it’s products collection.</p>

{
  "_id" : ObjectId("52e05133c68f0b2a95e2b67b"),
  "product_id" : 1,
  "title" : "Sharpie"
}

And another document in prices collection.

{
  "_id" : ObjectId("52e05473c68f0b2a95e2b682"),
  "product_id" : 1,
  "price" : 99
}

I want to end up with this.

{ "_id" : 1, "value" : { "title" : "Sharpie", "price" : 99 } }

Here’s what you can do. Insert your data first.

db.products.insert({"product_id" : 1, "title" : "Sharpie"});
db.products.insert({"product_id" : 2, "title" : "Sticky"});
db.prices.insert({"product_id" : 1, "price" : 99});
db.prices.insert({"product_id" : 2, "price" : 30});

Then you build maps to get key/value from the documents

products_map = function() {
  emit(this.product_id, {"title" : this.title});
}

prices_map = function() {
  emit(this.product_id, {"price" : this.price});
};

Then you build your reduce function

r = function(key, values) {
  var result = {
      "title" : "",
      "price" : ""
    };

    values.forEach(function(value) {
      if(value.title !== null) {result.title = value.title;}

      if(value.price !== null) {result.price = value.price;}
    });

    return result;
}

Then you run your map/reduce function.

res = db.products.mapReduce(products_map, r, {out: {reduce : 'joined'}});
res = db.prices.mapReduce(prices_map, r, {out: {reduce : 'joined'}});

Then you will get another collection joined with the information you want. One caveat is right now map/reduce function will result in value : { “field” : “value” }.

Til next time,
noppanit at 00:00

scribble