Computer >> 컴퓨터 >  >> 프로그래밍 >> MongoDB

MongoDB에서 한 컬렉션의 필드 값이 다른 컬렉션보다 클 때 두 컬렉션을 집계하는 방법

MongoDB에서 두 컬렉션을 하나로 묶어 처리할 때, 한 컬렉션의 특정 필드 값이 다른 컬렉션의 대응되는 값보다 큰 문서만 골라내야 하는 경우가 있습니다. 이런 조건 비교는 단순히 $lookup만으로는 처리할 수 없으며, $unwind$redact를 함께 사용해야 합니다. 아래에서 예제를 통해 전체 과정을 단계별로 살펴보겠습니다.

첫 번째 컬렉션 생성하기

먼저 제품 이름(ProductName)과 가격(ProductPrice)을 담은 demo446 컬렉션을 생성합니다.

> db.demo446.insert([
...     { "ProductName": "Product1", "ProductPrice": 60 },
...     { "ProductName": "Product2", "ProductPrice": 90 }
... ])
BulkWriteResult({
    "writeErrors" : [ ],
    "writeConcernErrors" : [ ],
    "nInserted" : 2,
    "nUpserted" : 0,
    "nMatched" : 0,
    "nModified" : 0,
    "nRemoved" : 0,
    "upserted" : [ ]
})

find() 메서드를 사용해 컬렉션의 모든 문서를 조회합니다.

> db.demo446.find();

위 명령은 다음과 같은 결과를 출력합니다.

{ "_id" : ObjectId("5e790766bbc41e36cc3caec3"), "ProductName" : "Product1", "ProductPrice" : 60 }
{ "_id" : ObjectId("5e790766bbc41e36cc3caec4"), "ProductName" : "Product2", "ProductPrice" : 90 }

두 번째 컬렉션 생성하기

비교 대상이 될 demo447 컬렉션도 동일한 구조로 생성합니다.

> db.demo447.insert([
...
...     { "ProductName": "Product1", "ProductPrice": 40 },
...     { "ProductName": "Product2", "ProductPrice": 70 }
... ])
BulkWriteResult({
    "writeErrors" : [ ],
    "writeConcernErrors" : [ ],
    "nInserted" : 2,
    "nUpserted" : 0,
    "nMatched" : 0,
    "nModified" : 0,
    "nRemoved" : 0,
    "upserted" : [ ]
})

마찬가지로 find() 메서드로 저장된 문서를 확인합니다.

> db.demo447.find();

위 명령은 다음과 같은 결과를 출력합니다.

{ "_id" : ObjectId("5e790789bbc41e36cc3caec5"), "ProductName" : "Product1", "ProductPrice" : 40 }
{ "_id" : ObjectId("5e790789bbc41e36cc3caec6"), "ProductName" : "Product2", "ProductPrice" : 70 }

집계 파이프라인 작성하기

이제 한 컬렉션의 ProductPrice가 다른 컬렉션의 가격에 배율(rate)을 곱한 값보다 클 때만 문서를 남기는 집계 쿼리를 작성합니다. 여기서는 rate 변수를 1로 설정했습니다.

  • $match: ProductPrice와 ProductName 필드가 실제로 존재하는 문서만 통과시켜 불필요한 연산을 줄입니다.
  • $lookup: ProductName 값을 기준으로 demo447 컬렉션과 조인하여 일치하는 문서를 demo447 배열 필드에 담습니다.
  • $unwind: 조인 결과로 생긴 배열을 개별 문서로 풀어냅니다.
  • $redact: 첫 번째 컬렉션의 가격이 상대 컬렉션 가격 × rate + 3보다 크면 $$KEEP으로 문서를 유지하고, 그렇지 않으면 $$PRUNE으로 제거합니다.
> var rate = 1;
> db.demo446.aggregate([
... { "$match": { "ProductPrice": { "$exists": true }, "ProductName": { "$exists": true } } },
... {
...     "$lookup": {
...         "from": "demo447",
...         "localField": "ProductName",
...         "foreignField": "ProductName",
...         "as": "demo447"
...     }
... },
... { "$unwind": "$demo447" },
... {
...     "$redact": {
...         "$cond": [
...             {
...                 "$gt": [
...                     "$ProductPrice", {
...                         "$add": [
...                             { "$multiply": [ "$demo447.ProductPrice",rate ] },
...                             3
...                         ]
...                     }
...                 ]
...             },
...             "$$KEEP",
...             "$$PRUNE"
...         ]
...     }
... }
... ])

위 파이프라인을 실행하면 다음과 같은 결과가 출력됩니다.

{ "_id" : ObjectId("5e790766bbc41e36cc3caec3"), "ProductName" : "Product1", "ProductPrice" : 60, "demo447" : { "_id" : ObjectId("5e790789bbc41e36cc3caec5"), "ProductName" : "Product1", "ProductPrice" : 40 } }
{ "_id" : ObjectId("5e790766bbc41e36cc3caec4"), "ProductName" : "Product2", "ProductPrice" : 90, "demo447" : { "_id" : ObjectId("5e790789bbc41e36cc3caec6"), "ProductName" : "Product2", "ProductPrice" : 70 } }

정리

이처럼 $lookup으로 두 컬렉션을 조인한 뒤 $unwind로 배열을 해제하고, $redact의 조건식으로 가격을 비교하면 한 컬렉션의 필드 값이 다른 컬렉션보다 큰 문서만 손쉽게 집계할 수 있습니다. 참고로 최신 MongoDB 버전에서는 $redact 대신 $match$expr을 조합해 동일한 결과를 더 간결하게 얻을 수도 있습니다.