Reputation: 19700
My question is closely related to this, but not similar.
I have a sample document in my collection:
db.t.insert({"a":1,"b":2});
My intent is to project a field named combined
of type array
with the values of both a
and b
together.([1,2])
.
I simply try to aggregate with a $project
stage:
db.t.aggregate([
{$project:{"combined":[]}}
])
MongoDB
throws an error: disallowed field type Array in object expression
.
Which means a field cannot be projected as a array.
But when i use a $cond
operator to project an array, the field gets projected.
db.t.aggregate([
{$project:{"combined":{$cond:[{$eq:[1,1]},["$a","$b"],"$a"]}}}
])
I get the o/p: {"combined" : [ "$a", "$b" ] }
.
If you notice the output, the value of a
and b
are treated as if they were literals and not a field paths.
Can anyone please explain to me this behavior?, When i make the condition to fail,
db.t.aggregate([
{$project:{"combined":{$cond:[{$eq:[1,2]},["$a","$b"],"$a"]}}}
])
I get the expected output where $a
is treated as a field path, since $a
is not enclosed as an array element.
Upvotes: 1
Views: 1472
Reputation: 11671
I've run into this before too and it's annoying, but it's actually working as documented for literal ["$a", "$b"]
; the first error about disallowed field type is...not as clear why it complains. You have to follow the description of the grammar of the $project
stage spread out in the documentation, however. I'll try to do that here. Starting at $project
,
The $project stage has the following prototype form:
{ $project: { <specifications> } }
and specifications can be one of the following:
1. <field> : <1 or true or 0 or false>
2. <field> : <expression>
What's an expression? From aggregation expressions,
Expressions can include field paths and system variables, literals, expression objects, and operator expressions.
What are each of those things? A field path/system variable should be familiar: it's a string literal prefixed with $ or $$. An expression object has the form
{ <field1>: <expression1>, ... }
while an operator expression has one of the forms
{ <operator>: [ <argument1>, <argument2> ... ] }
{ <operator>: <argument> }
for some enumerated list of values for <operator>
.
What's an <argument>
? The documentation isn't clear on it, but from my experience I think it's any expression, subject to the syntax rules of the given operator (examine the operator expression "cond" : ...
in the question).
Arrays fit in only as containers for argument lists and as literals. Literals are literals - their content is not evaluated for field paths or system variables, which is why the array literal argument in the $cond
ends up with the value [ "$a", "$b" ]
. The expressions in the argument array are evaluated.
The first error about Array being a disallowed value type is a bit odd to me, since an array literal is a valid expression, so according to the documentation it can be a value in an object expression. I don't see any ambiguity in parsing it as part of an object expression, either. It looks like it's just a rule they made to make the parsing easier? You can "dodge" it using $literal
to put in a constant array value:
db.collection.project([{ "$project" : { "combined" : { "$literal" : [1, 2] } } }])
I hope this helps explain why things work this way. I was surprised the first time I tried to do something like [ "$a", "$b" ]
and it didn't work as I expected. It'd be nice if there were a feature to pack field paths into an array, at least. I've found uses for it when $group
ing on ordered pairs of values, as well.
There's a JIRA ticket, SERVER-8141, requesting an $array
operator to help with cases like this.
Upvotes: 5