Docs Menu
Docs Home
/
MongoDB 매뉴얼
/ / /

$lookup (집계)

이 페이지의 내용

  • 정의
  • 호환성
  • 구문
  • 단일 조인 조건으로 동일성 일치
  • 조인된 컬렉션의 조인 조건 및 하위 쿼리
  • 간결한 구문을 사용한 상호 연관된 하위 쿼리
  • 행동
  • 보기 및 데이터 정렬
  • 제한 사항
  • Atlas Search 지원
  • 샤드 컬렉션
  • 슬롯 기반 쿼리 실행 엔진
  • 성능 고려 사항
  • 예시
  • 단일 동일성 조인 수행하기 - $lookup
  • 배열에 $lookup 사용
  • $mergeObjects과 함께 $lookup사용
  • 다중 조인 조건 및 상관관계 하위 쿼리 사용
  • 상관관계가 없는 하위 쿼리 수행하기 - $lookup
  • 상관관계가 있는 간결한 하위 쿼리 수행하기 - $lookup
  • 하위 파이프라인의 네임스페이스
$lookup

버전 8.0에서 변경되었습니다.

동일한 데이터베이스의 컬렉션에 왼쪽 외부 조인을 수행하여 "조인된" 컬렉션의 문서를 필터링하여 처리합니다. $lookup 단계에서는 각 입력 문서에 새 배열 필드를 추가합니다. 새 배열 필드에는 '조인'된 컬렉션에서 일치하는 문서들이 포함됩니다. $lookup 단계에서는 이렇게 재구성된 문서를 다음 단계로 넘깁니다.

MongoDB 5.1부터 샤딩된 컬렉션에 $lookup을 사용할 수 있습니다.

서로 다른 두 컬렉션의 요소를 결합하려면 $unionWith 파이프라인 단계를 사용합니다.

다음 환경에서 호스팅되는 배포에 $lookup 사용할 수 있습니다.

  • MongoDB Atlas: 클라우드에서의 MongoDB 배포를 위한 완전 관리형 서비스

$lookup 단계에는 다음 섹션에 표시된 구문 변형이 있습니다.

입력 문서의 필드와 '조인된' 컬렉션의 문서 필드 간에 동등성 매치를 수행하려면 $lookup 단계는 다음과 같은 구문을 가집니다.

{
$lookup:
{
from: <collection to join>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
as: <output array field>
}
}

$lookup 다음과 같은 필드가 있는 문서를 받습니다.

필드
설명

조인을 수행할 동일한 데이터베이스의 컬렉션을 지정합니다.

from 선택 사항이므로 대신 $lookup 단계에서 $documents 단계를 사용할 수 있습니다. 예시는 $lookup 단계에서 $documents 단계 사용을 참조하세요.

MongoDB 5.1부터 from 매개변수에 지정된 컬렉션을 샤딩할 수 있습니다.

$lookup 단계로 입력된 문서의 필드를 지정합니다. $lookupfrom 컬렉션의 문서에서 foreignField에 대한 localField에 대해 동등성 매치를 수행합니다. 입력 문서에 localField이 포함되어 있지 않은 경우 $lookup은 매칭을 위해 해당 필드의 값을 null로 간주합니다.

from 컬렉션의 문서에서 필드를 지정합니다. $lookup은 입력 문서의 foreignField에서 localField에 대해 동등성 매치를 수행합니다. from 컬렉션의 문서가 foreignField를 포함하지 않는 경우 $lookup은 매칭을 위해 값을 null로 간주합니다.

입력 문서에 추가할 새 배열 필드의 이름을 지정합니다. 새 배열 필드에는 from 컬렉션에서 매칭되는 문서가 포함됩니다. 지정된 이름이 입력 문서에 이미 존재하는 경우 기존 필드를 덮어씁니다.

이 작업은 다음과 같은 유사 SQL 문장에 해당합니다.

SELECT *, (
SELECT ARRAY_AGG(*)
FROM <collection to join>
WHERE <foreignField> = <collection.localField>
) AS <output array field>
FROM collection;

참고

이 페이지의 SQL 문은 MongoDB 집계 파이프라인 구문과 비교할 수 있도록 포함되어 있습니다. SQL 문을 실행할 수 없습니다.

MongoDB 예시는 이 페이지를 참조하세요.

MongoDB는 다음을 지원합니다.

  • 조인된 컬렉션에서 파이프라인 실행하기.

  • 여러 조인 조건.

  • 상관관계가 있는 하위 쿼리와 상관관계가 없는 하위 쿼리.

MongoDB에서 상관 관계 하위 쿼리는 조인된 컬렉션의 문서 필드를 참조하는 $lookup 단계의 파이프라인입니다. 상관 관계가 없는 하위 쿼리는 조인된 필드를 참조하지 않습니다.

참고

MongoDB 5.0부터는 $lookup 파이프라인 단계에서 $sample 단계, $sampleRate 연산자 또는 $rand 연산자를 포함하는 상관 관계가 없는 하위 쿼리의 경우, 반복 시 항상 하위 쿼리가 다시 실행됩니다. 이전에는 하위 쿼리 출력 크기에 따라 하위 쿼리 출력이 캐시되거나 하위 쿼리가 다시 실행되었습니다.

