Совокупное количество членов массива, соответствующее условию - PullRequest
0 голосов
/ 01 июня 2018

У меня возникли некоторые проблемы, как указано в заголовке, для подсчета элементов в массиве с использованием MongoDB.У меня есть БД только с одним документом, сделанным следующим образом:

 {_id: ObjectId("abcdefghilmnopq"),
    "Array": [
      {field1: "val1",
       field2: "val2",
       field3: "val3",
       ...
       },
       {field1: "Value1",
        field2: "Value2",
        field3: "Value3",
       ...
       },
        ...
     ]
 }

Я хочу посчитать количество элементов массива, которые имеют определенное условие (например, field1: "a", и подсчитать все элементы, которые имеют field1 = a).Я пытаюсь с этим кодом:

db.collection.aggregate([
{ $unwind : {path: "$Array", 
             includeArrayIndex: "arrayIndex"}},
{ $match : { "Array.field1" : "a"}},
{ $project : { _id : 0, 
               Array : 1, 
               arrayIndex: 1, 
               total: {$size: "$Array"}}}
])

, но я получаю эту ошибку:

Команда завершилась ошибкой 17124: «Аргумент $ size должен быть массивом, нобыл типа: объект 'на сервере

Я искал несколько ответов на эту проблему, но я не нашел ничего решающего для своей проблемы.Я имею в виду, «Массив» - это массив!

Заранее спасибо

1 Ответ

0 голосов
/ 01 июня 2018

Ошибка в том, что он больше не является массивом после $unwind и, следовательно, больше не является допустимым аргументом $size.

пытаться «объединить» пару существующих ответов, не понимая, что они делают.То, что вы действительно хотите здесь, это $filter и $size

db.collection.aggregate([
  { "$project": {
    "total": {
      "$size": {
        "$filter": {
          "input": "$Array",
          "cond": { "$eq": [ "$$this.field1", "a" ] }
        }
      }
    }
  }}
])

Или «заново изобрести колесо» с помощью $reduce:

db.collection.aggregate([
  { "$project": {
    "total": {
      "$reduce": {
        "input": "$Array",
        "initialValue": 0,
        "in": {
          "$sum": [
            "$$value", 
            { "$cond": [{ "$eq": [ "$$this.field1", "a" ] }, 1, 0] }
        }
      }
    }
  }}
])

Или для того, что вы пытались сделать с $unwind, вы фактически $group снова, чтобы «посчитать», сколько совпаденийбыло:

db.collection.aggregate([
  { "$unwind": "$Array" },
  { "$match": { "Array.field1": "a" } },
  { "$group": {
    "_id": "$_id",
    "total": { "$sum": 1 }
  }}
])

Первые две формы являются «оптимальными» для современных сред MongoDB.Окончательная форма с $unwind и $group является «устаревшей» конструкцией, которая действительно не была необходима для этого типа операций начиная с MongoDB 2.6, хотя с некоторыми немного другими операторами.

В этих первых двух мы в основном сравниваем значение field1 каждого элемента массива, пока он все еще является массивом.И $filter и $reduce - современные операторы, разработанные для работы с существующим массивом на месте.Такое же сравнение выполняется для каждого с использованием оператора агрегации $eq, который возвращает логическое значение, основанное на том, равны ли приведенные аргументы или нет.В этом случае для каждого члена массива ожидаемое значение "a".

В случае $filter массив фактически остается неизменным, за исключением любых элементов, которые не соответствуютпредоставленные условия в "cond" удаляются из массива.Поскольку у нас по-прежнему есть «массив» в качестве выходных данных, мы можем использовать оператор $size для измерения количества элементов массива, оставшихся после обработки этого условия фильтра.

* $reduce, с другой стороны, работает через элементы массива и предоставляет выражение для каждого элемента и сохраненное значение «аккумулятора», которое мы инициализировали с помощью "initialValue".В этом случае тот же тест $eq применяется в операторе $cond.Это условный оператор «троичный» или if/then/else, который позволяет проверенному выражению, которое возвращает логическое значение, возвращать значение then при true или else при false.

В этом выражении мы возвращаем 1 или 0 соответственно и предоставляем общий результат сложения возвращаемого значения и текущего "аккумулятора" "$$value" с помощью оператора $sum, чтобы сложить их вместе.

Окончательная форма, использованная $unwind в массиве.Что это на самом деле делает, так это деконструирует элементы массива, чтобы создать «новый документ» для каждого элемента массива и связанных с ним родительских полей в исходном документе.Это эффективно «копирует» основной документ для каждого элемента массива.

После того, как вы $unwind, структура документов будет изменена на «более плоскую» форму.Вот почему вы можете затем выполнить следующий этап конвейера $match, чтобы удалить несопоставленные документы.

Это приведет нас к $group, который применяется«собрать воедино» всю информацию, связанную с общим ключом.В данном случае это поле _id исходного документа, которое, конечно, было скопировано в каждый документ, созданный с помощью $unwind.Возвращаясь к этому «общему ключу» как к единому документу, мы можем «посчитать» оставшиеся «документы», извлеченные из массива, используя аккумулятор $sum.

Если мыхотел вернуть оставшийся «массив» обратно, тогда вы можете $push и перестроить массив только с оставшимися членами:

  { "$group": {
    "_id": "$_id",
    "Array": { "$push": "$Array" },
    "total": { "$sum": 1 }
  }}

Но, конечно, вместо использования $size на другой стадии конвейера, мы можем просто «посчитать», как мы уже сделали с $sum

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...