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

Interroga e inserisci con un solo comando

La query non è così complicata come potrebbe sembrare a prima vista:la query per trovare tutti i documenti che "si sovrappongono" all'intervallo che ti viene fornito è:

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

Ciò corrisponderà a qualsiasi documento con data di inizio precedente alla data di fine e data di fine maggiore dell'ora di inizio. Se visualizzi gli intervalli come punti su una linea:

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

le coppie sX-eX rappresentano intervalli esistenti. Se prendi un nuovo s5-e5 puoi vedere che se eliminiamo le coppie che iniziano dopo la nostra data di fine (non possono sovrapporci) e poi eliminiamo tutte le coppie che terminano prima della nostra data di inizio, se non abbiamo più niente allora siamo a posto per inserirle.

Tale condizione sarebbe un'unione di tutti i documenti con data di fine $lte il nostro inizio e quelli con data di inizio $gte i nostri includono tutti i documenti già in collezione. La nostra query lo capovolge per assicurarsi che nessun documento soddisfi l'opposto di questa condizione.

Sul fronte delle prestazioni, è un peccato che tu stia memorizzando le tue date solo come stringhe. Se li hai archiviati come timestamp (o qualsiasi numero, in realtà) potresti fare in modo che questa query utilizzi meglio gli indici. Così com'è, per le prestazioni vorresti avere un indice su { "startTime":1, "endTime":1 } .

È semplice scoprire se l'intervallo che desideri inserire si sovrappone a qualsiasi intervallo esistente, ma alla tua seconda domanda:

Non esiste un modo corretto per farlo con un inserto poiché non accettano una query (cioè non sono condizionali).

Tuttavia, puoi utilizzare un aggiornamento con una condizione upsert. Può inserire se la condizione non corrisponde a nulla, ma se corrisponde, proverà ad aggiornare il documento abbinato!

Quindi il trucco che useresti è rendere l'aggiornamento un noop e impostare i campi che ti servono solo su upsert. Dalla 2.4 esiste un $setOnInsert operatore da aggiornare. L'intera cosa sarebbe simile a questa:

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

Ho appena eseguito lo stesso "aggiornamento" due volte:la prima volta, non c'erano documenti sovrapposti, quindi l'aggiornamento ha eseguito un "upsert" che puoi vedere in WriteResult è tornato.

Quando l'ho eseguito una seconda volta, si sovrapponeva (ovviamente a se stesso), quindi ha provato ad aggiornare il documento corrispondente, ma ha notato che non c'era lavoro da fare. Puoi vedere che nMatched restituito è 1 ma non è stato inserito o modificato nulla.