MongoDB 상관 서브쿼리는 내부 쿼리가 외부 쿼리 값을 참조하는 SQL 상관 서브쿼리와 유사합니다. SQL 비연관 서브쿼리는 외부 쿼리 값을 참조하지 않습니다.

MongoDB 5.0은 상관관계가 있는 간결한 하위 쿼리도 지원합니다.

두 컬렉션에 대해 상관 관계 및 비상관 관계 하위 쿼리를 수행하고 단일 동등성 매치 외에 다른 조인 조건을 수행하려면 이 $lookup 구문을 사용합니다.

{
$lookup:
{
from: <joined collection>,
let: { <var_1>: <expression>, …, <var_n>: <expression> },
pipeline: [ <pipeline to run on joined collection> ],
as: <output array field>
}
}

$lookup 단계에서는 다음과 같은 필드가 있는 문서를 수락합니다.

필드
설명

조인 작업을 수행할 동일한 데이터베이스의 컬렉션을 지정합니다.

from 선택 사항이므로 대신 $lookup 단계에서 $documents 단계를 사용할 수 있습니다. 예시는 $lookup 단계에서 $documents 단계 사용을 참조하세요.

MongoDB 5.1부터 from 컬렉션을 샤딩할 수 있습니다.

선택 사항입니다. 파이프라인 단계에서 사용할 변수를 지정합니다. 변수 표현식을 사용하여 pipeline에 입력되는 조인된 컬렉션 문서의 필드에 액세스하세요.

파이프라인 단계에서 변수를 참조하려면 "$$<variable>" 구문을 사용합니다.

pipeline 내에 중첩된 추가 $lookup 단계를 포함하여 파이프라인의 단계에서 let 변수에 액세스할 수 있습니다.

  • $match 단계에서는 $expr 연산자를 사용하여 변수에 액세스해야 합니다. $expr 연산자를 사용하면 $match 구문 내에서 집계 표현식을 사용할 수 있습니다.

    $expr 연산자에 배치된 $eq, $lt, $lte, $gt, $gte 비교 연산자는 $lookup 단계에서 참조된 from 컬렉션의 인덱스를 사용할 수 있습니다. 제한 사항:

    • 인덱스는 필드와 상수 간의 비교에만 사용할 수 있으므로 let 피연산자는 상수로 해결되어야 합니다.

      예를 들어 $a 및 상수 값 간의 비교에는 인덱스를 사용할 수 있지만, $a$b 간의 비교에는 인덱스를 사용할 수 없습니다.

    • let 피연산자가 비어 있거나 누락된 값으로 확인되는 비교에는 인덱스가 사용되지 않습니다.

    • 다중 키 인덱스 는 사용되지 않습니다.

  • 파이프라인에서 $match가 아닌 다른 단계에서는 변수에 액세스하기 위해 $expr 연산자가 필요하지 않습니다.

조인된 컬렉션에서 실행할 pipeline을 지정합니다. pipeline은 조인된 컬렉션의 결과 문서를 결정합니다. 모든 문서를 반환하려면 빈 pipeline []을 지정합니다.

pipeline에는 $out 단계 또는 $merge 단계를 포함할 수 없습니다. v6.0부터 pipeline에는 파이프라인 내부의 첫 번째 단계로 Atlas Search $search 단계가 포함될 수 있습니다. 자세히 알아보려면 Atlas Search 지원을 참조하세요.

pipeline은 조인된 문서 필드에 직접 액세스할 수 없습니다. 대신 let 옵션을 사용하여 조인된 문서 필드에 대한 변수를 정의한 후 pipeline 단계에서 변수를 참조하세요.

파이프라인 단계에서 변수를 참조하려면 "$$<variable>" 구문을 사용합니다.

pipeline 내에 중첩된 추가 $lookup 단계를 포함하여 파이프라인의 단계에서 let 변수에 액세스할 수 있습니다.

  • $match 단계에서는 $expr 연산자를 사용하여 변수에 액세스해야 합니다. $expr 연산자를 사용하면 $match 구문 내에서 집계 표현식을 사용할 수 있습니다.

    $expr 연산자에 배치된 $eq, $lt, $lte, $gt, $gte 비교 연산자는 $lookup 단계에서 참조된 from 컬렉션의 인덱스를 사용할 수 있습니다. 제한 사항:

    • 인덱스는 필드와 상수 간의 비교에만 사용할 수 있으므로 let 피연산자는 상수로 해결되어야 합니다.

      예를 들어 $a 및 상수 값 간의 비교에는 인덱스를 사용할 수 있지만, $a$b 간의 비교에는 인덱스를 사용할 수 없습니다.

    • let 피연산자가 비어 있거나 누락된 값으로 확인되는 비교에는 인덱스가 사용되지 않습니다.

    • 다중 키 인덱스 는 사용되지 않습니다.

  • 파이프라인에서 $match가 아닌 다른 단계에서는 변수에 액세스하기 위해 $expr 연산자가 필요하지 않습니다.

조인된 문서에 추가할 새 배열 필드의 이름을 지정합니다. 새 배열 필드에는 조인된 컬렉션에서 일치하는 문서가 포함됩니다. 지정한 이름이 조인된 문서에 이미 존재하는 경우 기존 필드를 덮어씁니다.

이 작업은 다음과 같은 유사 SQL 문장에 해당합니다.

