2014-04-29 5 views
6

मैं अन्य सभी क्षेत्रों के साथ प्रत्येक स्टेशन के लिए पिछले दस्तावेज़ प्राप्त करना चाहते हैं:MongoDB: एकत्रीकरण ढांचा: प्रति समूह आईडी पिछले दिनांकित दस्तावेज़ जाओ

{ 
     "_id" : ObjectId("535f5d074f075c37fff4cc74"), 
     "station" : "OR", 
     "t" : 86, 
     "dt" : ISODate("2014-04-29T08:02:57.165Z") 
} 
{ 
     "_id" : ObjectId("535f5d114f075c37fff4cc75"), 
     "station" : "OR", 
     "t" : 82, 
     "dt" : ISODate("2014-04-29T08:02:57.165Z") 
} 
{ 
     "_id" : ObjectId("535f5d364f075c37fff4cc76"), 
     "station" : "WA", 
     "t" : 79, 
     "dt" : ISODate("2014-04-29T08:02:57.165Z") 
} 

मैं प्रति स्टेशन डीटी नवीनतम के लिए टी और स्टेशन की आवश्यकता है । एकत्रीकरण ढांचे के साथ:

db.temperature.aggregate([{$sort:{"dt":1}},{$group:{"_id":"$station", result:{$last:"$dt"}, t:{$last:"$t"}}}]) 

रिटर्न

{ 
     "result" : [ 
       { 
         "_id" : "WA", 
         "result" : ISODate("2014-04-29T08:02:57.165Z"), 
         "t" : 79 
       }, 
       { 
         "_id" : "OR", 
         "result" : ISODate("2014-04-29T08:02:57.165Z"), 
         "t" : 82 
       } 
     ], 
     "ok" : 1 
} 

यह है कि ऐसा करने के लिए सबसे कारगर तरीका है?

धन्यवाद

+3

आपके द्वारा निललुन से स्वीकार किए गए उत्तर वास्तव में गलत हैं। प्राकृतिक आदेश को सम्मिलन आदेश (कैप्ड संग्रहों को छोड़कर) होने की गारंटी नहीं है और _id केवल आपके क्लाइंट मशीनों के सभी * समय सिंक्रनाइज़ होने पर एकान्त रूप से बढ़ने की गारंटी है। –

उत्तर

5

सीधे अपने प्रश्न का उत्तर देने के लिए, हाँ यह सबसे प्रभावी तरीका है। लेकिन मुझे लगता है कि हमें यह स्पष्ट करने की जरूरत है कि ऐसा क्यों है।

के रूप में विकल्प में सुझाव दिया गया था, एक बात लोगों को है एक $group मंच और वे क्या, "टाइमस्टैम्प" मूल्य है पर देख रहे हैं तो आप बनाना चाहते हैं के लिए पार करने से पहले अपने परिणामों को "छँटाई" देख रहे हैं सुनिश्चित करें कि सब कुछ, "टाइमस्टैम्प" क्रम में है तो इसलिए प्रपत्र:

db.temperature.aggregate([ 
    { "$sort": { "station": 1, "dt": -1 } }, 
    { "$group": { 
     "_id": "$station", 
     "result": { "$first":"$dt"}, "t": {"$first":"$t"} 
    }} 
]) 

और जैसा कि कहा गया है कि आप निश्चित रूप से एक सूचकांक को प्रतिबिंबित करने के इसी क्रम तरह कुशल बनाने के लिए में चाहते हैं:

हालांकि , और थाई एस असली बिंदु है।ऐसा लगता है कि दूसरों द्वारा अनदेखा किया गया है (यदि आपके लिए ऐसा नहीं है) यह है कि इस डेटा के सभी समय पहले से डालने की संभावना है, जिसमें प्रत्येक पाठ को जोड़ा गया है।

तो यह की सुंदरता _id क्षेत्र (एक डिफ़ॉल्ट ObjectId के साथ) "टाइमस्टैम्प" क्रम में पहले से ही है, के रूप में यह अपने आप करता है वास्तव में एक समय मान है और इस बयान को संभव बनाता है:

db.temperature.aggregate([ 
    { "$group": { 
     "_id": "$station", 
     "result": { "$last":"$dt"}, "t": {"$last":"$t"} 
    }} 
]) 

और यह तेज है। क्यूं कर? खैर आपको एक इंडेक्स (आमंत्रित करने के लिए अतिरिक्त कोड) चुनने की आवश्यकता नहीं है, आपको दस्तावेज़ के अतिरिक्त इंडेक्स को "लोड" करने की आवश्यकता नहीं है।

