মঙ্গুজ সীমা / অফসেট এবং গণনা কোয়েরি


87

ক্যোয়ারী পারফরম্যান্সে একটি বিজোড়ের বিট ... আমার একটি ক্যোয়ারী চালানো দরকার যা ডকুমেন্টের মোট গণনা করে এবং সীমাবদ্ধ এবং অফসেট হতে পারে এমন একটি ফলাফল সেটও ফেরত দিতে পারে।

সুতরাং, আমার কাছে মোট 57 টি নথি রয়েছে এবং ব্যবহারকারী 20 দ্বারা অফসেটে 10 টি নথি চান।

আমি এটি করার 2 টি উপায় সম্পর্কে ভাবতে পারি, প্রথমে সমস্ত 57 টি নথির সন্ধান করা (অ্যারে হিসাবে প্রত্যাবর্তন করা), তারপরে অ্যারে ব্যবহার করে l স্লাইস তারা যে নথিগুলি চায় তা ফেরত দেয়। দ্বিতীয় বিকল্পটি হল 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()পিএইচপি-তে ডিফল্ট ফাংশন গ্রহণ না করে limitবা skipবিবেচনায় না নেওয়া হয় যদি না বলা হয় কেবল সীমাবদ্ধতার বিষয়ে একটি ক্যোয়ারী চালিয়ে যাওয়া এবং তারপরে গণনা পাওয়া সম্ভবত এখানে সবচেয়ে পারফরম্যান্ট সমাধান দেওয়া উচিত। তবে আপনি কীভাবে সেখানে 57 টি ডকুমেন্ট রাখবেন যদি আপনি বর্তমানে কী আছে তা গণনা করতে দুটি প্রশ্ন না করেন? আপনার কি স্থির নম্বর আছে যা কখনই বদলায় না? যদি না হয় তবে আপনাকে স্কিপ এবং সীমা উভয়ই করতে হবে তবে গণনা।
সামায়ায়ে

দুঃখিত, আমি db.collection.find(<query>).count();
মঙ্গোর

দুঃখিত এটি আমিই ছিলাম, আমি আপনার প্রশ্নটি ভুল করে পড়েছি। হুমমম আসলে আমি নিশ্চিত নই যেটি আরও ভাল হবে, আপনার ফলাফলটি কি সর্বদা 57 ডক্সের মতো কম থাকবে? যদি তা হয় তবে ক্লায়েন্টের সাইড স্লাইস আরও এক মিলিসেকেন্ড আরও অভিনয় করতে পারে be
সামায়ায়ে

আমি মূল প্রশ্নের উদাহরণ যোগ করেছি, আমি মনে করি না যে ডেটা 10,000++ এর বেশি হয়ে যাবে তবে সম্ভবত এটি সম্ভব হতে পারে।
লেপিওয়েল

10k রেকর্ড এ আপনি পারে দেখতে মেমরির জাতীয় এর হ্যান্ডলিং কম performant হতে count()MongoDB এর ফাংশন। count()MongoDB ফাংশন তুলনামূলকভাবে ধীর কিন্তু এটি এখনও বৃহত্তর সেটে সবচেয়ে ক্লায়েন্ট সাইড বৈচিত্র যেমন প্রায় কাছাকাছি হিসাবে দ্রুত এবং এটি দ্রুত ক্লায়েন্ট সাইড কাউন্টিং চেয়ে এখানে সম্ভবত হতে পারে। তবে সেই অংশটি আপনার নিজের পরীক্ষার সাপেক্ষে। মনে মনে আমি 10k দৈর্ঘ্যের অ্যারেগুলি সহজেই গণনা করেছি যাতে এটি ক্লায়েন্টের পক্ষে আরও দ্রুত হতে পারে, 10 কে উপাদানগুলিতে এটি বলা খুব শক্ত hard
সম্মেয়ে

উত্তর:


133

আমি আপনাকে 2 টি ক্যোয়ারী ব্যবহার করার পরামর্শ দিচ্ছি:

  1. db.collection.count()আইটেমের মোট সংখ্যা প্রদান করবে। এই মানটি কোথাও মঙ্গোতে সংরক্ষিত আছে এবং এটি গণনা করা হয় না।

  2. db.collection.find().skip(20).limit(10)এখানে আমি ধরে নিচ্ছি যে আপনি কোনও ক্ষেত্রের দ্বারা বাছাই করতে পারেন, তাই এই ক্ষেত্রে একটি সূচক যুক্ত করতে ভুলবেন না। এই প্রশ্নটিও দ্রুত হবে।

আমি মনে করি যে আপনি সমস্ত আইটেম জিজ্ঞাসা করা উচিত নয় এবং এড়িয়ে চলা এবং গ্রহণ করা, পরে কারণ যখন আপনার কাছে বড় ডেটা হয় তথ্য স্থানান্তর এবং প্রক্রিয়াজাতকরণে আপনার সমস্যা হবে।


