Подтвердить что ты не робот

MongoDB Aggregation: Как получить общее количество записей?

Я использовал агрегацию для извлечения записей из 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),
));

Если я выполню этот запрос без ограничений, вы получите 10 записей. Но я хочу сохранить лимит как 2. Поэтому я хотел бы получить общее количество записей. Как я могу сделать с агрегацией? Пожалуйста, посоветуйте мне. Благодаря

4b9b3361

Ответ 1

Это один из наиболее часто задаваемых вопросов для получения разбитого на результат результата и общего количества результатов одновременно в одном запросе. Я не могу объяснить, как я себя чувствовал, когда я, наконец, достиг этого 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 ) ) ) )
))

Результат будет выглядеть примерно так:

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

Ответ 2

Начиная с v.3.4 (я думаю), у MongoDB появился новый оператор конвейера агрегации с именем ' facet ', который, по их собственным словам:

Обрабатывает несколько конвейеров агрегации на одном этапе в одном наборе входных документов. Каждый субпроводник имеет свое поле в выходном документе, где его результаты хранятся в виде массива документов.

В данном конкретном случае это означает, что можно сделать что-то вроде этого:

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

Результат будет (например, из 100 результатов):

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

Ответ 3

Используйте это, чтобы найти общее количество в коллекции.

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

Ответ 4

Вы можете использовать функцию toArray, а затем получить ее длину для общего количества записей.

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

Ответ 5

Я сделал это следующим образом:

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

Агрегат вернет массив, поэтому просто зациклируйте его и получите окончательный индекс.

И другой способ сделать это:

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

Ответ 6

Используйте этап конвейера агрегации $ count, чтобы получить общее количество документов:

Запрос:

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

Результат:

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

Ответ 7

Решение, предоставляемое @Divergent, работает, но по моему опыту лучше иметь 2 запроса:

  • Сначала для фильтрации, а затем группировки по идентификатору, чтобы получить количество фильтрованных элементов. Не фильтруйте здесь, это не нужно.
  • Второй запрос, который фильтрует, сортирует и разбивает страницы.

Решение с нажатием $$ ROOT и использованием $slice работает с ограничением памяти документа 16 МБ для больших коллекций. Кроме того, для больших коллекций два запроса вместе работают быстрее, чем один с нажатием $$ ROOT. Вы можете запускать их параллельно, поэтому вы ограничены только медленными двумя запросами (возможно, теми, которые сортируются).

Я решил использовать это решение, используя 2 запроса и структуру агрегации (примечание - я использую 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
            });
          }
      });
    }
  });
});

Ответ 8

Это может работать для нескольких условий совпадения

            const query = [
                {
                    $facet: {
                    cancelled: [
                        { $match: { orderStatus: 'Cancelled' } },
                        { $count: 'cancelled' }
                    ],
                    pending: [
                        { $match: { orderStatus: 'Pending' } },
                        { $count: 'pending' }
                    ],
                    total: [
                        { $match: { isActive: true } },
                        { $count: 'total' }
                    ]
                    }
                },
                {
                    $project: {
                    cancelled: { $arrayElemAt: ['$cancelled.cancelled', 0] },
                    pending: { $arrayElemAt: ['$pending.pending', 0] },
                    total: { $arrayElemAt: ['$total.total', 0] }
                    }
                }
                ]
                Order.aggregate(query, (error, findRes) => {})

Ответ 9

Извините, но я думаю, вам нужны два запроса. Один для общих просмотров и другой для сгруппированных записей.

Вы можете найти полезный этот ответ

Ответ 10

//const total_count = await User.find(query).countDocuments();
//const users = await User.find(query).skip(+offset).limit(+limit).sort({[sort]: order}).select('-password');
const result = await User.aggregate([
  {$match : query},
  {$sort: {[sort]:order}},
  {$project: {password: 0, avatarData: 0, tokens: 0}},
  {$facet:{
      users: [{ $skip: +offset }, { $limit: +limit}],
      totalCount: [
        {
          $count: 'count'
        }
      ]
    }}
  ]);
console.log(JSON.stringify(result));
console.log(result[0]);
return res.status(200).json({users: result[0].users, total_count: result[0].totalCount[0].count});