2

I'm having some trouble using the MongoDB aggregation framework to count event types in my database. How do I calculate the sum of the value.count field for each unique 3rd index of the _id.val field?

The basic structure of my data looks like:

{ _id: { evt: "click", val: [ "default", "125", "311", "1" ] }, value: { count: 1 } } { _id: { evt: "click", val: [ "default", "154", "321", "2" ] }, value: { count: 2 } } { _id: { evt: "click", val: [ "default", "192", "263", "1" ] }, value: { count: 4 } } 

The values in the val field denote ["type","x","y","time"], respectively. I'm trying to extract the 3rd index, or time value of the _id.val key. The output I'm looking to achieve:

 1: 5 2: 2 

I've been trying to do it via this PHP:

$ops2 = array( array( '$match' => $q2 ), array( '$group' => array( '_id' => array( 'evt' => '$_id.evt', 'time' => '$_id.val.3' ), 'count' => array('$sum' => '$value.count' ) ) ) ); 

But it doesn't appear to like the 3 index in the group array

2 Answers 2

2

The data you are working with looks like it has come as the output of a mapReduce operation already, since it has that specific "_id" and "value" structure that mapReduce prodcues. As such you may be better off going back to the logic of how that process is implemented and follow the same to just extract and total what you want, or at least change it's output form to this:

{ _id: { evt: "click", val: { "type": "default", "x": "125", "y": "311", "time": "1" } }, value: { count: 1 } }, { _id: { evt: "click", val: { "type": "default", "x": "154", "y": "321", "time": "2" } }, value: { count: 2 } }, { _id: { evt: "click", val: { "type": "default", "x": "192", "y": "263", "time": "1" } }, value: { count: 4 } } 

As the problem is that the aggregation framework "presently" lacks the ability to address the "indexed" position of an array ( real "non-associative" array and not PHP array ) and would always return null when you try to do so.

Lacking the ability to go back to the original source or mapReduce operation, then you can write a mapReduce operation on this data to get the expected results ( shell representation since it's going to be a JavaScript anyway ):

db.collection.mapReduce( function() { emit({ evt: this._id.evt, time: this._id.val[3] }, this.value.count) }, function(key,values) { return Array.sum(values) }, { out: { inline: 1 } } ) 

Which returns typical mapReduce output like this:

{ "_id" : { "evt" : "click", "time" : "1" }, "value" : 5 }, { "_id" : { "evt" : "click", "time" : "2" }, "value" : 2 } 

If you were able to at least transform the current output collection to the form suggested at first above, then you would run with the aggregation framework like this instead ( again common representaion ):

 { "$group": { "_id": { "evt": "$_id.evt", "time": "$_id.val.time" }, "count": { "$sum": "$value.count" } }} 

Which of course would yield from the altered data:

{ "_id" : { "evt" : "click", "time" : "2" }, "count" : 2 } { "_id" : { "evt" : "click", "time" : "1" }, "count" : 5 } 

In future releases of MongoDB, there will be a $slice operator which allows the array handling, so with your current structure you could do this instead:

 { "$group": { "_id": { "evt": "$_id.evt", "time": { "$slice": [ "$_id.val", 3,1 ] } }, "count": { "$sum": "$value.count" } }} 

Which allows picking of the "third" index element from the array, albeit that this will of course still return an "array" as the element like this:

{ "_id" : { "evt" : "click", "time" : [ "2" ] }, "count" : 2 } { "_id" : { "evt" : "click", "time" : [ "1" ] }, "count" : 5 } 

So right now, if you can change your initial mapReduce output then do it. Either to the form as shown here or just work with modifications to the initial query to get the end result you want here. Modifying to the recommened form will at least allow the .aggregate() command to work as is shown in the second example here.

If not, then mapReduce is still the only way at present for writing, as shown in the "first" example.

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

Comments

1

At first, I think you may have something wrong in your understanding of Mongo...Because each document in mongo should have its unique _id, to identify itself from others. So I have add a _id to each object, and change your origin "_id" field to "data". Now the structure is:

/* 1 */ { "_id" : "ubLrDptWvJE7LZqDF", "data" : { "evt" : "click", "val" : [ "default", "125", "311", "1" ] }, "value" : { "count" : 1 } } /* 2 */ { "_id" : "C2QCEhvCsp3xG6EKZ", "data" : { "evt" : "click", "val" : [ "default", "154", "321", "2" ] }, "value" : { "count" : 2 } } /* 3 */ { "_id" : "bT72z7gMKoyX5JfHL", "data" : { "evt" : "click", "val" : [ "default", "192", "263", "1" ] }, "value" : { "count" : 4 } } 

I am not sure how to do this query in PHP, Because I only know a little PHP...... But I could give you an example of using aggregation in Javascript, its code and output are as follows: code and output

Here are some useful link: using mongo in PHP I wish it can help you solve your problem perfectly :-)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.