27 cases, recorded before a line of the evaluator is written, which is the
whole point of having built the recorder first: the expectations come from
mongod 8.3.7 rather than from what the implementation is about to do.
The corpus reads 1 pass / 26 fail, and the one pass is the unknown-operator
refusal M2 already answers correctly. Expressions are exercised through
`$group` because `_id` and the accumulator arguments are the only expression
positions that exist until `$addFields` and `$project`'s computed fields land;
testing them anywhere else would be testing a stage that is not there.
Ten things the recording settled that guessing would have got wrong:
$add over a missing field or null null -- not an error, and not 0
$add over a string error 7157723
$divide by zero error 4848401
$mod of -5 by 4 -1, the dividend's sign
$lt of a number and a string true, canonical type order
$and over -5 truthy
$not of a missing field true
$switch, no branch and no default error 40069
two operators in one expression error 15983, *not* $group's 40238
$subtract with one operand error 16020
The six error codes are in `ErrorCode` already, so the evaluator's refusals and
its runtime failures have somewhere measured to land. The evaluator itself is
the next commit and is not started.
191/191 unit tests, crud corpus unchanged at 201/90/196.
1140 lines
24 KiB
JSON
1140 lines
24 KiB
JSON
{
|
|
"description": "expressions",
|
|
"schemaVersion": "1.4",
|
|
"createEntities": [
|
|
{
|
|
"client": {
|
|
"id": "client0"
|
|
}
|
|
},
|
|
{
|
|
"database": {
|
|
"id": "database0",
|
|
"client": "client0",
|
|
"databaseName": "aggregate-corpus"
|
|
}
|
|
},
|
|
{
|
|
"collection": {
|
|
"id": "collection0",
|
|
"database": "database0",
|
|
"collectionName": "coll"
|
|
}
|
|
}
|
|
],
|
|
"initialData": [
|
|
{
|
|
"collectionName": "coll",
|
|
"databaseName": "aggregate-corpus",
|
|
"documents": [
|
|
{
|
|
"_id": 1,
|
|
"a": 10,
|
|
"b": 3,
|
|
"s": "x",
|
|
"n": null,
|
|
"f": false
|
|
},
|
|
{
|
|
"_id": 2,
|
|
"a": 20,
|
|
"b": 0,
|
|
"s": "y",
|
|
"f": true
|
|
},
|
|
{
|
|
"_id": 3,
|
|
"a": -5,
|
|
"b": 4,
|
|
"s": "x",
|
|
"n": 7,
|
|
"f": false
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"tests": [
|
|
{
|
|
"description": "a compound _id groups on every field",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"s": "$s",
|
|
"f": "$f"
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id.s": 1,
|
|
"_id.f": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": {
|
|
"s": "x",
|
|
"f": false
|
|
},
|
|
"n": 2
|
|
},
|
|
{
|
|
"_id": {
|
|
"s": "y",
|
|
"f": true
|
|
},
|
|
"n": 1
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "a nested compound _id",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"outer": {
|
|
"inner": "$s"
|
|
}
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id.outer.inner": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": {
|
|
"outer": {
|
|
"inner": "x"
|
|
}
|
|
},
|
|
"n": 2
|
|
},
|
|
{
|
|
"_id": {
|
|
"outer": {
|
|
"inner": "y"
|
|
}
|
|
},
|
|
"n": 1
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$literal keeps a $-string from being a path",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$literal": "$s"
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": "$s",
|
|
"n": 3
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$literal of a document",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$literal": {
|
|
"$sum": 1
|
|
}
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": {
|
|
"$sum": 1
|
|
},
|
|
"n": 3
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$add of a path and a constant",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$add": [
|
|
"$a",
|
|
1
|
|
]
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": -4,
|
|
"n": 1
|
|
},
|
|
{
|
|
"_id": 11,
|
|
"n": 1
|
|
},
|
|
{
|
|
"_id": 21,
|
|
"n": 1
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$subtract and $multiply",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": "$s",
|
|
"d": {
|
|
"$sum": {
|
|
"$subtract": [
|
|
"$a",
|
|
"$b"
|
|
]
|
|
}
|
|
},
|
|
"m": {
|
|
"$sum": {
|
|
"$multiply": [
|
|
"$a",
|
|
2
|
|
]
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": "x",
|
|
"d": -2,
|
|
"m": 10
|
|
},
|
|
{
|
|
"_id": "y",
|
|
"d": 20,
|
|
"m": 40
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$divide by zero is an error",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": null,
|
|
"v": {
|
|
"$sum": {
|
|
"$divide": [
|
|
"$a",
|
|
"$b"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectError": {
|
|
"isError": true,
|
|
"errorCode": 4848401
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$divide and $mod",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$match": {
|
|
"b": {
|
|
"$gt": 0
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$group": {
|
|
"_id": "$s",
|
|
"d": {
|
|
"$push": {
|
|
"$divide": [
|
|
"$a",
|
|
"$b"
|
|
]
|
|
}
|
|
},
|
|
"m": {
|
|
"$push": {
|
|
"$mod": [
|
|
"$a",
|
|
"$b"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": "x",
|
|
"d": [
|
|
3.3333333333333335,
|
|
-1.25
|
|
],
|
|
"m": [
|
|
1,
|
|
-1
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "arithmetic over a missing field",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$add": [
|
|
"$missing",
|
|
1
|
|
]
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": null,
|
|
"n": 3
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "arithmetic over null",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": "$s",
|
|
"v": {
|
|
"$push": {
|
|
"$add": [
|
|
"$n",
|
|
1
|
|
]
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": "x",
|
|
"v": [
|
|
null,
|
|
8
|
|
]
|
|
},
|
|
{
|
|
"_id": "y",
|
|
"v": [
|
|
null
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$eq and $ne",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$eq": [
|
|
"$s",
|
|
"x"
|
|
]
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": false,
|
|
"n": 1
|
|
},
|
|
{
|
|
"_id": true,
|
|
"n": 2
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "comparison across types follows canonical order",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$lt": [
|
|
"$a",
|
|
"$s"
|
|
]
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": true,
|
|
"n": 3
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$cmp returns the ordering",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": "$s",
|
|
"v": {
|
|
"$push": {
|
|
"$cmp": [
|
|
"$a",
|
|
"$b"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": "x",
|
|
"v": [
|
|
1,
|
|
-1
|
|
]
|
|
},
|
|
{
|
|
"_id": "y",
|
|
"v": [
|
|
1
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$and and $or are truthy over non-booleans",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$and": [
|
|
"$a",
|
|
"$f"
|
|
]
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": false,
|
|
"n": 2
|
|
},
|
|
{
|
|
"_id": true,
|
|
"n": 1
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$not of a missing field",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$not": [
|
|
"$missing"
|
|
]
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": true,
|
|
"n": 3
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$cond in its array form",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$cond": [
|
|
{
|
|
"$gt": [
|
|
"$a",
|
|
0
|
|
]
|
|
},
|
|
"positive",
|
|
"negative"
|
|
]
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": "negative",
|
|
"n": 1
|
|
},
|
|
{
|
|
"_id": "positive",
|
|
"n": 2
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$cond in its document form",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$cond": {
|
|
"if": "$f",
|
|
"then": "yes",
|
|
"else": "no"
|
|
}
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": "no",
|
|
"n": 2
|
|
},
|
|
{
|
|
"_id": "yes",
|
|
"n": 1
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$ifNull falls through on missing and on null",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$ifNull": [
|
|
"$n",
|
|
"fallback"
|
|
]
|
|
},
|
|
"count": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": 7,
|
|
"count": 1
|
|
},
|
|
{
|
|
"_id": "fallback",
|
|
"count": 2
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$switch picks the first matching branch",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$switch": {
|
|
"branches": [
|
|
{
|
|
"case": {
|
|
"$lt": [
|
|
"$a",
|
|
0
|
|
]
|
|
},
|
|
"then": "neg"
|
|
},
|
|
{
|
|
"case": {
|
|
"$lt": [
|
|
"$a",
|
|
15
|
|
]
|
|
},
|
|
"then": "small"
|
|
}
|
|
],
|
|
"default": "big"
|
|
}
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": "big",
|
|
"n": 1
|
|
},
|
|
{
|
|
"_id": "neg",
|
|
"n": 1
|
|
},
|
|
{
|
|
"_id": "small",
|
|
"n": 1
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$switch with no branch matching and no default is an error",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$switch": {
|
|
"branches": [
|
|
{
|
|
"case": false,
|
|
"then": "never"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectError": {
|
|
"isError": true,
|
|
"errorCode": 40069
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "an unknown expression operator is refused",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$bogusExpr": "$a"
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectError": {
|
|
"isError": true,
|
|
"errorCode": 168
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "two operators in one expression document are refused",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$add": [
|
|
"$a",
|
|
1
|
|
],
|
|
"$literal": 2
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectError": {
|
|
"isError": true,
|
|
"errorCode": 15983
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "a nested expression inside an accumulator argument",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": null,
|
|
"v": {
|
|
"$max": {
|
|
"$add": [
|
|
{
|
|
"$multiply": [
|
|
"$a",
|
|
2
|
|
]
|
|
},
|
|
"$b"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": null,
|
|
"v": 40
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "arithmetic over a non-numeric value is an error",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$add": [
|
|
"$s",
|
|
1
|
|
]
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectError": {
|
|
"isError": true,
|
|
"errorCode": 7157723
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$not takes a bare argument as well as an array",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$not": "$f"
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": false,
|
|
"n": 1
|
|
},
|
|
{
|
|
"_id": true,
|
|
"n": 2
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$add of several operands",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$add": [
|
|
"$a",
|
|
"$b",
|
|
100
|
|
]
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"$sort": {
|
|
"_id": 1
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectResult": [
|
|
{
|
|
"_id": 99,
|
|
"n": 1
|
|
},
|
|
{
|
|
"_id": 113,
|
|
"n": 1
|
|
},
|
|
{
|
|
"_id": 120,
|
|
"n": 1
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"description": "$subtract needs exactly two operands",
|
|
"operations": [
|
|
{
|
|
"object": "collection0",
|
|
"name": "aggregate",
|
|
"arguments": {
|
|
"pipeline": [
|
|
{
|
|
"$group": {
|
|
"_id": {
|
|
"$subtract": [
|
|
"$a"
|
|
]
|
|
},
|
|
"n": {
|
|
"$sum": 1
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"expectError": {
|
|
"isError": true,
|
|
"errorCode": 16020
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|