MongoDB에서 동등한 SQL 조인을 어떻게 수행합니까?


498

MongoDB에서 동등한 SQL 조인을 어떻게 수행합니까?

예를 들어 두 개의 모음 (사용자 및 의견)이 있고 각 사용자 정보와 함께 pid = 444로 모든 의견을 가져오고 싶습니다.

comments
  { uid:12345, pid:444, comment="blah" }
  { uid:12345, pid:888, comment="asdf" }
  { uid:99999, pid:444, comment="qwer" }

users
  { uid:12345, name:"john" }
  { uid:99999, name:"mia"  }

특정 필드 (예 : ... find ({pid : 444}))의 모든 댓글과 각 댓글과 관련된 사용자 정보를 한 번에 가져올 수있는 방법이 있습니까?

현재 우선 기준과 일치하는 주석을 얻은 다음 해당 결과 세트의 모든 uid를 파악하고 사용자 객체를 가져 와서 주석 결과와 병합합니다. 내가 잘못하고있는 것 같습니다.


35
MongoDB 3.2+가 $ lookup이라는 조인 솔루션을 구현했기 때문에이 질문에 대한 마지막 대답은 아마도 가장 관련이있을 것입니다. 모든 사람이 바닥을 읽지 않을 것이기 때문에 여기로 밀어 넣을 것이라고 생각했습니다. stackoverflow.com/a/33511166/2593330
thefourtheye

6
맞습니다. $ lookup 이 MongoDB 3.2에 도입되었습니다. 자세한 내용은 docs.mongodb.org/master/reference/operator/aggregation/lookup/…
NDB

답변:


306

Mongo 3.2 부터이 질문에 대한 답변은 더 이상 정확하지 않습니다. 집계 파이프 라인에 추가 된 새로운 $ lookup 연산자는 기본적으로 왼쪽 외부 조인과 동일합니다.

https://docs.mongodb.org/master/reference/operator/aggregation/lookup/#pipe._S_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>
     }
}

물론 Mongo는 관계형 데이터베이스 가 아니며 개발자는 $ lookup에 대한 특정 사용 사례를 권장하기 위해 신중을 기울이고 있지만 현재 3.2 이상으로 MongoDB를 사용하는 것이 가능합니다.


@clayton : 두 개 이상의 컬렉션은 어떻습니까?
Dipen Dedania

1
@DipenDedania는 추가 $ lookup 스테이지를 집계 파이프 라인에 추가합니다.
Clayton Gulick

오른쪽 컬렉션의 해당 ID로 왼쪽 컬렉션의 배열에서 필드를 조인 할 수 없습니다.
Prateek Singh

1
나는 이것에 대해 약간 혼란스러워합니다. "보낸 사람"콜렉션의 특정 문서 만 원하도록 지정하는 방법이 있습니까, 아니면 자동으로 db의 모든 것을 한 번에 결합합니까?
user3413723

최신 Spring Data MongoDB가 3.2를 지원하는지 궁금하십니까?
gtiwari333

142

공식 mongodb 사이트의이 페이지는 이 질문을 정확하게 다룹니다.

https://mongodb-documentation.readthedocs.io/en/latest/ecosystem/tutorial/model-data-for-ruby-on-rails.html

스토리 목록을 표시 할 때 스토리를 게시 한 사용자의 이름을 표시해야합니다. 관계형 데이터베이스를 사용하는 경우 사용자 및 저장소에 대한 조인을 수행하고 모든 개체를 단일 쿼리로 가져올 수 있습니다. 그러나 MongoDB는 조인을 지원하지 않으므로 때때로 약간의 비정규 화가 필요합니다. 여기서 이것은 'username'속성을 캐싱하는 것을 의미합니다.

