MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Verwenden des $slice-Operators, um das letzte Element des Arrays abzurufen

Wie Sie inzwischen wissen, wird $slice nur in der Projektion verwendet, um die in den Ergebnissen zurückgegebenen Array-Elemente zu begrenzen. Sie würden also mit der programmgesteuerten Verarbeitung der Liste mit Ergebnissen von find() feststecken.

Ein besserer Ansatz ist die Verwendung von Aggregaten. Aber zuerst betrachten wir, wie $slice wird verwendet:

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

Sie erhalten also das letzte Array-Element, aber Sie müssen die Ergebnisse in einer Schleife durchlaufen, da Sie nicht abgleichen können der letzte Elementwert. Sie hätten dies auch einfach im Code tun können.

Sehen wir uns nun aggregate an :

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

Auch wenn dies Ihre erste Verwendung von Aggregat() wäre, empfehle ich Ihnen, es zu lernen . Es ist vielleicht Ihr nützlichstes Werkzeug zur Problemlösung. Sicherlich war es für mich. Geben Sie jeden Schritt einmal ein zu einem Zeitpunkt, wenn Sie lernen.

Auch nicht sicher auf Ihrem Dokumentenformular, alle 1: { ... } Die Notation von Unterdokumenten scheint ein Fehler zu sein, aber Sie sollten das korrigieren oder den obigen Code anpassen, um auf "1.relevancy" zu verweisen stattdessen. Ich hoffe, Ihre Dokumente sehen in Wirklichkeit eher so aus:

{ "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 und höher

Natürlich führt MongoDB 3.2 einen "Aggregations"-Operator für $slice ein und ein noch besseres $arrayElemAt -Operator, der $unwind überflüssig macht und $group wird bearbeitet. Nach dem anfänglichen $match Abfrage machen Sie einfach eine "logische Übereinstimmung" mit $redact :

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

Dadurch wird das letzte Element des Arrays überprüft, wenn entschieden wird, ob $$KEEP ausgeführt werden soll oder $$PRUNE die Dokumente aus den zurückgegebenen Ergebnissen.

Wenn Sie immer noch die "Projektion" wollen, können Sie die $slice tatsächlich hinzufügen :

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

Oder der alternative Ansatz von:

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

Aber $redact ist wahrscheinlich weniger kostspielig first und "then" führen alle Umformungen in `$project.

durch