|
If you use a non-inclusion dotted field during a $project stage, you can get different results depending on if the incoming document contains anything in that path:
> db.foo.drop()
|
true
|
> db.foo.insert({a: [{b: 1}, {b: 2}]})
|
WriteResult({ "nInserted" : 1 })
|
> db.foo.aggregate([{$project: {"x.y": {$literal: 1}}}])
|
// "x.y" didn't exist, so we created a nested field.
|
{ "_id" : ObjectId("56fae6c03ef38a5ce210b232"), "x" : { "y" : 1 } }
|
> db.foo.aggregate([{$project: {"a.b": {$literal: 1}, c: "$a.b"}}])
|
// "a.b" did exist, so we modified each value of "a.b" to be 1.
|
{ "_id" : ObjectId("56fae6c03ef38a5ce210b232"), "a" : [ { "b" : 1 }, { "b" : 1 } ], "c" : [ 1, 2 ] }
|
What's even weirder is this behavior:
> db.foo.find()
|
{ "_id" : ObjectId("56fae6c03ef38a5ce210b232"), "a" : [ { "b" : 1 }, { "b" : 2 } ] }
|
> db.foo.aggregate([{$project: {"a.b": {$literal: 1}}}])
|
// We created a nested field "a.b", even though there was previously a different structure in "a.b".
|
{ "_id" : ObjectId("56fae6c03ef38a5ce210b232"), "a" : { "b" : 1 } }
|
Here the document did have something in "a.b", but nothing else was using it, so it was projected out, hence the different behavior from the example above, which specified {c: "$a.b"} so that the field "a.b" was needed, and hence remained in the incoming document.
> db.foo.explain().aggregate([{$project: {"a.b": {$literal: 1}}}])
|
{
|
"waitedMS" : NumberLong(0),
|
"stages" : [
|
{
|
"$cursor" : {
|
"query" : {
|
|
},
|
"fields" : {
|
"_id" : 1
|
},
|
...
|
> db.foo.explain().aggregate([{$project: {"a.b": {$literal: 1}, c: "$a.b"}}])
|
{
|
"waitedMS" : NumberLong(0),
|
"stages" : [
|
{
|
"$cursor" : {
|
"query" : {
|
|
},
|
"fields" : {
|
"a.b" : 1,
|
"_id" : 1
|
},
|
...
|
|