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

mongoDB-Upsert auf Array

Das ist nicht wirklich so einfach, wie Sie vielleicht denken, und eigentlich ist es interessant, dass Sie Ihre Analyse dazu in drei Teile geteilt haben. Denn, weißt du was? Das ist genau das, was Sie müssen tun. Betrachten wir die Schritte:

1. Fügen Sie ein Dokument ein, falls noch keines vorhanden ist

db.collection.update(
    { 
        "clientId":"123456"
    },
    {
        "$setOnInsert": {
            "clientId": "123456",
            "devices": [{
                "deviceId": "321",
                "deviceType" : "kindle",
                "notification" : false
            }]
        }
    },
    { "upsert": true }
)

Was Sie also tun möchten, ist Einfügen ein neues Dokument, in dem die "clientId" derzeit nicht existiert. Dies kann als "Upsert" erfolgen, um mögliche Konflikte mit eindeutigen Schlüsseln zu vermeiden, und selbst wenn es keine "eindeutige" Einschränkung gibt, stellt die "Upsert"-Natur sicher, dass Sie nur das "neue" Dokument erstellen, wenn es nicht gefunden wurde. Außerdem gibt es $setOnInsert hier, weil Sie nicht etwas mit einem Dokument machen wollen, das an dieser Stelle "gefunden" wird.

Beachten Sie hier, dass es nein gibt versuchen, das Element im Array abzugleichen. Dies liegt daran, dass Sie wahrscheinlich kein neues Dokument "erstellen" möchten, nur weil ein vorhandenes "dieses" Array-Element nicht hatte. Das bringt uns zum nächsten Schritt.

2. Aktualisieren Sie den Inhalt des Dokuments, sofern vorhanden

db.collection.update(
    { 
        "clientId":"123456",
        "devices": { "$elemMatch": { "deviceId" : "321" } }
    },
    {
        "$set": {
            "devices.$.deviceType" : "kindle",
            "devices.$.notification" : false
        }
    }
)

Jetzt möchten Sie hier tatsächlich versuchen, das Dokument für die "clientId" zu "abgleichen", was macht ein Element im Array enthalten, das auch mit der gesuchten "deviceId" übereinstimmt. Indem Sie also eine zu erfüllende Bedingung angeben, erhalten Sie die Verwendung des positionellen $ -Operator, um die Felder auf die Position "matching" zu setzen.

Wie oben würde dies entweder zu Eins passen Ding oder nichts Also entweder das Update wurde gemacht oder nicht. Damit kommen wir zu unserem letzten Teil der Kaskade hier:

3. Fügen Sie das Array-Element hinzu, wo es nicht vorhanden ist

db.collection.update(
    { 
        "clientId":"123456"
    },
    {
        "$addToset": { "devices": {
            "deviceId" : "321",
            "deviceType" : "kindle",
            "notification" : false
        }}
    }
)

Das ist also wichtig die letzte Stufe. Der Grund dafür ist, dass, wenn eine der vorherigen Operationen tat "erstellen" oder "aktualisieren" Sie das vorhandene Dokument, dann die Verwendung von $addToSet hier ist sicher Sie "schieben" kein anderes Dokument in das Array mit derselben "deviceId", aber anderen unterschiedlichen Werten. Wenn eine Wenn eine dieser Phasen funktioniert hat, würde dies alle Werte dieses Elements als bereits vorhanden sehen und dann keinen weiteren hinzufügen.

Wenn Sie versuchten, dies in einer anderen Reihenfolge zu tun, hätten Sie in Ihrem vorliegenden Fall zwei Dokumente im Array mit derselben "deviceId", aber unterschiedlichen Werten für "deviceType" und "notification". Deshalb steht es an letzter Stelle.

Schlussfolgerung

Leider gibt es keine einfache Möglichkeit, diese zu einer zu kombinieren Betrieb. Die Operatoren sind einfach nicht vorhanden, sodass dies in einer einzigen Anweisung erfolgen könnte, und Sie müssen es daher müssen Führen Sie drei durch Update-Operationen, um zu tun, was Sie wollen. Auch wie gesagt, die Bestellung der Anwendung dieser Updates ist wichtig damit Sie das gewünschte Ergebnis erhalten.

Während dies in aktuellen „Produktions“-Releases noch nicht vorhanden ist, bietet das kommende Release (2.6 und höher zum Zeitpunkt des Schreibens) eine Möglichkeit, diese Anforderungen mit einer neuen zu aktualisierenden Syntax zu „stapeln“:

db.runCommand(
    "update": "collection",
    "updates": [
        { 
            "q": { "clientId":"123456" },
            "u": {
                "$setOnInsert": {
                    "clientId": "123456",
                    "devices": [{
                    "deviceId": "321",
                    "deviceType" : "kindle",
                    "notification" : false
                }]
            },
            "upsert": true
        },
        {
            "q": { 
                 "clientId":"123456",
                 "devices": { "$elemMatch": { "deviceId" : "321" } }
            },
            "u": {
                "$set": {
                    "devices.$.deviceType" : "kindle",
                    "devices.$.notification" : false
                 }
            }
        },
        {
            "q": { "clientId":"123456" },
            "u": {
                "$addToset": { "devices": {
                    "deviceId" : "321",
                    "deviceType" : "kindle",
                    "notification" : false
                }}
            }
        }
    ]
)

Solange das noch ist Im Wesentlichen drei Operationen, zumindest können Sie sie über die Leitung senden nur einmal