우리가 어떤 보편적 인 법을 어 기고있는 것처럼 관계형 순수 주의자들은 이미 불안감을 느낄 수 있습니다. 그러나 MongoDB 콜렉션은 관계형 테이블과 동일하지 않다는 것을 명심하자. 각각 고유 한 디자인 목표를 제공합니다. 정규화 된 테이블은 원자 적으로 격리 된 데이터 청크를 제공합니다. 그러나 문서는 개체 전체를 더 자세히 나타냅니다. 소셜 뉴스 사이트의 경우 사용자 이름이 게시 된 기사에 내재되어 있다고 주장 할 수 있습니다.


51
@dudelgrincen 그것은 정규화 및 관계형 데이터베이스에서 패러다임 전환입니다. NoSQL의 목표는 데이터베이스에서 매우 빠르게 읽고 쓰는 것입니다. BigData를 사용하면 DB에 더 적은 수의 응용 프로그램 및 프런트 엔드 서버가 있습니다. 초당 수백만 건의 트랜잭션을 수행해야합니다. 데이터베이스에서 무거운 물건을 내리고 응용 프로그램 수준에 놓습니다. 심층 분석이 필요한 경우 데이터를 OLAP 데이터베이스에 저장하는 통합 작업을 실행합니다. 어쨌든 OLTP DB에서 많은 딥 쿼리를 가져 와서는 안됩니다.
Snowburnt

18
@dudelgrincen 나는 그것이 모든 프로젝트 나 디자인을위한 것은 아니라고 말해야한다. SQL 유형 데이터베이스에서 작동하는 것이 있으면 왜 변경합니까? noSQL로 작업하기 위해 스키마를 마사지 할 수 없다면 그렇게하지 마십시오.
Snowburnt

9
NoSQL 시스템에서 마이그레이션 및 지속적으로 발전하는 스키마를 훨씬 쉽게 관리 할 수 ​​있습니다.
저스틴

14
사용자가 웹 사이트에 3.540 개의 게시물을 가지고 있고 프로필에서 사용자 이름을 변경하면 어떻게됩니까? 모든 게시물을 새로운 사용자 이름으로 업데이트해야합니까?
Ivo Pereira

2
@IvoPereira 네, 이것이 바로 데이터 모델링을 피해야하는 이유입니다. 동일한 시나리오와 그 결과를 설명하는 기사가 있습니다. MongoDB를 사용해서는 안되는 이유
Omid

138

mongodb 클라이언트 콘솔을 사용하여 몇 줄로 쉬운 기능으로 하나의 컬렉션에만있는 모든 데이터를 병합 / 결합 할 수 있으며 이제 원하는 쿼리를 수행 할 수 있습니다. 완전한 예에서

-저자 :

db.authors.insert([
    {
        _id: 'a1',
        name: { first: 'orlando', last: 'becerra' },
        age: 27
    },
    {
        _id: 'a2',
        name: { first: 'mayra', last: 'sanchez' },
        age: 21
    }
]);

.- 카테고리 :

db.categories.insert([
    {
        _id: 'c1',
        name: 'sci-fi'
    },
    {
        _id: 'c2',
        name: 'romance'
    }
]);

.- 책

db.books.insert([
    {
        _id: 'b1',
        name: 'Groovy Book',
        category: 'c1',
        authors: ['a1']
    },
    {
        _id: 'b2',
        name: 'Java Book',
        category: 'c2',
        authors: ['a1','a2']
    },
]);

.- 도서 대출

db.lendings.insert([
    {
        _id: 'l1',
        book: 'b1',
        date: new Date('01/01/11'),
        lendingBy: 'jose'
    },
    {
        _id: 'l2',
        book: 'b1',
        date: new Date('02/02/12'),
        lendingBy: 'maria'
    }
]);

.- 마술 :

db.books.find().forEach(
    function (newBook) {
        newBook.category = db.categories.findOne( { "_id": newBook.category } );
        newBook.lendings = db.lendings.find( { "book": newBook._id  } ).toArray();
        newBook.authors = db.authors.find( { "_id": { $in: newBook.authors }  } ).toArray();
        db.booksReloaded.insert(newBook);
    }
);

