MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Utilizzo dell'operatore $slice per ottenere l'ultimo elemento dell'array

Come ormai saprai, $slice viene utilizzato solo nella proiezione per limitare gli elementi dell'array restituiti nei risultati. Quindi saresti bloccato con l'elaborazione dell'elenco a livello di codice con i risultati di un find().

Un approccio migliore consiste nell'utilizzare l'aggregato. Ma prima consideriamo come $slice viene utilizzato:

> 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" ] }

Quindi ottieni l'ultimo elemento dell'array, ma sei bloccato con il ciclo dei risultati poiché non puoi corrispondere il valore dell'ultimo elemento. Potresti anche averlo appena fatto nel codice.

Ora diamo un'occhiata a aggregare :

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 } }
])

Anche se questo sarebbe il tuo primo utilizzo di aggregate(), ti incoraggio a impararlo . È forse il tuo strumento di risoluzione dei problemi più utile. Sicuramente lo è stato per me. Inserisci ogni passaggio una volta alla volta se stai imparando.

Inoltre non sono sicuro sul modulo del documento, tutto il 1: { ... } la notazione del sottodocumento sembra essere un errore, ma dovresti chiarirlo o modificare il codice sopra per fare riferimento a "1.relevancy" invece. Spero che i tuoi documenti in realtà assomiglino di più a questo:

{ "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 versioni successive

Ovviamente MongoDB 3.2 introduce un operatore di "aggregazione" per $slice e un ancora migliore $arrayElemAt operatore che elimina la necessità di qualsiasi $unwind e $group in lavorazione. Dopo il $match iniziale query fai semplicemente una "corrispondenza logica" con $redact :

db.collection.aggregate([
    { "$match": { "relevancy": "Y" } },
    { "$redact": {
        "$cond": {
            "if": { "$eq": [{ "$arrayElemAt": [ "$relevancy", -1 ], "Y" ] },
            "then": "$$KEEP",
            "else": "$$PRUNE"
        }
    }}   
])

Questo farà l'ispezione sull'ultimo elemento dell'array quando deciderà se $$KEEP o $$PRUNE i documenti dai risultati restituiti.

Se volevi ancora la "proiezione", puoi effettivamente aggiungere il $slice :

db.collection.aggregate([
    { "$match": { "relevancy": "Y" } },
    { "$redact": {
        "$cond": {
            "if": { "$eq": [{ "$arrayElemAt": [ "$relevancy", -1 ], "Y" ] },
            "then": "$$KEEP",
            "else": "$$PRUNE"
        }
    }},
    { "$project": { "relevancy": { "$slice": [ "$relevancy", -1 ] } } }
])

O l'approccio alternativo di:

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

Ma è probabilmente meno costoso eseguire $redact first e "then" eseguono qualsiasi rimodellamento in `$project.