मानगो सीमा / ऑफसेट और गणना क्वेरी


84

क्वेरी प्रदर्शन पर एक अजीब सा ... मुझे एक क्वेरी चलाने की ज़रूरत है जो दस्तावेजों की कुल गिनती करती है, और एक परिणाम सेट भी लौटा सकती है जो सीमित और ऑफसेट हो सकता है।

तो, मेरे पास कुल 57 दस्तावेज हैं, और उपयोगकर्ता 10 दस्तावेजों को 20 से ऑफसेट करना चाहता है।

मैं ऐसा करने के 2 तरीकों के बारे में सोच सकता हूं, पहले सभी 57 दस्तावेजों के लिए क्वेरी है (एक सरणी के रूप में लौटा), फिर array.slice का उपयोग करके वे इच्छित दस्तावेज़ लौटाएं। दूसरा विकल्प 2 प्रश्नों को चलाने के लिए है, पहला है जो मोंगो की मूल 'गणना' पद्धति का उपयोग करता है, फिर एक दूसरी क्वेरी को मोंगो की मूल $ सीमा और $ स्किप एग्रीगेटर्स का उपयोग करके चलाएं।

जो आपको लगता है कि बेहतर होगा? यह सब एक क्वेरी में कर रहे हैं, या दो अलग-अलग चल रहे हैं?

संपादित करें:

// 1 query
var limit = 10;
var offset = 20;

Animals.find({}, function (err, animals) {
    if (err) {
        return next(err);
    }

    res.send({count: animals.length, animals: animals.slice(offset, limit + offset)});
});


// 2 queries
Animals.find({}, {limit:10, skip:20} function (err, animals) {            
    if (err) {
        return next(err);
    }

    Animals.count({}, function (err, count) {
        if (err) {
            return next(err);
        }

        res.send({count: count, animals: animals});
    });
});

मैं मानसून के बारे में अनिश्चित हूं, हालांकि count()PHP में डिफ़ॉल्ट फ़ंक्शन तब तक limitया skipखाते में नहीं आता है जब तक कि इसे केवल एक क्वेरी की सीमा और स्किप करने के लिए नहीं कहा जाता है और फिर गिनती प्राप्त करना संभवत: यहां सबसे अधिक प्रदर्शन योग्य समाधान देना चाहिए। हालाँकि, आप कैसे करेंगे कि 57 दस्तावेज़ हैं यदि आप दो प्रश्नों को गिनने के लिए नहीं करते हैं कि वर्तमान में क्या है? क्या आपके पास एक स्थिर संख्या है जो कभी नहीं बदलती है? यदि नहीं तो आपको स्किप और लिमिट दोनों की आवश्यकता होगी।
सम्मे

क्षमा करें, मैं db.collection.find(<query>).count();
मानगो

क्षमा करें यह मैं था, मैंने आपके प्रश्न को गलत बताया। हम्म वास्तव में मुझे यकीन नहीं है जो बेहतर होगा, क्या आपका परिणाम हमेशा 57 डॉक्स की तरह कम होगा? यदि ऐसा है तो क्लाइंट साइड स्लाइस अधिक प्रदर्शन करने वाला मिलिसेकंड हो सकता है।
सम्मे

मैंने मूल प्रश्न से उदाहरण जोड़ा है, मुझे नहीं लगता कि डेटा कभी भी 10,000+ जितना उच्च होगा, लेकिन संभवतः यह हो सकता है।
लेपॉवेल

10k रिकॉर्ड में आप देख सकते हैं कि JS की मेमोरी हैंडलिंग count()MongoDB के फंक्शन से कम परफॉर्मेंट है । count()MongoDB में समारोह अपेक्षाकृत धीमी है लेकिन यह अभी भी अपेक्षाकृत बड़े समूहों पर सबसे क्लाइंट पक्ष रूपों के रूप में काफी के रूप में तेजी से होता है और यह तेजी से ग्राहक के पक्ष गिनती की तुलना में यहां संभवतः हो सकता है। लेकिन वह हिस्सा आपके अपने परीक्षण के अधीन है। ध्यान रहे कि मैंने 10k लंबाई के सरणियों को आसानी से गिना है, इसलिए यह तेजी से ग्राहक पक्ष हो सकता है, 10k तत्वों पर यह कहना बहुत कठिन है।
सम्मे

