MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

mongoDB upsert sull'array

Non è così semplice come potresti pensare, ed in realtà è interessante che tu abbia suddiviso la tua analisi in tre parti. Perché, indovina un po'? Questo è esattamente ciò che devi fare. Consideriamo i passaggi:

1. Inserisci un documento se non esiste

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

Quindi quello che vuoi fare è inserire un nuovo documento in cui il "clientId" attualmente non esiste. Questo può essere fatto come "upsert" per evitare possibili conflitti di chiavi univoche e anche dove non esiste un vincolo "univoco", quindi la natura "upsert" di questo ti assicura di creare il "nuovo" documento solo quando non è stato trovato. Inoltre c'è $setOnInsert qui perché non vuoi fare qualsiasi cosa su un documento che viene "trovato" a questo punto.

Nota qui che non c'è nessun tentare di abbinare l'elemento nell'array. Questo perché probabilmente non vuoi "creare" un nuovo documento solo perché uno esistente non aveva "quel" elemento dell'array. Il che ci porta al passaggio successivo.

2. Aggiorna il contenuto del documento dove esiste

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

Ora qui vuoi effettivamente provare a "abbinare" il documento per il "clientId" che fa contengono un elemento nell'array che corrisponde anche al "deviceId" che stai cercando. Quindi, specificando una condizione da abbinare, ottieni l'uso del $ posizionale operatore per impostare i campi nella posizione di "corrispondenza".

Come sopra, questo corrispondeva a uno cosa o niente quindi o l'aggiornamento è stato fatto o non lo è stato. Quindi passiamo alla nostra parte finale della cascata qui:

3. Aggiungi l'elemento dell'array dove non esiste

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

Quindi questo è importante l'ultima fase. Il motivo è che se una delle operazioni precedenti fatto "crea" o "aggiorna" il documento esistente, quindi l'uso di $addToSet qui è sicuro non stai "spingendo" un altro documento nell'array con lo stesso "deviceId" ma altri valori diversi. Se uno di quelle fasi funzionate, questo vedrebbe già esistere tutti i valori di quell'elemento e non ne aggiungerebbe un altro.

Se provassi a farlo in un ordine diverso, nel caso in cui presenti ne avresti due documenti nell'array con lo stesso "deviceId", ma valori diversi per "deviceType" e "notification". Ecco perché arriva per ultimo.

Conclusione

Quindi, sfortunatamente, non esiste un modo semplice per combinarli come uno operazione. Gli operatori semplicemente non esistono in modo che ciò possa essere fatto in un'unica istruzione e quindi devi esegui tre aggiornare le operazioni per fare quello che vuoi. Inoltre, come detto, l'ordine dell'applicazione di tali aggiornamenti è importante in modo da ottenere il risultato desiderato.

Anche se questo non esiste ancora nelle attuali versioni di "produzione", la prossima versione (2.6 e successive al momento della scrittura) ha un modo per "raggruppare" queste richieste con una nuova sintassi da aggiornare:

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

Quindi, mentre è ancora essenzialmente tre operazioni, almeno puoi inviarle via cavo solo una volta