MongoDB - पेजिंग


81

MongoDB का उपयोग करते समय, उदाहरण के लिए पृष्ठांकित दृश्य बनाने के लिए कोई विशेष पैटर्न हैं? एक ऐसा ब्लॉग कहें जो उन 10 नवीनतम पोस्टों को सूचीबद्ध करता है जहाँ आप पुराने पोस्टों पर वापस नेविगेट कर सकते हैं।

या कोई इसे जैसे कि blogpost.publishdate पर एक सूचकांक के साथ हल करते हैं और परिणाम को छोड़ और सीमित करते हैं?


1
मैं इस एक को छोड़ने जा रहा हूं क्योंकि इस पैमाने को बनाने का सही तरीका क्या है, इस बारे में कुछ असहमति प्रतीत होती है।
रोजर जोहानसन

जवाबों:


98

स्किपिंग + सीमा का उपयोग करना एक अच्छा तरीका नहीं है जब प्रदर्शन एक मुद्दा है, या बड़े संग्रह के साथ पेजिंग करना है; जैसे ही आप पृष्ठ संख्या बढ़ाते हैं, यह धीमा और धीमा होता जाएगा। स्किप का उपयोग करने के लिए सर्वर को चलने की आवश्यकता होती है, हालांकि सभी दस्तावेज़ (या सूचकांक मान) 0 से ऑफसेट (छोड़ें) मान तक।

जहां आप अंतिम पृष्ठ के श्रेणी मान में पास होते हैं, वहां क्वेरी (+ सीमा) का उपयोग करना बेहतर होता है। उदाहरण के लिए यदि आप "publishdate" के आधार पर छंटनी कर रहे हैं, तो आप डेटा के अगले पृष्ठ को प्राप्त करने के लिए क्वेरी के मानदंड के रूप में अंतिम "publishdate" मान को पास कर देंगे।


4
कुछ दस्तावेज़ों को देखने के लिए बहुत अच्छा होगा, जो इस बात की पुष्टि करते हैं कि सभी दस्तावेजों के माध्यम से मोंगोडब इटरेट में छोड़ें।
एंड्रयू ओरिच

5
यहां आप जाते हैं: डॉक्स छोड़ें यदि कोई अन्य जगह है जहां जानकारी अपडेट की जानी चाहिए तो कृपया मुझे बताएं।
स्कॉट हर्नांडेज़

2
@ScottHernandez: मेरे पास कई पृष्ठों के लिंक के साथ पेजिंग है (जैसे: पृष्ठ: पहला, 2, 3, 4, 5, अंतिम) और सभी क्षेत्रों पर छंटनी। केवल मेरा एक क्षेत्र अद्वितीय है (और अनुक्रमित), क्या इस उपयोग-केस के लिए कोई श्रेणी क्वेरी काम करेगी? मुझे डर नहीं है, मैं बस यह पुष्टि करना चाहता था कि क्या यह संभव था। धन्यवाद।
user183037


8
ऐसा लगता है कि एक ही प्रकाशित मूल्य के साथ कई दस्तावेज थे, तो यह काम नहीं करेगा।
d512

12
  1. यदि आपको कई तरह से आइटम सॉर्ट करने की आवश्यकता है, तो रेंज आधारित पेजिंग को लागू करना कठिन है।
  2. याद रखें कि यदि सॉर्ट पैरामीटर का फ़ील्ड मान अद्वितीय नहीं है, तो श्रेणी आधारित पेजिंग अवास्तविक हो जाएगी।

संभावित समाधान: यह सोचकर कि हम केवल आईडी या कुछ अनूठे मूल्य के आधार पर छाँट सकते हैं, desgin को सरल बनाने का प्रयास करें?

और अगर हम कर सकते हैं, तो रेंज आधारित पेजिंग का उपयोग किया जा सकता है।

सामान्य तरीके का उपयोग किया जाता है सॉर्ट (), स्किप () और सीमा () को पेजिंग को लागू करने के लिए जो ऊपर वर्णित है।


पायथन कोड के उदाहरणों के साथ एक अच्छा लेख यहाँ पा
Gianfranco P.

1
साभार - शानदार जवाब! जब लोग फ़िल्टर का उपयोग करके पेजेशन का सुझाव देते हैं तो मैं नाराज़ हो जाता हूं { _id: { $gt: ... } }... यह कस्टम आदेश का उपयोग करते हुए बस काम नहीं करता है - जैसे .sort(...)
निक ग्रीली