SELECT *, <output array field>
FROM collection
WHERE <output array field> IN (
SELECT <documents as determined from the pipeline>
FROM <collection to join>
WHERE <pipeline>
);

다음 예시를 참조하세요.

버전 5.0에 추가.

MongoDB 5.0부터는 상관관계가 있는 하위 쿼리에 간결한 구문을 사용할 수 있습니다. 연결된 하위 쿼리는 조인된 "외부" 컬렉션 aggregate() 메서드가 실행된 "로컬" 컬렉션의 문서 필드를 참조합니다.

다음과 같은 새롭고 간결한 구문은 $expr 연산자 내부의 외부 및 로컬 필드에 대한 동등성 매치 요구 사항을 제거합니다.

{
$lookup:
{
from: <foreign collection>,
localField: <field from local collection's documents>,
foreignField: <field from foreign collection's documents>,
let: { <var_1>: <expression>, …, <var_n>: <expression> },
pipeline: [ <pipeline to run> ],
as: <output array field>
}
}

$lookup은 다음과 같은 필드가 있는 문서를 수락합니다.

필드
설명

동일한 데이터베이스에서 로컬 컬렉션에 조인할 외부 컬렉션을 지정합니다.

from 선택 사항이므로 대신 $lookup 단계에서 $documents 단계를 사용할 수 있습니다. 예시는 $lookup 단계에서 $documents 단계 사용을 참조하세요.

MongoDB 5.1부터 from 컬렉션을 샤딩할 수 있습니다.

외부 문서의 foreignField와 동등성 매치를 수행하도록 로컬 문서의 localField를 지정합니다.

로컬 문서에 localField 값이 포함되어 있지 않은 경우 $lookup은 일치 항목에 null 값을 사용합니다.

로컬 문서의 localField와 동등성 매치를 수행하도록 외부 문서의 foreignField를 지정합니다.

외부 문서에 foreignField 값이 포함되어 있지 않은 경우 $lookup은 일치 항목에 null 값을 사용합니다.

선택 사항입니다. 파이프라인 단계에서 사용할 변수를 지정합니다. 변수 표현식을 사용하여 pipeline에 입력되는 문서 필드에 액세스합니다.

파이프라인 단계에서 변수를 참조하려면 "$$<variable>" 구문을 사용합니다.

pipeline 내에 중첩된 추가 $lookup 단계를 포함하여 파이프라인의 단계에서 let 변수에 액세스할 수 있습니다.

  • $match 단계에서는 $expr 연산자를 사용하여 변수에 액세스해야 합니다. $expr 연산자를 사용하면 $match 구문 내에서 집계 표현식을 사용할 수 있습니다.

    $expr 연산자에 배치된 $eq, $lt, $lte, $gt, $gte 비교 연산자는 $lookup 단계에서 참조된 from 컬렉션의 인덱스를 사용할 수 있습니다. 제한 사항:

    • 인덱스는 필드와 상수 간의 비교에만 사용할 수 있으므로 let 피연산자는 상수로 해결되어야 합니다.

      예를 들어 $a 및 상수 값 간의 비교에는 인덱스를 사용할 수 있지만, $a$b 간의 비교에는 인덱스를 사용할 수 없습니다.

    • let 피연산자가 비어 있거나 누락된 값으로 확인되는 비교에는 인덱스가 사용되지 않습니다.

    • 다중 키 인덱스 는 사용되지 않습니다.

  • 파이프라인에서 $match가 아닌 다른 단계에서는 변수에 액세스하기 위해 $expr 연산자가 필요하지 않습니다.

외부 컬렉션에서 실행할 pipeline을 지정합니다. pipeline은 외부 컬렉션에서 문서를 반환합니다. 모든 문서를 반환하려면 빈 pipeline []을 지정합니다.

pipeline에는 $out 또는 $merge 단계는 포함할 수 없습니다. v6.0부터 pipeline에는 파이프라인 내부의 첫 번째 단계로 Atlas Search $search 단계가 포함될 수 있습니다. 자세히 알아보려면 Atlas Search 지원을 참조하세요.

pipeline은 문서 필드에 직접 액세스할 수 없습니다. 대신 let 옵션을 사용하여 문서 필드에 대한 변수를 정의한 후 pipeline 단계에서 변수를 참조하세요.

파이프라인 단계에서 변수를 참조하려면 "$$<variable>" 구문을 사용합니다.

pipeline 내에 중첩된 추가 $lookup 단계를 포함하여 파이프라인의 단계에서 let 변수에 액세스할 수 있습니다.

  • $match 단계에서는 $expr 연산자를 사용하여 변수에 액세스해야 합니다. $expr 연산자를 사용하면 $match 구문 내에서 집계 표현식을 사용할 수 있습니다.

    $expr 연산자에 배치된 $eq, $lt, $lte, $gt, $gte 비교 연산자는 $lookup 단계에서 참조된 from 컬렉션의 인덱스를 사용할 수 있습니다. 제한 사항:

    • 인덱스는 필드와 상수 간의 비교에만 사용할 수 있으므로 let 피연산자는 상수로 해결되어야 합니다.

      예를 들어 $a 및 상수 값 간의 비교에는 인덱스를 사용할 수 있지만, $a$b 간의 비교에는 인덱스를 사용할 수 없습니다.

    • let 피연산자가 비어 있거나 누락된 값으로 확인되는 비교에는 인덱스가 사용되지 않습니다.

    • 다중 키 인덱스 는 사용되지 않습니다.

  • 파이프라인에서 $match가 아닌 다른 단계에서는 변수에 액세스하기 위해 $expr 연산자가 필요하지 않습니다.

