0

I would like to have my aggregation query return a flat array instead of an array of objects exactly like .distinct() does.

Example Document:

{
    type: 'pageview',
    url: 'https://example.com/something',
    visitorId: '5df7c38abbdb1506dc048451'
}

Example Aggregation:

db.accounts.events.aggregate( [
    // First stage
    { $match: { url: 'https://example.com/something' } },
    // Second stage
    { $group: { _id: "$visitorId", count: { $sum: 1 } } },
    // Third stage
    { $match: { count: { $gt: 10 } } }
] )

Returns:

{ "_id" : ObjectId("5df7c38abbdb1506dc048451"), "count" : 13 }
{ "_id" : ObjectId("5df7c38abbdb1506dc048454"), "count" : 18 }
// ... and so forth

Should return:

[ ObjectId("5df7c38abbdb1506dc048451"), ObjectId("5df7c38abbdb1506dc048454") ]

I know that its fairly easy to do on the client side, but I wonder if mongodb is capable of doing it right within the aggregation as well.

3 Answers 3

1

This will give you something close to your expected result:

db.collection.aggregate([
    // First stage
    { $match: { url: 'https://example.com/something' } },
    // Second stage
    { $group: { _id: "$visitorId", count: { $sum: 1 } } },
    // Third stage
    { $match: { count: { $gt: 10 } } },
    {
        $group: {
            _id: null,
            data: {
                $push: "$_id"
            }
        }
    },
    {
        $project: {
            _id: 0,
            data: {
                $reduce: {
                    input: "$data",
                    initialValue: [],
                    in: {
                        $concatArrays: ["$$value", ["$$this"]]
                    }
                }
            }
        }
    }
])

Result:

[
    {
        "data": [
            ObjectId("5df7c38abbdb1506dc048451"),
            ObjectId("5df7c38abbdb1506dc048454")
        ]
    }
]
Sign up to request clarification or add additional context in comments.

Comments

1

Definiton of db.collection.aggregate() says that an Aggregation:

Returns: A cursor to the documents produced by the final stage of the aggregation pipeline operation, ...

The returned cursor has documents, and a document always has a key and a value, as defined in the MongoDB document structure.

So, the way to get an array of values (only) is to use one of the ways described in the already posted answers and the following discussions (i.e., by applying the cursor methods on the returned cursor by the aggregation).

Comments

0

You can use map on the aggregate to return a flat array of a desired field value:

db.accounts.events.aggregate( [
  // First stage
   { $match: { url: 'https://example.com/something' } },
  // Second stage
   { $group: { _id: "$visitorId", count: { $sum: 1 } } },
  // Third stage
   { $match: { count: { $gt: 10 } } }
]).map(function(obj) { return obj._id })

Here is a method utilizing mongo. The thing is, it returns an object with a flat array within. Aggregation will always return an object, unfortunately. You can chain an aggregate with .cursor().exec() in order to get the .toArray() method, but I'm not sure if that will help you here:

db.accounts.events.aggregate( [
  // First stage
   { $match: { url: 'https://example.com/something' } },
  // Second stage
   { $group: { _id: "$visitorId", count: { $sum: 1 } } },
  // Third stage
   { $match: { count: { $gt: 10 } } },
   {
    $project:{array:true,_id:false}
   }
])

4 Comments

Thanks. I am aware that I can do it with a simple map on the client side but thats why I was asking if there is a way to do it directly within the mongodb aggregation :)
@Snowball this isn't on the client... the .map method is available in the aggregate you are calling on the server.
I mean the mongodb definition of client and server side. So even if you are running your mongodb client on a server with node.js it is the mongodb client (side) while the aggregation runs on the mongodb server. The $match and $group stuff is no native JavaScript, but executed by mongodb on the server. The .map() is native JavaScript being executed on the "client side".
@Snowball I got ya, my apologies. I updated my answer. It may not be sufficient for you, but perhaps it will help.

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.