1
@NickGrealy मैंने सिर्फ ऐसा करने के लिए एक ट्यूटोरियल का अनुसरण किया और मैं अब ऐसी स्थिति में हूं जहां पेजिंग 'जैसा दिखता है' यह काम करता है, लेकिन मुझे लापता दस्तावेज मिलते हैं क्योंकि मैं मोंगो आईडी का उपयोग कर रहा हूं लेकिन जैसे ही नया डेटा डीबी में डाला जाता है, और फिर संग्रह को वर्णानुक्रम से क्रमबद्ध किया जाता है यदि आरंभिक पृष्ठ में ए दर्ज करने वाले रिकॉर्ड होते हैं लेकिन आईडी शुरू होने वाले रिकॉर्ड की तुलना में अधिक होते हैं क्योंकि उन्हें तब डाला जाता है जब AA रिकॉर्ड पेजिंग द्वारा वापस नहीं आते हैं। क्या स्किप और लिमिट उपयुक्त है? मेरे पास खोज के लिए 60 मिलियन दस्तावेजों के क्षेत्र में है।
बेरीम्बोलो

@berimbolo - यह बातचीत के योग्य है - आपको अपना जवाब यहां टिप्पणियों में नहीं मिलेगा। प्रश्न: आप किस व्यवहार की अपेक्षा करते हैं? आप एक लाइव सिस्टम के साथ काम कर रहे हैं, जिसमें रिकॉर्ड बनाए जा रहे हैं और हर समय हटाए जा रहे हैं। यदि आप हर नए पेज लोड के लिए अपने डेटा के लाइव स्नैपशॉट का पुनः अनुरोध करते हैं, तो आपको अपने अंतर्निहित डेटा को बदलने की उम्मीद करनी चाहिए। व्यवहार क्या होना चाहिए? यदि आप "पॉइंट इन टाइम" डेटा स्नैपशॉट के साथ काम करते हैं, तो आपके पास "निश्चित पृष्ठ" होंगे, लेकिन आपके पास "आउट ऑफ़ डेट" डेटा भी होगा। आपके द्वारा बताई गई समस्या कितनी बड़ी है, और लोग इसका सामना कितनी बार करते हैं?
निक ग्रीली

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

5

जब मेरे संग्रह में एक ही क्वेरी में वापसी करने के लिए बहुत बड़ा हो जाता है तो यह समाधान। यह _idक्षेत्र के निहित आदेश का लाभ उठाता है और आपको निर्दिष्ट बैच आकार द्वारा संग्रह के माध्यम से लूप करने की अनुमति देता है।

यहाँ यह एक npm मॉड्यूल के रूप में है, मैंगोज़-पेजिंग , पूर्ण कोड नीचे है:

function promiseWhile(condition, action) {
  return new Promise(function(resolve, reject) {
    process.nextTick(function loop() {
      if(!condition()) {
        resolve();
      } else {
        action().then(loop).catch(reject);
      }
    });
  });
}

function findPaged(query, fields, options, iterator, cb) {
  var Model  = this,
    step     = options.step,
    cursor   = null,
    length   = null;

  promiseWhile(function() {
    return ( length===null || length > 0 );
  }, function() {
    return new Promise(function(resolve, reject) {

        if(cursor) query['_id'] = { $gt: cursor };

        Model.find(query, fields, options).sort({_id: 1}).limit(step).exec(function(err, items) {
          if(err) {
            reject(err);
          } else {
            length  = items.length;
            if(length > 0) {
              cursor  = items[length - 1]._id;
              iterator(items, function(err) {
                if(err) {
                  reject(err);
                } else {
                  resolve();
                }
              });
            } else {
              resolve();
            }
          }
        });
      });
  }).then(cb).catch(cb);

}

module.exports = function(schema) {
  schema.statics.findPaged = findPaged;
};

इसे अपने मॉडल से इस तरह संलग्न करें:

MySchema.plugin(findPaged);

फिर इस तरह प्रश्न करें:

MyModel.findPaged(
  // mongoose query object, leave blank for all
  {source: 'email'},
  // fields to return, leave blank for all
  ['subject', 'message'],
  // number of results per page
  {step: 100},
  // iterator to call on each set of results
  function(results, cb) {
    console.log(results);
    // this is called repeatedly while until there are no more results.
    // results is an array of maximum length 100 containing the
    // results of your query

    // if all goes well
    cb();

    // if your async stuff has an error
    cb(err);
  },
  // function to call when finished looping
  function(err) {
    throw err;
    // this is called once there are no more results (err is null),
    // or if there is an error (then err is set)
  }
);

पता नहीं क्यों इस उत्तर में अधिक उत्थान नहीं है। यह स्किप / लिमिट से अधिक
आकर्षक

मैं भी इस पैकेज द्वारा आया हूं, लेकिन यह स्किप / सीमा और @Scott Hernandez द्वारा प्रदान किए गए उत्तर की तुलना में प्रदर्शन कैसा है?
तनकोम

5
किसी अन्य फ़ील्ड पर सॉर्ट करने के लिए यह उत्तर कैसे काम करेगा?
निक ग्रीली

1

श्रेणी आधारित पेजिंग करने योग्य है, लेकिन आपको क्वेरी के न्यूनतम / अधिकतम होने के बारे में स्मार्ट होने की आवश्यकता है।

यदि आप खर्च कर सकते हैं, तो आपको एक अस्थायी फ़ाइल या संग्रह में किसी क्वेरी के परिणामों को कैशिंग करने की कोशिश करनी चाहिए। MongoDB में TTL संग्रह के लिए धन्यवाद आप अपने परिणाम दो संग्रहों में सम्मिलित कर सकते हैं।

  1. खोज + उपयोगकर्ता + पैरामीटर क्वेरी (TTL जो भी हो)
  2. क्वेरी के परिणाम (टीटीएल जो भी + सफाई अंतराल + 1)

टीटीएल वर्तमान समय के पास होने पर दोनों आश्वासनों का उपयोग करने से आपको आंशिक परिणाम नहीं मिलेंगे। जब आप उस बिंदु पर एक बहुत ही सरल श्रेणी क्वेरी करने के लिए परिणाम संग्रहीत करते हैं तो आप एक साधारण काउंटर का उपयोग कर सकते हैं।


1

आधिकारिक सी # ड्राइवर का उपयोग Userकरके CreatedDate(जहां pageIndexशून्य-आधारित है) दस्तावेजों के आदेश की एक सूची प्राप्त करने का एक उदाहरण यहां दिया गया है ।

public void List<User> GetUsers() 
{
  var connectionString = "<a connection string>";
  var client = new MongoClient(connectionString);
  var server = client.GetServer();
  var database = server.GetDatabase("<a database name>");

  var sortBy = SortBy<User>.Descending(u => u.CreatedDate);
  var collection = database.GetCollection<User>("Users");
  var cursor = collection.FindAll();
  cursor.SetSortOrder(sortBy);

  cursor.Skip = pageIndex * pageSize;
  cursor.Limit = pageSize;
  return cursor.ToList();
}

सभी सॉर्टिंग और पेजिंग ऑपरेशन सर्वर साइड पर किए जाते हैं। हालाँकि यह C # में एक उदाहरण है, मुझे लगता है कि इसे अन्य भाषा पोर्ट पर भी लागू किया जा सकता है।

Http://docs.mongodb.org/ecosystem/tutorial/use-csharp-driver/#modifying-a-cursor-before-enumerating-it देखें ।


0
    // file:ad-hoc.js
    // an example of using the less binary as pager in the bash shell
    //
    // call on the shell by:
    // mongo localhost:27017/mydb ad-hoc.js | less
    //
    // note ad-hoc.js must be in your current directory
    // replace the 27017 wit the port of your mongodb instance
    // replace the mydb with the name of the db you want to query
    //
    // create the connection obj
    conn = new Mongo();

    // set the db of the connection
    // replace the mydb with the name of the db you want to query
    db = conn.getDB("mydb");

    // replace the products with the name of the collection
    // populate my the products collection
    // this is just for demo purposes - you will probably have your data already
    for (var i=0;i<1000;i++ ) {
    db.products.insert(
        [
            { _id: i, item: "lamp", qty: 50, type: "desk" },
        ],
        { ordered: true }
    )
    }


    // replace the products with the name of the collection
    cursor = db.products.find();

    // print the collection contents
    while ( cursor.hasNext() ) {
        printjson( cursor.next() );
    }
    // eof file: ad-hoc.js
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.