외부 문서에 추가할 새 배열 필드의 이름을 지정합니다. 새 배열 필드에는 외부 컬렉션에서 일치하는 문서가 포함됩니다. 지정한 이름이 외부 문서에 이미 존재하는 경우 기존 필드를 덮어씁니다.

이 작업은 다음과 같은 유사 SQL 문장에 해당합니다.

SELECT *, <output array field>
FROM localCollection
WHERE <output array field> IN (
SELECT <documents as determined from the pipeline>
FROM <foreignCollection>
WHERE <foreignCollection.foreignField> = <localCollection.localField>
AND <pipeline match condition>
);

이 예시를 참조하세요.

$lookup 또는 $graphLookup 등 여러 뷰가 포함된 집계를 수행하는 경우 반드시 뷰의 데이터 정렬이 동일해야 합니다.

$lookup단계에는 $out 단계나 $merge 단계를 포함할 수 없습니다. 즉, 조인(join)된 컬렉션에 대한 파이프라인을 지정할 때 pipeline 필드에 어느 단계도 포함할 수 없습니다.

{
$lookup:
{
from: <collection to join>,
let: { <var_1>: <expression>, …, <var_n>: <expression> },
pipeline: [ <pipeline to execute on the joined collection> ], // Cannot include $out or $merge
as: <output array field>
}
}

MongoDB 6.0부터 $lookup 파이프라인에서 Atlas Search $search 또는 $searchMeta 단계를 지정하여 Atlas 클러스터에서 컬렉션을 검색할 수 있습니다. $search 또는 $searchMeta 단계는 $lookup 파이프라인 내의 첫 번째 단계여야 합니다.

예를 들어, 조인된 컬렉션에서 조건 및 하위 쿼리를 조인하거나 간결한 구문을 사용하여 상호 연관된 하위 쿼리를 실행하는 경우 아래와 같이 파이프라인에 $search 또는 $searchMeta를 지정할 수 있습니다.

[{
"$lookup": {
"from": <joined collection>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
"as": <output array field>,
"pipeline": [{
"$search": {
"<operator>": {
<operator-specification>
}
},
...
}]
}
}]
[{
"$lookup": {
"from": <joined collection>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
"as": <output array field>,
"pipeline": [{
"$searchMeta": {
"<collector>": {
<collector-specification>
}
},
...
}]
}
}]

$search를 사용한 $lookup의 예를 보려면 $lookup을 사용하여 Atlas Search $search 쿼리 실행하기라는 Atlas Search 튜토리얼을 참조하세요.

MongoDB 5.1부터 $lookup 단계의 from 파라미터에서 샤딩된 컬렉션을 지정할 수 있습니다.

MongoDB 8.0부터 샤딩된 컬렉션을 대상으로 하는 트랜잭션 내에서 $lookup 단계를 사용할 수 있습니다.

6.0버전부터 파이프라인의 모든 이전 단계가 슬롯 기반 실행 엔진에서도 실행될 수 있고 다음 조건 중 어느 것도 참이 아닌 경우 MongoDB는 슬롯 기반 실행 쿼리 엔진을 사용하여 $lookup 단계를 실행할 수 있습니다.

  • $lookup 작업은 조인된 컬렉션에서 파이프라인을 실행합니다. 이러한 종류의 작업 예시를 보려면 조인된 컬렉션의 조인 조건 및 하위 쿼리를 참조하세요.

  • $lookuplocalField 또는 foreignField는 숫자 구성 요소를 지정합니다. 예시: { localField: "restaurant.0.review" }

  • 파이프라인에서 $lookupfrom 필드는 뷰 또는 샤드 컬렉션을 지정합니다.

자세한 내용은 $lookup 최적화를 참조하세요.

$lookup 성능은 수행되는 작업 유형에 따라 달라집니다. 다양한 $lookup 작업에 대한 성능 고려 사항은 다음 표를 참조하세요.

$lookup 작업
성능 고려 사항
  • $lookup 연산은 단일 조인으로 동등성 매치를 수행하고, 외부 컬렉션에 foreignField에 대한 인덱스가 포함된 경우 성능이 더 우수합니다.

    중요: foreignField에 대한 지원 인덱스가 없는 경우 단일 조인으로 동등성 매치를 수행하는 $lookup 작업의 성능이 저하될 가능성이 있습니다.

  • $lookup 연산은 관련 없는 하위 쿼리를 포함하며, 내부 파이프라인이 외부 컬렉션의 인덱스를 참조할 수 있을 때 성능이 더 우수합니다.

  • MongoDB는 소스 컬렉션과 외부 컬렉션 사이에 관계가 없기 때문에 쿼리를 캐싱하기 전에 $lookup 하위 쿼리를 한 번만 실행하면 됩니다. 하위 쿼리는 소스 컬렉션의 값을 기반으로 하지 않습니다. 이 동작은 $lookup 작업의 후속 실행에 대한 성능을 향상시킵니다.

  • $lookup 연산은 연관된 하위 쿼리를 포함하며, 다음의 조건이 적용될 때 성능이 더 우수합니다.

    • 외부 컬렉션에 foreignField에 대한 인덱스가 포함.

    • 외부 컬렉션에는 내부 파이프라인을 참조하는 인덱스가 포함.

  • 파이프라인이 많은 수의 문서를 $lookup 쿼리에 전달하는 경우 다음 전략을 사용하면 성능이 향상될 수 있습니다.

    • MongoDB가 $lookup 쿼리에 전달하는 문서 수를 줄입니다. 예를 들어 $match 단계에서 더 엄격한 필터를 설정합니다.

    • $lookup 하위 쿼리의 내부 파이프라인을 별도의 쿼리로 실행하고 $out를 사용하여 임시 컬렉션을 생성합니다. 그런 다음 단일 조인으로 동등성 매치를 실행합니다.

    • 데이터의 스키마를 재검토하여 사용 사례에 적합한지 확인합니다.