.- 새로운 수집 데이터를 얻으십시오 :

db.booksReloaded.find().pretty()

.- 응답 :)

{
    "_id" : "b1",
    "name" : "Groovy Book",
    "category" : {
        "_id" : "c1",
        "name" : "sci-fi"
    },
    "authors" : [
        {
            "_id" : "a1",
            "name" : {
                "first" : "orlando",
                "last" : "becerra"
            },
            "age" : 27
        }
    ],
    "lendings" : [
        {
            "_id" : "l1",
            "book" : "b1",
            "date" : ISODate("2011-01-01T00:00:00Z"),
            "lendingBy" : "jose"
        },
        {
            "_id" : "l2",
            "book" : "b1",
            "date" : ISODate("2012-02-02T00:00:00Z"),
            "lendingBy" : "maria"
        }
    ]
}
{
    "_id" : "b2",
    "name" : "Java Book",
    "category" : {
        "_id" : "c2",
        "name" : "romance"
    },
    "authors" : [
        {
            "_id" : "a1",
            "name" : {
                "first" : "orlando",
                "last" : "becerra"
            },
            "age" : 27
        },
        {
            "_id" : "a2",
            "name" : {
                "first" : "mayra",
                "last" : "sanchez"
            },
            "age" : 21
        }
    ],
    "lendings" : [ ]
}

이 줄이 당신을 도울 수 있기를 바랍니다.


2
이 동일한 코드가 교리 mongodb를 사용하여 실행될 수 있는지 궁금합니다.
abbood

4
참조 객체 중 하나가 업데이트되면 어떻게됩니까? 해당 업데이트가 책 객체에 자동으로 반영됩니까? 아니면 루프를 다시 실행해야합니까?
balupton 2016 년

14
데이터가 작 으면 괜찮습니다. 각 책 내용을 고객에게 가져오고 각 범주, 대출 및 저자를 하나씩 가져옵니다. 책이 수천 권이되는 순간 정말 느려집니다. 더 나은 기술은 집계 파이프 라인을 사용하고 병합 된 데이터를 별도의 컬렉션으로 출력하는 것입니다. 다시 연락 드리겠습니다. 그 대답을 추가하겠습니다.
Sandeep Giri 2016 년

이 다른 예제에 알고리즘을 적용 할 수 있습니까? stackoverflow.com/q/32718079/287948
Peter Krauss

1
@ SandeepGiri 분리 된 컬렉션에서 실제로 집중적 인 데이터를 가지고 있기 때문에 어떻게 집계 파이프 라인을 수행 할 수 있습니까?
Yassine Abdul-Rahman

38

설명 된대로해야합니다. MongoDB는 비 관계형 데이터베이스이며 조인을 지원하지 않습니다.


4
SQL 서버 배경에서 잘못된 성능을 얻는 것이 현명하지만 문서 DB로는 그다지 좋지 않습니까?
terjetyl

3
뿐만 아니라 SQL 서버 배경에서, 나는 MongoDB를 훨씬 SQL에서 중첩 된 쿼리처럼, 한 번에 새 쿼리에 대한 입력으로 (선택 반환 필드)는 '결과 세트'를 주셔서 감사 것
스테인 샌더스

1
@terjetyl 당신은 정말로 그것을 계획해야합니다. 프런트 엔드에 표시 할 필드는 무엇입니까? 개별보기에서 제한된 양이면 포함 된 문서로 간주합니다. 열쇠는 조인을 할 필요가 없습니다. 심층 분석을 원한다면 다른 데이터베이스에서 사실을 분석하십시오. 최적의 성능을 위해 데이터를 OLAP 큐브로 변환하는 작업을 실행하십시오.
Snowburnt

4
mongo 3.2 버전부터 왼쪽 조인이 지원됩니다.
Somnath Muluk

