Aggregazione MongoDB: come ottenere il totale dei record?

Ho usato l’aggregazione per il recupero di record da 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), )); 

Se eseguo questa query senza limiti, verranno recuperati 10 record. Ma voglio mantenere il limite come 2. Quindi vorrei contare i record totali. Come posso fare con l’aggregazione? Per favore mi consigli Grazie

Questa è una delle domande più frequenti per ottenere contemporaneamente il risultato impaginato e il numero totale di risultati in una singola query. Non riesco a spiegare come mi sono sentito quando finalmente l’ho raggiunto. 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 ) ) ) ) )) 

Il risultato sarà simile a questo:

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

Usalo per trovare il conteggio totale nella collezione.

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

È ansible utilizzare la funzione toArray e quindi ottenere la sua durata per il conteggio totale dei record.

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

L’ho fatto in questo modo:

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

L’aggregato restituirà l’array in modo da eseguirne il ciclo e ottenere l’indice finale.

E un altro modo per farlo è:

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

Dal momento che v.3.4 (credo) MongoDB ha ora un nuovo operatore di pipeline di aggregazioni chiamato ” facet ” che nelle loro stesse parole:

Elabora più pipeline di aggregazione in un’unica fase sullo stesso set di documenti di input. Ciascuna sotto-pipeline ha il proprio campo nel documento di output in cui i suoi risultati sono archiviati come una matrice di documenti.

In questo caso particolare, questo significa che si può fare qualcosa del genere:

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

Il risultato sarà (con ex 100 risultati totali):

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

La soluzione fornita da @Divergent funziona, ma nella mia esperienza è meglio avere 2 query:

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

La soluzione con la spinta di $$ ROOT e l’utilizzo di $ slice viene eseguita nella limitazione della memoria dei documenti di 16 MB per le raccolte di grandi dimensioni. Inoltre, per le raccolte di grandi dimensioni, due query insieme sembrano essere più veloci di quelle con $$ ROOT push. Puoi eseguirli anche in parallelo, quindi sei limitato solo dal più lento tra le due query (probabilmente quella che ordina).

Ho risolto questa soluzione utilizzando 2 query e framework di aggregazione (nota: in questo esempio utilizzo node.js, ma l’idea è la stessa):

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

Utilizzare l’operatore $ count per ottenere il conteggio totale del risultato dell’aggregazione

Domanda:

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

Risultato:

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

Scusa, ma penso che tu abbia bisogno di due domande. Uno per le visualizzazioni totali e un altro per i record raggruppati.

Puoi trovare utile questa risposta