У меня следующие документы в коллекции
{
"_id": 1,
"values":[
{
"value1":10,
"value2":12,
"value3":30
},
{
"value1":20,
"value2":12,
"value3":100
},
{
"value1":30,
"value2":14,
"value3":50
}
]
},
{
"_id": 2,
"values":[
{
"value1":10,
"value2":12,
"value3":60
},
{
"value1":20,
"value2":12,
"value3":80
},
{
"value1":30,
"value2":14,
"value3":70
}
]
}
Мне нужно оставить один документ с одним элементом в массиве по следующим критериям:
values.value1 = 20
values.value2 = 12
values.value3 // максимальное значение из всех значений values.value3 во всех документах
Например, по вышеуказанным критериям должен получиться такой документ:
{
"_id": 1,
"values":[
{
"value1":20,
"value2":12,
"value3":100
}
]
}
Также, подойдет решение, просто сортирующие документы и элементы по указанным критериям. Чтобы первым документом был тот, в котором значение values.value3 максимально из возможных и первым элементом в массиве был объект с максимальным значением. Например:
{
"_id": 1,
"values":[
{
"value1":20,
"value2":12,
"value3":100
},
{
elements...
}
]
},
{
"_id": 2,
"values":[
{
elements...
}
]
}
Сделать это у меня получается, но я использую $match, $unwind, $group, $redact, $sort по несколько раз. Может быть кто-то предложит более правильную реализацию.
Ответ
Лучший способ это запустить два запроса. Первый, чтобы нашел максимальное значение и второй, чтобы остался только документ, где value3 равно этому значению.
Решение для MongoDB версия 3.2 или новее:
Начиная с версии 3.2 можно использовать оператор $max, который возвращает максимальное значение для каждого документа в $project этапе. Оператор $map здесь возвращает массив value3.
Следующий этап в цепочке — это $group, где нужно группировать документы и возвращать максимальное значение.
var result = db.collection.aggregate([
{ "$project": {
"maxVal": {
"$max": {
"$map": {
"input": "$values",
"as": "value",
"in": "$$value.value3"
}
}
}
} },
{ "$group": { "_id": null, "mval": { "$max": "$maxVal" } } }
]);
Поскольку метод .aggregate() возвращает курсор, нужно использовать метод toArray() и оператор [] для получения значения.
var maximumValue = result.toArray()[0]['mval'];
Теперь можно использовать это значение, чтобы оставить в массиве только элемент с максимальным значением. Для этого нужно использовать оператор $filter
db.collection.aggregate( [
{ "$match": { "values.value3": maximumValue } },
{ "$project": {
"values": {
"$filter": {
"input": "$values",
"as": "value",
"cond": { "$eq": [ "$$value.value3", maximumValue ] }
}
}
} }
])
Решение для MongoDB версии 2.6 или новее:
Решение для версий с 2.6 по 3.2 менее эффективно. Сначала в первом запросе нужно денормализовать массив value3 сразу после $project, используя оператор $unwind. Последний этап в цепочке это $group, где группируем документы и возвращаем максимальное значение value3
var result = db.collection.aggregate([
{ "$project": {
"value3": {
"$map": {
"input": "$values",
"as": "value",
"in": "$$value.value3"
}
}
} },
{ "$unwind": "$value3" },
{ "$group": { "_id": null, "mval": { "$max": "$value3" } } }
]);
var maximumValue = result.toArray()[0]['mval'];
Чтобы оставить в массиве только элемент с максимальным значением, у нас есть два варианта:
Используя оператор $redact
db.collection.aggregate( [
{ "$match": { "values.value3": maximumValue } },
{ "$redact": {
"$cond": [
{ "$or": [
{ "$eq": [ "$value3", maximumValue ] },
{ "$not": "$value3" }
]},
"$$DESCEND",
"$$PRUNE"
]
}}
])
Используя оператор $project и $setDifference
db.collection.aggregate( [
{ "$match": { "values.value3": maximumValue } },
{ "$project": {
"values": {
"$setDifference": [
{ "$map": {
"input": "$values",
"as": "value",
"in": {
"$cond": [
{ "$eq": [ "$$value.value3", maximumValue ] },
"$$value",
false
]
}
}},
[false]
]
}
}}
])
Комментариев нет:
Отправить комментарий