0

I have a collection of objects in MongoDB that looks like this

    { "event_type": "event1", "created_at": some_date, ...more_fields... },
    { "event_type": "event2", "created_at": some_date, ...other_fields... },
    { "event_type": "event1", "created_at": some_date, ...more_fields... }

Now I want to have the above data with all fields grouped by some field (which might not be present in all objects) and order it by the highest created_at date. I have tried to do this using the Aggregation Framework with the following query:

  collection.aggregate([
    { "$group" => {
        :_id       => "$somefield",
        :last_time => { "$max"  => "$created_at" },
        :events    => { "$push" => { ... } }
      }
    },
    { "$match" => { "_id" => { "$ne" => nil } } },
    { "$sort" => { "last_time" => -1 } }
  ])

The problem I'm facing is related to the line

 :events    => { "$push" => { ... } }

If I put in some specific fields then it works, but I don't know which exact fields the collection contains. But rather I want the whole object returned like this:

    {
        "event_type": "event1", "last_time": some_date, "events": [
            { "event_type": "event1", "created_at": some_date, ...more_fields... },
            { "event_type": "event1", "created_at": some_date, ...more_fields... },
            ...
        ]
    }

3 Answers 3

1

Assuming you want to push ALL the fields in your object, without knowing exactly what those fields are, couldn't you just wrap them in a known field parent and push that?

{ events: {
    "event_type": "event1", "created_at": some_date, ...more_fields... 
  }
},

Then you just need to push "events".

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

1 Comment

That would work, but I would rather not have to change the shape of the data. I ended up doing it with a simple group call and sorting on the client.
0

if you do the match first, you'll be able to use the index (if one exists on the "somefield")

could it be you need to project the sub doc you want to create an array of for each aggregated value? I haven't tested this, but might will hopefully point you in right direction?

something like?

db.collection.aggregate( 
{ $match : { "some_field" : { $ne:null} } },  
{ $project : 
    { 
        event_type :"event_type", 
        created_at: "created_at",
        event: 
        {
            another_att: "attrib1",
            another_att2: "attrib2"
        }
     },
{ $group : 
    { 
        _id: "$event_type",
        lasttime : { $max: "$created_at"},
        theevents: { $addToSet : "$event" }
    }
 })

Comments

0

This problem is solved more easily using the group function:

options = {
  key:      "field_to_group_by",
  cond:     { ..some filter criteria.. },
  initial:  { :events  => [] },
  reduce:   "function(obj, agg) { agg.events.push(obj); }"
}
collection.group(options)

This doesn't return the items in the order I want, but sorting can be done on the client.

1 Comment

can't you just add .Sort() on group() ?

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.