updateone updatemany update multi item example and actualizar mongodb mongodb-query upsert

mongodb - updatemany - mongoose upsert



MongoDB: subeporte de sub-documento (3)

No hay una forma de hacer esto si la actualización se basa en una referencia al registro que se está actualizando (por ejemplo, actualizar x => x + 1). Emitir 2 comandos separados (configurar y luego insertar) da como resultado una condición de carrera que no se puede resolver al buscar duplicados, porque si su inserción se rechaza debido a un duplicado, perdería el efecto de su actualización (por ejemplo, x no lo haría aumentarse adecuadamente en el ejemplo anterior). Sería bueno si MongoDB agregara esta funcionalidad, ya que la capacidad de implementar documentos incrustados es un gran atractivo para MongoDB para ciertas aplicaciones.

Tengo documentos que se parecen a eso, con un índice único en bars.name :

{ name: ''foo'', bars: [ { name: ''qux'', somefield: 1 } ] }

. Quiero actualizar el { name: ''foo'', ''bars.name'': ''qux'' } donde { name: ''foo'', ''bars.name'': ''qux'' } y $set: { ''bars.$.somefield'': 2 } , o crear un nuevo sub-documento con { name: ''qux'', somefield: 2 } debajo de { name: ''foo'' } .

¿Es posible hacer esto usando una sola consulta con upsert, o tendré que emitir dos por separado?

Relacionado: ''upsert'' en un documento incrustado (sugiere cambiar el esquema para tener el identificador de subdocumento como la clave, pero esto es de hace dos años y me pregunto si ahora hay mejores soluciones).


No, realmente no hay una mejor solución para esto, así que quizás con una explicación.

Supongamos que tiene un documento en su lugar que tiene la estructura que muestra:

{ "name": "foo", "bars": [{ "name": "qux", "somefield": 1 }] }

Si haces una actualización como esta

db.foo.update( { "name": "foo", "bars.name": "qux" }, { "$set": { "bars.$.somefield": 2 } }, { "upsert": true } )

Entonces todo está bien porque se encontró el documento coincidente. Pero si cambia el valor de "bars.name":

db.foo.update( { "name": "foo", "bars.name": "xyz" }, { "$set": { "bars.$.somefield": 2 } }, { "upsert": true } )

Entonces obtendrás un fracaso. Lo único que realmente ha cambiado aquí es que en MongoDB 2.6 y superior el error es un poco más sucinto:

WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0, "writeError" : { "code" : 16836, "errmsg" : "The positional operator did not find the match needed from the query. Unexpanded update: bars.$.somefield" } })

Eso es mejor en algunos aspectos, pero de todos modos no quieres "mejorar". Lo que quiere hacer es agregar el elemento a la matriz donde el "nombre" no existe actualmente.

Entonces, lo que realmente quiere es el "resultado" del intento de actualización sin el indicador de "restablecer" para ver si se vieron afectados algunos documentos:

db.foo.update( { "name": "foo", "bars.name": "xyz" }, { "$set": { "bars.$.somefield": 2 } } )

Rendición en respuesta:

WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })

Entonces, cuando los documentos modificados son 0 , sabrá que desea emitir la siguiente actualización:

db.foo.update( { "name": "foo" }, { "$push": { "bars": { "name": "xyz", "somefield": 2 }} )

Realmente no hay otra manera de hacer exactamente lo que quieres. Como las adiciones a la matriz no son estrictamente un tipo de operación "configurada", no puede usar $addToSet combinado con la funcionalidad de "actualización masiva" allí, para que pueda "poner en cascada" sus solicitudes de actualización.

En este caso, parece que necesita verificar el resultado o, de lo contrario, aceptar leer todo el documento y verificar si desea actualizar o insertar un nuevo elemento de matriz en el código.


si no te importa cambiar un poco el esquema y tener una estructura como esta:

{ "name": "foo", "bars": { "qux": { "somefield": 1 }, "xyz": { "somefield": 2 }, } }

Puede realizar sus operaciones de una vez. Reiterando ''upsert'' en un documento incrustado para completar