MongoDB एकत्रीकरण: कुल रिकॉर्ड कैसे प्राप्त करें?


99

मैंने मंगोलोड से रिकॉर्ड लाने के लिए एकत्रीकरण का उपयोग किया है।

$result = $collection->aggregate(array(
  array('$match' => $document),
  array('$group' => array('_id' => '$book_id', 'date' => array('$max' => '$book_viewed'),  'views' => array('$sum' => 1))),
  array('$sort' => $sort),
  array('$skip' => $skip),
  array('$limit' => $limit),
));

अगर मैं इस क्वेरी को बिना सीमा के निष्पादित करता हूं तो 10 रिकॉर्ड प्राप्त होंगे। लेकिन मैं 2 के रूप में सीमा रखना चाहता हूं। इसलिए मैं कुल रिकॉर्डों की गिनती करना चाहता हूं। मैं एकत्रीकरण के साथ कैसे कर सकता हूं? कृपया मुझे सलाह दें। धन्यवाद


यदि परिणाम केवल 2 होते तो क्या दिखता?
वायर्डपाइरी

यह मई मदद $ पहलू पर एक नजर डालें stackoverflow.com/questions/61812361/...
सोहम

जवाबों:


100

यह पृष्ठबद्ध परिणाम और एकल क्वेरी में एक साथ परिणाम की कुल संख्या प्राप्त करने के लिए सबसे अधिक पूछे जाने वाले प्रश्न में से एक है। मैं समझा नहीं सकता कि जब मैंने अंततः इसे प्राप्त किया तो मुझे कैसा लगा।