18

다른 사람들이 지적했듯이 실제로 원하지 않는 관계형 데이터베이스에서 관계형 데이터베이스를 만들려고 노력하고 있습니다. 어쨌든이 작업을 수행 해야하는 경우 사용할 수있는 솔루션입니다. 우리는 먼저 컬렉션 ​​A (또는 귀하의 경우 사용자)에서 foreach find를 수행 한 다음 각 항목을 객체로 얻은 다음 객체 속성 (귀하의 경우 uid)을 사용하여 두 번째 컬렉션 (귀하의 의견)에서 조회합니다 그것을 찾은 다음 우리는 성냥을 가지고 그것을 인쇄하거나 무언가를 할 수 있습니다. 희망이 당신과 행운을 빕니다 :)

db.users.find().forEach(
function (object) {
    var commonInBoth=db.comments.findOne({ "uid": object.uid} );
    if (commonInBoth != null) {
        printjson(commonInBoth) ;
        printjson(object) ;
    }else {
        // did not match so we don't care in this case
    }
});

현재 루핑중인 항목을 찾을 수 없습니까?
Skarlinski

18

$ lookup , $ project$ match 의 올바른 조합 으로 여러 매개 변수에서 여러 테이블을 조인 할 수 있습니다. 여러 번 연결할 수 있기 때문입니다.

다음과 같이하고 싶다고 가정하자 ( reference )

SELECT S.* FROM LeftTable S
LEFT JOIN RightTable R ON S.ID =R.ID AND S.MID =R.MID WHERE R.TIM >0 AND 
S.MOB IS NOT NULL

1 단계 : 모든 테이블 연결

원하는만큼 테이블을 $ lookup 할 수 있습니다.

$ lookup- 쿼리에서 각 테이블 당 하나씩

$ unwind- 데이터가 올바르게 비정규 화되었으므로 배열로 래핑되기 때문에

파이썬 코드

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "R"}

                        ])

2 단계 : 모든 조건부 정의

$ 프로젝트 : 여기에 모든 조건문과 선택하려는 모든 변수를 정의하십시오.

파이썬 코드

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "R"},

                        # define conditionals + variables

                        {"$project": {
                          "midEq": {"$eq": ["$MID", "$R.MID"]},
                          "ID": 1, "MOB": 1, "MID": 1
                        }}
                        ])

3 단계 : 모든 조건에 가입

$ match -OR 또는 AND 등을 사용하여 모든 조건에 참여하십시오.

$ 프로젝트 : 모든 조건을 정의하지 않음

파이썬 코드

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "$R"},

                        # define conditionals + variables

                        {"$project": {
                          "midEq": {"$eq": ["$MID", "$R.MID"]},
                          "ID": 1, "MOB": 1, "MID": 1
                        }},

                        # join all conditionals

                        {"$match": {
                          "$and": [
                            {"R.TIM": {"$gt": 0}}, 
                            {"MOB": {"$exists": True}},
                            {"midEq": {"$eq": True}}
                        ]}},

                        # undefine conditionals

                        {"$project": {
                          "midEq": 0
                        }}

                        ])

테이블, 조건부 및 조인의 거의 모든 조합이이 방식으로 수행 될 수 있습니다.


17

다음은 "join" 의 예입니다. * 배우영화 컬렉션 :

https://github.com/mongodb/cookbook/blob/master/content/patterns/pivot.txt

.mapReduce()방법 을 사용합니다

* join- 문서 지향 데이터베이스에 참여하는 대안


19
-1, 두 컬렉션의 데이터를 결합하지 않습니다. 데이터를 피봇 팅하는 단일 컬렉션 (액터)의 데이터를 사용합니다. 따라서 키였던 것은 이제 값이고 값은 이제 키입니다 ... JOIN과는 매우 다릅니다.
Evan Teran

