oarfish
oarfish

Reputation: 4631

How do I use $map in a MongoDB $projection

I have a database with documents like this:

{
   _id: 123, 
   metrics: [{ 
               _id: ObjectID(…), 
               name: 'foo',
               steps: [1, 2, 3, …], 
               values: [10, 12, 13, …]
              },
              { 
               _id: ObjectID(…), 
               name: 'bar',
               steps: [1, 2, 3, …], 
               values: [1.2, 1.2, 1.3, …]
              },
             ]
}

Now my objective is to retrieve all documents, but $map all array entries to their name attribute, discarding the other fields. I thought I could do it like this:

pipeline = [{'$project': {'metrics': {'$map': {'input': '$metrics', 'in': '$this.name'}]
results = client.db.runs.aggregate(pipeline)

But the result has just None values for each array entry ({'metrics': [None, None, None, …]}), as if the requested field name did not exist. But it does. I've also tried with the id field, to no avail. I can see in inspecting the data that the fields are there. I can't figure out what I'm doing wrong here.

Upvotes: 1

Views: 1969

Answers (1)

mickl
mickl

Reputation: 49975

Inside of $map you can use as to define temporary variable which will represent single document from your metrics and then reference that variable in in part.

db.col.aggregate([{'$project': {'metrics': {'$map': {'input': '$metrics', as: 'metric', 'in': '$$metric.name' } } } }])

or you can directly refer to a temporary variable this

db.col.aggregate([{'$project': {'metrics': {'$map': {'input': '$metrics', 'in': '$$this.name' } } } }])

The thing is that every time you use single dollar sign, you refer to a field that's defined in the document that's being processed in current pipeline stage. Additionally you can use double dollar sign to refer to some variables that can be defined by operators like $map, $filter and so on.

You can try your initial code for following document:

{
    _id: 123, 
    this: { name: "x" },
    metrics: [{ }, { } ]
}

and you will get an array with two x values as there are two metrics but every iteration refers to the same this.x value:

{ "_id" : 123, "metrics" : [ "x", "x" ] }

Upvotes: 1

Related Questions