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.
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
,and specifications can be one of the following:
What's an expression? From aggregation 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
while an operator expression has one of the forms
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: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.