Ich versuche, eine Abfrage über die Sample-Bios-Sammlung Ссылка zu formulieren:
Erhalte alle Personen und ihre Auszeichnungen, die sie erhalten haben, bevor sie einen Turing-Preis erhalten haben.
Ich habe diese Frage gestellt:
%Vor%Und das ist die Antwort:
%Vor% Was mir an dieser Lösung nicht gefällt ist, dass ich $award2
abwickle. Stattdessen würde ich gerne award2 als ein Array behalten und nur die Preise entfernen, die nach dem Award1 erhalten wurden. So sollte zum Beispiel die Antwort für John Backus lauten:
Kann man es mit $redact
erreichen, ohne $unwind: "$award2"
zu machen?
Es wäre vielleicht ein wenig hilfreicher gewesen, wenn Sie den Originalzustand des Dokuments als Beispiel in Ihre Frage aufgenommen hätten, da dies deutlich zeigt, "woher Sie kommen" und dann "wohin Sie kommen möchten" ein Ziel neben Ihrer gewünschten Ausgabe als gegeben.
Das ist nur ein Tipp, aber es scheint, dass Sie mit einem Dokument wie diesem beginnen:
%Vor% Die wirklichen Punkte hier sind also, dass ihr vielleicht $redact
$project
für eine logische Bedingung zu verwenden und dann $match
zu verwenden, um diese logische Übereinstimmung zu filtern), ist dies wahrscheinlich nicht das beste Werkzeug für den gewünschten Vergleich mach es hier.
Bevor ich fortfahre, möchte ich hier nur auf das Hauptproblem mit $redact
hinweisen. Was auch immer Sie hier tun, die Logik (ohne eine Abwicklung) wäre im Wesentlichen "direkt" auf $$DESCEND
zu vergleichen, um die Array-Elemente auf dem Wert von "Jahr" auf jeder Ebene zu verarbeiten.
Diese Rekursion wird auch die Bedingung "award1" ungültig machen, da sie denselben Feldnamen hat. Selbst das Umbenennen dieses Feldes bringt die Logik zum Erliegen, da ein projizierter Wert, an dem sie fehlte, nicht größer als der getestete Wert wäre.
Kurz gesagt, $redact
wird direkt ausgeschlossen, da Sie nicht sagen können, "nimm nur von hier" mit der Logik, die es anwendet.
Die Alternative ist $map
und $setDifference
um Inhalte aus den Arrays wie folgt zu filtern:
Und es gibt wirklich keinen "hübschen" Weg, um die Verwendung von % zu umgehen. co_de% in der itermediatary stage oder auch die zweite $unwind
hier, da $project
(und der $map
filter) zurückgibt, was "immer noch ein Array" ist. Also ist das $setDifference
notwendig, um das "Array" zu einem Singular zu machen (sofern Ihre Bedingung nur 1 Element entspricht), das im Vergleich verwendet werden soll.
Der Versuch, die gesamte Logik in einem einzigen $unwind
zu "quetschen", führt nur zu "arrays of arrays" in der zweiten Ausgabe, und trotzdem ist ein gewisses "unwinding" erforderlich, aber zumindest auf diese Weise (hoffentlich) 1 Übereinstimmung ist nicht wirklich so teuer und hält die Ausgabe sauber.
Aber die andere Sache, die Sie hier wirklich beachten sollten, ist, dass Sie hier überhaupt nichts "aggregieren". Dies ist nur eine Dokumentmanipulation, also sollten Sie in Betracht ziehen, diese Manipulation direkt im Client-Code durchzuführen. Wie in diesem Shell-Beispiel gezeigt:
%Vor%Jedenfalls werden beide Ansätze dasselbe ausgeben:
%Vor% Der einzige wirkliche Unterschied ist hier, dass mit $project
der Inhalt von "award2" bereits gefiltert wird, wenn er vom Server zurückgegeben wird, was wahrscheinlich nicht viel anders ist als bei der Client-Verarbeitung, es sei denn, Elemente, die entfernt werden würden, umfassen eine ziemlich große Liste pro Dokument.
Zur Erinnerung: Die einzige Änderung an Ihrer bestehenden Aggregationspipeline, die hier wirklich erforderlich ist, wäre die .aggregate()
bis zum Ende, um die Array-Einträge in ein einzelnes Dokument zu kombinieren:
Aber andererseits gibt es all diese "Array-Duplizierung" und die "Kosten der Abwicklung", die mit allen Operationen hier verbunden sind. Daher ist einer der beiden ersten Ansätze das, was Sie wirklich wollen, um das zu vermeiden.
Sie können eine einzelne Projektstufe mit verschachtelten Ausdrücken verwenden, um dies zu erreichen, indem Sie mehrere Stufen vermeiden:
%Vor%Bitte lesen Sie die Dokumentation hier für eine Reihe nützlicher Array-Operatoren.
Die Aggregation sieht jedoch für diese Abfrage nicht gut aus und die Logik ist einfach genug, um im Code selbst ohne große Leistungseinbußen implementiert zu werden. stimmt nur zu Blakes Seven's Antwort zu. Aber eines der schönen Dinge an MongoDB ist, dass wir das Schema so entwerfen können, dass es unsere Zugriffsmuster unterstützt und unseren Code sauber hält. Wenn eine solche Funktionalität in einem realen Szenario benötigt wird, können wir einfach ein Feld namens "turing_award_year" in das Dokument einfügen. Dies hat Auswirkungen auf die CRUD-Operationen in der Sammlung, aber der Code wird sauber sein, und jetzt können wir eine Abfrage wie diese verwenden, die hübsch und einfacher zu pflegen ist:
%Vor%Tags und Links mongodb aggregation-framework mongodb-query