12
MongoDB는 관계형이 아니라 문서 지향적입니다. MapReduce를 사용하면 성능이 큰 데이터 (클러스터 등을 사용할 수 있음)로 재생할 수 있지만 간단한 경우에도 매우 유용합니다!
Thomas Decaux 2016 년

14

3.2 버전으로 제공되는 조회를 사용하여 Mongo에서 두 개의 콜렉션에 참여할 수 있습니다. 귀하의 경우 쿼리는

db.comments.aggregate({
    $lookup:{
        from:"users",
        localField:"uid",
        foreignField:"uid",
        as:"users_comments"
    }
})

또는 사용자와 관련하여 참여할 수 있다면 아래와 같이 약간의 변경이있을 것입니다.

db.users.aggregate({
    $lookup:{
        from:"comments",
        localField:"uid",
        foreignField:"uid",
        as:"users_comments"
    }
})

SQL에서 왼쪽 및 오른쪽 조인과 동일하게 작동합니다.


11

당신이하려는 일에 달려 있습니다.

현재 데이터베이스를 표준화 된 데이터베이스로 설정했으며, 이는 올바른 방식입니다.

그러나 다른 방법이 있습니다.

반복적으로 쿼리 할 수있는 사용자에 대한 참조가있는 각 게시물에 대한 주석이 포함 된 게시물 모음이있을 수 있습니다. 주석과 함께 사용자 이름을 저장할 수 있으며 모든 문서를 한 문서에 저장할 수 있습니다.

NoSQL의 장점은 유연한 스키마와 매우 빠른 읽기 및 쓰기를 위해 설계되었습니다. 일반적인 빅 데이터 팜에서 데이터베이스는 가장 큰 병목 현상이며 응용 프로그램 및 프런트 엔드 서버보다 데이터베이스 엔진 수가 적습니다. 비싸지 만 더 강력하고 하드 드라이브 공간도 비교적 저렴합니다. 정규화는 공간 절약이라는 개념에서 비롯되지만 데이터베이스가 복잡한 조인을 수행하고 관계의 무결성을 확인하고 계단식 작업을 수행하는 데 드는 비용이 발생합니다. 데이터베이스를 올바르게 설계하면 개발자가 두통을 덜 수 있습니다.

NoSQL을 사용하면 중복성 및 스토리지 공간이 비용 (업데이트를 수행하는 데 필요한 프로세서 시간과 추가 데이터를 저장하는 하드 드라이브 비용 모두)으로 인해 문제가되지 않는다는 사실을 받아들이면 비정규 화는 문제가되지 않습니다 (임베디드 어레이의 경우). 수십만 개의 항목이 성능 문제 일 수 있지만 대부분 문제가되지는 않습니다). 또한 모든 데이터베이스 클러스터에 대해 여러 응용 프로그램 및 프런트 엔드 서버가 있습니다. 그들이 조인을 많이 들어 올리고 데이터베이스 서버가 읽고 쓰는 것을 고수하게하십시오.

TL; DR : 당신이하고있는 일은 괜찮으며, 다른 방법도 있습니다. 좋은 예를 보려면 mongodb 문서의 데이터 모델 패턴을 확인하십시오. http://docs.mongodb.org/manual/data-modeling/


8
"정규화는 공간을 절약하려는 개념에서 비롯된 것입니다." IMHO 정규화는 중복을 피하는 개념에서 비롯됩니다. 블로그 게시물과 함께 사용자 이름을 저장한다고 가정 해 보겠습니다. 그녀가 결혼한다면? 정규화되지 않은 모델에서는 모든 게시물을 살펴보고 이름을 변경해야합니다. 정규화 된 모델에서는 일반적으로 하나의 레코드를 변경합니다.
DanielKhan

중복을 방지하고 공간을 절약하는 @DanielKhan은 비슷한 개념이지만 재분석에서 중복이이 설계의 근본 원인이라는 데 동의합니다. 다시 말씀 드리겠습니다. 메모 주셔서 감사합니다.
Snowburnt

