Както вече знаете, $slice се използва само в проекция за ограничаване на елементите на масива, върнати в резултатите. Така че ще останете да обработвате списъка програмно с резултати от find().
По-добър подход е да се използва агрегат. Но първо нека разгледаме как $slice се използва:
> db.collection.find({},{ relevancy: {$slice: -1} })
{ "_id" : ObjectId("530824b95f44eac1068b45c0"), "relevancy" : [ "Y" ] }
{ "_id" : ObjectId("530824b95f44eac1068b45c2"), "relevancy" : [ "Y" ] }
{ "_id" : ObjectId("530824b95f44eac1068b45c3"), "relevancy" : [ "N" ] }
{ "_id" : ObjectId("530824b95f44eac1068b45c4"), "relevancy" : [ "Y" ] }
{ "_id" : ObjectId("530824b95f44eac1068b45c6"), "relevancy" : [ "N" ] }
{ "_id" : ObjectId("530824b95f44eac1068b45c7"), "relevancy" : [ "N" ] }
{ "_id" : ObjectId("530824b95f44eac1068b45c8"), "relevancy" : [ "N" ] }
Така че получавате последния елемент от масива, но сте заседнали в цикъла на резултатите, тъй като не можете да съпоставите стойността на последния елемент. Може и просто да сте направили това в код.
Сега нека разгледаме агрегат :
db.collection.aggregate([
// Match things so we get rid of the documents that will never match, but it will
// still keep some of course since they are arrays, that *may* contain "N"
{ "$match": { "relevancy": "Y" } },
// De-normalizes the array
{ "$unwind": "$relevancy" },
// The order of the array is retained, so just look for the $last by _id
{ "$group": { "_id": "$_id", "relevancy": { "$last": "$relevancy" } }},
// Match only the records with the results you want
{ "$match": { "relevancy": "Y" }},
// Oh, and maintain the original _id order [ funny thing about $last ]
{ "$sort": { "_id": 1 } }
])
Дори ако това е първото ви използване на aggregate(), препоръчвам ви да научите . Това е може би най-полезният ви инструмент за решаване на проблеми. Определено беше за мен. Поставете всяка стъпка веднъж в даден момент, ако учите.
Също така не сте сигурни във формуляра на вашия документ, всички 1: { ... }
нотацията на поддокумента изглежда е грешка, но трябва да изчистите това или да коригирате кода по-горе, за да се позовава на "1.relevancy"
вместо. Надявам се, че документите ви наистина изглеждат по-така:
{ "relevancy" : [ "Y" ] , "_id" : ObjectId("530824b95f44eac1068b45c0") }
{ "relevancy" : [ "Y", "Y" ] , "_id" : ObjectId("530824b95f44eac1068b45c2") }
{ "relevancy" : [ "N" ], "_id" : ObjectId("530824b95f44eac1068b45c3") }
{ "relevancy" : [ "Y", "Y" ], "_id" : ObjectId("530824b95f44eac1068b45c4") }
{ "relevancy" : [ "Y", "N" ], "_id" : ObjectId("530824b95f44eac1068b45c6") }
{ "relevancy" : [ "N" ], "_id" : ObjectId("530824b95f44eac1068b45c7") }
{ "relevancy" : [ "Y", "N" ], "_id" : ObjectId("530824b95f44eac1068b45c8") }
MongoDB 3.2.x и по-нова версия
Разбира се, MongoDB 3.2 въвежда оператор за "агрегиране" за $slice
и още по-добър $arrayElemAt
оператор, който премахва необходимостта от $unwind
и $group
обработка. След първоначалния $match
заявка просто правите "логическо съвпадение" с $redact
:
db.collection.aggregate([
{ "$match": { "relevancy": "Y" } },
{ "$redact": {
"$cond": {
"if": { "$eq": [{ "$arrayElemAt": [ "$relevancy", -1 ], "Y" ] },
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
Това ще направи проверката на последния елемент от масива, когато реши дали да $$KEEP
или $$PRUNE
документите от върнатите резултати.
Ако все още искате "проекцията", тогава всъщност можете да добавите $slice
:
db.collection.aggregate([
{ "$match": { "relevancy": "Y" } },
{ "$redact": {
"$cond": {
"if": { "$eq": [{ "$arrayElemAt": [ "$relevancy", -1 ], "Y" ] },
"then": "$$KEEP",
"else": "$$PRUNE"
}
}},
{ "$project": { "relevancy": { "$slice": [ "$relevancy", -1 ] } } }
])
Или алтернативният подход на:
db.collection.aggregate([
{ "$match": { "relevancy": "Y" } },
{ "$project": { "relevancy": { "$slice": [ "$relevancy", -1 ] } } },
{ "$match": { "relevancy": "Y" } }
])
Но вероятно е по-евтино да направите $redact
първо и "след това" направете преоформяне в `$project.