programing

MongoDB 집약:총 레코드 수를 어떻게 얻습니까?

stoneblock 2023. 3. 20. 21:23

MongoDB 집약:총 레코드 수를 어떻게 얻습니까?

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로 제한을 유지하고 싶다.그래서 나는 총 기록 수를 알고 싶다.집약을 어떻게 해야 합니까?조언해 주세요.감사해요.

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}]
  }
]

이것은 단일 쿼리에서 페이지화된 결과와 총 결과 수를 동시에 얻기 위해 가장 자주 묻는 질문 중 하나입니다.드디어 달성했을 때의 기분을 설명할 수 없어요(웃음).

$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": [
      {...},
      {...},
      {...},
    ]
  }
]

이를 통해 결과 컬렉션의 총 개수를 찾을 수 있습니다.

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

toArray 함수를 사용하여 총 레코드 수에 대한 길이를 얻을 수 있습니다.

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

다음은 MongoDB Aggregation 실행 시 총 레코드 수를 가져오는 몇 가지 방법입니다.


  • 사용방법:

    db.collection.aggregate([
       // Other stages here
       { $count: "Total" }
    ])
    

    1000개의 레코드를 취득하기 위해서는 평균 2ms가 소요되며 이것이 가장 빠른 방법입니다.


  • 사용방법:

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

    1000개의 레코드를 취득하는 데 걸리는 시간은 평균 18밀리초입니다.


  • 사용방법:

    db.collection.aggregate([...]).itcount()
    

    1000개의 레코드를 취득하기 위해서는 평균 14밀리초가 걸립니다.

$count 집계 파이프라인 단계를 사용하여 총 문서 수를 가져옵니다.

쿼리:

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

결과:

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

나는 이렇게 했다:

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

@Divergent에서 제공하는 솔루션은 작동하지만, 제 경험으로는 다음 두 가지 질문을 받는 것이 좋습니다.

  1. 먼저 필터링을 수행한 후 ID별로 그룹화하여 필터링된 요소의 수를 가져옵니다.여기서 필터링하지 마세요, 불필요합니다.
  2. 필터링, 정렬 및 페이지 매김을 수행하는 두 번째 쿼리입니다.

$$ROOT를 푸시하고 $slice를 사용하는 솔루션은 대규모 컬렉션의 경우 문서 메모리 제한이 16MB에 달합니다.또한 대규모 컬렉션의 경우 $$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
            });
          }
      });
    }
  });
});

이는 여러 일치 조건에 사용할 수 있습니다.

            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) => {})

집계를 적용한 후 절대 총계수가 필요했습니다.이 방법은 효과가 있었습니다.

db.mycollection.aggregate([
    {
        $group: { 
            _id: { field1: "$field1", field2: "$field2" },
        }
    },
    { 
        $group: { 
            _id: null, count: { $sum: 1 } 
        } 
    }
])

결과:

{
    "_id" : null,
    "count" : 57.0
}

그룹화하지 않을 경우 다음 방법을 사용합니다.

db.collection.aggregate( [ { $match : { score : { $gt : 70, $lte : 90 } } }, { $count: 'count' } ] );

다음은 mongoose aggregate에 페이지화, 일치 및 정렬을 사용한 예입니다.

const [response] = await Prescribers.aggregate([
      { $match: searchObj },
      { $sort: sortObj },
      {
        $facet: {
          response: [{ $skip: count * page }, { $limit: count }],
          pagination: [
            {
              $count: 'totalDocs',
            },
            {
              $addFields: {
                page: page + 1,
                totalPages: {
                  $floor: {
                    $divide: ['$totalDocs', count],
                  },
                },
              },
            },
          ],
        },
      },
    ]);

여기서 개수는 각 페이지의 제한이며 페이지는 페이지 번호입니다.처방전이 모델입니다.

이렇게 하면 이와 유사한 레코드가 반환됩니다.

"data": {
    "response": [
        {
            "_id": "6349308c90e58c6820bbc682",
            "foo": "bar"
        }
        {
            "_id": "6349308c90e58c6820bbc682",
            "foo": "bar"
        },
        {
            "_id": "6349308c90e58c6820bbc682",
            "foo": "bar"
        }
        {
            "_id": "6349308c90e58c6820bbc682",
            "foo": "bar"
        },
        {
            "_id": "6349308c90e58c6820bbc682",
            "foo": "bar"
        },
        {
            "_id": "6349308c90e58c6820bbc682",
            "foo": "bar"
        }
        {
            "_id": "6349308c90e58c6820bbc682",
            "foo": "bar"
        },
        {
            "_id": "6349308c90e58c6820bbc682",
            "foo": "bar"
        }
        {
            "_id": "6349308c90e58c6820bbc682",
            "foo": "bar"
        },
        {
            "_id": "6349308c90e58c6820bbc682",
            "foo": "bar"
        },
    ],
    "pagination": [
        {
            "totalDocs": 592438,
            "page": 1,
            "totalPages": 59243
        }
    ]
}

죄송하지만 두 가지 질문이 필요한 것 같습니다.하나는 전체 보기용이고 다른 하나는 그룹화된 레코드용입니다.

답변은 도움이 됩니다.

중첩된 문서와 $일치해야 하는 경우

https://mongoplayground.net/p/DpX6cFhR_mm

db.collection.aggregate([
  {
    "$unwind": "$tags"
  },
  {
    "$match": {
      "$or": [
        {
          "tags.name": "Canada"
        },
        {
          "tags.name": "ABC"
        }
      ]
    }
  },
  {
    "$group": {
      "_id": null,
      "count": {
        "$sum": 1
      }
    }
  }
])

조회하고 대조한 다음 받은 문서를 세어야 했습니다.mongoose를 사용한 방법은 다음과 같습니다.

ModelName.aggregate([
  {
    '$lookup': {
      'from': 'categories', 
      'localField': 'category', 
      'foreignField': '_id', 
      'as': 'category'
    }
  }, {
    '$unwind': {
      'path': '$category'
    }
  }, {
    '$match': {
      'category.price': {
        '$lte': 3, 
        '$gte': 0
      }
    }
  }, {
    '$count': 'count'
  }
]);

언급URL : https://stackoverflow.com/questions/20348093/mongodb-aggregation-how-to-get-total-records-count