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

MongoDB-Listen – erhalten Sie jedes N-te Element

Es scheint, dass Ihre Frage eindeutig "jede n-te Instanz abrufen" lautet, was eine ziemlich klare Frage zu sein scheint.

Abfrageoperationen wie .find() kann das Dokument wirklich nur "wie es ist" zurückgeben, mit Ausnahme des allgemeinen Feldes "Auswahl" in der Projektion und Operatoren wie dem Position $ Übereinstimmungsoperator oder $elemMatch die ein singuläres Matched-Array-Element zulassen.

Natürlich gibt es $slice , aber das erlaubt nur eine "Bereichsauswahl" auf dem Array, trifft also wieder nicht zu.

Die "einzigen" Dinge, die ein Ergebnis auf dem Server ändern können, sind .aggregate() und .mapReduce() . Ersteres "spielt" in keiner Weise "sehr gut" mit dem "Slicing" von Arrays, zumindest nicht mit "n" Elementen. Da die "function()"-Argumente von mapReduce jedoch JavaScript-basierte Logik sind, haben Sie etwas mehr Spielraum zum Spielen.

Für analytische Prozesse und "nur" zu analytischen Zwecken filtern Sie dann einfach die Array-Inhalte über mapReduce mit .filter() :

db.collection.mapReduce(
    function() {
        var id = this._id;
        delete this._id;

        // filter the content of "instances" to every 3rd item only
        this.instances = this.instances.filter(function(el,idx) {
            return ((idx+1) % 3) == 0;
        });
        emit(id,this);
    },
    function() {},
    { "out": { "inline": 1 } } // or output to collection as required
)

Es ist an dieser Stelle wirklich nur ein "JavaScript-Runner", aber wenn dies nur zur Analyse / zum Testen dient, ist an dem Konzept im Allgemeinen nichts auszusetzen. Natürlich ist die Ausgabe nicht "exakt", wie Ihr Dokument strukturiert ist, aber es ist so nah an einem Faksimile, wie es mapReduce nur erreichen kann.

Der andere Vorschlag, den ich hier sehe, erfordert das Erstellen einer neuen Sammlung mit allen "denormalisierten" Elementen und das Einfügen des "Index" aus dem Array als Teil der eindeutigen _id Schlüssel. Das kann etwas ergeben, das Sie direkt abfragen können, aber für "jedes n-te Element" müssten Sie immer noch Folgendes tun:

db.resultCollection.find({
     "_id.index": { "$in": [2,5,8,11,14] } // and so on ....
})

Berechnen Sie also den Indexwert von "jedem n-ten Element" und geben Sie es an, um "jedes n-te Element" zu erhalten. Das scheint das gestellte Problem also nicht wirklich zu lösen.

Wenn das Ausgabeformular für Ihre "Test"-Zwecke wünschenswerter erscheint, wäre eine bessere nachfolgende Abfrage dieser Ergebnisse die Verwendung der Aggregationspipeline mit $redact

db.newCollection([
    { "$redact": {
        "$cond": {
            "if": {
                "$eq": [ 
                    { "$mod": [ { "$add": [ "$_id.index", 1] }, 3 ] },
                0 ]
            },
            "then": "$$KEEP",
            "else": "$$PRUNE"
        }
    }}
])

Das verwendet zumindest eine "logische Bedingung", die derjenigen ähnelt, die mit .filter() angewendet wurde bevor Sie nur die "n-ten Index"-Elemente auswählen, ohne alle möglichen Indexwerte als Abfrageargument aufzulisten.