4
আমি যেটি লিখছি তা কোনও প্রতিক্রিয়া ছাড়াই কেবল একটি মন্তব্য তবে আমি শুনেছি যে .skip()নির্দেশটি সিপিইউর পক্ষে ভারী কারণ এটি সংগ্রহের শুরুতে যায় এবং এর প্যারামিটারে নির্দিষ্ট মানটি পেতে পারে .skip()। এটি বড় সংগ্রহের উপর সত্যিকারের প্রভাব ফেলতে পারে! তবে আমি জানি না যে কোনটি ব্যবহারের মধ্যে সবচেয়ে বেশি ভারী .skip()বা পুরো সংগ্রহটি পান এবং জেএসের সাথে ছাঁটাই করুন ... আপনি কী ভাবেন?
জাকারি দহন

4
@ স্টাফিক্স আমি ব্যবহার সম্পর্কে একই উদ্বেগ শুনেছি .skip()। এই উত্তরটি এতে স্পর্শ করে এবং একটি তারিখের ক্ষেত্রে ফিল্টার ব্যবহার করার পরামর্শ দেয়। কেউ এটিকে .skip()& .take()পদ্ধতিগুলির সাথে ব্যবহার করতে পারেন । এটি একটি ভাল ধারণা মত মনে হচ্ছে। তবে, কীভাবে মোট ডকুমেন্টগুলির একটি গণনা পাবেন সে সম্পর্কে এই ওপি-র প্রশ্নে আমি সমস্যায় পড়ছি। যদি কোনও ফিল্টার যদি পারফরম্যান্সের প্রভাবগুলিকে মোকাবেলা করতে ব্যবহৃত হয়, তবে .skip()আমরা কীভাবে একটি সঠিক গণনা রাখতে পারি? ডিবিতে সঞ্চিত গণনা আমাদের ফিল্টার করা ডেটা সেটকে প্রতিফলিত করবে না।
মাইকেল লিনোস

হাই @ মিশেললিনোস, আমি একই সমস্যার মুখোমুখি হচ্ছি: অর্থাত্ কীভাবে মোট নথির একটি গণনা পাবেন। যদি একটি ফিল্টার ব্যবহার করা হয় তবে আমাদের সঠিক গণনা কীভাবে হবে? আপনি কি এর সমাধান পেয়েছেন?
বীরশা

@ ভাইরাশা, cursor.count()ফিল্টার করা ডকুমেন্টের সংখ্যা ফেরত দিতে ব্যবহার করুন (এটি কোয়েরি কার্যকর করবে না এটি আপনাকে মিলিত ডকসের সংখ্যা ফিরিয়ে দেবে)। আপনি ফিল্টার এবং অর্ডার বৈশিষ্ট্যগুলি সূচিবদ্ধ এবং সব কিছু ঠিক আছে তা নিশ্চিত করুন।
ব্যবহারকারী 854301

@virsha cursor.count()@ ব্যবহারকারী 854301 হিসাবে নির্দেশিত হিসাবে কাজ করা উচিত। যাইহোক, আমি যা করতে পেরেছি তা আমার API ( /api/my-colllection/stats) -এ একটি অন্তিম পয়েন্ট যুক্ত করছে যা আমি মঙ্গুজের db.collection.stats বৈশিষ্ট্যটি ব্যবহার করে আমার সংগ্রহগুলিতে বিভিন্ন পরিসংখ্যান ফিরিয়েছিলাম । যেহেতু আমার সত্যিই কেবল আমার ফ্রন্ট-এন্ডের জন্য এটির প্রয়োজন ছিল, তাই আমি আমার সার্ভার-সাইড পৃষ্ঠাগুলি থেকে স্বাধীনভাবে তথ্যটি ফিরিয়ে দিতে শেষ পয়েন্টটি জিজ্ঞাসা করেছি।
মাইকেল লানোস

20

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/references/operator/aggregation/facet/ এ একবার দেখুন


2

এই সমস্যাটি নিজেই মোকাবেলা করার পরে, আমি ব্যবহারকারীর 8543030 এর উত্তরটি তৈরি করতে চাই।

মঙ্গুজ ^ 4.13.8 আমি একটি ফাংশন ডাকে ব্যবহার করতে সক্ষম হয়েছি toConstructor() যা ফিল্টার প্রয়োগ করা হলে আমাকে একাধিকবার ক্যোয়ারী তৈরি করা এড়াতে দেয়। আমি জানি যে এই ফাংশনটি পুরানো সংস্করণগুলিতেও উপলব্ধ। তবে এটি নিশ্চিত করতে আপনাকে মঙ্গুজ ডক্স পরীক্ষা করতে হবে।

নিম্নলিখিত ব্লুবার্ড প্রতিশ্রুতি ব্যবহার করে:

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. মোট_সংখ্যক সন্ধান করুন
  3. রেকর্ড এড়িয়ে যান
  4. এবং কোয়েরিতে আমাদের চাহিদা অনুযায়ী ডেটা পুনরায় আকার দিতে পারে।

4
দয়া করে আপনার উত্তরে কিছু ব্যাখ্যা যুক্ত করুন যাতে অন্যরা এটি থেকে শিখতে পারে
নিকো হেস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.