MongoDB: How to aggregate the lengths of nested arrays

Lets say you have collection (mycollection) that has an embedded object (tags) which happens to be an array.

> db.mycollection.find()
{ "_id" : ObjectId("515c8ab8e4b06d8f844ac0bd"), "tags" : [ "a", "b", "c" ] }
{ "_id" : ObjectId("515c8ab8e4b06d8f844ac0bc"), "tags" : [ "b", "c" ] }

You have to count the total number of tags across all documents in the collection. Here are a couple of ways to accomplish this.

1. MapReduce

db.mycollection.mapReduce(
    function() { emit('tags', { count: this.tags.length }); },
    function(key, values) {
        var result = { count: 0 };
        values.forEach(function(value) {
            result.count += value.count;
        });
        return result;
    },
    { out: { inline: 1 }}
);

First argument above is the map function. It scans the entire collection and emits the number of replies in each document under a constant key.

Second argument above is the reduce function. It will examine the emitted values consolidate (literally reduce) the result.

2. Aggregation Framework

db.mycollection.aggregate(
    { $project: { tags: 1 }},
    { $unwind: "$tags" },
    { $group: { _id: "result", count: { $sum: 1 }}}
);

First argument specifies that the field of interest is tags.

Second argument unwinds the array so that its elements can be iterated over.

Third argument does the total under a bucket called “result”

5 thoughts on “MongoDB: How to aggregate the lengths of nested arrays

  1. how to write it using aggregation in a Java code using mongoTemplate. Something like :
    GroupOperation groupOperation = group();
    ProjectionOperation projectionOperation = project(“);
    Aggregation aggregation = newAggregation(projectionOperation,
    unwind(“”), groupOperation);

    Thanks in advance.

    Like

  2. what about array like below , tried Countries.Properties but it didn’t work

    “Countries” : {
    “Properties” : [
    {
    “kyu” : “NAmerica”,
    “triggerString” : “America”,
    “user” : “ME”
    },
    {
    “kyu” : “ASIA”,
    “triggerString” : “China”,
    “user” : “You”
    },
    {
    “kyu” : “Europe”,
    “triggerString” : “France”,
    “user” : “HE”
    }
    ]
    },

    Like

  3. Hello

    Pretty good 🙂 !! just 1 open suggestion for map reduce… probably you should validate if the array exist in the collection to get the lenght… (you know…, schema less)

    something like…:

    function() {
    emit(‘tags’, { count: this.tags ? this.tags.length : 0 });
    }

    Thanks you

    Like

Leave a comment