HOWTO · MongoDB
MongoDB의 레코드 수 계산
MongoDB에서 전체 문서와 필터에 맞는 문서 수를 세고, 그룹별 개수를 정렬하며, 날짜별 개수를 구하는 방법과 커서 결과를 다루는 방법을 알아봅니다.
이 페이지의 내용
MongoDB에서 조건에 맞는 문서 수를 세려면 mongosh에서 db.collection.countDocuments(filter)를 사용하거나, 집계 파이프라인에서 $match 다음에 $count를 배치합니다. 전체 문서 수를 세려면 db.collection.countDocuments({})를 사용합니다. 필드나 날짜별 그룹화처럼 더 큰 쿼리의 한 단계로 개수를 세야 한다면 집계가 유용합니다.
다음 예제를 실행하세요.
scores = [88, 92, 97, 71, 79, 83]
passing = [score for score in scores if score > 80]
print(len(passing))
4
이 로컬 Python 예제는 작은 목록에 같은 필터를 적용하며 MongoDB에는 연결하지 않습니다. MongoDB 예제는 아래 집계 섹션에 나옵니다.
MongoDB 작업
MongoDB는 컬렉션 안에 문서 형태로 데이터를 저장합니다. 문서 수를 세는 작업은 읽기 작업입니다. 컬렉션을 선택하고 필요하면 필터를 적용한 다음, 개수나 그룹별 결과를 반환합니다. 예제에서는 시험 결과에 scores를, 날짜가 있는 거래에 sales를 사용합니다. 실제 데이터베이스에 맞게 컬렉션 이름과 필드 이름을 바꾸세요.
MongoDB 집계
집계 파이프라인은 문서를 순서가 정해진 단계들에 통과시킵니다. 각 단계는 해당 단계에 도달한 문서를 필터링하거나 그룹화하거나 계산하거나 정렬할 수 있습니다. 예를 들어 aggregate([{ $match: { status: "paid" } }, { $group: { _id: "$customerId", orders: { $sum: 1 } } }])는 결제된 주문만 추린 다음 고객별로 개수를 셉니다. 여기에 나온 코드는 MongoDB 셸 문법이므로 실행하려면 MongoDB 서버나 셸이 필요합니다.
$count in MongoDB
$count 단계는 해당 단계에 도달한 문서 수를 세고, $count에 전달한 문자열을 필드 이름으로 하는 문서를 출력합니다. 조건에 맞는 문서만 세려면 앞에 $match를 배치합니다. 위의 점수 6개를 기준으로 같은 결과를 내는 파이프라인은 db.scores.aggregate([{ $match: { score: { $gt: 80 } } }, { $count: "passing_scores" }])입니다. 결과 문서의 형태는 { "passing_scores": 4 }입니다. passing_scores는 출력 필드이지 변수가 아닙니다.
해당 단계에 도달하는 문서가 없으면 $count는 결과 문서를 출력하지 않습니다. 결과에 항상 숫자 0을 포함해야 한다면 애플리케이션에서 이 경우를 처리하거나 파이프라인의 결과 형태를 조정하세요. 파이프라인에서 개수를 표현하는 또 다른 방법으로 $group을 사용해 입력 문서마다 1씩 더할 수 있습니다. 예를 들면 [{ $group: { _id: null, myCount: { $sum: 1 } } }, { $project: { _id: 0, myCount: 1 } }]입니다. 프로젝션은 개수 필드를 유지하고 _id를 제거합니다.
MongoDB의 db.collection.count()
mongosh에서 db.collection.count()는 개수를 반환하며, db.collection.count(query)는 쿼리 필터를 적용합니다. 예를 들어 db.orders.count({ status: "paid" })는 조건에 맞는 주문 수를 셉니다. MongoDB 4.0에서 이 메서드는 더 이상 사용되지 않도록 지정되었습니다. 현재 문서에서는 정확한 조건부 개수에는 countDocuments()를, 컬렉션 전체에 대한 빠른 메타데이터 추정치로 충분할 때는 estimatedDocumentCount()를 권장합니다. db.collection.count()는 트랜잭션 안에서 사용할 수 없으므로 그 경우에는 countDocuments()를 사용하세요. 드라이버에 따라 메서드 이름이 다를 수 있으니 애플리케이션에서 사용하는 드라이버의 문서를 확인하세요. 개수를 파이프라인 단계와 결합하거나 그룹별 값과 함께 반환해야 한다면 aggregate()를 사용합니다. MongoDB의 count() 참조 문서와 countDocuments() 참조 문서를 확인하세요.
샤딩된 클러스터
MongoDB 문서에 따르면 샤딩된 클러스터에 고아 문서가 있거나 청크 마이그레이션이 진행 중이면 count()가 부정확한 결과를 반환할 수 있습니다. 쿼리 조건에 맞는 개수를 세려면 countDocuments()를 사용하세요. 주의 사항은 count() 문서에서, 권장 대안은 countDocuments() 문서에서 확인할 수 있습니다. 이 설명은 현재 매뉴얼을 기준으로 하므로 서버 버전에 해당하는 문서도 확인하세요.
인덱스 사용
인덱스가 개수 필터에 도움이 되는지는 쿼리와 사용 가능한 인덱스, 선택된 실행 계획에 따라 달라집니다. explain()으로 실제 작업을 살펴보고 MongoDB의 쿼리 최적화 안내를 확인하세요. 인덱스가 있다고 해서 인덱스 키만으로 개수를 계산할 수 있는 것은 아닙니다.
예기치 않은 종료 후의 정확성
MongoDB 문서에 따르면 count()는 컬렉션 메타데이터를 활용할 수 있기 때문에 비정상 종료 후 부정확한 결과를 반환할 수 있습니다. 조건에 맞는 문서 수가 필요하면 countDocuments()를 사용해 필터에 맞는 문서를 세세요. validate는 컬렉션 데이터와 인덱스를 검사하는 진단 명령이며, 개수 쿼리를 대신하지 않습니다. 복구 조치를 취하기 전에 count() 관련 주의 사항과 버전에 맞는 validate 명령 안내를 따르세요.
클라이언트 연결 해제
개수 응답이 클라이언트에 도착하기 전에 연결이 끊기면 클라이언트는 반환된 개수를 확인할 수 없습니다. 선택한 드라이버의 안내에 따라 오류를 처리하고, 응답이 없다는 사실을 개수가 확인된 것으로 간주하지 마세요. MongoDB의 드라이버 문서에서 드라이버별 동작과 API 세부 정보를 확인할 수 있습니다.
컬렉션의 모든 문서 세기
집계 단계를 사용해 orders의 모든 문서를 세려면 db.orders.aggregate([{ $count: "total" }])를 사용합니다. 애플리케이션에서 숫자 개수를 얻으려면 해당 언어용 MongoDB 드라이버의 개수 API를 사용하세요. mongosh에서는 db.orders.countDocuments({})로 전체 문서를 대상으로 한다는 점을 명시할 수 있습니다.
쿼리와 일치하는 모든 문서 세기
일치하는 문서만 세려면 필터를 전달합니다. 예를 들어 db.orders.countDocuments({ orderDate: { $gte: ISODate("2024-01-01T00:00:00Z"), $lt: ISODate("2025-01-01T00:00:00Z") } })는 UTC 기준 2024년에 해당하는 주문 수를 셉니다. 시작 경계는 포함되고 끝 경계는 제외되므로 2025년 시작 시각의 타임스탬프는 포함되지 않습니다.
MongoDB에서 문서 그룹화 및 개수 세기
다음 students 문서를 사용하면 예제를 구체적으로 살펴볼 수 있습니다. { name: "Ava", city: "Paris", courses: ["math", "science"] }, { name: "Ben", city: "Paris", courses: ["math"] }, { name: "Cam", city: "Lyon", courses: ["science", "history"] }입니다. 이는 설명을 위한 입력 문서입니다. 아래의 MongoDB 셸 파이프라인은 이 문서의 로컬 Python 예제에서 실행되지 않습니다.
필드의 서로 다른 값별 문서 수를 세려면 $group과 $sum: 1을 함께 사용합니다. db.students.aggregate([{ $group: { _id: "$city", students: { $sum: 1 } } }])는 { "_id": "Paris", "students": 2 }와 { "_id": "Lyon", "students": 1 }을 반환합니다. _id: null처럼 상수 그룹 키를 지정하면 입력 전체가 하나의 그룹이 됩니다. 이 방식은 필드별로 문서를 그룹화하지 않습니다.
MongoDB에서 그룹별 개수 정렬하기
$sortByCount는 표현식을 기준으로 문서를 그룹화하고 각 그룹의 개수를 센 뒤, 개수가 큰 순서대로 결과를 정렬합니다. 예제의 courses 배열을 사용하면 db.students.aggregate([{ $unwind: "$courses" }, { $sortByCount: "$courses" }])는 먼저 배열 요소마다 파이프라인 문서 하나를 내보냅니다. 예상 결과는 { "_id": "math", "count": 2 }, { "_id": "science", "count": 2 }, { "_id": "history", "count": 1 }입니다. 개수가 같은 두 그룹의 순서는 달라질 수 있습니다. $unwind를 사용하면 각 배열 요소가 개수에 따로 반영됩니다. 이에 해당하는 명시적 단계는 [{ $unwind: "$courses" }, { $group: { _id: "$courses", count: { $sum: 1 } } }, { $sort: { count: -1 } }]입니다.
MongoDB에서 여러 필드로 그룹화하고 개수 세기
서로 다른 필드 값 조합별로 개수를 세려면 각 그룹화 필드를 _id 문서에 넣습니다. 예를 들어 요청 문서가 { source: "web", status: "ok" }, { source: "web", status: "error" }, { source: "web", status: "ok" }일 때 db.requests.aggregate([{ $group: { _id: { source: "$source", status: "$status" }, requests: { $sum: 1 } } }, { $sort: { "_id.source": 1, "_id.status": 1 } }])는 { "_id": { "source": "web", "status": "error" }, "requests": 1 }과 { "_id": { "source": "web", "status": "ok" }, "requests": 2 }를 반환합니다. $count는 해당 단계에 도달한 파이프라인 문서 수를 셉니다. 그룹별 개수에는 $group과 $sum: 1을 사용하는 패턴을 적용합니다.
MongoDB에서 날짜별로 그룹화하고 개수 세기
일별 주문 수와 매출 합계를 계산하려면 연도, 월, 일 형식의 날짜를 기준으로 그룹화합니다. 다음 sales 문서는 UTC 날짜와 숫자형 price, quantity 필드를 사용합니다. { date: ISODate("2024-03-01T12:00:00Z"), price: 10, quantity: 2 }, { date: ISODate("2024-03-01T18:00:00Z"), price: 5, quantity: 1 }, { date: ISODate("2024-04-02T09:00:00Z"), price: 7, quantity: 3 }입니다. 시작은 포함하고 끝은 제외하는 이 범위에는 2024년 3월과 4월이 포함되고 2024년 5월 1일은 제외됩니다.
db.sales.aggregate([{ $match: { date: { $gte: ISODate("2024-03-01T00:00:00Z"), $lt: ISODate("2024-05-01T00:00:00Z") } } }, { $group: { _id: { $dateToString: { format: "%Y-%m-%d", date: "$date", timezone: "UTC" } }, totalSales: { $sum: { $multiply: ["$price", "$quantity"] } }, orderCount: { $sum: 1 } } }, { $sort: { _id: 1 } }]).
위 문서에 대한 예상 결과는 { "_id": "2024-03-01", "totalSales": 25, "orderCount": 2 }와 { "_id": "2024-04-02", "totalSales": 21, "orderCount": 1 }입니다. 각 합계는 해당 UTC 날짜의 price * quantity를 더한 값입니다. 저장된 날짜나 업무상 하루의 기준 시간대가 다르다면 범위 경계와 그룹화 시간대를 일관되게 설정하세요. 이 쿼리는 MongoDB 셸 문법이며 설명한 필드를 가진 데이터베이스가 필요합니다. 결과는 위의 샘플 문서에서 계산한 것으로, 실행 결과를 근거로 한 것은 아닙니다.
toArray()로 커서를 배열로 변환하기
toArray()는 커서에 남은 결과를 읽어 배열로 반환합니다. 결과를 여러 번 인덱싱하거나 반복해야 할 때 편리하지만, 문서가 한꺼번에 애플리케이션 메모리에 올라갑니다. 결과가 많다면 모든 문서를 한 번에 배열로 만들기보다 커서를 순회하세요. mongosh에서 db.products.find({ active: true }).toArray()는 조건에 맞는 제품 문서 배열을 반환하며, 개수를 세는 작업은 아닙니다.
itcount()로 커서에 남은 문서 수 세기
mongosh에서 itcount()는 커서에 남은 결과를 소비하면서 문서 수를 셉니다. 대화형 셸에서 커서를 살펴볼 때 유용하지만, 실행 후 커서는 소진됩니다. 재사용 가능한 개수 쿼리가 아니며, 애플리케이션이 계속 사용할 커서를 소비하지 않고 개수를 알아야 할 때 countDocuments()를 대신하지 않습니다.