Truy vấn này không phức tạp như thoạt nhìn - truy vấn để tìm tất cả các tài liệu "trùng lặp" với phạm vi mà bạn được cung cấp là:
db.test.find( { "startTime" : { "$lt" : new_end_time },
"endTime" : { "$gt": new_start_time }
}
)
Điều này sẽ khớp với bất kỳ tài liệu nào có ngày bắt đầu sớm hơn ngày kết thúc của chúng tôi và ngày kết thúc lớn hơn thời gian bắt đầu của chúng tôi. Nếu bạn hình dung các phạm vi như là các điểm trên một đường:
-----|*********|----------|****|-----------|******||********|--- s1 e1 s2 e2 s3 e3s4 e4
các cặp sX-eX đại diện cho các phạm vi hiện có. Nếu bạn lấy s5-e5 mới, bạn có thể thấy điều đó nếu chúng tôi loại bỏ các cặp bắt đầu sau ngày kết thúc của chúng tôi (chúng không thể trùng lặp với chúng tôi) và sau đó chúng tôi loại bỏ tất cả các cặp kết thúc trước ngày bắt đầu của chúng tôi, nếu chúng tôi không còn gì, thì chúng tôi tốt để chèn.
Điều kiện đó sẽ là sự kết hợp của tất cả các tài liệu có ngày kết thúc $lte
ngày bắt đầu của chúng tôi và những người có ngày bắt đầu $gte
của chúng tôi bao gồm tất cả các tài liệu đã được thu thập. Truy vấn của chúng tôi lật ngược điều này để đảm bảo rằng không có tài liệu nào đáp ứng điều kiện ngược lại với điều kiện này.
Về mặt hiệu suất, thật không may khi bạn chỉ lưu trữ ngày tháng của mình dưới dạng chuỗi ký tự. Nếu bạn đã lưu trữ chúng dưới dạng dấu thời gian (hoặc bất kỳ số nào, thực sự), bạn có thể làm cho truy vấn này sử dụng các chỉ mục tốt hơn. Như vậy, để đạt được hiệu suất, bạn muốn có chỉ mục trên { "startTime":1, "endTime":1 }
.
Thật đơn giản để tìm xem liệu phạm vi bạn muốn chèn có trùng lặp với bất kỳ phạm vi hiện có nào hay không, nhưng đối với câu hỏi thứ hai của bạn:
Không có cách nào thích hợp để thực hiện điều đó với các đoạn chèn vì chúng không thực hiện truy vấn (tức là chúng không có điều kiện).
Tuy nhiên, bạn có thể sử dụng bản cập nhật với điều kiện nâng cấp. Nó có thể chèn nếu điều kiện không khớp với bất kỳ điều gì, nhưng nếu nó khớp, nó sẽ cố gắng cập nhật tài liệu phù hợp!
Vì vậy, mẹo bạn sẽ sử dụng là làm cho bản cập nhật không hoạt động và chỉ đặt các trường bạn cần ở chế độ nâng cấp. Vì 2.4 có một $setOnInsert
để cập nhật. Toàn bộ sẽ trông giống như sau:
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 })
Tôi vừa thực hiện cùng một "cập nhật" hai lần - lần đầu tiên, không có (các) tài liệu chồng chéo nên bản cập nhật đã thực hiện "nâng cấp" mà bạn có thể thấy trong WriteResult
nó đã trả lại.
Khi tôi chạy nó lần thứ hai, nó sẽ trùng lặp (tất nhiên là chính nó) nên nó đã cố gắng cập nhật tài liệu phù hợp, nhưng nhận thấy không có việc gì phải làm. Bạn có thể thấy nM match trả về là 1 nhưng không có gì được chèn hoặc sửa đổi.