जैसा कि आप सही ढंग से उल्लेख करते हैं, उनके निष्पादन में निहित जटिलता के साथ अलग-अलग दृष्टिकोण हैं। यह मूल रूप से कवर करता है कि उन्हें कैसे किया जाता है और आप किसको लागू करते हैं यह वास्तव में इस पर निर्भर करता है कि आपका डेटा और उपयोग का मामला किसके लिए सबसे उपयुक्त है।
वर्तमान रेंज मैच
MongoDB 3.6 $लुकअप
सबसे आसान तरीका के नए सिंटैक्स का इस्तेमाल करके इस्तेमाल किया जा सकता है $लुकअप
MongoDB 3.6 वाला ऑपरेटर जो पाइपलाइन
. की अनुमति देता है उसी संग्रह में "सेल्फ जॉइन" की अभिव्यक्ति के रूप में दिया जाना है। यह मूल रूप से किसी भी आइटम के लिए संग्रह को फिर से क्वेरी कर सकता है जहां स्टार्टटाइम
"या" समाप्ति का समय
वर्तमान दस्तावेज़ का मूल्य किसी अन्य दस्तावेज़ के समान मूल्यों के बीच आता है, पाठ्यक्रम के मूल सहित नहीं:
db.getCollection('collection').aggregate([
{ "$lookup": {
"from": "collection",
"let": {
"_id": "$_id",
"starttime": "$starttime",
"endtime": "$endtime"
},
"pipeline": [
{ "$match": {
"$expr": {
"$and": [
{ "$ne": [ "$$_id", "$_id" },
{ "$or": [
{ "$and": [
{ "$gte": [ "$$starttime", "$starttime" ] },
{ "$lte": [ "$$starttime", "$endtime" ] }
]},
{ "$and": [
{ "$gte": [ "$$endtime", "$starttime" ] },
{ "$lte": [ "$$endtime", "$endtime" ] }
]}
]},
]
},
"as": "overlaps"
}},
{ "$count": "count" },
]
}},
{ "$match": { "overlaps.0": { "$exists": true } } }
])
सिंगल $lookup
उसी संग्रह पर "जॉइन" करता है जिससे आप "_id"
के लिए "वर्तमान दस्तावेज़" मान रख सकते हैं , "प्रारंभ समय"
और "एंडटाइम"
क्रमशः "let"
. के माध्यम से मान पाइपलाइन चरण का विकल्प। ये $$
. का उपयोग करके "स्थानीय चर" के रूप में उपलब्ध होंगे बाद के "पाइपलाइन"
. में उपसर्ग अभिव्यक्ति की।
इस "उप-पाइपलाइन" के भीतर आप $match<का उपयोग करते हैं /कोड>
पाइपलाइन चरण और $expr
क्वेरी ऑपरेटर, जो आपको क्वेरी कंडीशन के हिस्से के रूप में एग्रीगेशन फ्रेमवर्क लॉजिकल एक्सप्रेशन का मूल्यांकन करने की अनुमति देता है। यह मानों के बीच तुलना की अनुमति देता है क्योंकि यह शर्तों से मेल खाने वाले नए दस्तावेज़ों का चयन करता है।
शर्तें केवल "संसाधित दस्तावेज़" की तलाश करती हैं जहां "_id"
फ़ील्ड "वर्तमान दस्तावेज़" के बराबर नहीं है, $and
जहां या तो "स्टार्टटाइम"
$or
"एंडटाइम"
"वर्तमान दस्तावेज़" के मान "संसाधित दस्तावेज़" के समान गुणों के बीच आते हैं। यहां ध्यान दें कि ये और साथ ही संबंधित $gte
कोड>
और $lte
ऑपरेटर "एकत्रीकरण तुलना ऑपरेटर"
हैं न कि "query operator"
फॉर्म, जैसा कि $expr
बूलियन
होना चाहिए संदर्भ में। यह वही है जो एकत्रीकरण तुलना ऑपरेटर वास्तव में करते हैं, और यह तुलना के लिए मूल्यों को पारित करने का एकमात्र तरीका भी है।
चूँकि हम केवल मैचों की "गिनती" चाहते हैं, $गिनती
ऐसा करने के लिए पाइपलाइन चरण का उपयोग किया जाता है। समग्र $lookup
का परिणाम
एक "एकल तत्व" सरणी होगी जहां एक गिनती थी, या एक "खाली सरणी" जहां शर्तों से कोई मेल नहीं था।
एक वैकल्पिक मामला $count<को "छोड़ना" होगा /कोड>
चरण और बस मिलान करने वाले दस्तावेज़ों को वापस जाने की अनुमति दें। यह आसान पहचान की अनुमति देता है, लेकिन "दस्तावेज़ के भीतर एम्बेडेड सरणी" के रूप में आपको "ओवरलैप्स" की संख्या के प्रति सावधान रहने की आवश्यकता है जो पूरे दस्तावेज़ के रूप में लौटाए जाएंगे और इससे 16 एमबी की बीएसओएन सीमा का उल्लंघन नहीं होता है। ज्यादातर मामलों में यह ठीक होना चाहिए, लेकिन उन मामलों के लिए जहां आप किसी दिए गए दस्तावेज़ के लिए बड़ी संख्या में ओवरलैप की अपेक्षा करते हैं, यह एक वास्तविक मामला हो सकता है। तो यह वास्तव में जागरूक होने के लिए कुछ और है।
$lookup
इस संदर्भ में पाइपलाइन चरण परिणाम में "हमेशा" एक सरणी लौटाएगा, भले ही खाली हो। मौजूदा दस्तावेज़ में आउटपुट प्रॉपर्टी "विलय" का नाम होगा "ओवरलैप्स"
जैसा कि "as"
. में निर्दिष्ट है $lookup
के लिए प्रॉपर्टी
मंच।
$lookup
के बाद
, फिर हम एक आसान $match
कर सकते हैं
एक नियमित क्वेरी अभिव्यक्ति के साथ $exists
0
. के लिए परीक्षण करें आउटपुट सरणी का सूचकांक मूल्य। जहां वास्तव में सरणी में कुछ सामग्री है और इसलिए "ओवरलैप" स्थिति सही होगी और दस्तावेज़ आपके चयन के अनुसार गिनती या दस्तावेज़ "ओवरलैपिंग" दिखाते हुए वापस आ जाएगा।
अन्य संस्करण - "शामिल होने" के लिए प्रश्न
वैकल्पिक मामला जहां आपके MongoDB में इस समर्थन की कमी है, प्रत्येक जांच किए गए दस्तावेज़ के लिए ऊपर उल्लिखित समान क्वेरी शर्तों को जारी करके मैन्युअल रूप से "जुड़ना" है:
db.getCollection('collection').find().map( d => {
var overlaps = db.getCollection('collection').find({
"_id": { "$ne": d._id },
"$or": [
{ "starttime": { "$gte": d.starttime, "$lte": d.endtime } },
{ "endtime": { "$gte": d.starttime, "$lte": d.endtime } }
]
}).toArray();
return ( overlaps.length !== 0 )
? Object.assign(
d,
{
"overlaps": {
"count": overlaps.length,
"documents": overlaps
}
}
)
: null;
}).filter(e => e != null);
यह अनिवार्य रूप से एक ही तर्क है सिवाय इसके कि हमें वास्तव में "डेटाबेस पर वापस" जाने की आवश्यकता है ताकि ओवरलैपिंग दस्तावेज़ों से मिलान करने के लिए क्वेरी जारी की जा सके। इस बार यह "क्वेरी ऑपरेटर" है जिसका उपयोग यह पता लगाने के लिए किया जाता है कि वर्तमान दस्तावेज़ मान संसाधित दस्तावेज़ के बीच कहाँ आते हैं।
चूंकि परिणाम सर्वर से पहले ही वापस आ चुके हैं, इसलिए आउटपुट में सामग्री जोड़ने पर कोई बीएसओएन सीमा प्रतिबंध नहीं है। आपके पास स्मृति प्रतिबंध हो सकते हैं, लेकिन यह एक और मुद्दा है। सीधे शब्दों में कहें तो हम .toArray()
. के माध्यम से कर्सर के बजाय सरणी लौटाते हैं इसलिए हमारे पास मिलान करने वाले दस्तावेज़ हैं और गिनती प्राप्त करने के लिए केवल सरणी लंबाई तक पहुंच सकते हैं। यदि आपको वास्तव में दस्तावेज़ों की आवश्यकता नहीं है, तो .गिनती ()
इसके बजाय .find()
अधिक कुशल है क्योंकि ओवरहेड लाने वाला दस्तावेज़ नहीं है।
तब आउटपुट को केवल मौजूदा दस्तावेज़ के साथ मिला दिया जाता है, जहां अन्य महत्वपूर्ण अंतर यह है कि चूंकि थीसिस "एकाधिक प्रश्न" हैं, इस शर्त को प्रदान करने का कोई तरीका नहीं है कि उन्हें कुछ "मिलान" करना होगा। तो यह हमें इस बात पर विचार करने के लिए छोड़ देता है कि ऐसे परिणाम होंगे जहां गिनती (या सरणी लंबाई) 0
. है और इस समय हम जो कुछ भी कर सकते हैं, वह है null
मूल्य जिसे हम बाद में .filter()
. कर सकते हैं परिणाम सरणी से। कर्सर को पुनरावृत्त करने के अन्य तरीके "त्याग" परिणामों के समान मूल सिद्धांत को नियोजित करते हैं जहां हम उन्हें नहीं चाहते हैं। लेकिन सर्वर पर चल रही क्वेरी को कुछ भी नहीं रोकता है और यह फ़िल्टरिंग किसी न किसी रूप में "पोस्ट प्रोसेसिंग" है।
जटिलता कम करना
तो उपरोक्त दृष्टिकोण वर्णित संरचना के साथ काम करते हैं, लेकिन निश्चित रूप से समग्र जटिलता की आवश्यकता है कि प्रत्येक दस्तावेज़ के लिए आपको ओवरलैप देखने के लिए संग्रह में हर दूसरे दस्तावेज़ की अनिवार्य रूप से जांच करनी चाहिए। इसलिए $lookup
का उपयोग करते समय
कुछ "दक्षता" . के लिए अनुमति देता है परिवहन और प्रतिक्रिया ओवरहेड में कमी में, यह अभी भी वही समस्या है कि आप अभी भी अनिवार्य रूप से प्रत्येक दस्तावेज़ की तुलना हर चीज़ से कर रहे हैं।
एक बेहतर समाधान "जहां आप इसे उपयुक्त बना सकते हैं" इसके बजाय प्रत्येक दस्तावेज़ पर अंतराल के "हार्ड वैल्यू"* प्रतिनिधि को स्टोर करना है। उदाहरण के लिए हम "अनुमान" कर सकते हैं कि कुल 24 बुकिंग अवधि के लिए एक दिन के भीतर एक घंटे की ठोस "बुकिंग" अवधि है। यह "हो सकता है" कुछ इस तरह से दर्शाया जा सकता है:
{ "_id": "A", "booking": [ 10, 11, 12 ] }
{ "_id": "B", "booking": [ 12, 13, 14 ] }
{ "_id": "C", "booking": [ 7, 8 ] }
{ "_id": "D", "booking": [ 9, 10, 11 ] }
उस तरह व्यवस्थित डेटा के साथ जहां अंतराल के लिए एक सेट संकेतक था, जटिलता बहुत कम हो गई है क्योंकि यह वास्तव में "बुकिंग"
के भीतर सरणी से अंतराल मान पर "समूहीकरण" का मामला है। संपत्ति:
db.booking.aggregate([
{ "$unwind": "$booking" },
{ "$group": { "_id": "$booking", "docs": { "$push": "$_id" } } },
{ "$match": { "docs.1": { "$exists": true } } }
])
और आउटपुट:
{ "_id" : 10, "docs" : [ "A", "D" ] }
{ "_id" : 11, "docs" : [ "A", "D" ] }
{ "_id" : 12, "docs" : [ "A", "B" ] }
यह सही ढंग से पहचानता है कि 10
. के लिए और 11
अंतराल दोनों "A"
और "डी"
ओवरलैप होते हैं, जबकि "B"
और "ए"
12
. पर ओवरलैप करें . अन्य अंतराल और दस्तावेज़ मिलान को उसी $exists<के माध्यम से बाहर रखा गया है /कोड>
इस समय को छोड़कर 1
. पर परीक्षण करें अनुक्रमणिका (या दूसरा सरणी तत्व मौजूद होना) यह देखने के लिए कि समूह में "एक से अधिक" दस्तावेज़ थे, इसलिए एक ओवरलैप का संकेत दे रहा था।
यह बस $unwind
का इस्तेमाल करता है
एकत्रीकरण पाइपलाइन चरण सरणी सामग्री को "डीकंस्ट्रक्ट/डिनॉर्मलाइज़" करने के लिए ताकि हम समूहीकरण के लिए आंतरिक मूल्यों तक पहुंच सकें। $group
में ठीक ऐसा ही होता है
चरण जहां प्रदान की गई "कुंजी" बुकिंग अंतराल आईडी है और $पुश
ऑपरेटर का उपयोग उस समूह में पाए गए वर्तमान दस्तावेज़ के बारे में डेटा "एकत्र" करने के लिए किया जाता है। $match
जैसा कि पहले बताया गया है।
इसे वैकल्पिक प्रस्तुतिकरण के लिए विस्तारित भी किया जा सकता है:
db.booking.aggregate([
{ "$unwind": "$booking" },
{ "$group": { "_id": "$booking", "docs": { "$push": "$_id" } } },
{ "$match": { "docs.1": { "$exists": true } } },
{ "$unwind": "$docs" },
{ "$group": {
"_id": "$docs",
"intervals": { "$push": "$_id" }
}}
])
आउटपुट के साथ:
{ "_id" : "B", "intervals" : [ 12 ] }
{ "_id" : "D", "intervals" : [ 10, 11 ] }
{ "_id" : "A", "intervals" : [ 10, 11, 12 ] }
यह एक सरलीकृत प्रदर्शन है, लेकिन जहां डेटा आपके पास आवश्यक विश्लेषण के लिए अनुमति देगा तो यह कहीं अधिक कुशल दृष्टिकोण है। इसलिए यदि आप "ग्रैन्युलैरिटी" को "सेट" अंतराल के लिए तय कर सकते हैं, जिसे आमतौर पर प्रत्येक दस्तावेज़ पर रिकॉर्ड किया जा सकता है, तो विश्लेषण और रिपोर्टिंग बाद के दृष्टिकोण का उपयोग ऐसे ओवरलैप्स को जल्दी और कुशलता से पहचानने के लिए कर सकते हैं।
अनिवार्य रूप से, यह है कि आप मूल रूप से "बेहतर" दृष्टिकोण के रूप में जो भी उल्लेख करते हैं उसे कार्यान्वित करेंगे, और मूल रूप से आपके द्वारा सिद्धांतित किए गए "मामूली" सुधार के रूप में पहला है। देखें कि वास्तव में आपकी स्थिति के लिए कौन सा उपयुक्त है, लेकिन यह कार्यान्वयन और अंतरों को स्पष्ट करना चाहिए।