जवाबों:


129

मैं आपको 2 प्रश्नों का उपयोग करने का सुझाव देता हूं:

  1. db.collection.count()मदों की कुल संख्या वापस आ जाएगी। यह मान मानगो में कहीं संग्रहीत है और इसकी गणना नहीं की गई है।

  2. db.collection.find().skip(20).limit(10)यहाँ मैं मानता हूँ कि आप किसी क्षेत्र द्वारा एक प्रकार का उपयोग कर सकते हैं, इसलिए इस क्षेत्र पर एक सूचकांक जोड़ना न भूलें। यह क्वेरी तेज़ भी होगी।

मुझे लगता है कि आपको सभी आइटम्स को क्वेरी नहीं करना चाहिए और स्किप करने और लेने के बजाय, बाद में जब आपके पास बड़ा डेटा होगा तो आपको डेटा ट्रांसफर और प्रोसेसिंग में समस्या होगी।


1
मैं जो लिख रहा हूं वह बिना किसी दिखावा के सिर्फ एक टिप्पणी है, लेकिन मैंने सुना है कि यह .skip()निर्देश सीपीयू के लिए भारी है क्योंकि यह संग्रह की शुरुआत में जाता है और के पैरामीटर में निर्दिष्ट मूल्य पर पहुंच जाता है .skip()। बड़े संग्रह पर इसका वास्तविक प्रभाव हो सकता है! लेकिन मुझे नहीं पता कि जो .skip()भी वैसे भी उपयोग के बीच सबसे भारी है या पूरे संग्रह को प्राप्त करता है और जेएस के साथ ट्रिम करता है ... आपको क्या लगता है?
ज़ाचरी दहन

2
@Stuffix मैंने उपयोग करने पर समान चिंताओं को सुना है .skip()। यह उत्तर उस पर स्पर्श करता है, और दिनांक फ़ील्ड पर फ़िल्टर का उपयोग करने की सलाह देता है। कोई इसे .skip()& .take()विधियों के साथ उपयोग कर सकता है । यह एक अच्छा विचार है। हालाँकि, मैं कुल दस्तावेजों की गिनती कैसे प्राप्त कर सकता हूं, इस ओपी के सवाल से परेशान हूं। यदि फ़िल्टर का उपयोग प्रदर्शन निहितार्थों से निपटने के लिए किया जाता है .skip(), तो हमारे पास एक सटीक गिनती कैसे हो सकती है? Db में संग्रहीत गणना हमारे फ़िल्टर किए गए डेटा सेट को प्रतिबिंबित नहीं करेगी।
माइकल लीनोस

हाय @MichaelLeanos, मैं एक ही मुद्दे का सामना कर रहा हूँ: यानी कुल दस्तावेजों की एक गिनती कैसे प्राप्त करें। यदि एक फिल्टर का उपयोग किया जाता है तो हमारे पास एक सटीक गिनती कैसे हो सकती है? क्या आपको इसके लिए समाधान मिला?
विरास

@virsha, cursor.count()फ़िल्टर किए गए डॉक्यूमेंट्स की संख्या वापस करने के लिए उपयोग करें (यह क्वेरी को निष्पादित नहीं करेगा यह आपको मिलान किए गए डॉक्स की संख्या लौटा देगा)। सुनिश्चित करें कि आप फ़िल्टर करें और ऑर्डर गुण अनुक्रमित हैं और सब कुछ ठीक हो जाएगा।
user854301