हम पहले से ही जानते हैं कि दस्तावेजों के क्रम में (_id द्वारा) कर रहे हैं तो $last सीमाओं पूरी तरह से वैध है। आप वैसे भी सबकुछ स्कैन कर रहे हैं, और आप _id मानों पर भी "रेंज" क्वेरी को दो तिथियों के बीच समान रूप से वैध मान सकते हैं।

यहाँ कहने के लिए केवल असली बात यह है कि "वास्तविक दुनिया" में उपयोग, यह सिर्फ आप के लिए और अधिक व्यावहारिक $match तिथियों की सीमाओं के रूप में हो रही है "पहले करने का विरोध किया जब संचय की इस तरह कर रही है के बीच करने के लिए हो सकता है "और" अंतिम "_id मान" वास्तविक "या आपके वास्तविक उपयोग में समान कुछ परिभाषित करने के लिए मान।

तो इसका सबूत कहां है? खैर यह पुन: पेश करने में काफी आसान है, तो मैं बस कुछ नमूना डेटा उत्पन्न करके वैसा ही किया: (Spinny डिस्क, जो तारकीय नहीं है, लेकिन निश्चित रूप से पर्याप्त के साथ 8 जीबी लैपटॉप) मेरी हार्डवेयर पर

var stations = [ 
    "AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL", 
    "GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", 
    "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", 
    "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "OH", "OK", 
    "OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT", 
    "VA", "WA", "WV", "WI", "WY" 
]; 


for (i=0; i<200000; i++) { 

    var station = stations[Math.floor(Math.random()*stations.length)]; 
    var t = Math.floor(Math.random() * (96 - 50 + 1)) +50; 
    dt = new Date(); 

    db.temperatures.insert({ 
     station: station, 
     t: t, 
     dt: dt 
    }); 

} 

के प्रत्येक फार्म चल रहे कथन स्पष्ट रूप से एक इंडेक्स और एक प्रकार का उपयोग कर संस्करण के साथ एक उल्लेखनीय विराम दिखाता है (सॉर्ट स्टेटमेंट के रूप में इंडेक्स पर एक ही कुंजी)। यह केवल मामूली विराम है, लेकिन अंतर ध्यान देने के लिए पर्याप्त महत्वपूर्ण है।

भी समझाने उत्पादन को देखकर (संस्करण 2.6 और ऊपर, या वास्तव में 2.4.9 हालांकि दस्तावेज नहीं में है) आपको लगता है कि में अंतर देख सकते हैं, हालांकि $sort एक की उपस्थिति के कारण बाहर अनुकूलित है सूचकांक, लिया गया समय इंडेक्स चयन के साथ होता है और फिर अनुक्रमित प्रविष्टियों को लोड करता है। "कवर" अनुक्रमणिका क्वेरी के लिए सभी फ़ील्ड सहित कोई फर्क नहीं पड़ता।

रिकॉर्ड के लिए, तारीख को पूरी तरह से अनुक्रमणित करना और तारीख मानों पर केवल क्रमबद्ध करना एक ही परिणाम देता है। शायद थोड़ा तेज, लेकिन बिना किसी प्रकार के प्राकृतिक सूचकांक रूप से धीमा।

तो जब तक आप खुशी से पहले और पिछले_id मूल्यों पर "सीमा" के रूप में है, तो यह सच है कि प्रविष्टि के आदेश पर प्राकृतिक सूचकांक का उपयोग कर वास्तव में यह करने के लिए सबसे कारगर तरीका है। आपका वास्तविक विश्व लाभ इस बात पर भिन्न हो सकता है कि यह आपके लिए व्यावहारिक है या नहीं और यह सूचकांक को लागू करने और तिथि को क्रमबद्ध करने के लिए अधिक सुविधाजनक हो सकता है।

लेकिन अगर आप अपनी क्वेरी में _id श्रेणियों या "अंतिम" _id से अधिक से अधिक खुश हैं, तो शायद आपके परिणामों के साथ मूल्य प्राप्त करने के लिए एक ट्विक करें ताकि आप वास्तव में उस जानकारी को स्टोर और उपयोग कर सकें लगातार प्रश्नों:

db.temperature.aggregate([ 
    // Get documents "greater than" the "highest" _id value found last time 
    { "$match": { 
     "_id": { "$gt": ObjectId("536076603e70a99790b7845d") } 
    }}, 

    // Do the grouping with addition of the returned field 
    { "$group": { 
     "_id": "$station", 
     "result": { "$last":"$dt"}, 
     "t": {"$last":"$t"}, 
     "lastDoc": { "$last": "$_id" } 
    }} 
]) 

