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

Mongodb-Aggregationsframework-Match nach verschachtelten Dokumenten

Das Abfragen dieser Struktur nach den gewünschten Ergebnissen ist nicht möglich, ohne alle möglichen forms zu kennen Namen vor und verwenden sie in der Abfrage. Es wäre auf jeden Fall sehr chaotisch. Das heißt, lesen Sie weiter, während ich erkläre, wie es gemacht werden kann.

Es gibt ein Problem mit der Struktur dieser Dokumente, das Sie daran hindern wird, eine vernünftige Abfrageanalyse durchzuführen. So wie es aussieht, müssten Sie alle möglichen Felder für Formularnamen kennen, um irgendetwas herauszufiltern.

Ihre aktuelle Struktur enthält Formulare, die ein untergeordnetes Dokument enthalten, von denen jeder Schlüssel ein weiteres untergeordnetes Dokument mit einer einzigen Eigenschaft, status, enthält . Dies ist als Ihre forms schwierig zu durchqueren -Element hat für jedes von Ihnen erstellte Dokument eine beliebige Struktur. Das bedeutet, dass das Muster absteigen soll zum status Informationen, die Sie Änderungen für jedes Dokument in Ihrer Sammlung vergleichen möchten.

Hier ist, was ich mit Pfad meine. Um den Status eines beliebigen Elements zu erhalten, müssen Sie Folgendes tun

Mit dem zweiten Element, das sich ständig ändert. Es gibt keine Möglichkeit zu Platzhalter so etwas, da die Benennung als explizit angesehen wird.

Dies wurde möglicherweise als eine einfache Möglichkeit angesehen, die Serialisierung der Daten aus Ihren Formularen zu implementieren aber ich sehe eine flexiblere Alternative. Was Sie brauchen, ist eine Dokumentstruktur, die Sie in einem Standardmuster durchlaufen können. Dies ist beim Design immer eine Überlegung wert. Nehmen Sie Folgendes:

{
    "_id" : "Tvq444454j",
    "name": "Jim",
    "forms": [
        {
             "name": "Jorney",
             "status":"closed"          
        },
        {
            "name": "Women",
            "status":"void"            
        },
        {
            "name": "Child",
            "status":"closed"           
        },
        {
            "name": "Farm",
            "status":"closed"            
        }  
    ]
}

Also wird die Struktur des Dokuments geändert, um die forms zu erstellen Element ein Array, und anstatt das Statusfeld unter einem Schlüssel zu platzieren, der das "Formularfeld" benennt, haben wir jedes Mitglied des Arrays als Unterdokument, das das "Formularfeld" name enthält und der status . Sowohl die Kennung als auch der Status sind also immer noch miteinander gepaart, werden aber jetzt nur noch als untergeordnetes Dokument dargestellt. Dies ändert vor allem den Zugriffspfad auf diese Schlüssel, wie jetzt für beide den Feldnamen und seinen Status, den wir tun können

Was das bedeutet, dass Sie abfragen können, um die Namen aller Felder im form zu finden oder alle status Felder im form , oder sogar alle Dokumente mit einem bestimmten name Feld und bestimmten status . Das ist viel besser als das, was mit der ursprünglichen Struktur getan werden könnte.

In Ihrem speziellen Fall möchten Sie nun nur erhalten die Dokumente waren alle die Felder sind nicht void . Jetzt gibt es in einer einzigen Abfrage keine Möglichkeit, dies zu tun, da es keinen Operator gibt, um alle Elemente in einem Array auf diese Weise zu vergleichen und festzustellen, ob sie gleich sind. Aber es gibt zwei Ansätze, die Sie verfolgen können:

Die erste und wahrscheinlich nicht so effiziente Methode ist die Abfrage nach all Dokumente, die ein Element in forms enthalten die einen status hat von „leer“. Mit den resultierenden Dokument-IDs können Sie eine weitere Abfrage durchführen, die die Dokumente zurückgibt, die nicht haben die angegebenen IDs.

db.forms.find({ "forms.status": "void" },{ _id: 1})

db.forms.find({ _id: $not: { $in: [<Object1>,<Object2>,<Object3>,... ] } })

Angesichts der Ergebnisgröße ist dies möglicherweise nicht möglich und im Allgemeinen keine gute Idee, da der Ausschlussoperator $not verwendet wird erzwingt im Grunde einen vollständigen Scan der Sammlung, sodass Sie keinen Index verwenden konnten.

Ein anderer Ansatz besteht darin, die Aggregationspipeline wie folgt zu verwenden:

db.forms.aggregate([
    { "$unwind": "$forms" },
    { "$group": { "_id": "$_id", "status": { "$addToSet": "$forms.status" }}},
    { "$unwind": "$status" },
    { "$sort": { "_id": 1, "status": -1 }},
    { "$group": { "_id": "$_id", "status": { "$first": "$status"}}},
    { "$match":{ "status": "closed" }}
])

Das gibt natürlich nur die _id für die übereinstimmenden Dokumente zurück, aber Sie können eine Abfrage mit $in durchführen und die gesamten übereinstimmenden Dokumente zurückgeben. Dies ist besser als der zuvor verwendete Ausschlussoperator, und jetzt können wir einen Index verwenden, um vollständige Sammlungsscans zu vermeiden.

Als letzten Ansatz und für das Beste Aus Leistungsgründen könnten Sie das Dokument erneut so ändern, dass Sie auf der obersten Ebene den "Status" behalten, ob ein Feld in den Formularen "ungültig" oder "geschlossen" ist. Auf der obersten Ebene würde der Wert also nur geschlossen, wenn alle Elemente "geschlossen" wären, und "ungültig", wenn etwas ungültig wäre, und so weiter.

Diese letzte würde eine weitere programmatische Änderung und alle Änderungen an den forms bedeuten Elemente müssten dieses Feld ebenfalls aktualisieren, um den "Status" beizubehalten. Es ist jedoch der effizienteste Weg, um die benötigten Dokumente zu finden, und kann eine Überlegung wert sein.

BEARBEITEN :

Abgesehen von der Änderung des Dokuments in einen Master-Status ist das schnellste Abfrageformular für die überarbeitete Struktur tatsächlich:

db.forms.find({ "forms": { "$not": { "$elemMatch": { "status": "void" } } } })