De zoekopdracht is niet zo ingewikkeld als hij op het eerste gezicht lijkt - de zoekopdracht om alle documenten te vinden die het bereik dat u krijgt "overlappen", is:
db.test.find( { "startTime" : { "$lt" : new_end_time },
"endTime" : { "$gt": new_start_time }
}
)
Dit komt overeen met elk document met een begindatum eerder dan onze einddatum en een einddatum die groter is dan onze begintijd. Als u de bereiken visualiseert als punten op een lijn:
-----|*********|----------|****|-----------|******||********|--- s1 e1 s2 e2 s3 e3s4 e4
de sX-eX-paren vertegenwoordigen bestaande bereiken. Als u een nieuwe s5-e5 neemt, kunt u zien dat als we paren elimineren die na beginnen onze einddatum (ze kunnen ons niet overlappen) en dan elimineren we alle paren die eindigen voor onze startdatum, als we niets meer hebben, dan zijn we goed om in te voegen.
Die voorwaarde zou zijn een unie van alle documenten met einddatum $lte
onze start en die met startdatum $gte
die van ons bevatten alle documenten die al in collectie zijn. Onze vraag draait dit om om er zeker van te zijn dat geen enkel document aan het tegenovergestelde van deze voorwaarde voldoet.
Op het gebied van prestaties is het jammer dat u uw datums alleen als tekenreeksen opslaat. Als u ze als tijdstempels (of een willekeurig nummer eigenlijk) hebt opgeslagen, kunt u ervoor zorgen dat deze zoekopdracht indexen beter gebruikt. Zoals het is, zou je voor prestaties een index willen hebben op { "startTime":1, "endTime":1 }
.
Het is eenvoudig om te zien of het bereik dat u wilt invoegen bestaande bereiken overlapt, maar op uw tweede vraag:
Er is geen juiste manier om dit te doen met een invoegtoepassing, omdat ze geen zoekopdracht aannemen (d.w.z. ze zijn niet voorwaardelijk).
U kunt echter een update met upsert-voorwaarde gebruiken. Het kan invoegen als de voorwaarde nergens mee overeenkomt, maar als het wel overeenkomt, zal het proberen het overeenkomende document bij te werken!
Dus de truc die je zou gebruiken is om van de update een noop te maken en de velden die je nodig hebt alleen op upsert in te stellen. Sinds 2.4 is er een $setOnInsert
exploitant om te updaten. Het volledige ding zou er ongeveer zo uitzien:
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 })
Ik heb net twee keer dezelfde "update" gedaan - de eerste keer waren er geen overlappende documenten, dus de update voerde een "upsert" uit die je kunt zien in de WriteResult
het keerde terug.
Toen ik het een tweede keer uitvoerde, overlapte het (zelf natuurlijk) dus het probeerde het overeenkomende document bij te werken, maar merkte dat er geen werk te doen was. U kunt zien dat de geretourneerde nMatched 1 is, maar er is niets ingevoegd of gewijzigd.