sql >> Base de Datos >  >> NoSQL >> MongoDB

mongoDB upsert en matriz

Esto no es realmente tan simple como podría pensar, y en realidad es interesante que haya dividido su análisis de esto en tres partes. Porque, ¿adivinen qué? Eso es exactamente lo que debes hacer. Consideremos los pasos:

1. Inserta un documento si no existe

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

Así que lo que quieres hacer es insertar un nuevo documento donde el "clientId" actualmente no existe. Esto se puede hacer como un "upsert" para evitar posibles conflictos de claves únicas e incluso cuando no hay una restricción "única", entonces la naturaleza "upsert" de esto garantiza que solo cree el documento "nuevo" cuando no se encontró. También hay $setOnInsert aquí porque no quiere hacer algo con un documento que se "encuentra" en este punto.

Tenga en cuenta aquí que no hay no intente hacer coincidir el elemento en la matriz. Esto se debe a que probablemente no desee "crear" un nuevo documento solo porque uno existente no tenía "ese" elemento de matriz. Lo que nos lleva al siguiente paso.

2. Actualizar el contenido del documento donde exista

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

Ahora, aquí quiere intentar "hacer coincidir" el documento con el "ID de cliente" que hace contenga un elemento en la matriz que también coincida con el "deviceId" que está buscando. Entonces, al especificar una condición para que coincida, obtiene el uso del $ posicional operador para establecer los campos en la posición de "coincidencia".

Como arriba, esto iba a coincidir con uno cosa o nada así que la actualización se realizó o no. Eso nos lleva a nuestra parte final de la cascada aquí:

3. Agregue el elemento de matriz donde no existe

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

Así que esto es importante la última etapa La razón es que si alguna de las operaciones anteriores hizo "crear" o "actualizar" el documento existente, luego el uso de $addToSet aquí se asegura seguro no está "empujando" otro documento a la matriz con el mismo "deviceId" pero con otros valores diferentes. Si uno de esas etapas funcionó, entonces esto vería que todos los valores de ese elemento ya existen, y luego no agregaría otro.

Si intentaste hacerlo en un orden diferente, en el caso que presentas tendrías dos documentos en la matriz con el mismo "deviceId", pero diferentes valores para "deviceType" y "notification". Por eso es que viene en último lugar.

Conclusión

Desafortunadamente, no hay una manera simple de combinarlos como uno. operación. Los operadores simplemente no existen, por lo que esto podría hacerse en una sola declaración y, por lo tanto, debe realizar tres actualizar las operaciones para hacer lo que quieras. También como se indicó, el orden de aplicación para esas actualizaciones es importante para que obtengas el resultado deseado.

Si bien esto aún no existe en las versiones actuales de "producción", la próxima versión (2.6 y posteriores al momento de escribir) tiene una forma de "agrupar" estas solicitudes con una nueva sintaxis para actualizar:

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

Entonces, mientras eso está todavía esencialmente tres operaciones, al menos puedes enviarlas por cable solo una vez