Frågan är inte så komplicerad som den kan se ut till en början - frågan för att hitta alla dokument som "överlappar" intervallet du får är:
db.test.find( { "startTime" : { "$lt" : new_end_time },
"endTime" : { "$gt": new_start_time }
}
)
Detta kommer att matcha alla dokument med ett startdatum tidigare än vårt slutdatum och ett slutdatum som är högre än vår starttid. Om du visualiserar intervallen som punkter på en linje:
-----|*********|----------|****|-----------|******||********|--- s1 e1 s2 e2 s3 e3s4 e4
sX-eX-paren representerar befintliga intervall. Om du tar en ny s5-e5 kan du se att om vi eliminerar par som börjar efter vårt slutdatum (de kan inte överlappa oss) och sedan tar vi bort alla par som slutar före vårt startdatum, om vi inte har något kvar så är vi bra att infoga.
Det villkoret skulle vara gör en förening av alla dokument med slutdatum $lte
vår start och de med startdatum $gte
vår inkluderar alla dokument som redan finns i insamling. Vår fråga vänder på detta för att se till att inga dokument uppfyller motsatsen till detta villkor.
På prestandafronten är det olyckligt att du bara lagrar dina datum som strängar. Om du lagrade dem som tidsstämplar (eller vilket nummer som helst) kan du få den här frågan att använda index bättre. Som det är, för prestanda skulle du vilja ha ett index på { "startTime":1, "endTime":1 }
.
Det är enkelt att ta reda på om intervallet du vill infoga överlappar befintliga intervall, men till din andra fråga:
Det finns inget riktigt sätt att göra det med en insert eftersom de inte tar en fråga (dvs. de är inte villkorade).
Däremot kan du använda en uppdatering med upsert-villkor. Det kan infogas om villkoret inte matchar något, men om det matchar kommer det att försöka uppdatera det matchade dokumentet!
Så tricket du skulle använda är att göra uppdateringen en noop, och ställ in fälten du behöver endast på upsert. Sedan 2.4 finns en $setOnInsert
operatör för att uppdatera. Det hela skulle se ut ungefär så här:
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 })
Jag gjorde precis samma "uppdatering" två gånger - första gången fanns det inga överlappande dokument så uppdateringen utförde en "upsert" som du kan se i WriteResult
den kom tillbaka.
När jag körde det en andra gång överlappade det (självklart) så det försökte uppdatera det matchade dokumentet, men märkte att det inte fanns något arbete att göra. Du kan se att den returnerade nMatched är 1 men ingenting har infogats eller ändrats.