일반적인 성능 전략은 인덱싱 전략쿼리 최적화를 참조하세요.

중요

쿼리 내에서 $lookup을 과도하게 사용하면 성능이 저하될 수 있습니다. 여러 $lookup 단계를 피하려면 쿼리 성능을 최적화하는 임베디드 데이터 모델을 고려하세요.

이 문서로 orders 컬렉션을 만듭니다.

db.orders.insertMany( [
{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 },
{ "_id" : 3 }
] )

이 문서로 다른 컬렉션 inventory를 만듭니다.

db.inventory.insertMany( [
{ "_id" : 1, "sku" : "almonds", "description": "product 1", "instock" : 120 },
{ "_id" : 2, "sku" : "bread", "description": "product 2", "instock" : 80 },
{ "_id" : 3, "sku" : "cashews", "description": "product 3", "instock" : 60 },
{ "_id" : 4, "sku" : "pecans", "description": "product 4", "instock" : 70 },
{ "_id" : 5, "sku": null, "description": "Incomplete" },
{ "_id" : 6 }
] )

orders 컬렉션에 대한 다음 집계 작업은 orders 컬렉션의 item 필드와 inventory 컬렉션의 sku 필드를 사용하여 orders 문서를 inventory 컬렉션의 문서와 결합합니다.

db.orders.aggregate( [
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}
] )

이 작업은 다음 문서를 반환합니다.

{
"_id" : 1,
"item" : "almonds",
"price" : 12,
"quantity" : 2,
"inventory_docs" : [
{ "_id" : 1, "sku" : "almonds", "description" : "product 1", "instock" : 120 }
]
}
{
"_id" : 2,
"item" : "pecans",
"price" : 20,
"quantity" : 1,
"inventory_docs" : [
{ "_id" : 4, "sku" : "pecans", "description" : "product 4", "instock" : 70 }
]
}
{
"_id" : 3,
"inventory_docs" : [
{ "_id" : 5, "sku" : null, "description" : "Incomplete" },
{ "_id" : 6 }
]
}

이 작업은 다음과 같은 유사 SQL 문장에 해당합니다.

SELECT *, inventory_docs
FROM orders
WHERE inventory_docs IN (
SELECT *
FROM inventory
WHERE sku = orders.item
);

자세한 내용은 동등성 매치 성능 고려 사항을 참조하세요.

localField가 배열인 경우 $unwind 단계 없이 스칼라 foreignField에 일치시킬 수 있습니다.

예를 들어 다음 문서를 사용하여 예시 컬렉션 classes를 만듭니다.

db.classes.insertMany( [
{ _id: 1, title: "Reading is ...", enrollmentlist: [ "giraffe2", "pandabear", "artie" ], days: ["M", "W", "F"] },
{ _id: 2, title: "But Writing ...", enrollmentlist: [ "giraffe1", "artie" ], days: ["T", "F"] }
] )

이 문서로 다른 컬렉션 members를 만듭니다.

db.members.insertMany( [
{ _id: 1, name: "artie", joined: new Date("2016-05-01"), status: "A" },
{ _id: 2, name: "giraffe", joined: new Date("2017-05-01"), status: "D" },
{ _id: 3, name: "giraffe1", joined: new Date("2017-10-01"), status: "A" },
{ _id: 4, name: "panda", joined: new Date("2018-10-11"), status: "A" },
{ _id: 5, name: "pandabear", joined: new Date("2018-12-01"), status: "A" },
{ _id: 6, name: "giraffe2", joined: new Date("2018-12-01"), status: "D" }
] )

다음 집계 작업은 classes 컬렉션의 문서를 members 컬렉션과 결합하여 enrollmentlist 필드를 name 필드와 일치시킵니다.

db.classes.aggregate( [
{
$lookup:
{
from: "members",
localField: "enrollmentlist",
foreignField: "name",
as: "enrollee_info"
}
}
] )

이 연산은 다음을 반환합니다:

