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

Consultar e insertar con un solo comando

La consulta no es tan complicada como puede parecer al principio:la consulta para encontrar todos los documentos que se "superponen" con el rango que se le proporciona es:

db.test.find( { "startTime" : { "$lt" : new_end_time }, 
                "endTime"   : { "$gt": new_start_time } 
            } 
)

Esto coincidirá con cualquier documento con una fecha de inicio anterior a nuestra fecha de finalización y una fecha de finalización posterior a nuestra hora de inicio. Si visualiza los rangos como puntos en una línea:

-----|*********|----------|****|-----------|******||********|---
    s1         e1         s2   e2         s3     e3s4       e4

los pares sX-eX representan rangos existentes. Si toma un nuevo s5-e5, puede ver que si eliminamos los pares que comienzan después nuestra fecha de finalización (no pueden superponerse a nosotros) y luego eliminamos todos los pares que finalizan antes de nuestra fecha de inicio, si no nos queda nada, entonces estamos listos para insertar.

Esa condición sería una unión de todos los documentos con fecha de finalización $lte nuestro inicio y aquellos con fecha de inicio $gte los nuestros incluyen todos los documentos que ya están en la colección. Nuestra consulta invierte esto para asegurarse de que ningún documento satisfaga lo contrario de esta condición.

En el frente del rendimiento, es desafortunado que almacene sus fechas solo como cadenas. Si los almacenó como marcas de tiempo (o cualquier número, en realidad), podría hacer que esta consulta utilice mejor los índices. Tal como está, para el rendimiento, le gustaría tener un índice en { "startTime":1, "endTime":1 } .

Es simple encontrar si el rango que desea insertar se superpone a los rangos existentes, pero a su segunda pregunta:

No hay una forma adecuada de hacerlo con inserciones, ya que no aceptan una consulta (es decir, no son condicionales).

Sin embargo, puede usar una actualización con condición upsert. Puede insertar si la condición no coincide con nada, pero si coincide, ¡intentará actualizar el documento coincidente!

Entonces, el truco que usaría es hacer que la actualización sea un noop y configurar los campos que necesita solo en upsert. Desde 2.4 hay un $setOnInsert operador para actualizar. La cosa completa se vería así:

db.test.update( 
   { startTime: { "$lt" : new_end_time }, "endTime" : { "$gt": new_start_time } }, 
   { $setOnInsert:{ startTime:new_start_time, endTime: new_end_time}},
   {upsert:1}
)
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("538e0f6e7110dddea4383938")
})
db.test.update(
   { startTime:{ "$lt" : new_end_time }, "endTime" : { "$gt": new_start_time } },
   { $setOnInsert:{ startTime:new_start_time, endTime: new_end_time}},
   {upsert:1}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })

Acabo de hacer la misma "actualización" dos veces:la primera vez, no hubo documentos superpuestos, por lo que la actualización realizó una "inserción superior" que puede ver en WriteResult volvió.

Cuando lo ejecuté por segunda vez, se superponía (en sí mismo, por supuesto), por lo que intentó actualizar el documento coincidente, pero notó que no había trabajo por hacer. Puede ver que el nMatched devuelto es 1, pero no se insertó ni modificó nada.