11

DBRef라고하는 많은 드라이버가 지원하는 사양이 있습니다.

DBRef는 문서간에 참조를 작성하기위한보다 공식적인 사양입니다. DBRefs (일반적으로)는 컬렉션 이름과 개체 ID를 포함합니다. 대부분의 개발자는 컬렉션이 한 문서에서 다음 문서로 변경 될 수있는 경우에만 DBRefs를 사용합니다. 참조 된 컬렉션이 항상 동일 할 경우 위에서 설명한 수동 참조가 더 효율적입니다.

MongoDB 문서에서 가져온 것 : 데이터 모델> 데이터 모델 참조> 데이터베이스 참조


11

$ 조회 (집계)

처리를 위해 "조인 된"컬렉션의 문서를 필터링하기 위해 동일한 데이터베이스의 샤딩되지 않은 컬렉션에 대해 왼쪽 외부 조인을 수행합니다. 각 입력 문서에 $ lookup 스테이지는 요소가 "결합 된"콜렉션의 일치하는 문서 인 새 배열 필드를 추가합니다. $ lookup 단계는 이러한 재구성 된 문서를 다음 단계로 전달합니다. $ 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>
     }
}

작업은 다음 의사 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> );

몽고 URL


하위 테이블은 조인과 완전히 다릅니다. 왼쪽 테이블이 크면 하위 쿼리는 각 행이 쿼리 자체를 수행해야 함을 의미합니다. 매우 느려질 것입니다. 조인은 SQL에서 매우 빠릅니다.
yww325

8

3.2.6 이전 에 Mongodb는 mysql처럼 join query를 지원하지 않습니다. 당신을 위해 작동하는 솔루션 아래.

 db.getCollection('comments').aggregate([
        {$match : {pid : 444}},
        {$lookup: {from: "users",localField: "uid",foreignField: "uid",as: "userData"}},
   ])

4

Postgres의 mongo_fdw 를 사용 하여 MongoDB에서 조인을 포함하여 SQL 쿼리를 실행할 수 있습니다 .


3

MongoDB는 조인을 허용하지 않지만 플러그인을 사용하여 처리 할 수 ​​있습니다. mongo-join 플러그인을 확인하십시오. 최고이며 이미 사용했습니다. 이처럼 npm을 사용하여 직접 설치할 수 있습니다 npm install mongo-join. 예제를 통해 전체 문서를 확인할 수 있습니다 .

(++) 컬렉션에 참여해야 할 때 정말 유용한 도구

(-) 쿼리의 최상위 수준에서만 조건을 적용 할 수 있습니다.

