Agregación MongoDB: ¿Cómo contar los registros totales?

He usado la agregación para obtener registros de mongodb.

$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), )); 

Si ejecuto esta consulta sin límite, se recuperarán 10 registros. Pero quiero mantener el límite como 2. Así que me gustaría que los registros totales cuenten. ¿Cómo puedo hacer con la agregación? Por favor, avísame. Gracias

Esta es una de las preguntas más frecuentes para obtener el resultado paginado y el número total de resultados simultáneamente en una sola consulta. No puedo explicar cómo me sentí cuando finalmente lo logré LOL.

 $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 ) ) ) ) )) 

El resultado será algo como esto:

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

Use esto para encontrar el conteo total en la colección.

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

Puede usar la función Armar y luego obtener su longitud para contar los registros totales.

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

Lo hice de esta manera:

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

El agregado devolverá la matriz así que solo buclee y obtenga el índice final.

Y otra forma de hacerlo es:

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

Desde v.3.4 (creo) MongoDB ahora tiene un nuevo operador de canalización de agregación llamado ‘ faceta ‘ que en sus propias palabras:

Procesa varias interconexiones de agregación en una sola etapa en el mismo conjunto de documentos de entrada. Cada subproducción tiene su propio campo en el documento de salida donde sus resultados se almacenan como una matriz de documentos.

En este caso particular, esto significa que uno puede hacer algo como esto:

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

El resultado será (con alrededor de 100 resultados totales):

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

La solución provista por @Divergent funciona, pero en mi experiencia es mejor tener 2 consultas:

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

La solución al presionar $$ ROOT y usar $ slice se ejecuta en la limitación de memoria de documentos de 16 MB para colecciones grandes. Además, para grandes colecciones, dos consultas juntas parecen ejecutarse más rápido que el que tiene $$ ROOT empujando. También puedes ejecutarlos en paralelo, por lo que estás limitado solo por la más lenta de las dos consultas (probablemente la que ordena).

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

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

Use el operador de $ count para obtener el recuento total del resultado de la agregación

Consulta :

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

Resultado:

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

Lo siento, pero creo que necesitas dos consultas. Uno para vistas totales y otro para registros agrupados.

Puedes encontrar útil esta respuesta