@virsha cursor.count()@ user854301 के अनुसार काम करना चाहिए। हालाँकि, मैंने जो कुछ किया वह मेरे एपीआई ( /api/my-colllection/stats) के लिए एक समापन बिंदु जोड़ रहा था कि मैं अपने संग्रह पर विभिन्न आँकड़ों को मानगो की db.collection.stats सुविधा का उपयोग करके वापस करता था । चूँकि मुझे वास्तव में केवल अपने फ्रंट-एंड के लिए ही इसकी आवश्यकता थी, इसलिए मैंने अपने सर्वर-साइड विभाजन के स्वतंत्र रूप से उस जानकारी को वापस करने के लिए समापन बिंदु को समझा।
माइकल लीनोस

19

2 अलग-अलग प्रश्नों का उपयोग करने के बजाय, आप aggregate()एक ही क्वेरी में उपयोग कर सकते हैं :

एग्रीगेट "$ पहलू" को अधिक तेज़ी से प्राप्त किया जा सकता है, कुल गणना और स्किप और सीमा के साथ डेटा

    db.collection.aggregate([

      //{$sort: {...}}

      //{$match:{...}}

      {$facet:{

        "stage1" : [ {"$group": {_id:null, count:{$sum:1}}} ],

        "stage2" : [ { "$skip": 0}, {"$limit": 2} ]
  
      }},
     
     {$unwind: "$stage1"},
  
      //output projection
     {$project:{
        count: "$stage1.count",
        data: "$stage2"
     }}

 ]);

आउटपुट निम्नानुसार है: -

[{
     count: 50,
     data: [
        {...},
        {...}
      ]
 }]

इसके अलावा, https://docs.mongodb.com/manual/reference/operator/aggregation/facet/ पर एक नज़र डालें


2

इस मुद्दे से खुद को निपटने के बाद, मैं user854301 के उत्तर पर निर्माण करना चाहूंगा।

Mongoose ^ 4.13.8 मैं एक फ़ंक्शन का उपयोग करने में सक्षम था, जिसे toConstructor()फ़िल्टर लागू होने पर मुझे कई बार क्वेरी बनाने से बचने की अनुमति दी गई थी। मुझे पता है कि यह फ़ंक्शन पुराने संस्करणों में भी उपलब्ध है, लेकिन इसकी पुष्टि के लिए आपको Mongoose डॉक्स की जांच करनी होगी।

निम्नलिखित ब्लूबर्ड वादों का उपयोग करता है:

let schema = Query.find({ name: 'bloggs', age: { $gt: 30 } });

// save the query as a 'template'
let query = schema.toConstructor();

return Promise.join(
    schema.count().exec(),
    query().limit(limit).skip(skip).exec(),

    function (total, data) {
        return { data: data, total: total }
    }
);

अब गणना क्वेरी उसके मिलान किए गए कुल रिकॉर्ड को वापस कर देगी और लौटाया गया डेटा कुल रिकॉर्ड का सबसेट होगा।

कृपया क्वेरी के आसपास () क्वेरी का निर्माण करने वाले () पर ध्यान दें ।



0
db.collection_name.aggregate([
    { '$match'    : { } },
    { '$sort'     : { '_id' : -1 } },
    { '$facet'    : {
        metadata: [ { $count: "total" } ],
        data: [ { $skip: 1 }, { $limit: 10 },{ '$project' : {"_id":0} } ] // add projection here wish you re-shape the docs
    } }
] )

कुल गणना खोजने और मिलान किए गए रिकॉर्ड को छोड़ने के लिए दो प्रश्नों का उपयोग करने के बजाय।
$ पहलू सबसे अच्छा और अनुकूलित तरीका है।

  1. रिकॉर्ड का मिलान करें
  2. Total_count ज्ञात करें
  3. रिकॉर्ड छोड़ें
  4. और क्वेरी में हमारी आवश्यकताओं के अनुसार डेटा को फिर से सेट कर सकते हैं।

1
कृपया अपने उत्तर में कुछ स्पष्टीकरण जोड़ें जैसे कि अन्य इससे सीख सकते हैं
निको हेस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.