Подтвердить что ты не робот

Общее количество агрегатов MongoDB

Мне тяжело полагать, что этот вопрос не задан и нигде не ответил, но я не могу найти его.

У меня есть запрос агрегации MongoDB, который нужно группировать по булеву: наличие другого поля.

Например, начните с этой коллекции:

> db.test.find()
{ "_id" : ObjectId("53fbede62827b89e4f86c12e"),
  "field" : ObjectId("53fbede62827b89e4f86c12d"), "name" : "Erik" }
{ "_id" : ObjectId("53fbee002827b89e4f86c12f"), "name" : "Erik" }
{ "_id" : ObjectId("53fbee092827b89e4f86c131"),
  "field" : ObjectId("53fbee092827b89e4f86c130"), "name" : "John" }
{ "_id" : ObjectId("53fbee122827b89e4f86c132"), "name" : "Ben" }

2 документа имеют "поле", а 2 нет. Обратите внимание, что каждое значение поля может отличаться; мы просто хотим сгруппировать по его существованию (или для меня тоже ненулевое значение, у меня нет никаких нулевых значений).

Я пробовал использовать $project, но $существует там не существует, а $cond и $ifNull мне не помогли. Поле всегда, кажется, существует, даже если оно не выполняется:

> db.test.aggregate(
  {$project:{fieldExists:{$cond:[{$eq:["$field", null]}, false, true]}}},
  {$group:{_id:"$fieldExists", count:{$sum:1}}}
)
{ "_id" : true, "count" : 4 }

Я бы ожидал, что следующий гораздо более простой агрегат будет работать, но по какой-то причине $exists не поддерживается таким образом:

> db.test.aggregate({$group:{_id:{$exists:"$field"}, count:{$sum:1}}})
assert: command failed: {
  "errmsg" : "exception: invalid operator '$exists'",
  "code" : 15999,
  "ok" : 0
} : aggregate failed
Error: command failed: {
  "errmsg" : "exception: invalid operator '$exists'",
  "code" : 15999,
  "ok" : 0
} : aggregate failed
    at Error (<anonymous>)
    at doassert (src/mongo/shell/assert.js:11:14)
    at Function.assert.commandWorked (src/mongo/shell/assert.js:244:5)
    at DBCollection.aggregate (src/mongo/shell/collection.js:1149:12)
    at (shell):1:9
2014-08-25T19:19:42.344-0700 Error: command failed: {
  "errmsg" : "exception: invalid operator '$exists'",
  "code" : 15999,
  "ok" : 0
} : aggregate failed at src/mongo/shell/assert.js:13

Кто-нибудь знает, как получить желаемый результат из такой коллекции?

Ожидаемый результат:

{ "_id" : true, "count" : 2 }
{ "_id" : false, "count" : 2 }
4b9b3361

Ответ 1

Я решил ту же проблему только прошлой ночью, таким образом:

> db.test.aggregate({$group:{_id:{$gt:["$field", null]}, count:{$sum:1}}})
{ "_id" : true, "count" : 2 }
{ "_id" : false, "count" : 2 }

См. http://docs.mongodb.org/manual/reference/bson-types/#bson-types-comparison-order для полного объяснения того, как это работает.

Ответ 2

Оператор $exists - это оператор запроса, поэтому он используется в основном для "фильтрации" результатов, а не для идентификации логического условия.

В качестве "логического" оператора структура агрегации поддерживает оператор $ifNull. Это возвращает значение поля там, где оно существует, или альтернативное предоставленное значение, где оно отсутствует или иначе оценивается как null

db.test.aggregate([
    { "$group": {
        "_id": { "$ifNull": [ "$field", false ] },
        "count": { "$sum": 1 }
    }}
])

Но, конечно, даже это не является "истинным/ложным" сравнением, поэтому, если вы на самом деле не хотите возвращать фактическое значение поля, в котором оно присутствует, то вам, вероятно, лучше использовать оператор $cond похожий на ваш

db.test.aggregate([
    { "$group": {
        "_id": { "$cond": [{ "$eq": [ "$field", null ] }, true, false ] },
        "count": { "$sum": 1 }
    }}
])

$ifNull может быть очень полезен при замене несуществующих полей массива, которые в противном случае могли бы вызвать ошибку, используя $unwind. Затем вы можете сделать что-то вроде возврата одного элемента или пустого массива, чтобы это не вызывало проблем в остальной части обработки вашего конвейера.

Ответ 3

Я решил его с проверкой на undefined

$ne : [$var_to_check, undefined]

или

$ne:  [ { $type : "$var_to_check"}, 'missing'] }

Это возвращает true, если var определен

Ответ 4

Не знаю, как это было, но сейчас в 2019 году есть чистое решение. В агрегационном конвейере сделайте это

$match: {"my_field": {$ne: null}}

Хорошая вещь в моем lang 'ne' значит нет :)