{
"_id" : 1,
"title" : "Reading is ...",
"enrollmentlist" : [ "giraffe2", "pandabear", "artie" ],
"days" : [ "M", "W", "F" ],
"enrollee_info" : [
{ "_id" : 1, "name" : "artie", "joined" : ISODate("2016-05-01T00:00:00Z"), "status" : "A" },
{ "_id" : 5, "name" : "pandabear", "joined" : ISODate("2018-12-01T00:00:00Z"), "status" : "A" },
{ "_id" : 6, "name" : "giraffe2", "joined" : ISODate("2018-12-01T00:00:00Z"), "status" : "D" }
]
}
{
"_id" : 2,
"title" : "But Writing ...",
"enrollmentlist" : [ "giraffe1", "artie" ],
"days" : [ "T", "F" ],
"enrollee_info" : [
{ "_id" : 1, "name" : "artie", "joined" : ISODate("2016-05-01T00:00:00Z"), "status" : "A" },
{ "_id" : 3, "name" : "giraffe1", "joined" : ISODate("2017-10-01T00:00:00Z"), "status" : "A" }
]
}

$mergeObjects 연산자는 여러 문서를 단일 문서로 결합합니다.

이 문서로 orders 컬렉션을 만듭니다.

db.orders.insertMany( [
{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 }
] )

이 문서로 다른 컬렉션 items를 만듭니다.

db.items.insertMany( [
{ "_id" : 1, "item" : "almonds", description: "almond clusters", "instock" : 120 },
{ "_id" : 2, "item" : "bread", description: "raisin and nut bread", "instock" : 80 },
{ "_id" : 3, "item" : "pecans", description: "candied pecans", "instock" : 60 }
] )

다음 작업은 먼저 $lookup 단계를 사용하여 item 필드를 기준으로 두 컬렉션을 조인한 다음 $replaceRoot에서 $mergeObjects를 사용하여 itemsorders에서 조인된 문서를 병합합니다.

db.orders.aggregate( [
{
$lookup: {
from: "items",
localField: "item", // field in the orders collection
foreignField: "item", // field in the items collection
as: "fromItems"
}
},
{
$replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ "$fromItems", 0 ] }, "$$ROOT" ] } }
},
{ $project: { fromItems: 0 } }
] )

이 작업은 다음 문서를 반환합니다.

{
_id: 1,
item: 'almonds',
description: 'almond clusters',
instock: 120,
price: 12,
quantity: 2
},
{
_id: 2,
item: 'pecans',
description: 'candied pecans',
instock: 60,
price: 20,
quantity: 1
}

파이프라인은 조인된 컬렉션에서 실행할 수 있으며 여러 조인 조건을 포함할 수 있습니다. $expr 연산자를 사용하면 접속사 및 비동등성 매치를 포함한 더 복잡한 조인 조건이 가능합니다.

조인 조건은 aggregate() 메서드가 실행된 로컬 컬렉션의 필드를 참조하고 조인된 컬렉션의 필드를 참조할 수 있습니다. 이렇게 하면 두 컬렉션 간에 상호 연관된 하위 쿼리가 가능합니다.

MongoDB 5.0은 상관관계가 있는 간결한 하위 쿼리를 지원합니다.

이 문서로 orders 컬렉션을 만듭니다.

db.orders.insertMany( [
{ "_id" : 1, "item" : "almonds", "price" : 12, "ordered" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "ordered" : 1 },
{ "_id" : 3, "item" : "cookies", "price" : 10, "ordered" : 60 }
] )

이 문서로 다른 컬렉션 warehouses를 만듭니다.

db.warehouses.insertMany( [
{ "_id" : 1, "stock_item" : "almonds", warehouse: "A", "instock" : 120 },
{ "_id" : 2, "stock_item" : "pecans", warehouse: "A", "instock" : 80 },
{ "_id" : 3, "stock_item" : "almonds", warehouse: "B", "instock" : 60 },
{ "_id" : 4, "stock_item" : "cookies", warehouse: "B", "instock" : 40 },
{ "_id" : 5, "stock_item" : "cookies", warehouse: "A", "instock" : 80 }
] )

다음 예제입니다.

  • orders.itemwarehouse.stock_item 필드에 대한 조인과 함께 상관된 하위 쿼리를 사용합니다.

  • 재고가 있는 품목의 수량이 주문한 수량을 충족할 수 있는지 확인합니다.

db.orders.aggregate( [
{
$lookup:
{
from: "warehouses",
let: { order_item: "$item", order_qty: "$ordered" },
pipeline: [
{ $match:
{ $expr:
{ $and:
[
{ $eq: [ "$stock_item", "$$order_item" ] },
{ $gte: [ "$instock", "$$order_qty" ] }
]
}
}
},
{ $project: { stock_item: 0, _id: 0 } }
],
as: "stockdata"
}
}
] )

이 작업은 다음 문서를 반환합니다.

{
_id: 1,
item: 'almonds',
price: 12,
ordered: 2,
stockdata: [
{ warehouse: 'A', instock: 120 },
{ warehouse: 'B', instock: 60 }
]
},
{
_id: 2,
item: 'pecans',
price: 20,
ordered: 1,
stockdata: [ { warehouse: 'A', instock: 80 } ]
},
{
_id: 3,
item: 'cookies',
price: 10,
ordered: 60,
stockdata: [ { warehouse: 'A', instock: 80 } ]
}

이 작업은 다음과 같은 유사 SQL 문장에 해당합니다.

SELECT *, stockdata
FROM orders
WHERE stockdata IN (
SELECT warehouse, instock
FROM warehouses
WHERE stock_item = orders.item
AND instock >= orders.ordered
);

