#mongodb #mongodb-query #aggregation-framework
#mongodb #mongodb-запрос #платформа агрегации
Вопрос:
Я создаю панель мониторинга, которая вращается между разными веб-страницами. Я хочу извлечь все слайды, которые являются частью «тестовой» колоды, и упорядочить их соответствующим образом. После запроса мой результат в идеале выглядел бы так.
[
{ "url" : "http://10.0.1.187", "position": 1, "duartion": 10 },
{ "url" : "http://10.0.1.189", "position": 2, "duartion": 3 }
]
В настоящее время у меня есть набор данных, который выглядит следующим образом
{
"_id" : ObjectId("53a612043c24d08167b26f82"),
"url" : "http://10.0.1.189",
"decks" : [
{
"title" : "Test",
"position" : 2,
"duration" : 3
}
]
}
{
"_id" : ObjectId("53a6103e3c24d08167b26f81"),
"decks" : [
{
"title" : "Test",
"position" : 1,
"duration" : 2
},
{
"title" : "Other Deck",
"position" : 1,
"duration" : 10
}
],
"url" : "http://10.0.1.187"
}
Моя попытка запроса выглядит так:
db.slides.aggregate([
{
"$match": {
"decks.title": "Test"
}
},
{
"$sort": {
"decks.position": 1
}
},
{
"$project": {
"_id": 0,
"position": "$decks.position",
"duration": "$decks.duration",
"url": 1
}
}
]);
Но это не дает желаемых результатов. Как я могу запросить свой набор данных и получить ожидаемые результаты оптимальным способом?
Ответ №1:
Ну, чтобы по-настоящему «сгладить» документ, как следует из вашего названия, тогда $unwind
всегда будет использоваться, поскольку другого способа сделать это действительно нет. Однако есть несколько разных подходов, если вы можете жить с массивом, отфильтрованным до соответствующего элемента.
В принципе, если у вас действительно есть только одна вещь для сопоставления в массиве, то ваш самый быстрый подход — просто использовать .find()
сопоставление требуемого элемента и проецирование:
db.slides.find(
{ "decks.title": "Test" },
{ "decks.$": 1 }
).sort({ "decks.position": 1 }).pretty()
Это все еще массив, но пока у вас есть только один элемент, который соответствует, это работает. Кроме того, элементы сортируются, как и ожидалось, хотя, конечно, поле «заголовок» не удаляется из сопоставленных документов, поскольку это выходит за рамки возможностей простого проектирования.
{
"_id" : ObjectId("53a6103e3c24d08167b26f81"),
"decks" : [
{
"title" : "Test",
"position" : 1,
"duration" : 2
}
]
}
{
"_id" : ObjectId("53a612043c24d08167b26f82"),
"decks" : [
{
"title" : "Test",
"position" : 2,
"duration" : 3
}
]
}
Другой подход, если у вас есть MongoDB 2.6 или выше, использует $map
operator и некоторые другие, чтобы как «фильтровать», так и изменять форму массива «на месте» без фактического применения $unwind
:
db.slides.aggregate([
{ "$project": {
"url": 1,
"decks": {
"$setDifference": [
{
"$map": {
"input": "$decks",
"as": "el",
"in": {
"$cond": [
{ "$eq": [ "$$el.title", "Test" ] },
{
"position": "$$el.position",
"duration": "$$el.duration"
},
false
]
}
}
},
[false]
]
}
}},
{ "$sort": { "decks.position": 1 }}
])
Преимущество заключается в том, что вы можете вносить изменения без «разматывания», что может сократить время обработки больших массивов, поскольку вы по существу не создаете новые документы для каждого элемента массива, а затем запускаете отдельный $match
этап для «фильтрации» или другой $project
для изменения формы.
{
"_id" : ObjectId("53a6103e3c24d08167b26f81"),
"decks" : [
{
"position" : 1,
"duration" : 2
}
],
"url" : "http://10.0.1.187"
}
{
"_id" : ObjectId("53a612043c24d08167b26f82"),
"url" : "http://10.0.1.189",
"decks" : [
{
"position" : 2,
"duration" : 3
}
]
}
Вы можете снова либо работать с «отфильтрованным» массивом, либо, если хотите, вы можете снова «сгладить» это по-настоящему, добавив дополнительный $unwind
, в котором вам не нужно фильтровать, $match
поскольку результат уже содержит только совпадающие элементы.
Но, вообще говоря, если вы можете с этим смириться, просто используйте .find()
, поскольку это будет самый быстрый способ. В противном случае то, что вы делаете, подходит для небольших данных, или есть другой вариант для рассмотрения.
Ответ №2:
Ну, как только я опубликовал, я понял, что должен использовать an $unwind
. Является ли этот запрос оптимальным способом сделать это, или это можно сделать по-другому?
db.slides.aggregate([
{
"$unwind": "$decks"
},
{
"$match": {
"decks.title": "Test"
}
},
{
"$sort": {
"decks.position": 1
}
},
{
"$project": {
"_id": 0,
"position": "$decks.position",
"duration": "$decks.duration",
"url": 1
}
}
]);