Zapytanie nie jest tak skomplikowane, jak mogłoby się wydawać na pierwszy rzut oka - zapytanie do znalezienia wszystkich dokumentów, które "nakładają się" na podany zakres to:
db.test.find( { "startTime" : { "$lt" : new_end_time },
"endTime" : { "$gt": new_start_time }
}
)
Dopasuje to dowolny dokument, którego data rozpoczęcia jest wcześniejsza niż nasza data zakończenia i data zakończenia jest większa niż nasza godzina rozpoczęcia. Jeśli wizualizujesz zakresy jako punkty na linii:
-----|*********|----------|****|-----------|******||********|--- s1 e1 s2 e2 s3 e3s4 e4
pary sX-eX reprezentują istniejące zakresy. Jeśli weźmiesz nowy s5-e5, zobaczysz, że jeśli wyeliminujemy pary, które zaczynają się po naszą datę końcową (nie mogą się na nas nakładać), a następnie eliminujemy wszystkie pary, które kończą się przed naszą datą początkową, jeśli nic nam nie zostało, dobrze jest wstawić.
Takim warunkiem byłoby zrobienie unii wszystkich dokumentów z datą końcową $lte
nasz start i te z datą rozpoczęcia $gte
nasze obejmują wszystkie dokumenty już w kolekcji. Nasze zapytanie odwraca to, aby upewnić się, że żaden dokument nie spełnia przeciwnego warunku.
Jeśli chodzi o wydajność, to niefortunne, że przechowujesz daty tylko jako ciągi. Jeśli zapisałeś je jako znaczniki czasu (lub dowolną liczbę, naprawdę), możesz sprawić, że to zapytanie będzie lepiej wykorzystywać indeksy. Tak jak jest, dla wydajności chciałbyś mieć indeks na { "startTime":1, "endTime":1 }
.
Łatwo jest sprawdzić, czy zakres, który chcesz wstawić, pokrywa się z istniejącymi zakresami, ale na drugie pytanie:
Nie ma sposobu, aby zrobić to we właściwy sposób za pomocą wstawek, ponieważ nie przyjmują one zapytania (tj. nie są warunkowe).
Możesz jednak użyć aktualizacji z warunkiem upsert. Może wstawić, jeśli warunek niczego nie pasuje, ale jeśli pasuje, spróbuje zaktualizować dopasowany dokument!
Tak więc sztuczka, której użyjesz, polega na tym, aby aktualizacja była błędna i ustaw potrzebne pola tylko na upsert. Od wersji 2.4 istnieje $setOnInsert
operatora do aktualizacji. Całość wyglądałaby mniej więcej tak:
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 })
Właśnie wykonałem tę samą "aktualizację" dwa razy - za pierwszym razem nie było nakładania się dokumentów, więc aktualizacja wykonała "upsert", który można zobaczyć w WriteResult
wrócił.
Kiedy uruchomiłem go po raz drugi, nakładał się (oczywiście sam), więc próbował zaktualizować dopasowany dokument, ale zauważyłem, że nie ma pracy do wykonania. Możesz zobaczyć, że zwrócona wartość nMatched wynosi 1, ale nic nie zostało wstawione ani zmodyfikowane.