$expr 연산자에 배치된 $eq, $lt, $lte, $gt, $gte 비교 연산자는 $lookup 단계에서 참조된 from 컬렉션의 인덱스를 사용할 수 있습니다. 제한 사항:

  • 인덱스는 필드와 상수 간의 비교에만 사용할 수 있으므로 let 피연산자는 상수로 해결되어야 합니다.

    예를 들어 $a 및 상수 값 간의 비교에는 인덱스를 사용할 수 있지만, $a$b 간의 비교에는 인덱스를 사용할 수 없습니다.

  • let 피연산자가 비어 있거나 누락된 값으로 확인되는 비교에는 인덱스가 사용되지 않습니다.

  • 다중 키 인덱스 는 사용되지 않습니다.

예를 들어 warehouses 컬렉션에 인덱스 { stock_item: 1, instock: 1 }가 존재하는 경우입니다.

  • warehouses.stock_item 필드의 동등성 매치는 인덱스를 사용합니다.

  • warehouses.instock 필드에 대한 쿼리의 범위 부분도 복합 인덱스의 인덱스 필드를 사용합니다.

다음도 참조하세요.

집계 파이프라인 $lookup 단계는 조인된 컬렉션에서 파이프라인을 실행할 수 있으며, 이는 상호 관련되지 않은 하위 쿼리를 허용합니다. 상호 관련되지 않은 하위 쿼리는 조인된 문서 필드를 참조하지 않습니다.

참고

MongoDB 5.0부터는 $lookup 파이프라인 단계에서 $sample 단계, $sampleRate 연산자 또는 $rand 연산자를 포함하는 상관 관계가 없는 하위 쿼리의 경우, 반복 시 항상 하위 쿼리가 다시 실행됩니다. 이전에는 하위 쿼리 출력 크기에 따라 하위 쿼리 출력이 캐시되거나 하위 쿼리가 다시 실행되었습니다.

이 문서로 absences 컬렉션을 만듭니다.

db.absences.insertMany( [
{ "_id" : 1, "student" : "Ann Aardvark", sickdays: [ new Date ("2018-05-01"),new Date ("2018-08-23") ] },
{ "_id" : 2, "student" : "Zoe Zebra", sickdays: [ new Date ("2018-02-01"),new Date ("2018-05-23") ] },
] )

이 문서로 다른 컬렉션 holidays를 만듭니다.

db.holidays.insertMany( [
{ "_id" : 1, year: 2018, name: "New Years", date: new Date("2018-01-01") },
{ "_id" : 2, year: 2018, name: "Pi Day", date: new Date("2018-03-14") },
{ "_id" : 3, year: 2018, name: "Ice Cream Day", date: new Date("2018-07-15") },
{ "_id" : 4, year: 2017, name: "New Years", date: new Date("2017-01-01") },
{ "_id" : 5, year: 2017, name: "Ice Cream Day", date: new Date("2017-07-16") }
] )

다음 작업은 holidays 컬렉션의 2018년 공휴일 정보와 함께 absences 컬렉션을 조인합니다.

db.absences.aggregate( [
{
$lookup:
{
from: "holidays",
pipeline: [
{ $match: { year: 2018 } },
{ $project: { _id: 0, date: { name: "$name", date: "$date" } } },
{ $replaceRoot: { newRoot: "$date" } }
],
as: "holidays"
}
}
] )

이 연산은 다음을 반환합니다:

{
_id: 1,
student: 'Ann Aardvark',
sickdays: [
ISODate("2018-05-01T00:00:00.000Z"),
ISODate("2018-08-23T00:00:00.000Z")
],
holidays: [
{ name: 'New Years', date: ISODate("2018-01-01T00:00:00.000Z") },
{ name: 'Pi Day', date: ISODate("2018-03-14T00:00:00.000Z") },
{ name: 'Ice Cream Day', date: ISODate("2018-07-15T00:00:00.000Z")
}
]
},
{
_id: 2,
student: 'Zoe Zebra',
sickdays: [
ISODate("2018-02-01T00:00:00.000Z"),
ISODate("2018-05-23T00:00:00.000Z")
],
holidays: [
{ name: 'New Years', date: ISODate("2018-01-01T00:00:00.000Z") },
{ name: 'Pi Day', date: ISODate("2018-03-14T00:00:00.000Z") },
{ name: 'Ice Cream Day', date: ISODate("2018-07-15T00:00:00.000Z")
}
]
}

이 작업은 다음과 같은 유사 SQL 문장에 해당합니다.

SELECT *, holidays
FROM absences
WHERE holidays IN (
SELECT name, date
FROM holidays
WHERE year = 2018
);

자세한 내용은 상관관계가 없는 하위 쿼리 성능 고려 사항을참조하세요.

버전 5.0에 추가.

MongoDB 5.0부터 집계 파이프라인 $lookup 단계는 컬렉션 간의 조인을 개선하는 간결한 상관관계 하위 쿼리 구문을 지원합니다. 새로운 간결한 구문은 $match 단계에서 $expr 연산자 내부의 외부 및 로컬 필드에 대한 동등성 매치 요구 사항을 제거합니다.

컬렉션 restaurants을 만듭니다:

db.restaurants.insertMany( [
{
_id: 1,
name: "American Steak House",
food: [ "filet", "sirloin" ],
beverages: [ "beer", "wine" ]
},
{
_id: 2,
name: "Honest John Pizza",
food: [ "cheese pizza", "pepperoni pizza" ],
beverages: [ "soda" ]
}
] )

음식 및 음료 주문(선택 사항)이 포함된 다른 컬렉션 orders를 만듭니다.

db.orders.insertMany( [
{
_id: 1,
item: "filet",
restaurant_name: "American Steak House"
},
{
_id: 2,
item: "cheese pizza",
restaurant_name: "Honest John Pizza",
drink: "lemonade"
},
{
_id: 3,
item: "cheese pizza",
restaurant_name: "Honest John Pizza",
drink: "soda"
}
] )

다음 예제입니다.

  • orders.restaurant_name localFieldrestaurants.name foreignField와 매칭시켜 ordersrestaurants 컬렉션을 조인합니다. pipeline이 실행되기 전에 매치가 수행됩니다.

  • 각각 $$orders_drink$beverages를 사용하여 액세스하는 orders.drinkrestaurants.beverages 필드 간에 $in 배열 일치를 수행합니다.

db.orders.aggregate( [
{
$lookup: {
from: "restaurants",
localField: "restaurant_name",
foreignField: "name",
let: { orders_drink: "$drink" },
pipeline: [ {
$match: {
$expr: { $in: [ "$$orders_drink", "$beverages" ] }
}
} ],
as: "matches"
}
}
] )

orders.drinkrestaurants.beverages 필드에 soda 값과 일치하는 항목이 있습니다. 이 출력은 matches 배열을 표시하고 일치 항목에 대해 restaurants 컬렉션의 조인된 모든 필드를 포함합니다.

{
"_id" : 1, "item" : "filet",
"restaurant_name" : "American Steak House",
"matches" : [ ]
}
{
"_id" : 2, "item" : "cheese pizza",
"restaurant_name" : "Honest John Pizza",
"drink" : "lemonade",
"matches" : [ ]
}
{
"_id" : 3, "item" : "cheese pizza",
"restaurant_name" : "Honest John Pizza",
"drink" : "soda",
"matches" : [ {
"_id" : 2, "name" : "Honest John Pizza",
"food" : [ "cheese pizza", "pepperoni pizza" ],
"beverages" : [ "soda" ]
} ]
}

상관관계가 있는 간결한 하위 쿼리가 소개되기 전에는 다중 조인 조건 및 상관 하위 쿼리 수행에서 보여진 것처럼 pipeline $lookup 단계에서 $expr 연산자를 활용해 로컬 필드와 조인 필드 사이에서$eq 동등성 매치를 사용해야 했습니다.

이 예에서는 5.0 이전 버전의 구형 MongoDB 구문을 사용하며 버전에서 앞의 간결한 예와 동일한 결과를 반환합니다.

db.orders.aggregate( [
{
$lookup: {
from: "restaurants",
let: { orders_restaurant_name: "$restaurant_name",
orders_drink: "$drink" },
pipeline: [ {
$match: {
$expr: {
$and: [
{ $eq: [ "$$orders_restaurant_name", "$name" ] },
{ $in: [ "$$orders_drink", "$beverages" ] }
]
}
}
} ],
as: "matches"
}
}
] )

이전 예시는 다음 유사 SQL 문에 해당합니다.

SELECT *, matches
FROM orders
WHERE matches IN (
SELECT *
FROM restaurants
WHERE restaurants.name = orders.restaurant_name
AND restaurants.beverages = orders.drink
);

자세한 내용은 상관관계가 있는 하위 쿼리 성능 고려 사항을 참조하세요.

MongoDB 8.0부터 $lookup$unionWith 내의 서브파이프라인의 네임스페이스가 fromcoll 필드의 올바른 사용을 보장하기 위해 검증됩니다.

  • $lookup의 경우 지정된 컬렉션이 필요하지 않은 단계를 사용하는 서브파이프라인을 사용할 때는 from 필드를 생략합니다. 예를 들어 $documents 단계입니다.

  • 마찬가지로, $unionWith의 경우 coll 필드를 생략합니다.

변경되지 않은 동작:

  • 예를 들어 $match 또는 $collStats서브파이프라인과 같이 컬렉션을 위한 단계로 시작하는 $lookup의 경우 from 필드를 포함하고 컬렉션을 지정해야 합니다.

  • 마찬가지로 $unionWith의 경우 coll 필드를 포함하고 컬렉션을 지정합니다.

다음 시나리오는 한 가지 예시를 보여줍니다.

컬렉션 cakeFlavors을 만듭니다:

db.cakeFlavors.insertMany( [
{ _id: 1, flavor: "chocolate" },
{ _id: 2, flavor: "strawberry" },
{ _id: 3, flavor: "cherry" }
] )

MongoDB 8.0부터 다음 예시는 잘못된 from 필드를 포함하고 있기 때문에 오류를 반환합니다.

db.cakeFlavors.aggregate( [ {
$lookup: {
from: "cakeFlavors",
pipeline: [ { $documents: [ {} ] } ],
as: "test"
}
} ] )

8.0 이전의 MongoDB 버전에서는 이전 예시 가 실행됩니다.

유효한 from 필드가 있는 예시를 보려면 $lookup를 사용하여 단일 동등 조인 수행을 참조하세요.

돌아가기

$listSessions