और अगर आप वास्तव में "पर निम्नलिखित" कर रहे थे कि जैसे परिणाम तो आप अपने परिणामों से ObjectId की अधिकतम मूल्य निर्धारित और अगले क्वेरी में इसका इस्तेमाल कर सकते हैं।

किसी भी तरह, इसके साथ मज़े करना मज़ेदार है, लेकिन फिर हां, इस मामले में यह प्रश्न सबसे तेज़ तरीका है।

2

एक सूचकांक सब है क्या तुम सच में की जरूरत है:

db.temperature.ensureIndex({ 'station': 1, 'dt': 1 }) 
for s in db.temperature.distinct('station'): 
    db.temperature.find({ station: s }).sort({ dt : -1 }).limit(1) 
का उपयोग कर जो भी वाक्य रचना वास्तव में अपनी भाषा के लिए मान्य है निश्चित रूप से

संपादित करें: आप सही हैं कि इस तरह का एक लूप प्रति स्टेशन एक राउंड-ट्रिप करता है, और यह कुछ स्टेशनों के लिए बहुत अच्छा है, और 1000 के लिए इतना अच्छा नहीं है। आप अभी भी स्टेशन + डीटी पर कंपाउंड इंडेक्स चाहते हैं, हालांकि , और एक अवरोही तरह का लाभ लेने के लिए:

db.temperature.aggregate([ 
    { $sort: { station: 1, dt: -1 } }, 
    { $group: { _id: "$station", result: {$first:"$dt"}, t: {$first:"$t"} } } 
]) 
+1

आप अपने कोड के साथ n खोज करेंगे। मेरे पास हजारों स्टेशन हैं ... यही कारण है कि मैं केवल एक अनुरोध के लिए एकत्रीकरण ढांचे का उपयोग करना चाहता हूं। इंडेक्स सुझाव – hotips

+0

के लिए धन्यवाद, इसलिए, रिकॉर्ड के लिए, इस मामले को परिभाषित करने में इस मामले में वास्तव में धीमी गति से चलेंगे। यहां विचार करने की बात यह है कि दस्तावेज़ वास्तव में पहले ही सम्मिलन आदेश में हैं। इस मामले के साथ, मैंने यह साबित करने के लिए एक टेस्ट केस उदाहरण के साथ लिखा था कि ऐसा क्यों है। –

1

जहाँ तक एकत्रीकरण क्वेरी द्वारा पोस्ट की गई के रूप में, मैं तुम्हें डीटी पर एक सूचकांक है कि कुछ करना चाहते हैं:

db.temperature.ensureIndex({'dt': 1 }) 

हो जाएगा ताकि सुनिश्चित करें कि एकत्रीकरण पाइपलाइन की शुरुआत में $ प्रकार possibl के रूप में कुशल है ई।

इस डेटा को प्राप्त करने का यह सबसे प्रभावी तरीका है या नहीं, एक लूप में एक क्वेरी बनाम, यह संभवतः आपके पास कितने डेटा बिंदुओं का एक कार्य होगा। शुरुआत में, "हजारों स्टेशनों" और शायद सैकड़ों हजारों डेटा बिंदुओं के साथ मुझे लगता है कि एकत्रीकरण दृष्टिकोण तेज होगा।

हालांकि, जैसा कि आप अधिक से अधिक डेटा जोड़ते हैं, एक मुद्दा यह है कि एकत्रीकरण क्वेरी सभी दस्तावेजों को छूती रहेगी। जब आप लाखों या अधिक दस्तावेजों तक पहुंचते हैं तो यह तेजी से महंगा हो जाएगा। उस मामले के लिए एक दृष्टिकोण $ 150 के बाद सही तरीके से दस्तावेजों की कुल संख्या को सीमित करने के लिए $ सीमा जोड़ना होगा। यह थोड़ा हैकी और अचूक है लेकिन यह उन दस्तावेजों की कुल संख्या को सीमित करने में मदद करेगा जिन्हें एक्सेस करने की आवश्यकता है।

+1

मैं सॉर्टिंग के लिए _id का उपयोग कर सकता हूं, यह मुझे लगता है IsoDate से तेज़ है। – hotips

+0

और वास्तव में ऐसा नहीं है। '_id' मान पहले से ही आवश्यक क्रम में हैं, और एक परीक्षण केस (दिखाया गया है) साबित होता है कि यह मामला है, एक इंडेक्स को परिभाषित करना और एक प्रकार वास्तव में धीमा चल जाएगा। –

+0

@NeilLunn गलत, _id मान पहले से ही आवश्यक क्रम में नहीं हैं, जब तक कि आप उन्हें एक अनुक्रमणिका से पढ़ रहे हों (जो तब होता है जब आप _id द्वारा सॉर्ट करते हैं)। –

संबंधित मुद्दे