0

How to refer to each property of an object in an array of objects in MongoDB MapReduce JavaScript query?

Here is my data:

{
  "_id": ObjectId("544ae3de7a6025f0470041a7"),
  "name": "Bundle 4",
  "product_groups": [
    {
      "name": "camera group",
      "products": [
        {
          "$ref": "products",
          "$id": ObjectId("531a2fcd26718dbd3200002a"),
          "$db": "thisDB"
        },
        {
          "$ref": "products",
          "$id": ObjectId("538baf7c26718d0a55000043"),
          "$db": "thisDB"
        },
        {
          "$ref": "products",
          "$id": ObjectId("538baf7c26718d0a55000045"),
          "$db": "thisDB"
        }
      ]
    },
    {
      "name": "lens group",
      "products": [
        {
          "$ref": "products",
          "$id": ObjectId("531e3ce926718d0d45000112"),
          "$db": "thisDB"
        },
        {
          "$ref": "products",
          "$id": ObjectId("531e3ce926718d0d45000113"),
          "$db": "thisDB"
        }
      ]
    }
  ]
}

Here is my map function: (for simplicity I took out the reduce option since it doesn't matter if the map doesn't work right)

var map = function() { emit(this.product_groups, this.product_groups.products); };
db.instant_rebates.mapReduce(
    map,
    {
        out: "map_reduce_example",
        query: {"_id": ObjectId("544ae3de7a6025f0470041a7")}
    }
);

However the problem is that the "value" field in the result always comes up as "undefined". Why? Why doesn't this.product_groups.products return the products array? How do I fix this?

Also, I want it to do is to emit TWICE, once for each of the two product_groups. But so far it only emits ONCE. How do I fix that?

1 Answer 1

1

Under mapReduce operations the documents are presented as JavaScript objects so you need to treat them as such and traverse them. That means processing each member of the array:

var map = function() { 
    this.product_groups.forEach(function(group) {
        emit( group.name, { products: group.products } );
    });
};
var reduce = function(){};

db.instant_rebates.mapReduce(
    map,
    reduce,
    {
        out: "map_reduce_example",
        query: {"_id": ObjectId("544ae3de7a6025f0470041a7")}
    }
);

The requirements of the "emit" function is both a "key" and a "value" to be presented as arguments which are emitted. The "value" must be singular therefore to emit an "array" of data you need to wrap this under the property of an object. The "key" must be a singular value as it's intent to it be used as the "grouping key" in the reduce operation, and the "name" field should be sufficient at least for example.

Naturally since there is a top level array in the document you process "each element" as is done with the function, and then each result is "emitted" so there are "two" results emitted from this one document.

You also need to at least define a "reduce" function even if it never gets called because all of the emitted keys are different, as is the case here.

So, it's JavaScript. Treat a list structure as a list.

Please note. This is all your question is about. If you want to ask further questions on mapReduce then please ask other questions. Don't ask any more of this one. I don't want to talk about your field naming, or go into detail of how this seems to be working towards "how do I pull in data from the other collection", which is something you cannot do.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.