sql >> Base de Datos >  >> NoSQL >> MongoDB

MongoDB:marco de agregación (recuento total)

Hay una solución usando push and slice:https://stackoverflow.com/a/39784851/4752635 (@emaniacs lo menciona aquí también).

Pero prefiero usar 2 consultas. La solución con empujar $$ROOT y usar $slice se encuentra con una limitación de memoria de documentos de 16 MB para colecciones grandes. Además, para colecciones grandes, dos consultas juntas parecen ejecutarse más rápido que la que tiene $$ROOT empujando. También puede ejecutarlas en paralelo, por lo que solo está limitado por la más lenta de las dos consultas (probablemente la que ordena).

  1. Primero para filtrar y luego agrupar por ID para obtener el número de elementos filtrados. No filtre aquí, es innecesario.
  2. Segunda consulta que filtra, ordena y pagina.

Me he conformado con esta solución usando 2 consultas y un marco de agregación (nota:uso node.js en este ejemplo):

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