$merge (집계)
정의
참고
이 페이지에서는 집계 파이프라인 결과를 컬렉션에 출력하는 $merge
단계를 설명합니다. 문서를 단일 문서로 병합하는 $mergeObjects
연산자에 대한 자세한 내용은 $mergeObjects
를 참조하세요.
$merge
집계 파이프라인의 결과를 지정된 컬렉션에 씁니다.
$merge
연산자는 반드시 파이프라인의 마지막 단계여야 합니다.다음은
$merge
단계입니다.동일하거나 다른 데이터베이스의 컬렉션으로 출력할 수 있습니다.
집계 중인 동일한 컬렉션으로 출력할 수 있습니다. 자세한 내용은 집계 중인 동일한 컬렉션에 출력하기를 참조하세요.
집계 파이프라인에서
$merge
또는$out
단계를 사용할 때 다음 사항을 고려하세요.MongoDB 5.0부터
$merge
단계가 있는 파이프라인은 클러스터의 모든 노드에서 featureCompatibilityVersion이5.0
이상으로 설정되어 있고 읽기 설정이 세컨더리 읽기를 허용하는 경우 복제본 세트 세컨더리 노드에서 실행될 수 있습니다.이전 MongoDB 버전에서는
$out
또는$merge
단계가 있는 파이프라인은 항상 기본 노드에서 실행되며 읽기 기본 설정은 고려되지 않았습니다.
출력 컬렉션이 아직 존재하지 않는 경우 새 컬렉션을 생성합니다.
결과(새 문서 삽입, 문서 병합, 문서 교체, 기존 문서 유지, 작업 실패, 사용자 지정 업데이트 파이프라인으로 문서 처리)를 기존 컬렉션에 통합할 수 있습니다.
샤드된 컬렉션으로 출력할 수 있습니다. 입력 컬렉션도 샤딩할 수 있습니다.
집계 결과를 컬렉션으로 출력하는
$out
단계와 비교하려면$merge
및$out
비교를 참조하세요.
참고
온디맨드 구체화된 보기
$merge
컬렉션을 완전히 교체하는 대신 파이프라인 결과를 기존 출력 컬렉션에 통합할 수 있습니다. 이 기능을 사용하면 파이프라인이 실행될 때 출력 컬렉션의 콘텐츠가 점진적으로 업데이트되는 온디맨드 구체화된 보기를 만들 수 있습니다.
이 사용 사례에 대한 자세한 내용은 이 페이지의 예시 및 온디맨드 구체화된 뷰를 참조하세요.
구체화된 보기는 읽기 전용 보기와 별개입니다. 읽기 전용 보기를 만드는 방법에 대한 자세한 내용은 읽기 전용 보기를 참조하십시오.
호환성
다음 환경에서 호스팅되는 배포에 $merge
사용할 수 있습니다.
MongoDB Atlas: 클라우드에서의 MongoDB 배포를 위한 완전 관리형 서비스
MongoDB Enterprise: MongoDB의 구독 기반 자체 관리 버전
MongoDB Community: MongoDB의 소스 사용 가능 무료 자체 관리 버전
구문
$merge
의 구문은 다음과 같습니다:
{ $merge: { into: <collection> -or- { db: <db>, coll: <collection> }, on: <identifier field> -or- [ <identifier field1>, ...], // Optional let: <variables>, // Optional whenMatched: <replace|keepExisting|merge|fail|pipeline>, // Optional whenNotMatched: <insert|discard|fail> // Optional } }
예를 들면 다음과 같습니다.
{ $merge: { into: "myOutput", on: "_id", whenMatched: "replace", whenNotMatched: "insert" } }
동일한 데이터베이스의 컬렉션에 쓰는 것을 포함하여 $merge
에 대한 모든 기본 옵션을 사용하는 경우 간소화된 양식을 사용할 수 있습니다.
{ $merge: <collection> } // Output collection is in the same database
$merge
는 다음 필드가 포함된 문서를 사용합니다.
필드 | 설명 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
출력 컬렉션입니다. 다음 중 하나를 지정합니다.
출력 컬렉션 이 존재하지 않으면
출력 컬렉션은 샤드된 컬렉션일 수 있습니다. | |||||||||||
선택 사항. 문서 의 고유 식별자 역할을 하는 필드입니다. 식별자는 결과 문서 가 출력 컬렉션 의 기존 문서 와 일치하는지 여부를 결정합니다. 다음 중 하나를 지정합니다.
지정된 필드에 대해
에는 on 식별자
on의 기본값 은 출력 컬렉션 에 따라 다릅니다.
| |||||||||||
선택 사항. 다음 두 가지 중 하나를 지정할 수 있습니다.
| |||||||||||
선택 사항. whenMatched 파이프라인 에서 사용할변수를 지정합니다. 변수 이름과 값 표현식이 있는 문서를 지정합니다.
whenMatched 파이프라인의 변수에 액세스 하려면 다음을 수행합니다. 변수 이름과 함께 이중 달러 기호 ($$) 접두사를 | |||||||||||
고려 사항
_id
필드 생성
집계 파이프라인 결과에서 _id
필드가 문서에 없는 경우 $merge
단계에서 자동으로 생성합니다.
예를 들어, 다음 집계 파이프라인에서 $project
는 $merge
에 전달된 문서에서 _id
필드를 제외합니다. $merge
가 이러한 문서를 "newCollection"
에 쓰면 $merge
는 새로운 _id
필드와 값을 생성합니다.
db.sales.aggregate( [ { $project: { _id: 0 } }, { $merge : { into : "newCollection" } } ] )
출력 컬렉션이 존재하지 않는 경우 새 컬렉션 만들기
지정한 출력 컬렉션이 없는 경우 $merge
작업은 새 컬렉션을 생성합니다.
출력 컬렉션이 존재하지 않는 경우 $merge
는 on 식별자가 _id
필드여야 합니다. 존재하지 않는 컬렉션에 다른 on
필드 값을 사용하려면 먼저 원하는 필드에 고유 인덱스를 생성하여 컬렉션을 만들 수 있습니다. 예를 들어 출력 컬렉션 newDailySales201905
가 존재하지 않고 salesDate
필드를 on 식별자로 지정하려는 경우입니다.
db.newDailySales201905.createIndex( { salesDate: 1 }, { unique: true } ) db.sales.aggregate( [ { $match: { date: { $gte: new Date("2019-05-01"), $lt: new Date("2019-06-01") } } }, { $group: { _id: { $dateToString: { format: "%Y-%m-%d", date: "$date" } }, totalqty: { $sum: "$quantity" } } }, { $project: { _id: 0, salesDate: { $toDate: "$_id" }, totalqty: 1 } }, { $merge : { into : "newDailySales201905", on: "salesDate" } } ] )
샤드된 컬렉션으로 출력
$merge
단계는 샤딩된 컬렉션으로 출력할 수 있습니다. 출력 컬렉션이 샤딩되면 $merge
는 _id
필드와 모든 샤드 키 필드를 식별자 on의 기본값으로 사용합니다. 기본값을 재정의하는 경우, on 식별자에는 모든 샤드 키 필드가 포함되어야 합니다.
{ $merge: { into: "<shardedColl>" or { db:"<sharding enabled db>", coll: "<shardedColl>" }, on: [ "<shardkeyfield1>", "<shardkeyfield2>",... ], // Shard key fields and any additional fields let: <variables>, // Optional whenMatched: <replace|keepExisting|merge|fail|pipeline>, // Optional whenNotMatched: <insert|discard|fail> // Optional } }
예를 들어 sh.shardCollection()
메서드를 사용하여 postcode
필드를 샤드 키로 사용하여 새 샤드된 컬렉션 newrestaurants
를 만듭니다.
sh.shardCollection( "exampledb.newrestaurants", // Namespace of the collection to shard { postcode: 1 }, // Shard key );
newrestaurants
컬렉션에는 월별(date
필드) 및 우편 번호(샤드 키)로 새 레스토랑 개점 정보가 있는 문서가 포함되며, 특히 on 식별자는 ["date", "postcode"]
입니다(필드 순서는 중요하지 않음). $merge
에는 on 식별자 필드에 해당하는 키가 있는 고유 인덱스가 필요하므로 고유 인덱스를 생성합니다(필드 순서는 중요하지 않음). [1]
use exampledb db.newrestaurants.createIndex( { postcode: 1, date: 1 }, { unique: true } )
샤드된 컬렉션 restaurants
과 생성된 고유 인덱스를 사용하면 $merge
를 사용하여 이 컬렉션에 집계 결과를 출력하고, 이 예시에서처럼 [ "date", "postcode" ]
에 일치하는 결과를 출력할 수 있습니다:
use exampledb db.openings.aggregate([ { $group: { _id: { date: { $dateToString: { format: "%Y-%m", date: "$date" } }, postcode: "$postcode" }, restaurants: { $push: "$restaurantName" } } }, { $project: { _id: 0, postcode: "$_id.postcode", date: "$_id.date", restaurants: 1 } }, { $merge: { into: "newrestaurants", "on": [ "date", "postcode" ], whenMatched: "replace", whenNotMatched: "insert" } } ])
[1] | sh.shardCollection() 2} 메서드는 옵션을 전달할 때 샤드 { unique: true
} 키가 범위 기반이고 , 컬렉션이 비어 있으며, 샤드 키의 고유 인덱스가 아직 존재하지 않는 경우 샤드 키에 고유 인덱스를 생성할 수도 있습니다. 이전 예시에서는 on 식별자가 샤드 키와 다른 필드이므로 해당 인덱스를 생성하기 위한 별도의 작업이 필요합니다. |
문서 바꾸기($merge
) vs 컬렉션 바꾸기($out
)
집계 결과에 on 사양에 따라 일치하는 문서가 하나 이상 포함된 경우 $merge
는 출력 컬렉션의 기존 문서를 대체할 수 있습니다. 따라서 집계 결과에 컬렉션의 모든 기존 문서에 대해 일치하는 문서가 포함되어 있고 whenMatched에 'replace'를 지정한 경우 $merge
는 기존 컬렉션의 모든 문서를 대체할 수 있습니다.
그러나 집계 결과에 관계없이 기존 컬렉션을 바꾸려면 $out
를 대신 사용합니다.
기존 문서 및 _id
및 샤드 키 값
고유 인덱스 제약 조건
on 필드에 대해 $merge
가 사용하는 고유 인덱스가 집계 도중에 제거되는 경우 집계가 중단된다는 보장이 없습니다. 집계가 계속되면 문서에 중복된 on
필드 값이 없다는 보장이 없습니다.
$merge
가 출력 컬렉션의 고유 인덱스를 위반하는 문서를 작성하려고 하면 작업이 오류를 생성합니다. 예를 들면 다음과 같습니다.
온 필드의 인덱스가 아닌 고유 인덱스를 위반하는 일치하지 않는 문서를 삽입합니다 .
컬렉션에 일치하는 문서가 있으면실패합니다. 즉, 이 작업은 on 필드의 고유 인덱스를 위반하는 일치하는 문서를 삽입하려고 시도합니다.
일치하는 문서를 병합하면 온 필드의 인덱스가 아닌 고유 인덱스를 위반하는 문서가 생성됩니다.
스키마 유효성 검사
컬렉션이 스키마 유효성 검사를 사용하고 validationAction
이 error
로 설정된 경우, $merge
로 잘못된 문서를 삽입하거나 잘못된 값으로 문서를 업데이트하면 MongoServerError
가 발생하며, 문서는 대상 컬렉션에 기록되지 않습니다. 잘못된 문서가 여러 개 있는 경우, 처음으로 발견된 잘못된 문서만 오류를 발생시킵니다. 모든 유효한 문서는 대상 컬렉션에 기록되며 모든 잘못된 문서는 기록에 실패합니다.
whenMatched
파이프라인 동작
$merge
단계에 대해 다음 사항이 모두 true인 경우에는 $merge
가 문서를 출력 컬렉션에 직접 삽입합니다.
whenMatched의 값은 집계 파이프라인입니다,
whenNotMatched의 값은
insert
입니다.출력 컬렉션에 일치하는 문서가 없습니다,
$merge
는 문서를 출력 컬렉션에 바로 삽입합니다.
$merge
와 $out
비교
$merge
의 도입으로 MongoDB는 집계 파이프라인의 결과를 컬렉션에 기록하기 위해 $merge
및 $out
의 두 단계를 제공합니다.
|
|
|
|
|
|
|
|
|
|
집계 중인 동일한 컬렉션에 대한 출력
경고
집계 중인 동일한 컬렉션에 $merge
출력이 발생하면 문서가 여러 번 업데이트되거나 작업이 무한 루프가 발생할 수 있습니다. 이 동작은 $merge
에서 수행한 업데이트로 인해 디스크에 저장된 문서의 물리적 위치가 변경될 때 발생합니다. 문서의 물리적 위치가 변경되면 $merge
에서 이를 완전히 새로운 문서로 간주하여 추가 업데이트를 수행할 수 있습니다. 이 동작에 대한 자세한 내용은 ' 할로윈 문제'를 참조하세요.
$merge
는 집계 중인 컬렉션과 동일한 컬렉션으로 출력할 수 있습니다. $lookup
과 같이 파이프라인의 다른 단계에 나타나는 컬렉션으로 출력할 수도 있습니다.
제한 사항
제한 사항 | 설명 |
---|---|
집계 파이프라인은 | |
Separate from materialized view | |
| |
| |
|
|
|
|
예시
온디맨드 구체화된 뷰: 초기 생성
출력 컬렉션이 존재하지 않으면 $merge
가 컬렉션을 생성합니다.
예를 들어, zoo
데이터베이스의 salaries
이라는 컬렉션은 직원 급여 및 부서 내역으로 채워집니다.
db.getSiblingDB("zoo").salaries.insertMany([ { "_id" : 1, employee: "Ant", dept: "A", salary: 100000, fiscal_year: 2017 }, { "_id" : 2, employee: "Bee", dept: "A", salary: 120000, fiscal_year: 2017 }, { "_id" : 3, employee: "Cat", dept: "Z", salary: 115000, fiscal_year: 2017 }, { "_id" : 4, employee: "Ant", dept: "A", salary: 115000, fiscal_year: 2018 }, { "_id" : 5, employee: "Bee", dept: "Z", salary: 145000, fiscal_year: 2018 }, { "_id" : 6, employee: "Cat", dept: "Z", salary: 135000, fiscal_year: 2018 }, { "_id" : 7, employee: "Gecko", dept: "A", salary: 100000, fiscal_year: 2018 }, { "_id" : 8, employee: "Ant", dept: "A", salary: 125000, fiscal_year: 2019 }, { "_id" : 9, employee: "Bee", dept: "Z", salary: 160000, fiscal_year: 2019 }, { "_id" : 10, employee: "Cat", dept: "Z", salary: 150000, fiscal_year: 2019 } ])
$group
및 $merge
단계를 사용하여 현재 salaries
컬렉션에 있는 데이터에서 budgets
(reporting
데이터베이스에 있는)라는 컬렉션을 처음 만들 수 있습니다.
참고
복제 세트 또는 독립형 배포의 경우 출력 데이터베이스가 존재하지 않으면 $merge
가 데이터베이스를 생성하기도 합니다.
샤딩된 클러스터 배포의 경우, 지정된 출력 데이터베이스가 이미 존재해야 합니다.
db.getSiblingDB("zoo").salaries.aggregate( [ { $group: { _id: { fiscal_year: "$fiscal_year", dept: "$dept" }, salaries: { $sum: "$salary" } } }, { $merge : { into: { db: "reporting", coll: "budgets" }, on: "_id", whenMatched: "replace", whenNotMatched: "insert" } } ] )
급여를
fiscal_year
및dept
기준으로 그룹화하는$group
단계.$merge
단계는 앞의$group
단계의 출력을reporting
데이터베이스의budgets
컬렉션에 기록합니다.
새 budgets
컬렉션의 문서를 보려면 다음과 같이 하세요:
db.getSiblingDB("reporting").budgets.find().sort( { _id: 1 } )
budgets
컬렉션에는 다음 문서가 포함되어 있습니다.
{ "_id" : { "fiscal_year" : 2017, "dept" : "A" }, "salaries" : 220000 } { "_id" : { "fiscal_year" : 2017, "dept" : "Z" }, "salaries" : 115000 } { "_id" : { "fiscal_year" : 2018, "dept" : "A" }, "salaries" : 215000 } { "_id" : { "fiscal_year" : 2018, "dept" : "Z" }, "salaries" : 280000 } { "_id" : { "fiscal_year" : 2019, "dept" : "A" }, "salaries" : 125000 } { "_id" : { "fiscal_year" : 2019, "dept" : "Z" }, "salaries" : 310000 }
온디맨드 구체화된 뷰: 데이터 업데이트/바꾸기
다음 예시에서는 이전 예시의 컬렉션을 사용합니다.
예시 salaries
컬렉션에는 직원 급여 및 부서 기록이 포함되어 있습니다.
{ "_id" : 1, employee: "Ant", dept: "A", salary: 100000, fiscal_year: 2017 }, { "_id" : 2, employee: "Bee", dept: "A", salary: 120000, fiscal_year: 2017 }, { "_id" : 3, employee: "Cat", dept: "Z", salary: 115000, fiscal_year: 2017 }, { "_id" : 4, employee: "Ant", dept: "A", salary: 115000, fiscal_year: 2018 }, { "_id" : 5, employee: "Bee", dept: "Z", salary: 145000, fiscal_year: 2018 }, { "_id" : 6, employee: "Cat", dept: "Z", salary: 135000, fiscal_year: 2018 }, { "_id" : 7, employee: "Gecko", dept: "A", salary: 100000, fiscal_year: 2018 }, { "_id" : 8, employee: "Ant", dept: "A", salary: 125000, fiscal_year: 2019 }, { "_id" : 9, employee: "Bee", dept: "Z", salary: 160000, fiscal_year: 2019 }, { "_id" : 10, employee: "Cat", dept: "Z", salary: 150000, fiscal_year: 2019 }
예시 budgets
컬렉션에는 연간 누적 예산이 포함되어 있습니다.
{ "_id" : { "fiscal_year" : 2017, "dept" : "A" }, "salaries" : 220000 } { "_id" : { "fiscal_year" : 2017, "dept" : "Z" }, "salaries" : 115000 } { "_id" : { "fiscal_year" : 2018, "dept" : "A" }, "salaries" : 215000 } { "_id" : { "fiscal_year" : 2018, "dept" : "Z" }, "salaries" : 280000 } { "_id" : { "fiscal_year" : 2019, "dept" : "A" }, "salaries" : 125000 } { "_id" : { "fiscal_year" : 2019, "dept" : "Z" }, "salaries" : 310000 }
현재 회계연도(이 예에서는 2019
) 동안 새 직원이 salaries
컬렉션에 추가되고 새 인원수가 다음 연도에 미리 할당됩니다.
db.getSiblingDB("zoo").salaries.insertMany([ { "_id" : 11, employee: "Wren", dept: "Z", salary: 100000, fiscal_year: 2019 }, { "_id" : 12, employee: "Zebra", dept: "A", salary: 150000, fiscal_year: 2019 }, { "_id" : 13, employee: "headcount1", dept: "Z", salary: 120000, fiscal_year: 2020 }, { "_id" : 14, employee: "headcount2", dept: "Z", salary: 120000, fiscal_year: 2020 } ])
새 급여 정보를 반영하도록 budgets
컬렉션을 업데이트하기 위해 다음 집계 파이프라인은 다음을 사용합니다.
$match
{3}2019
2} 단계로 이 보다 크거나 같은 모든 문서를 찾습니다.급여를
fiscal_year
및dept
기준으로 그룹화하는$group
단계.$merge
를 사용하여 결과 집합을budgets
컬렉션에 쓰고, 동일한_id
값(이 예에서는 회계 연도와 부서가 있는 문서)을 가진 문서로 대체합니다. 컬렉션에 일치하는 문서가 없는 문서의 경우$merge
가 새 문서를 삽입합니다.
db.getSiblingDB("zoo").salaries.aggregate( [ { $match : { fiscal_year: { $gte : 2019 } } }, { $group: { _id: { fiscal_year: "$fiscal_year", dept: "$dept" }, salaries: { $sum: "$salary" } } }, { $merge : { into: { db: "reporting", coll: "budgets" }, on: "_id", whenMatched: "replace", whenNotMatched: "insert" } } ] )
집계가 실행된 후 budgets
컬렉션에서 문서를 봅니다.
db.getSiblingDB("reporting").budgets.find().sort( { _id: 1 } )
budgets
컬렉션은 2019 회계연도의 새 급여 데이터를 통합하고 2020 회계연도에 대한 새 문서를 추가합니다.
{ "_id" : { "fiscal_year" : 2017, "dept" : "A" }, "salaries" : 220000 } { "_id" : { "fiscal_year" : 2017, "dept" : "Z" }, "salaries" : 115000 } { "_id" : { "fiscal_year" : 2018, "dept" : "A" }, "salaries" : 215000 } { "_id" : { "fiscal_year" : 2018, "dept" : "Z" }, "salaries" : 280000 } { "_id" : { "fiscal_year" : 2019, "dept" : "A" }, "salaries" : 275000 } { "_id" : { "fiscal_year" : 2019, "dept" : "Z" }, "salaries" : 410000 } { "_id" : { "fiscal_year" : 2020, "dept" : "Z" }, "salaries" : 240000 }
새 데이터만 삽입
$merge
가 컬렉션의 기존 데이터를 덮어쓰지 않도록 하려면 whenMatched를 keepExisting 또는 fail로 설정합니다.
zoo
데이터베이스의 예시 salaries
컬렉션에는 직원 급여 및 부서 내역이 포함되어 있습니다.
{ "_id" : 1, employee: "Ant", dept: "A", salary: 100000, fiscal_year: 2017 }, { "_id" : 2, employee: "Bee", dept: "A", salary: 120000, fiscal_year: 2017 }, { "_id" : 3, employee: "Cat", dept: "Z", salary: 115000, fiscal_year: 2017 }, { "_id" : 4, employee: "Ant", dept: "A", salary: 115000, fiscal_year: 2018 }, { "_id" : 5, employee: "Bee", dept: "Z", salary: 145000, fiscal_year: 2018 }, { "_id" : 6, employee: "Cat", dept: "Z", salary: 135000, fiscal_year: 2018 }, { "_id" : 7, employee: "Gecko", dept: "A", salary: 100000, fiscal_year: 2018 }, { "_id" : 8, employee: "Ant", dept: "A", salary: 125000, fiscal_year: 2019 }, { "_id" : 9, employee: "Bee", dept: "Z", salary: 160000, fiscal_year: 2019 }, { "_id" : 10, employee: "Cat", dept: "Z", salary: 150000, fiscal_year: 2019 }
reporting
데이터베이스의 컬렉션 orgArchive
에는 지난 회계연도의 과거 부서별 조직 기록이 포함되어 있습니다. 보관된 기록은 수정해서는 안 됩니다.
{ "_id" : ObjectId("5cd8c68261baa09e9f3622be"), "employees" : [ "Ant", "Gecko" ], "dept" : "A", "fiscal_year" : 2018 } { "_id" : ObjectId("5cd8c68261baa09e9f3622bf"), "employees" : [ "Ant", "Bee" ], "dept" : "A", "fiscal_year" : 2017 } { "_id" : ObjectId("5cd8c68261baa09e9f3622c0"), "employees" : [ "Bee", "Cat" ], "dept" : "Z", "fiscal_year" : 2018 } { "_id" : ObjectId("5cd8c68261baa09e9f3622c1"), "employees" : [ "Cat" ], "dept" : "Z", "fiscal_year" : 2017 }
orgArchive
0} 컬렉션은 및 필드에 고유한 복합 인덱스가 fiscal_year
dept
있습니다. 특히 동일한 회계 연도 및 부서 조합에 대해 최대 하나의 레코드가 있어야 합니다.
db.getSiblingDB("reporting").orgArchive.createIndex ( { fiscal_year: 1, dept: 1 }, { unique: true } )
현재 회계연도 말(이 예에서는 2019
)에 salaries
컬렉션에 다음 문서가 포함됩니다.
{ "_id" : 1, "employee" : "Ant", "dept" : "A", "salary" : 100000, "fiscal_year" : 2017 } { "_id" : 2, "employee" : "Bee", "dept" : "A", "salary" : 120000, "fiscal_year" : 2017 } { "_id" : 3, "employee" : "Cat", "dept" : "Z", "salary" : 115000, "fiscal_year" : 2017 } { "_id" : 4, "employee" : "Ant", "dept" : "A", "salary" : 115000, "fiscal_year" : 2018 } { "_id" : 5, "employee" : "Bee", "dept" : "Z", "salary" : 145000, "fiscal_year" : 2018 } { "_id" : 6, "employee" : "Cat", "dept" : "Z", "salary" : 135000, "fiscal_year" : 2018 } { "_id" : 7, "employee" : "Gecko", "dept" : "A", "salary" : 100000, "fiscal_year" : 2018 } { "_id" : 8, "employee" : "Ant", "dept" : "A", "salary" : 125000, "fiscal_year" : 2019 } { "_id" : 9, "employee" : "Bee", "dept" : "Z", "salary" : 160000, "fiscal_year" : 2019 } { "_id" : 10, "employee" : "Cat", "dept" : "Z", "salary" : 150000, "fiscal_year" : 2019 } { "_id" : 11, "employee" : "Wren", "dept" : "Z", "salary" : 100000, "fiscal_year" : 2019 } { "_id" : 12, "employee" : "Zebra", "dept" : "A", "salary" : 150000, "fiscal_year" : 2019 } { "_id" : 13, "employee" : "headcount1", "dept" : "Z", "salary" : 120000, "fiscal_year" : 2020 } { "_id" : 14, "employee" : "headcount2", "dept" : "Z", "salary" : 120000, "fiscal_year" : 2020 }
방금 종료된 회계연도 2019
를 포함하도록 orgArchive
컬렉션을 업데이트하기 위해 다음 집계 파이프라인은 다음을 사용합니다.
$match
단계는fiscal_year
가2019
인 모든 문서를 찾는 단계입니다.$group
단계는fiscal_year
및dept
별로 직원을 그룹화합니다.$project
단계에서_id
필드를 생략하고 별도의dept
및fiscal_year
필드를 추가합니다. 문서가$merge
로 전달되면$merge
는 문서에 대한 새_id
필드를 자동으로 생성합니다.$merge
결과를orgArchive
로 설정합니다.$merge
단계는dept
및fiscal_year
필드에 있는 문서를 일치시키고 일치하지 않으면fails
로 표시합니다. 즉, 동일한 부서 및 회계 연도에 대한 문서가 이미 존재하는 경우$merge
오류가 발생합니다.
db.getSiblingDB("zoo").salaries.aggregate( [ { $match: { fiscal_year: 2019 }}, { $group: { _id: { fiscal_year: "$fiscal_year", dept: "$dept" }, employees: { $push: "$employee" } } }, { $project: { _id: 0, dept: "$_id.dept", fiscal_year: "$_id.fiscal_year", employees: 1 } }, { $merge : { into : { db: "reporting", coll: "orgArchive" }, on: [ "dept", "fiscal_year" ], whenMatched: "fail" } } ] )
작업 후 orgArchive
컬렉션에는 다음 문서가 포함됩니다.
{ "_id" : ObjectId("5caccc6a66b22dd8a8cc419f"), "employees" : [ "Ahn", "Bess" ], "dept" : "A", "fiscal_year" : 2017 } { "_id" : ObjectId("5caccc6a66b22dd8a8cc419e"), "employees" : [ "Ahn", "Gee" ], "dept" : "A", "fiscal_year" : 2018 } { "_id" : ObjectId("5caccd0b66b22dd8a8cc438e"), "employees" : [ "Ahn", "Zeb" ], "dept" : "A", "fiscal_year" : 2019 } { "_id" : ObjectId("5caccc6a66b22dd8a8cc41a0"), "employees" : [ "Carl" ], "dept" : "Z", "fiscal_year" : 2017 } { "_id" : ObjectId("5caccc6a66b22dd8a8cc41a1"), "employees" : [ "Bess", "Carl" ], "dept" : "Z", "fiscal_year" : 2018 } { "_id" : ObjectId("5caccd0b66b22dd8a8cc438d"), "employees" : [ "Bess", "Carl", "Wen" ], "dept" : "Z", "fiscal_year" : 2019 }
orgArchive
컬렉션에 "A"
및/또는 "B"
부서에 대한 2019년 문서가 이미 포함되어 있는 경우 중복 키 오류로 인해 집계가 실패합니다. 그러나 오류가 발생하기 전에 삽입된 문서는 롤백되지 않습니다.
일치하는 문서에 대해 keepExisting을 지정하면 집계가 일치하는 문서에 영향을 주지 않고 중복 키 오류를 발생시키지 않습니다. 마찬가지로 바꾸기를 지정해도 작업이 실패하지는 않지만 기존 문서를 대체하게 됩니다.
여러 컬렉션의 결과 병합
기본적으로 집계 결과의 문서가 컬렉션의 문서와 일치하면 $merge
단계에서 문서가 병합됩니다.
예시 컬렉션 purchaseorders
은 분기 및 리전별 구매 주문 정보로 채워집니다.
db.purchaseorders.insertMany( [ { _id: 1, quarter: "2019Q1", region: "A", qty: 200, reportDate: new Date("2019-04-01") }, { _id: 2, quarter: "2019Q1", region: "B", qty: 300, reportDate: new Date("2019-04-01") }, { _id: 3, quarter: "2019Q1", region: "C", qty: 700, reportDate: new Date("2019-04-01") }, { _id: 4, quarter: "2019Q2", region: "B", qty: 300, reportDate: new Date("2019-07-01") }, { _id: 5, quarter: "2019Q2", region: "C", qty: 1000, reportDate: new Date("2019-07-01") }, { _id: 6, quarter: "2019Q2", region: "A", qty: 400, reportDate: new Date("2019-07-01") }, ] )
예시 collection reportedsales
는 분기 및 리전별로 보고된 판매 정보로 채워집니다.
db.reportedsales.insertMany( [ { _id: 1, quarter: "2019Q1", region: "A", qty: 400, reportDate: new Date("2019-04-02") }, { _id: 2, quarter: "2019Q1", region: "B", qty: 550, reportDate: new Date("2019-04-02") }, { _id: 3, quarter: "2019Q1", region: "C", qty: 1000, reportDate: new Date("2019-04-05") }, { _id: 4, quarter: "2019Q2", region: "B", qty: 500, reportDate: new Date("2019-07-02") }, ] )
보고 목적으로 다음 형식으로 분기별로 데이터를 보려고 한다고 가정합니다.
{ "_id" : "2019Q1", "sales" : 1950, "purchased" : 1200 } { "_id" : "2019Q2", "sales" : 500, "purchased" : 1700 }
$merge
를 사용하여 purchaseorders
컬렉션과 reportedsales
컬렉션의 결과를 병합하여 새 컬렉션 quarterlyreport
를 만들 수 있습니다.
0} 컬렉션을 만들려면 다음 파이프라인을 사용할 수 있습니다:quarterlyreport
db.purchaseorders.aggregate( [ { $group: { _id: "$quarter", purchased: { $sum: "$qty" } } }, // group purchase orders by quarter { $merge : { into: "quarterlyreport", on: "_id", whenMatched: "merge", whenNotMatched: "insert" } } ])
- 첫 번째 단계:
2} 단계는
$group
분기별로 그룹화하고 를 사용하여 필드를$sum
새qty
purchased
필드에 추가합니다. 예시:quarterlyreport
0} 컬렉션을 만들려면 이 파이프라인을 사용할 수 있습니다:{ "_id" : "2019Q2", "purchased" : 1700 } { "_id" : "2019Q1", "purchased" : 1200 } - 두 번째 단계:
$merge
단계에서는 동일한 데이터베이스의quarterlyreport
컬렉션에 문서를 씁니다. 해당 단계가 컬렉션에서_id
필드와 일치하는 기존 문서를 찾으면 해당 단계는 일치하는 문서를 병합합니다. 그렇지 않으면 단계가 문서를 삽입합니다. 처음 생성할 때는 일치하는 문서가 없어야 합니다.
컬렉션의 문서를 보려면 다음 작업을 실행합니다.
db.quarterlyreport.find().sort( { _id: 1 } )
컬렉션에는 다음 문서가 포함되어 있습니다.
{ "_id" : "2019Q1", "sales" : 1200, "purchased" : 1200 } { "_id" : "2019Q2", "sales" : 1700, "purchased" : 1700 }
마찬가지로, reportedsales
컬렉션에 대해 다음 집계 파이프라인을 실행하여 판매 결과를 quarterlyreport
컬렉션에 병합합니다.
db.reportedsales.aggregate( [ { $group: { _id: "$quarter", sales: { $sum: "$qty" } } }, // group sales by quarter { $merge : { into: "quarterlyreport", on: "_id", whenMatched: "merge", whenNotMatched: "insert" } } ])
- 첫 번째 단계:
2} 단계는
$group
분기별로 그룹화하고 를 사용하여 필드를$sum
새qty
sales
필드에 추가합니다. 예시:{ "_id" : "2019Q2", "sales" : 500 } { "_id" : "2019Q1", "sales" : 1950 } - 두 번째 단계:
$merge
스테이지에서는 동일한 데이터베이스의quarterlyreport
컬렉션에 문서를 씁니다._id
필드(분기)에서 일치하는 기존 문서가 컬렉션에서 발견되면 스테이지에서 일치하는 문서를 병합합니다. 그렇지 않으면 스테이지가 문서를 삽입합니다.
데이터가 병합된 후 quarterlyreport
컬렉션의 문서를 보려면 다음 작업을 실행하세요.
db.quarterlyreport.find().sort( { _id: 1 } )
컬렉션에는 다음 문서가 포함되어 있습니다.
{ "_id" : "2019Q1", "sales" : 1950, "purchased" : 1200 } { "_id" : "2019Q2", "sales" : 500, "purchased" : 1700 }
파이프라인을 사용하여 병합 사용자 지정하기
$merge
는 문서가 일치할 때 사용자 지정 업데이트 파이프라인을 사용할 수 있습니다. whenMatched 파이프라인은 다음과 같은 단계를 가질 수 있습니다.
$addFields
및 그 별칭$set
$replaceRoot
및 그 별칭$replaceWith
예시 컬렉션 votes
은 일일 투표 집계로 채워집니다. 다음 문서로 collection을 생성합니다.
db.votes.insertMany( [ { date: new Date("2019-05-01"), "thumbsup" : 1, "thumbsdown" : 1 }, { date: new Date("2019-05-02"), "thumbsup" : 3, "thumbsdown" : 1 }, { date: new Date("2019-05-03"), "thumbsup" : 1, "thumbsdown" : 1 }, { date: new Date("2019-05-04"), "thumbsup" : 2, "thumbsdown" : 2 }, { date: new Date("2019-05-05"), "thumbsup" : 6, "thumbsdown" : 10 }, { date: new Date("2019-05-06"), "thumbsup" : 13, "thumbsdown" : 16 } ] )
또 다른 예시 컬렉션 monthlytotals
에는 최신 월별 투표 합계가 있습니다. 다음 문서로 collection을 생성합니다.
db.monthlytotals.insertOne( { "_id" : "2019-05", "thumbsup" : 26, "thumbsdown" : 31 } )
매일 하루가 끝나면 그날의 투표가 votes
컬렉션에 삽입됩니다:
db.votes.insertOne( { date: new Date("2019-05-07"), "thumbsup" : 14, "thumbsdown" : 10 } )
사용자 지정 파이프라인과 함께 $merge
를 사용하여 컬렉션 monthlytotals
의 기존 문서를 업데이트할 수 있습니다:
db.votes.aggregate([ { $match: { date: { $gte: new Date("2019-05-07"), $lt: new Date("2019-05-08") } } }, { $project: { _id: { $dateToString: { format: "%Y-%m", date: "$date" } }, thumbsup: 1, thumbsdown: 1 } }, { $merge: { into: "monthlytotals", on: "_id", whenMatched: [ { $addFields: { thumbsup: { $add:[ "$thumbsup", "$$new.thumbsup" ] }, thumbsdown: { $add: [ "$thumbsdown", "$$new.thumbsdown" ] } } } ], whenNotMatched: "insert" } } ])
- 첫 번째 단계:
$match
단계에서는 특정 날짜의 투표를 찾습니다. 예시:{ "_id" : ObjectId("5ce6097c436eb7e1203064a6"), "date" : ISODate("2019-05-07T00:00:00Z"), "thumbsup" : 14, "thumbsdown" : 10 } - 두 번째 단계:
$project
단계는_id
필드를 연도-월 문자열로 설정합니다. 예시:{ "thumbsup" : 14, "thumbsdown" : 10, "_id" : "2019-05" } - 세 번째 단계:
$merge
단계에서는 동일한 데이터베이스의monthlytotals
컬렉션에 문서를 씁니다. 스테이지가 컬렉션에서_id
필드와 일치하는 기존 문서를 찾으면 스테이지는 파이프라인을 사용하여thumbsup
표와thumbsdown
표를 추가합니다.이 파이프라인은 결과 문서의 필드에 직접 액세스할 수 없습니다. 결과 문서의
thumbsup
필드와thumbsdown
필드에 액세스하기 위해 파이프라인은$$new
변수 (예:$$new.thumbsup
및$new.thumbsdown
) 를 사용합니다.이 파이프라인은 컬렉션에 있는 기존 문서의
thumbsup
필드와thumbsdown
필드 (예:$thumbsup
및$thumbsdown
) 에 직접 액세스할 수 있습니다.
결과 문서가 기존 문서를 대체합니다.
병합 작업 후 monthlytotals
컬렉션의 문서를 보려면 다음 작업을 실행하세요.
db.monthlytotals.find()
컬렉션에는 다음 문서가 포함되어 있습니다.
{ "_id" : "2019-05", "thumbsup" : 40, "thumbsdown" : 41 }
변수를 사용하여 병합 사용자 지정
$merge
단계의 whenMatched 필드에 변수를 사용할 수 있습니다. 변수를 사용하려면 먼저 변수를 정의해야 합니다.
다음 중 하나 또는 둘 모두에서 변수를 정의합니다:
때매치에서변수를 사용하려면:
변수 이름과 함께 이중 달러 기호 ($$) 접두사를 $$<variable_name>
형식으로 지정합니다. 예시: $$year
. 변수가 문서로 설정된 경우 $$<variable_name>.<field>
형식으로 문서 필드를 포함할 수도 있습니다. 예시: $$year.month
.
아래 탭은 병합 단계, 집계 명령 또는 둘 다에서 변수가 정의될 때의 동작을 보여줍니다.
병합 단계에서 정의된 변수 사용
$merge
단계에서 변수를 정의 하고 whenMatched 필드에서 변수를 사용할 수 있습니다.
예시:
db.cakeSales.insertOne( [ { _id: 1, flavor: "chocolate", salesTotal: 1580, salesTrend: "up" } ] ) db.runCommand( { aggregate: db.cakeSales.getName(), pipeline: [ { $merge: { into: db.cakeSales.getName(), let : { year: "2020" }, whenMatched: [ { $addFields: { "salesYear": "$$year" } } ] } } ], cursor: {} } ) db.cakeSales.find()
예시입니다:
라는 이름의 컬렉션 생성
cakeSales
$merge
let에서year
변수를 정의하는aggregate
명령을 실행하고 whenMatched를 사용하여cakeSales
에 연도를 추가합니다.retrieves the
cakeSales
document
출력:
{ "_id" : 1, "flavor" : "chocolate", "salesTotal" : 1580, "salesTrend" : "up", "salesYear" : "2020" }
집계 명령에 정의된 변수 사용
버전 5.0에 추가.
aggregate
명령 let에서 변수를 정의하고 $merge
단계 whenMatched 필드에서 변수를 사용할 수 있습니다.
예시:
db.cakeSales.insertOne( { _id: 1, flavor: "chocolate", salesTotal: 1580, salesTrend: "up" } ) db.runCommand( { aggregate: db.cakeSales.getName(), pipeline: [ { $merge: { into: db.cakeSales.getName(), whenMatched: [ { $addFields: { "salesYear": "$$year" } } ] } } ], cursor: {}, let : { year: "2020" } } ) db.cakeSales.find()
예시입니다:
라는 이름의 컬렉션 생성
cakeSales
aggregate
명령 let에서year
변수를 정의하는aggregate
명령을 실행하고 whenMatched를 사용하여cakeSales
에 연도를 추가합니다.retrieves the
cakeSales
document
출력:
{ "_id" : 1, "flavor" : "chocolate", "salesTotal" : 1580, "salesTrend" : "up", "salesYear" : "2020" }
병합 단계 및 집계 명령에 정의된 변수 사용
$merge
단계에서 변수를 정의할 수 있으며 MongoDB 5.0부터는 aggregate
명령입니다.
동일한 이름을 가진 두 개의 변수가 $merge
단계와 aggregate
명령에 정의된 경우 $merge
단계 변수가 사용됩니다.
이 예에서는 year: "2019"
명령 aggregate
변수 대신 year: "2020"
$merge
단계 변수가 사용됩니다.
db.cakeSales.insertOne( { _id: 1, flavor: "chocolate", salesTotal: 1580, salesTrend: "up" } ) db.runCommand( { aggregate: db.cakeSales.getName(), pipeline: [ { $merge: { into: db.cakeSales.getName(), let : { year: "2020" }, whenMatched: [ { $addFields: { "salesYear": "$$year" } } ] } } ], cursor: {}, let : { year: "2019" } } ) db.cakeSales.find()
출력:
{ _id: 1, flavor: 'chocolate', salesTotal: 1580, salesTrend: 'up', salesYear: '2020' }