Question

Following is the mapreduce query:

mapper=function () {
    if (this.meta != null && this.meta.active == true && this.data != null && this.data.attributes != null && this.data.attributes.lang == "en" && this.data.attributes.geo == "us" && this.meta.client.id == "1") {
        if (this.meta.created > ISODate("2014-03-30 12:27:50")) {
            emit("querisAddedSinceLastWeek", {
                "count": 1
            });
        }
        if (this.data.expectedResults == null && this.data.attributes.lang == "en" && this.data.attributes.geo == "us" && this.meta.client.id == "1") {
            emit("queriesWithExpectedResultsCount", {
                "count": 1
            });
        }
        if (this.data.attributes.lang == "en" && this.data.attributes.geo == "us" && this.meta.client.id == "1" && this.meta.active == true) {
            emit("totalActiveQueriesCount", {
                "count": 1
            });
        }
    }
}

reducer=function (k, v) {
    counter = 0;
    for (i = 0; i < v.length; i++) {
        counter += v[i].count;
    }
    return {
        "count": counter
    }
}

db['ARTDocument.dev'].mapReduce(mapper,reducer,{out:{inline:1}});

In this query, some conditions are common for all 3 queries and there is only single condition for each of them.I want to fire these queries in a single query if possible using aggregation framework in monodb. I want this exact query to be written in aggregation framework in mongodb.

I have tried this but it doesnt work.

db['ARTDocument.dev'].aggregate(

    {
        $match: {
            "meta.active": true,
            "meta.client.id": 1,
            "data.attributes.lang": "en",
            "data.attributes.geo": "us"
        }
    }, {
        $group: {
            _id: {
                $cond: [{
                        $lt: ["meta.created", ISODate("2014-03-30 12:27:50")]
                    },
                    "querisAddedSinceLastWeek",
                    "null"
                ]
            },
            count: {
                $sum: 1
            }
        }
    }, {
        $group: {
            _id: {
                $ifNull: ["data.expectedResults", "queriesWithoutExpectedResultsCount"]
            },
            count: {
                $sum: 1
            }
        }
    }

)
Was it helpful?

Solution

You have the right general idea about the flow, but your structure is slightly off.

Your first stage, $match the conditions to qualify to be counted is fine.

match = {$match: {
         "meta.active": true,
         "meta.client.id": 1,
         "data.attributes.lang": "en",
         "data.attributes.geo": "us"
        }

Your next stage is $group, as it should be, but you are not quite grouping around the correct key. Since you want to get totals over all the qualified documents, you should be grouping with _id as a constant, and doing $sum of 1 or 0 depending on whether the conditional evaluations to true or not.

group = {$group: {
         _id: null,
         querisAddedSinceLastWeek : { $sum : {$cond: [
                    { $gt : [ meta.created, ISODate("2014-03-30 12:27:50") ] },
                    1, 0 ] }
         },
         queriesWithExpectedResultsCount: { $sum:
                    { $eq : [ data.expectedResults, null ] }, 
                    1, 0 ] }
         },
         totalActiveQueriesCount: { $sum : 1 }
} }

Your map function has a lot of unnecessary (redundant) conditions, it's usually better to express what you actual want - but what it's currently doing is summing up all the active queries, and also getting a sum of queries since last week, and ones that have null data.expectedResults.

Full aggregate, with above two stages defined:

db['ARTDocument.dev'].aggregate( match, group );
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top