$result = $collection->aggregate(array(
  array('$match' => $document),
  array('$group' => array('_id' => '$book_id', 'date' => array('$max' => '$book_viewed'),  'views' => array('$sum' => 1))),
  array('$sort' => $sort),

// get total, AND preserve the results
  array('$group' => array('_id' => null, 'total' => array( '$sum' => 1 ), 'results' => array( '$push' => '$$ROOT' ) ),
// apply limit and offset
  array('$project' => array( 'total' => 1, 'results' => array( '$slice' => array( '$results', $skip, $length ) ) ) )
))

परिणाम कुछ इस तरह दिखेगा:

[
  {
    "_id": null,
    "total": ...,
    "results": [
      {...},
      {...},
      {...},
    ]
  }
]

8
इस पर दस्तावेज़ीकरण: docs.mongodb.com/v3.2/reference/operator/aggregation/group/… ... ध्यान दें कि इस दृष्टिकोण के साथ, पूरे गैर-पृष्ठांकित परिणाम सेट को 16MB में फिट होना चाहिए।
btown

7
यह शुद्ध सोना है! मैं इस काम को करने की कोशिश कर रहा था।
हेनरिक मिरांडा

4
धन्यवाद भाई ! मुझे बस जरूरत है { $group: { _id: null, count: { $sum:1 }, result: { $push: '$$ROOT' }}}( {$group:{}}कुल गिनती के लिए डालें) ।
लिबर्टी

1
आप परिणाम सेट की सीमा कैसे लागू करते हैं? परिणाम अब एक नेस्टेड सरणी है
वैलेन

@valen आप कोड की अंतिम पंक्ति देख सकते हैं " 'परिणाम' => सरणी ( '$ टुकड़ा' => सरणी ( '$ परिणाम', $ छोड़, $ लंबाई))" यहाँ आप सीमा लागू करते हैं और छोड़ पैरामीटर कर सकते हैं
अनुराग पारीक

80

V.3.4 (मुझे लगता है) के बाद से MongoDB के पास अब एक नया एकत्रीकरण पाइपलाइन ऑपरेटर है जिसका नाम ' facet ' है जो उनके अपने शब्दों में है:

इनपुट दस्तावेजों के एक ही सेट पर एक ही चरण के भीतर कई एकत्रीकरण पाइपलाइनों को संसाधित करता है। प्रत्येक उप-पाइपलाइन का आउटपुट दस्तावेज़ में अपना क्षेत्र होता है, जहाँ इसके परिणाम दस्तावेज़ों की एक सरणी के रूप में संग्रहीत किए जाते हैं।

इस विशेष मामले में, इसका मतलब है कि कोई इस तरह से कुछ कर सकता है:

$result = $collection->aggregate([
  { ...execute queries, group, sort... },
  { ...execute queries, group, sort... },
  { ...execute queries, group, sort... },
  $facet: {
    paginatedResults: [{ $skip: skipPage }, { $limit: perPage }],
    totalCount: [
      {
        $count: 'count'
      }
    ]
  }
]);

परिणाम होगा (पूर्व कुल 100 परिणामों के लिए):

[
  {
    "paginatedResults":[{...},{...},{...}, ...],
    "totalCount":[{"count":100}]
  }
]

13
यह महान काम करता है, 3.4 के रूप में यह स्वीकृत उत्तर होना चाहिए
एडम रीस

इतने सरल परिणाम को सरल दो फ़ील्ड ऑब्जेक्ट में परिवर्तित करने के लिए मुझे एक और की आवश्यकता है $project?
सर्ज

1
यह अब स्वीकृत उत्तर होना चाहिए। आकर्षण की तरह काम किया।
अरोतिन अघाजरीन

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

57

परिणामी संग्रह में कुल गणना खोजने के लिए इसका उपयोग करें।

db.collection.aggregate( [
{ $match : { score : { $gt : 70, $lte : 90 } } },
{ $group: { _id: null, count: { $sum: 1 } } }
] );

3
धन्यवाद। लेकिन, मैंने अपने कोडिंग में "विचार" का उपयोग इसी समूह की गिनती (यानी, समूह 1 => 2 रिकॉर्ड, समूह 3 => 5 रिकॉर्ड और इतने पर) प्राप्त करने के लिए किया है। मैं रिकॉर्ड्स गिनना चाहता हूं (यानी, कुल: 120 रिकॉर्ड)। आशा है आप समझ गए होंगे ..
user2987836

33

आप ऐरे फ़ंक्शन का उपयोग कर सकते हैं और फिर कुल रिकॉर्ड संख्या के लिए इसकी लंबाई प्राप्त कर सकते हैं।

db.CollectionName.aggregate([....]).toArray().length

1
हालांकि यह "उचित" समाधान के रूप में काम नहीं कर सकता है, इससे मुझे कुछ डिबग करने में मदद मिली - यह काम करता है, भले ही यह 100% समाधान न हो।
जोहान मार्क्स

3
यह वास्तविक समाधान नहीं है।
फुरकान बैसारन

1
TypeError: Parent.aggregate(...).toArray is not a functionयह त्रुटि है जो मैंने इस समाधान के साथ दी है।
मोहम्मद होसैन शोअज़िनिया

धन्यवाद। यह वही है जिसे मैं देख रहा था।
skvp

यह सभी एकत्रित डेटा को प्राप्त करेगा और फिर उस सरणी की लंबाई लौटाएगा। एक अच्छा अभ्यास नहीं है। इसके बजाय आप एकत्रीकरण पाइपलाइन में {$ गिनती: 'गिनती'} जोड़ सकते हैं
असलम शेख

18

कुल दस्तावेज़ संख्या प्राप्त करने के लिए $ गिनती एकत्रीकरण पाइपलाइन चरण का उपयोग करें :

प्रश्न:

db.collection.aggregate(
  [
    {
      $match: {
        ...
      }
    },
    {
      $group: {
        ...
      }
    },
    {
      $count: "totalCount"
    }
  ]
)

परिणाम:

{
   "totalCount" : Number of records (some integer value)
}

यह एक आकर्षण की तरह काम करता है, लेकिन प्रदर्शन-वार क्या यह अच्छा है?
ana.arede

साफ समाधान। धन्यवाद
skvp

13

मैंने इसे इस तरह किया:

db.collection.aggregate([
     { $match : { score : { $gt : 70, $lte : 90 } } },
     { $group: { _id: null, count: { $sum: 1 } } }
] ).map(function(record, index){
        print(index);
 });

एग्रीगेट सरणी को लौटा देगा, इसलिए इसे लूप करें और अंतिम इंडेक्स प्राप्त करें।

और यह करने का अन्य तरीका है:

var count = 0 ;
db.collection.aggregate([
{ $match : { score : { $gt : 70, $lte : 90 } } },
{ $group: { _id: null, count: { $sum: 1 } } }
] ).map(function(record, index){
        count++
 }); 
print(count);

fwiw आपको varघोषणा की आवश्यकता नहीं है और न ही mapकॉल की। आपके पहले उदाहरण की पहली 3 पंक्तियाँ पर्याप्त हैं।
मदब्रिक्स 23

7

@Divergent द्वारा दिया गया समाधान काम करता है, लेकिन मेरे अनुभव में 2 प्रश्नों का होना बेहतर है:

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

$ $ ROOT धकेलने के साथ समाधान और बड़े संग्रह के लिए $ 16MB की दस्तावेज़ स्मृति सीमा में $ स्लाइस का उपयोग करना। इसके अलावा, बड़े संग्रह के लिए दो क्वेरीज़ $$ ROOT पुश के साथ एक से अधिक तेज़ी से चलने लगती हैं। आप उन्हें समानांतर में भी चला सकते हैं, इसलिए आप केवल दो प्रश्नों के धीरज (शायद एक तरह से) तक सीमित हैं।

मैंने 2 प्रश्नों और एकत्रीकरण ढांचे का उपयोग करके इस समाधान के साथ समझौता किया है (नोट - मैं इस उदाहरण में नोड.जेएस का उपयोग करता हूं, लेकिन विचार एक ही है):

var aggregation = [
  {
    // If you can match fields at the begining, match as many as early as possible.
    $match: {...}
  },
  {
    // Projection.
    $project: {...}
  },
  {
    // Some things you can match only after projection or grouping, so do it now.
    $match: {...}
  }
];


// Copy filtering elements from the pipeline - this is the same for both counting number of fileter elements and for pagination queries.
var aggregationPaginated = aggregation.slice(0);

// Count filtered elements.
aggregation.push(
  {
    $group: {
      _id: null,
      count: { $sum: 1 }
    }
  }
);

// Sort in pagination query.
aggregationPaginated.push(
  {
    $sort: sorting
  }
);

// Paginate.
aggregationPaginated.push(
  {
    $limit: skip + length
  },
  {
    $skip: skip
  }
);

// I use mongoose.

// Get total count.
model.count(function(errCount, totalCount) {
  // Count filtered.
  model.aggregate(aggregation)
  .allowDiskUse(true)
  .exec(
  function(errFind, documents) {
    if (errFind) {
      // Errors.
      res.status(503);
      return res.json({
        'success': false,
        'response': 'err_counting'
      });
    }
    else {
      // Number of filtered elements.
      var numFiltered = documents[0].count;

      // Filter, sort and pagiante.
      model.request.aggregate(aggregationPaginated)
      .allowDiskUse(true)
      .exec(
        function(errFindP, documentsP) {
          if (errFindP) {
            // Errors.
            res.status(503);
            return res.json({
              'success': false,
              'response': 'err_pagination'
            });
          }
          else {
            return res.json({
              'success': true,
              'recordsTotal': totalCount,
              'recordsFiltered': numFiltered,
              'response': documentsP
            });
          }
      });
    }
  });
});

5
//const total_count = await User.find(query).countDocuments();
//const users = await User.find(query).skip(+offset).limit(+limit).sort({[sort]: order}).select('-password');
const result = await User.aggregate([
  {$match : query},
  {$sort: {[sort]:order}},
  {$project: {password: 0, avatarData: 0, tokens: 0}},
  {$facet:{
      users: [{ $skip: +offset }, { $limit: +limit}],
      totalCount: [
        {
          $count: 'count'
        }
      ]
    }}
  ]);
console.log(JSON.stringify(result));
console.log(result[0]);
return res.status(200).json({users: result[0].users, total_count: result[0].totalCount[0].count});

1
कोड उत्तर के साथ व्याख्यात्मक पाठ को शामिल करना आमतौर पर अच्छा अभ्यास है।

3

यह कई मैच स्थितियों के लिए काम कर सकता है

            const query = [
                {
                    $facet: {
                    cancelled: [
                        { $match: { orderStatus: 'Cancelled' } },
                        { $count: 'cancelled' }
                    ],
                    pending: [
                        { $match: { orderStatus: 'Pending' } },
                        { $count: 'pending' }
                    ],
                    total: [
                        { $match: { isActive: true } },
                        { $count: 'total' }
                    ]
                    }
                },
                {
                    $project: {
                    cancelled: { $arrayElemAt: ['$cancelled.cancelled', 0] },
                    pending: { $arrayElemAt: ['$pending.pending', 0] },
                    total: { $arrayElemAt: ['$total.total', 0] }
                    }
                }
                ]
                Order.aggregate(query, (error, findRes) => {})

2

मुझे एकत्रीकरण लागू करने के बाद पूर्ण कुल गणना की आवश्यकता थी। यह मेरे लिए काम किया:

db.mycollection.aggregate([
    {
        $group: { 
            _id: { field1: "$field1", field2: "$field2" },
        }
    },
    { 
        $group: { 
            _id: null, count: { $sum: 1 } 
        } 
    }
])

परिणाम:

{
    "_id" : null,
    "count" : 57.0
}

2

MongoDB एकत्रीकरण करते समय कुल रिकॉर्ड संख्या प्राप्त करने के कुछ तरीके यहां दिए गए हैं:


  • का उपयोग कर $count:

    db.collection.aggregate([
       // Other stages here
       { $count: "Total" }
    ])

    1000 रिकॉर्ड प्राप्त करने के लिए यह औसतन 2 एमएस है और सबसे तेज़ तरीका है।


  • का उपयोग कर .toArray():

    db.collection.aggregate([...]).toArray().length

    1000 रिकॉर्ड प्राप्त करने के लिए यह औसतन 18 एमएस लेता है।


  • का उपयोग कर .itcount():

    db.collection.aggregate([...]).itcount()

    1000 रिकॉर्ड प्राप्त करने के लिए यह औसतन 14 एमएस लेता है।


0

क्षमा करें, लेकिन मुझे लगता है कि आपको दो प्रश्नों की आवश्यकता है। कुल विचारों के लिए एक और समूहीकृत रिकॉर्ड के लिए एक अन्य।

आप इस उत्तर को उपयोगी पा सकते हैं


धन्यवाद..मुझे ऐसा लगता है..लेकिन, एकत्रीकरण का कोई विकल्प नहीं है .. :(
user2987836

1
मैं एक समान स्थिति में भाग गया। 2 क्वेरी करने के अलावा कोई जवाब नहीं था। :( stackoverflow.com/questions/20113731/…
astroanu

0

यदि आप समूह बनाना नहीं चाहते हैं, तो निम्न विधि का उपयोग करें:

db.collection.aggregate( [ { $match : { score : { $gt : 70, $lte : 90 } } }, { $count: 'count' } ] );


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