tl;डॉ
आप जो चाहते हैं उसके लिए कोई आसान समाधान नहीं है, क्योंकि सामान्य प्रश्न उनके द्वारा लौटाए गए फ़ील्ड को संशोधित नहीं कर सकते हैं। एक समाधान है (एक संग्रह में आउटपुट करने के बजाय नीचे दिए गए mapReduce इनलाइन का उपयोग करके), लेकिन बहुत छोटे डेटाबेस को छोड़कर, रीयलटाइम में ऐसा करना संभव नहीं है।
समस्या
जैसा लिखा है, एक सामान्य क्वेरी वास्तव में उसके द्वारा लौटाए गए फ़ील्ड को संशोधित नहीं कर सकती है। लेकिन अन्य समस्याएं हैं। यदि आप आधे समय में रेगेक्स खोज करना चाहते हैं, तो आपको सभी . को अनुक्रमित करना होगा फ़ील्ड, जिसे उस सुविधा के लिए अनुपातहीन मात्रा में RAM की आवश्यकता होगी। यदि आप सभीको अनुक्रमित नहीं करेंगे फ़ील्ड, एक रेगेक्स खोज एक संग्रह स्कैन का कारण बनेगी, जिसका अर्थ है कि प्रत्येक दस्तावेज़ को डिस्क से लोड करना होगा, जो स्वत:पूर्णता के लिए सुविधाजनक होने में बहुत अधिक समय लेगा। इसके अलावा, स्वत:पूर्णता का अनुरोध करने वाले एक साथ कई उपयोगकर्ता बैकएंड पर काफी भार पैदा करेंगे।
समाधान
समस्या काफी हद तक उसी के समान है जिसका मैंने पहले ही उत्तर दिया है:हमें प्रत्येक शब्द को कई क्षेत्रों से निकालने की जरूरत है, स्टॉप शब्दों को हटा दें और शेष शब्दों को संबंधित दस्तावेज़ (दस्तावेज़ों) के लिंक के साथ एक संग्रह में पाया गया था। . अब, स्वत:पूर्णता सूची प्राप्त करने के लिए, हम केवल अनुक्रमित शब्द सूची को क्वेरी करते हैं।
चरण 1:शब्दों को निकालने के लिए मानचित्र का उपयोग करें/नौकरी कम करें
db.yourCollection.mapReduce(
// Map function
function() {
// We need to save this in a local var as per scoping problems
var document = this;
// You need to expand this according to your needs
var stopwords = ["the","this","and","or"];
for(var prop in document) {
// We are only interested in strings and explicitly not in _id
if(prop === "_id" || typeof document[prop] !== 'string') {
continue
}
(document[prop]).split(" ").forEach(
function(word){
// You might want to adjust this to your needs
var cleaned = word.replace(/[;,.]/g,"")
if(
// We neither want stopwords...
stopwords.indexOf(cleaned) > -1 ||
// ...nor string which would evaluate to numbers
!(isNaN(parseInt(cleaned))) ||
!(isNaN(parseFloat(cleaned)))
) {
return
}
emit(cleaned,document._id)
}
)
}
},
// Reduce function
function(k,v){
// Kind of ugly, but works.
// Improvements more than welcome!
var values = { 'documents': []};
v.forEach(
function(vs){
if(values.documents.indexOf(vs)>-1){
return
}
values.documents.push(vs)
}
)
return values
},
{
// We need this for two reasons...
finalize:
function(key,reducedValue){
// First, we ensure that each resulting document
// has the documents field in order to unify access
var finalValue = {documents:[]}
// Second, we ensure that each document is unique in said field
if(reducedValue.documents) {
// We filter the existing documents array
finalValue.documents = reducedValue.documents.filter(
function(item,pos,self){
// The default return value
var loc = -1;
for(var i=0;i<self.length;i++){
// We have to do it this way since indexOf only works with primitives
if(self[i].valueOf() === item.valueOf()){
// We have found the value of the current item...
loc = i;
//... so we are done for now
break
}
}
// If the location we found equals the position of item, they are equal
// If it isn't equal, we have a duplicate
return loc === pos;
}
);
} else {
finalValue.documents.push(reducedValue)
}
// We have sanitized our data, now we can return it
return finalValue
},
// Our result are written to a collection called "words"
out: "words"
}
)
इस मानचित्र को चलाने से आपके उदाहरण के विपरीत परिणाम होगा db.words
इस तरह दिखें:
{ "_id" : "can", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
{ "_id" : "canada", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
{ "_id" : "candid", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
{ "_id" : "candle", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
{ "_id" : "candy", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
{ "_id" : "cannister", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
{ "_id" : "canteen", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
{ "_id" : "canvas", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
ध्यान दें कि अलग-अलग शब्द _id
हैं दस्तावेजों की। _id
फ़ील्ड को MongoDB द्वारा स्वचालित रूप से अनुक्रमित किया जाता है। चूंकि सूचकांकों को रैम में रखने की कोशिश की जाती है, इसलिए हम स्वत:पूर्णता को गति देने और सर्वर पर डाले गए भार को कम करने के लिए कुछ तरकीबें अपना सकते हैं।
चरण 2:स्वत:पूर्णता के लिए क्वेरी
स्वत:पूर्णता के लिए, हमें दस्तावेज़ों के लिंक के बिना केवल शब्दों की आवश्यकता होती है। चूंकि शब्दों को अनुक्रमित किया जाता है, इसलिए हम एक कवर की गई क्वेरी का उपयोग करते हैं - एक क्वेरी का उत्तर केवल इंडेक्स से दिया जाता है, जो आमतौर पर रैम में रहता है।
आपके उदाहरण पर कायम रहने के लिए, हम उम्मीदवारों को स्वतः पूर्णता के लिए प्राप्त करने के लिए निम्नलिखित क्वेरी का उपयोग करेंगे:
db.words.find({_id:/^can/},{_id:1})
जो हमें परिणाम देता है
{ "_id" : "can" }
{ "_id" : "canada" }
{ "_id" : "candid" }
{ "_id" : "candle" }
{ "_id" : "candy" }
{ "_id" : "cannister" }
{ "_id" : "canteen" }
{ "_id" : "canvas" }
.explain()
. का उपयोग करना विधि, हम सत्यापित कर सकते हैं कि यह क्वेरी केवल अनुक्रमणिका का उपयोग करती है।
{
"cursor" : "BtreeCursor _id_",
"isMultiKey" : false,
"n" : 8,
"nscannedObjects" : 0,
"nscanned" : 8,
"nscannedObjectsAllPlans" : 0,
"nscannedAllPlans" : 8,
"scanAndOrder" : false,
"indexOnly" : true,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"_id" : [
[
"can",
"cao"
],
[
/^can/,
/^can/
]
]
},
"server" : "32a63f87666f:27017",
"filterSet" : false
}
ध्यान दें indexOnly:true
फ़ील्ड.
चरण 3:वास्तविक दस्तावेज़ को क्वेरी करें
यद्यपि हमें वास्तविक दस्तावेज़ प्राप्त करने के लिए दो प्रश्न करने होंगे, क्योंकि हम समग्र प्रक्रिया को गति देते हैं, उपयोगकर्ता अनुभव पर्याप्त होना चाहिए।
चरण 3.1:words
का दस्तावेज़ प्राप्त करें संग्रह
जब उपयोगकर्ता स्वत:पूर्णता के विकल्प का चयन करता है, तो हमें उन दस्तावेजों को खोजने के लिए शब्दों के पूरे दस्तावेज़ को क्वेरी करना होगा जहां से स्वत:पूर्णता के लिए चुना गया शब्द उत्पन्न हुआ था।
db.words.find({_id:"canteen"})
जिसके परिणामस्वरूप इस तरह का एक दस्तावेज़ होगा:
{ "_id" : "canteen", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
चरण 3.2:वास्तविक दस्तावेज़ प्राप्त करें
उस दस्तावेज़ के साथ, अब हम या तो खोज परिणामों वाला एक पृष्ठ दिखा सकते हैं या, जैसे इस मामले में, वास्तविक दस्तावेज़ पर रीडायरेक्ट कर सकते हैं, जिसे आप निम्न द्वारा प्राप्त कर सकते हैं:
db.yourCollection.find({_id:ObjectId("553e435f20e6afc4b8aa0efb")})
नोट
हालांकि यह दृष्टिकोण पहली बार में जटिल लग सकता है (ठीक है, mapReduce है थोड़ा सा), यह वास्तव में अवधारणात्मक रूप से बहुत आसान है। मूल रूप से, आप वास्तविक समय के परिणामों का व्यापार कर रहे हैं (जो आपके पास तब तक नहीं होगा जब तक कि आप लॉट खर्च नहीं करते हैं। RAM का) गति के लिए। इम्हो, यह एक अच्छा सौदा है। अपेक्षाकृत महंगा नक्शा कम करें चरण को और अधिक कुशल बनाने के लिए, वृद्धिशील मानचित्र को लागू करना एक दृष्टिकोण हो सकता है - मेरे स्वीकार्य रूप से हैक किए गए मानचित्र को सुधारना एक और हो सकता है।
अंतिम लेकिन कम से कम, यह तरीका पूरी तरह से बदसूरत हैक नहीं है। आप इलास्टिक्स खोज या ल्यूसीन में खुदाई करना चाह सकते हैं। वे उत्पाद इम्हो जो आप चाहते हैं उसके लिए बहुत अधिक उपयुक्त हैं।