Ошибка в том, что он больше не является массивом после $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