MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

MongoDB – Aggregation Framework (Gesamtzahl)

Es gibt eine Lösung mit Push and Slice:https://stackoverflow.com/a/39784851/4752635 (@emaniacs erwähnt es hier auch).

Aber ich bevorzuge die Verwendung von 2 Abfragen. Die Lösung mit dem Pushen von $$ROOT und der Verwendung von $slice führt bei großen Sammlungen zu einer Begrenzung des Dokumentenspeichers von 16 MB. Außerdem scheinen bei großen Sammlungen zwei Abfragen zusammen schneller zu laufen als die mit $$ROOT-Pushing. Sie können sie auch parallel ausführen, sodass Sie nur durch die langsamere der beiden Abfragen (wahrscheinlich diejenige, die sortiert) eingeschränkt sind.

  1. Zuerst zum Filtern und dann zum Gruppieren nach ID, um die Anzahl der gefilterten Elemente zu erhalten. Filtern Sie hier nicht, das ist unnötig.
  2. Zweite Abfrage, die filtert, sortiert und paginiert.

Ich habe mich mit dieser Lösung unter Verwendung von 2 Abfragen und einem Aggregationsframework niedergelassen (Hinweis:Ich verwende in diesem Beispiel node.js):

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
            });
          }
      });
    }
  });
});