MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

MongoDB - Cadre d'agrégation (nombre total)

Il existe une solution utilisant push et slice :https://stackoverflow.com/a/39784851/4752635 (@emaniacs le mentionne également ici).

Mais je préfère utiliser 2 requêtes. La solution consistant à pousser $$ROOT et à utiliser $slice se heurte à une limitation de mémoire de document de 16 Mo pour les grandes collections. De plus, pour les grandes collections, deux requêtes ensemble semblent s'exécuter plus rapidement que celle avec $$ROOT poussant. Vous pouvez également les exécuter en parallèle, vous n'êtes donc limité que par la plus lente des deux requêtes (probablement celle qui trie).

  1. D'abord pour le filtrage, puis le regroupement par ID pour obtenir le nombre d'éléments filtrés. Ne filtrez pas ici, c'est inutile.
  2. Deuxième requête qui filtre, trie et pagine.

Je me suis installé avec cette solution en utilisant 2 requêtes et un framework d'agrégation (note - j'utilise node.js dans cet exemple):

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