MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

MongoDB - Framework di aggregazione (conteggio totale)

Esiste una soluzione che utilizza push and slice:https://stackoverflow.com/a/39784851/4752635 (@emaniacs lo menziona anche qui).

Ma preferisco usare 2 query. La soluzione con il push di $$ROOT e l'utilizzo di $slice comporta una limitazione della memoria del documento di 16 MB per raccolte di grandi dimensioni. Inoltre, per raccolte di grandi dimensioni, due query insieme sembrano essere più veloci di quella con push $$ROOT. Puoi anche eseguirli in parallelo, quindi sei limitato solo dalla più lenta delle due query (probabilmente quella che ordina).

  1. Prima per filtrare e poi raggruppare per ID per ottenere il numero di elementi filtrati. Non filtrare qui, non è necessario.
  2. Seconda query che filtra, ordina e impagina.

Ho risolto con questa soluzione utilizzando 2 query e framework di aggregazione (nota:in questo esempio uso 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
            });
          }
      });
    }
  });
});