Таким образом, ошибки заключаются в том, что без $match
ваш конвейер пытается получить доступ к другим документам, которые не имеют ожидаемой структуры. Это действительно что-то отдельное, чтобы разобраться, хотя.
Чтобы на самом деле ответить на ваш вопрос с его конечной целью, вам нужен такой конвейер:
var _id = "Stats1";
var target = 30;
db.stats.aggregate([
{ "$match": { "_id" : _id } },
{ "$replaceRoot": {
"newRoot": {
"$let": {
"vars": {
"working": {
"$map": {
"input": { "$objectToArray": "$minutes" },
"in": {
"k": { "$toInt": "$$this.k" },
"v": "$$this.v",
"diff": { "$abs": { "$subtract": [ target, { "$toInt": "$$this.k" }] } }
}
}
}
},
"in": {
"$arrayToObject": {
"$map": {
"input": {
"$filter": {
"input": {
"$objectToArray": {
"$arrayElemAt": [
"$$working",
{ "$indexOfArray": [ "$$working.diff", { "$min": "$$working.diff" } ] }
]
}
},
"cond": { "$ne": [ "$$this.k", "diff" ] }
}
},
"in": {
"k": { "$cond": [{ "$eq": [ "$$this.k", "k"] }, "minute", "value" ] },
"v": { "$cond": [{ "$eq": [ "$$this.k", "k"] }, { "$toString": "$$this.v" }, "$$this.v" ] }
}
}
}
}
}
}
}}
])
Что, конечно, возвращает желаемый результат:
{ "minute" : "29", "value" : [ { "32" : "2" } ] }
В последовательности вы делаете $objectToArray
, как вы изначально пытались, но затем вам нужно, чтобы значение key или "k"
было фактически преобразовано в числовое значение для сравнения. Вам также необходимо вычислить разницу этого значения из значения, которое вы ищете, в данном случае 30
. Это дает вам «рабочую» копию данных в виде массива, что важно для следующих этапов ввода.
Следующий раздел в основном читается внутрь от уровней отступа, чтобы лучше понять порядок.
Сначала вы хотите извлечь элемент из этого рабочего массива, где разница (при использовании $abs
, так что положительные и отрицательные значения совпадают) - это минимальное значение с $min
. Это дает позицию первого совпадения из $indexOfArray
и используется с $arrayElemAt
, чтобы вернуть этот единственный выбранный элемент из рабочего массива.
Нам не нужны все поля в этом объекте, поэтому $objectToArray
преобразует этот отдельный объект в парные объекты "k"
и "v"
, и первый шаг - $filter
где ключ - это поле разница и удалите его из этого списка.
Затем вы хотите переименовать поля и изменить некоторые форматы данных, поэтому $map
выполняет итерацию оставшегося массива (всего две записи), присваивая читаемые имена и устанавливая формат строки для "minute"
.
Наконец, это может вернуться к объекту как $arrayToObject
в качестве конечного результата. Поскольку мы хотели ссылаться на этот массив "working"
несколько раз, мы объявляем в $let
, что позволяет нам это делать. И поскольку все это было выражением, которое выводит то, что вы хотите в качестве документа, вы используете $replaceRoot
, чтобы обернуть это как «выражение», в основном это единственный ожидаемый аргумент.