var Join = require('mongo-join').Join, mongodb = require('mongodb'), Db = mongodb.Db, Server = mongodb.Server;
db.open(function (err, Database) {
    Database.collection('Appoint', function (err, Appoints) {

        /* we can put conditions just on the top level */
        Appoints.find({_id_Doctor: id_doctor ,full_date :{ $gte: start_date },
            full_date :{ $lte: end_date }}, function (err, cursor) {
            var join = new Join(Database).on({
                field: '_id_Doctor', // <- field in Appoints document
                to: '_id',         // <- field in User doc. treated as ObjectID automatically.
                from: 'User'  // <- collection name for User doc
            }).on({
                field: '_id_Patient', // <- field in Appoints doc
                to: '_id',         // <- field in User doc. treated as ObjectID automatically.
                from: 'User'  // <- collection name for User doc
            })
            join.toArray(cursor, function (err, joinedDocs) {

                /* do what ever you want here */
                /* you can fetch the table and apply your own conditions */
                .....
                .....
                .....


                resp.status(200);
                resp.json({
                    "status": 200,
                    "message": "success",
                    "Appoints_Range": joinedDocs,


                });
                return resp;


            });

    });

2

집계 파이프 라인을 사용하여 수행 할 수 있지만 직접 작성하는 것은 쉽지 않습니다.

mongo-join-query쿼리에서 집계 파이프 라인을 자동으로 만드는 데 사용할 수 있습니다 .

쿼리는 다음과 같습니다.

const mongoose = require("mongoose");
const joinQuery = require("mongo-join-query");

joinQuery(
    mongoose.models.Comment,
    {
        find: { pid:444 },
        populate: ["uid"]
    },
    (err, res) => (err ? console.log("Error:", err) : console.log("Success:", res.results))
);

결과는 uid필드에 사용자 개체가 있으며 원하는만큼 많은 수준을 연결할 수 있습니다. 사용자에 대한 참조를 채우면 팀을 참조하고 다른 것을 참조하는 등의 작업을 수행 할 수 있습니다.

면책 조항 : 나는 mongo-join-query이 정확한 문제를 해결하기 위해 썼다 .


0

playORM은 파티션 내에서 조인을 수행 할 수 있도록 파티셔닝을 추가하는 S-SQL (Scalable SQL)을 사용하여이를 수행 할 수 있습니다.


-2

아니, 당신이 잘못하고있는 것 같지 않습니다. MongoDB 조인은 "클라이언트 측"입니다. 당신이 말한 것처럼 :

현재 우선 기준과 일치하는 주석을 얻은 다음 해당 결과 세트의 모든 uid를 파악하고 사용자 객체를 가져 와서 주석 결과와 병합합니다. 내가 잘못하고있는 것 같습니다.

1) Select from the collection you're interested in.
2) From that collection pull out ID's you need
3) Select from other collections
4) Decorate your original results.

"실제"조인은 아니지만 "많은"면 조인에 대한 중복 행을 처리 할 필요가 없기 때문에 원래 선택된 세트를 장식하기 때문에 실제로 SQL 조인보다 훨씬 유용합니다.

이 페이지에는 넌센스와 FUD가 많이 있습니다. 5 년이 지난 후에도 MongoDB는 여전히 문제입니다.


'다수의 양면 조인을 위해 중복 된 행을 처리 할 필요가 없습니다.'-이것이 무슨 의미인지 모르겠습니다. 당신은 명확히 할 수 있습니까?
Mark Amery

1
@MarkAmery입니다. SQL에서 nn 관계는 중복 행을 리턴합니다. 예 : 친구. Bob이 Mary 및 Jane과 친구 인 경우 Bob에 대해 Bob, Mary 및 Bob, Jane의 2 행을 얻게됩니다. 2 Bobs는 거짓말이며 Bob이 하나뿐입니다. 클라이언트 측 조인을 사용하면 Bob으로 시작하여 Bob, "Mary and Jane"등 원하는 방식으로 장식 할 수 있습니다. SQL에서는 하위 쿼리를 사용하여이 작업을 수행하지만 클라이언트에서 수행 할 수있는 db 서버에서 작업하고 있습니다.
Michael Cole

-3

정규화 된 데이터 테이블이 필요한 경우 다른 데이터베이스 솔루션을 시도해야한다고 생각합니다.

그러나 나는 Git 에서 MOngo에 대한 해결책을 코드에 삽입했다-영화 이름은 있지만 noi 영화 ID는있다 .

문제

당신이 한 영화의 배열을 가진 액터 컬렉션이 있습니다.

각각의 액터 배열로 영화 컬렉션을 생성하려고합니다.

일부 샘플 데이터

 db.actors.insert( { actor: "Richard Gere", movies: ['Pretty Woman', 'Runaway Bride', 'Chicago'] });
 db.actors.insert( { actor: "Julia Roberts", movies: ['Pretty Woman', 'Runaway Bride', 'Erin Brockovich'] });

해결책

액터 문서에서 각 무비를 반복하고 각 무비를 개별적으로 방출해야합니다.

