18

Is there a way, in a MongoDB projection, to specify some new fields (but at the same time, retain fields that were input to the projection stage of the pipeline)? I'm not renaming any of the existing fields.

So if I start with a collection that has 'field1' and 'field2', and do the following projection:

{ $project: { field3: { $gt: ['$field1', 10] } } }

I want to end up with 'field1', 'field2', and 'field3' present as input to the next stage, or output from the aggregation framework.

I attempted to put the projection into exclusion mode by excluding _id, but that doesn't work.

BraedenP
  • 7,125
  • 4
  • 33
  • 42
  • 2
    This question is also raised at http://stackoverflow.com/questions/19431773/mongodb-aggregation-project-include-all-fields-plus-new-one, with some useful (different) answers. – Vince Bowdren Aug 26 '16 at 08:46

3 Answers3

14

where $project needs to specify which fields to pass through, $addFields will return all fields and add or replace specified fields.

{ $addFields: { field3: { $gt: ['$field1', 10] } } }

will achieve exactly what you want.

Please note, this feature was added in Mongo version 3.4.

JohnnyHK
  • 305,182
  • 66
  • 621
  • 471
Frazer Kirkman
  • 1,003
  • 1
  • 14
  • 23
  • 1
    Note that `$addFields` can also replace or change an existing field if you give it the same name, *and* at the same time include all the other fields, Useful for example if you want to use `$filter` on an array but retain all other fields in the projection. – Adam Reis Jul 13 '19 at 23:08
10

MongoDB 2.6 introduced a new $$ROOT aggregation variable which almost does what you want: it will include the full document being processed in the current pipeline stage, so you don't need to know or specify what fields to include.

Taking your example aggregation and adding a fields projection with $$ROOT:

db.pipeline.aggregate(
    { $project: {
        field3: {
            $gt: ['$field1', 10]
        },
        fields: "$$ROOT"
    }}
)

The output will include both new calculated field(s) and all the original fields:

{
    "_id" : ObjectId("53b56cc542974939144cee2a"),
    "field3" : false,
    "fields" : {
        "_id" : ObjectId("53b56cc542974939144cee2a"),
        "field1" : 1,
        "field2" : 2,
        "field3" : 3
    }
}

MongoDB 3.3.11 introduces the addFields aggregation stage that does what you want. See this entry in MongoDB issue tracker: SERVER-5781 - Implement $addFields aggregation stage for using expression language to add new fields to a document.

Jérôme
  • 13,328
  • 7
  • 56
  • 106
Stennie
  • 63,885
  • 14
  • 149
  • 175
1

Only _id will be projected by default. You have to specify what other fields have to be projected.

{ $project: { field1:1,field2:1, field3: { $gt: ['$field1', 10] } } }

or

{ $project: { field1:'$field1',field2:'$field2', field3: { $gt: ['$field1', 10] } } }
4J41
  • 5,005
  • 1
  • 29
  • 41
  • 2
    That works if I know all of the fields that exist as input to the projection. Since this is part of an automated generation process, it's difficult (not impossible, but not ideal) to keep track of which fields are available before any given projection. I'm looking for a solution that can automatically let those existing fields through (but this may not exist -- just asking in case it does). – BraedenP Dec 10 '13 at 15:09
  • `The _id field is always included by default.` statement in http://docs.mongodb.org/manual/reference/operator/aggregation/project/ implicitly mentions that the rest will not be projected by default. – 4J41 Dec 10 '13 at 15:11