Lo que debo hacer es actualizar los contadores dentro del único objeto anidado o insertar el documento completo que contiene la matriz con contadores iniciados.
Ejemplo de documento:
{ "_id":{"$oid":"61f020f40e2f03f0b93b69f1"}, "v":"xxxxxxxxxxxxxxxx", "list":[{"p":{"$oid":"61f020f40e2f03f0b93b69f0"},"c":1,"hom":0,"het":1}] }
Todos los documentos tendrán con seguridad una matriz de "lista" con al menos y como máximo un objeto anidado. Y el par (v,list.p)
es único.
Entonces, a partir de una búsqueda anterior, para cada elemento del cursor, estoy llamando a la función upsert en js, que en realidad encuentra el documento, luego inserta o actualiza la colección si se encuentra.
function upsert(doc){ _hom = doc.field1.field2 == "hom" ? 1 : 0; _het = doc.field1.field2 == "het" ? 1 : 0; filter = {"v": doc.anId, "list.p" : "61f020f40e2f03f0b93b69f0"}; project = {"list.c":1, "list.hom":1, "list.het":1, "_id":0}; res = db.collection.find(filter, project); if(!res.hasNext()){ doc = {"v": doc.anId, "list" : [{"p" : "61f020f40e2f03f0b93b69f0", "c" : 1, "hom" : _hom, "het" : _het}]} db.collection.insertOne(doc) } else { list = res.next().list[0]; _c = list.c + 1; _hom += list.hom; _het += list.het; update = {$set : {"list" : [{"p" : "61f020f40e2f03f0b93b69f0", "c" : _c, "hom" : _hom, "het" : _het}]}} db.collection.updateOne(filter, update) } }
Así que imagina algo como:
db.anotherCollection.find({...},{...}).forEach(doc => upsert(doc))
Funciona, pero es bastante lento. Hay alguna otra manera de hacer esto? Leí en línea que no es posible alterar una matriz para hacer algo como esto, porque necesita empujar, tirar, según la consulta, etc. Pero necesito actualizar los contadores existentes de un objeto anidado, si se encuentra doc, o más bien inserte un documento completo, si no se encuentra el documento.
Mongo 4.4.6
Por lo tanto, esto realmente no se puede hacer en una sola llamada a la base de datos; sin embargo, podría simplificar el código y reducir la cantidad promedio de llamadas a la base de datos realizadas en total.
Específicamente hablando, podemos en lugar de find -> update
simplemente ejecutar una actualización, si la actualización es "exitosa", entonces podemos regresar, de lo contrario insertamos. Esto significa que cada vez que la list
ya existe, tiene una consulta de find
redundante que no es necesaria.
Aquí está el código:
function upsert(doc){ _hom = doc.field1.field2 == "hom" ? 1 : 0; _het = doc.field1.field2 == "het" ? 1 : 0; filter = {"v": doc.anId, "list.p" : "61f020f40e2f03f0b93b69f0"}; update = {$inc : {"list.$.c": 1}} updateRes = db.collection.updateOne(filter, update) if(updateRes.matchedCount == 0){ doc = {"v": doc.anId, "list" : [{"p" : "61f020f40e2f03f0b93b69f0", "c" : 1, "hom" : _hom, "het" : _het}]} db.collection.insertOne(doc) } }
Ahora, la cantidad de rendimiento mejorado depende de su tasa de "aciertos", supongo que para la mayoría de los procesos debería ser alta, lo que significa que será significativa. El rendimiento de los "errores" no se ve afectado ya que la actualización "actúa" igual que una búsqueda si no existe un documento coincidente.