여기서 캐치는 축소 단계에 있습니다. Reduce 단계에서 배열을 방출 할 수 없으므로 반환 된 "value"문서 내에 Actors 배열을 작성해야합니다.

코드
map = function() {
  for(var i in this.movies){
    key = { movie: this.movies[i] };
    value = { actors: [ this.actor ] };
    emit(key, value);
  }
}

reduce = function(key, values) {
  actor_list = { actors: [] };
  for(var i in values) {
    actor_list.actors = values[i].actors.concat(actor_list.actors);
  }
  return actor_list;
}

actor_list가 실제로 배열을 포함하는 자바 스크립트 객체 인 방법에 주목하십시오. 또한 map은 동일한 구조를 나타냅니다.

다음을 실행하여 맵 / 축소를 실행하고 "피벗"콜렉션으로 출력 한 후 결과를 인쇄하십시오.

printjson (db.actors.mapReduce (지도, 축소, "피벗")); db.pivot.find (). forEach (printjson);

다음은 샘플 출력입니다. "Pretty Woman"과 "Runaway Bride"에는 "Richard Gere"와 "Julia Roberts"가 있습니다.

{ "_id" : { "movie" : "Chicago" }, "value" : { "actors" : [ "Richard Gere" ] } }
{ "_id" : { "movie" : "Erin Brockovich" }, "value" : { "actors" : [ "Julia Roberts" ] } }
{ "_id" : { "movie" : "Pretty Woman" }, "value" : { "actors" : [ "Richard Gere", "Julia Roberts" ] } }
{ "_id" : { "movie" : "Runaway Bride" }, "value" : { "actors" : [ "Richard Gere", "Julia Roberts" ] } }


이 답변의 대부분의 내용 (즉, 영어로 이해되는 비트)은 답변자가 제공 한 GitHub 링크의 MongoDB 쿡북에서 복사됩니다.
Mark Amery

-4

mongoDB 하위 쿼리를 사용하여 두 컬렉션을 병합 할 수 있습니다. 예를 들면 다음과 같습니다.

`db.commentss.insert([
  { uid:12345, pid:444, comment:"blah" },
  { uid:12345, pid:888, comment:"asdf" },
  { uid:99999, pid:444, comment:"qwer" }])`

사용자-

db.userss.insert([
  { uid:12345, name:"john" },
  { uid:99999, name:"mia"  }])

JOIN에 대한 MongoDB 하위 쿼리

`db.commentss.find().forEach(
    function (newComments) {
        newComments.userss = db.userss.find( { "uid": newComments.uid } ).toArray();
        db.newCommentUsers.insert(newComments);
    }
);`

새로 생성 된 컬렉션에서 결과 가져 오기

db.newCommentUsers.find().pretty()

결과--

`{
    "_id" : ObjectId("5511236e29709afa03f226ef"),
    "uid" : 12345,
    "pid" : 444,
    "comment" : "blah",
    "userss" : [
        {
            "_id" : ObjectId("5511238129709afa03f226f2"),
            "uid" : 12345,
            "name" : "john"
        }
    ]
}
{
    "_id" : ObjectId("5511236e29709afa03f226f0"),
    "uid" : 12345,
    "pid" : 888,
    "comment" : "asdf",
    "userss" : [
        {
            "_id" : ObjectId("5511238129709afa03f226f2"),
            "uid" : 12345,
            "name" : "john"
        }
    ]
}
{
    "_id" : ObjectId("5511236e29709afa03f226f1"),
    "uid" : 99999,
    "pid" : 444,
    "comment" : "qwer",
    "userss" : [
        {
            "_id" : ObjectId("5511238129709afa03f226f3"),
            "uid" : 99999,
            "name" : "mia"
        }
    ]
}`

희망이 도움이 될 것입니다.


7
왜 기본적으로 거의 동일한 1 년 된 답을 복사 했습니까? stackoverflow.com/a/22739813/4186945
hackel
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.