MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Usando o operador $ slice para obter o último elemento da matriz


Como você já deve saber, $slice é usado apenas na projeção para limitar os elementos do array retornados nos resultados. Portanto, você ficaria preso ao processamento da lista programaticamente com os resultados de um find().

Uma abordagem melhor é usar agregado. Mas primeiro vamos considerar como $slice é usado:
> 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" ] }

Então você obtém o último elemento da matriz, mas está preso ao loop dos resultados, pois não pode combinar o valor do último elemento. Você também pode ter feito isso no código.

Agora vamos ver agregar :
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 } }
])

Mesmo que este seja seu primeiro uso de agregador(), eu o encorajo a aprender . É talvez a sua ferramenta de resolução de problemas mais útil. Certamente foi para mim. Coloque cada etapa uma vez de cada vez se você estiver aprendendo.

Também não tenho certeza em seu formulário de documento, todos os 1: { ... } a notação de subdocumento parece ser um erro, mas você deve esclarecer isso ou ajustar o código acima para referenciar "1.relevancy" em vez de. Espero que seus documentos realmente se pareçam mais com isso:
{ "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.xe superior


É claro que o MongoDB 3.2 introduz um operador de "agregação" para $slice e um $arrayElemAt ainda melhor operador que elimina a necessidade de qualquer $unwind e $group em processamento. Após o $match inicial consulta você apenas faz uma "correspondência lógica" com $redact :
db.collection.aggregate([
    { "$match": { "relevancy": "Y" } },
    { "$redact": {
        "$cond": {
            "if": { "$eq": [{ "$arrayElemAt": [ "$relevancy", -1 ], "Y" ] },
            "then": "$$KEEP",
            "else": "$$PRUNE"
        }
    }}   
])

Isso fará a inspeção no último elemento da matriz ao decidir se $$KEEP ou $$PRUNE os documentos dos resultados retornados.

Se você ainda deseja a "projeção", pode adicionar o $slice :
db.collection.aggregate([
    { "$match": { "relevancy": "Y" } },
    { "$redact": {
        "$cond": {
            "if": { "$eq": [{ "$arrayElemAt": [ "$relevancy", -1 ], "Y" ] },
            "then": "$$KEEP",
            "else": "$$PRUNE"
        }
    }},
    { "$project": { "relevancy": { "$slice": [ "$relevancy", -1 ] } } }
])

Ou a abordagem alternativa de:
db.collection.aggregate([
    { "$match": { "relevancy": "Y" } },
    { "$project": { "relevancy": { "$slice": [ "$relevancy", -1 ] } } },
    { "$match": { "relevancy": "Y" } }
])

Mas provavelmente é mais barato fazer o $redact first e "then" fazem qualquer remodelação em `$project.