MongoDB : 여러 컬렉션의 데이터를 하나로 통합합니다.


229

MongoDB에서 여러 컬렉션의 데이터를 하나의 컬렉션으로 결합하는 방법은 무엇입니까?

map-reduce를 사용할 수 있습니까? 그렇다면 어떻게 사용합니까?

나는 초보자이기 때문에 몇 가지 예를 크게 감사하겠습니다.


18
다른 컬렉션의 문서를 하나의 단일 컬렉션으로 복사 하시겠습니까? 아니면 계획이 무엇입니까? "결합"을 지정할 수 있습니까? mongo shell을 통해 복사하려면 a db.collection1.find().forEach(function(doc){db.collection2.save(doc)});면 충분합니다. mongo 셸을 사용하지 않는 경우 사용한 드라이버 (java, php, ...)를 지정하십시오.
proximus

그래서 다른 컬렉션보다 주소록 컬렉션, 서적 컬렉션 목록 등이있는 컬렉션 (예 : 사용자)이 있습니다. say user_id 키를 기반으로 이러한 컬렉션을 하나의 단일 컬렉션으로 결합하는 방법은 무엇입니까? ?
user697697

답변:


147

이 실시간을 수행 할 수는 없지만 MongoDB 1.8+ map / reduce의 "reduce"out 옵션을 사용하여 map-reduce를 여러 번 실행하여 데이터를 병합 할 수 있습니다 ( http://www.mongodb.org/ display / DOCS / MapReduce # MapReduce-Outputoptions ). 두 컬렉션 모두에 _id로 사용할 수있는 키가 있어야합니다.

예를 들어, 당신이 있다고 가정 해 봅시다 users수집하고, comments수집하고 각 의견에 대한 몇 가지 사용자 인구 통계 학적 정보를 가지고 새 컬렉션을 갖고 싶어.

users컬렉션에 다음 필드가 있다고 가정 해 봅시다 .

  • _신분증
  • 이름
  • 국가
  • 성별
  • 나이

그리고 다음 comments모음에는 다음과 같은 필드가 있습니다 :

  • _신분증
  • userId
  • 논평
  • 만들어진

이지도 / 축소를 할 것입니다 :

var mapUsers, mapComments, reduce;
db.users_comments.remove();

// setup sample data - wouldn't actually use this in production
db.users.remove();
db.comments.remove();
db.users.save({firstName:"Rich",lastName:"S",gender:"M",country:"CA",age:"18"});
db.users.save({firstName:"Rob",lastName:"M",gender:"M",country:"US",age:"25"});
db.users.save({firstName:"Sarah",lastName:"T",gender:"F",country:"US",age:"13"});
var users = db.users.find();
db.comments.save({userId: users[0]._id, "comment": "Hey, what's up?", created: new ISODate()});
db.comments.save({userId: users[1]._id, "comment": "Not much", created: new ISODate()});
db.comments.save({userId: users[0]._id, "comment": "Cool", created: new ISODate()});
// end sample data setup

mapUsers = function() {
    var values = {
        country: this.country,
        gender: this.gender,
        age: this.age
    };
    emit(this._id, values);
};
mapComments = function() {
    var values = {
        commentId: this._id,
        comment: this.comment,
        created: this.created
    };
    emit(this.userId, values);
};
reduce = function(k, values) {
    var result = {}, commentFields = {
        "commentId": '', 
        "comment": '',
        "created": ''
    };
    values.forEach(function(value) {
        var field;
        if ("comment" in value) {
            if (!("comments" in result)) {
                result.comments = [];
            }
            result.comments.push(value);
        } else if ("comments" in value) {
            if (!("comments" in result)) {
                result.comments = [];
            }
            result.comments.push.apply(result.comments, value.comments);
        }
        for (field in value) {
            if (value.hasOwnProperty(field) && !(field in commentFields)) {
                result[field] = value[field];
            }
        }
    });
    return result;
};
db.users.mapReduce(mapUsers, reduce, {"out": {"reduce": "users_comments"}});
db.comments.mapReduce(mapComments, reduce, {"out": {"reduce": "users_comments"}});
db.users_comments.find().pretty(); // see the resulting collection

users_comments이제 병합 된 데이터가 포함 된 새 모음이 생성되었으며 이제이를 사용할 수 있습니다. 이 축소 모음에는 모두 _id맵 함수에서 방출 한 키가 있으며 모든 값은 value키 내부의 하위 오브젝트입니다 . 값은 축소 된 문서의 최상위 레벨이 아닙니다.

이것은 다소 간단한 예입니다. 축소 된 컬렉션을 계속 유지하려는만큼 더 많은 컬렉션으로이 작업을 반복 할 수 있습니다. 프로세스에서 데이터 요약 및 집계를 수행 할 수도 있습니다. 기존 필드를 집계하고 보존하기위한 논리가 복잡 해짐에 따라 둘 이상의 감소 함수를 정의 할 수 있습니다.

또한 각 사용자에 대해 하나의 문서가 배열에있는 모든 사용자의 설명과 함께 있음을 알게됩니다. 일대 다가 아닌 일대일 관계가있는 데이터를 병합하는 경우 단순하고 단순하게 다음과 같이 축소 기능을 사용할 수 있습니다.

reduce = function(k, values) {
    var result = {};
    values.forEach(function(value) {
        var field;
        for (field in value) {
            if (value.hasOwnProperty(field)) {
                result[field] = value[field];
            }
        }
    });
    return result;
};

users_comments컬렉션 을 병합하여 주석 당 하나의 문서가되도록하려면 다음을 추가로 실행하십시오.

var map, reduce;
map = function() {
    var debug = function(value) {
        var field;
        for (field in value) {
            print(field + ": " + value[field]);
        }
    };
    debug(this);
    var that = this;
    if ("comments" in this.value) {
        this.value.comments.forEach(function(value) {
            emit(value.commentId, {
                userId: that._id,
                country: that.value.country,
                age: that.value.age,
                comment: value.comment,
                created: value.created,
            });
        });
    }
};
reduce = function(k, values) {
    var result = {};
    values.forEach(function(value) {
        var field;
        for (field in value) {
            if (value.hasOwnProperty(field)) {
                result[field] = value[field];
            }
        }
    });
    return result;
};
db.users_comments.mapReduce(map, reduce, {"out": "comments_with_demographics"});

이 기술은 즉시 수행해서는 안됩니다. 크론 작업이나 병합 된 데이터를 주기적으로 업데이트하는 작업에 적합합니다. ensureIndex새 컬렉션 에서 실행 하여 쿼리에 대해 수행하는 쿼리가 빠르게 실행되도록 할 수 있습니다 (데이터가 여전히 value키 내부 comments_with_demographics에 있으므로 주석 created시간 에 색인을 작성 해야한다면db.comments_with_demographics.ensureIndex({"value.created": 1});


1
나는 아마 프로덕션 소프트웨어에서는 그렇게하지 않을 것이지만 여전히 사악한 멋진 기술입니다.
Dave Griffith

3
고마워, 데이브 지난 3 개월 동안 프로덕션에서 트래픽이 많은 사이트에 대한 내보내기 및보고 테이블을 생성하는 데 문제없이이 기술을 사용했습니다. 여기에 기술의 유사한 사용에 대해 설명 다른 기사는 다음과 같습니다 tebros.com/2011/07/...
rmarscher

1
@rmarscher에게 감사드립니다. 자세한 내용은 모든 것을 더 잘 이해하는 데 도움이되었습니다.
benstr

5
집계 파이프 라인과 새로운 $ lookup 작업을 사용하는 예제로이 답변을 업데이트해야합니다. 적절한 글을 작성할 때까지 여기에 언급하십시오. docs.mongodb.org/manual/reference/operator/aggregation/lookup
rmarscher

1
참고로 신속하게이 무엇을 grok 수하고자하는 사람들을 위해, 여기에있어 무엇 users_comments코드의 첫 번째 블록 이후에 수집 gist.github.com/nolanamy/83d7fb6a9bf92482a1c4311ad9c78835
놀란 에이미

127

MongoDB 3.2는 이제 $ lookup 집계 단계를 통해 여러 컬렉션의 데이터를 하나로 통합 할 수 있습니다 . 실용적인 예로, 책에 대한 데이터가 두 개의 서로 다른 모음으로 나뉘어져 있다고 가정하겠습니다.

books다음 데이터가있는 첫 번째 컬렉션 ()

{
    "isbn": "978-3-16-148410-0",
    "title": "Some cool book",
    "author": "John Doe"
}
{
    "isbn": "978-3-16-148999-9",
    "title": "Another awesome book",
    "author": "Jane Roe"
}

그리고라는 두 번째 컬렉션 books_selling_data에는 다음 데이터가 있습니다.

{
    "_id": ObjectId("56e31bcf76cdf52e541d9d26"),
    "isbn": "978-3-16-148410-0",
    "copies_sold": 12500
}
{
    "_id": ObjectId("56e31ce076cdf52e541d9d28"),
    "isbn": "978-3-16-148999-9",
    "copies_sold": 720050
}
{
    "_id": ObjectId("56e31ce076cdf52e541d9d29"),
    "isbn": "978-3-16-148999-9",
    "copies_sold": 1000
}

두 컬렉션을 병합하려면 다음과 같은 방식으로 $ lookup을 사용하면됩니다.

db.books.aggregate([{
    $lookup: {
            from: "books_selling_data",
            localField: "isbn",
            foreignField: "isbn",
            as: "copies_sold"
        }
}])

이 집계 후 books컬렉션은 다음과 같습니다.

{
    "isbn": "978-3-16-148410-0",
    "title": "Some cool book",
    "author": "John Doe",
    "copies_sold": [
        {
            "_id": ObjectId("56e31bcf76cdf52e541d9d26"),
            "isbn": "978-3-16-148410-0",
            "copies_sold": 12500
        }
    ]
}
{
    "isbn": "978-3-16-148999-9",
    "title": "Another awesome book",
    "author": "Jane Roe",
    "copies_sold": [
        {
            "_id": ObjectId("56e31ce076cdf52e541d9d28"),
            "isbn": "978-3-16-148999-9",
            "copies_sold": 720050
        },
        {
            "_id": ObjectId("56e31ce076cdf52e541d9d28"),
            "isbn": "978-3-16-148999-9",
            "copies_sold": 1000
        }
    ]
}

몇 가지 사항에 유의해야합니다.

  1. 이 경우 "보낸 사람"모음 books_selling_data은 분할 할 수 없습니다.
  2. "as"필드는 위의 예와 같이 배열이됩니다.
  3. $ lookup 스테이지의 "localField"및 "foreignField"옵션은 각각의 콜렉션에 존재하지 않는 경우 일치하는 목적으로 널 (null)로 취급됩니다 ( $ lookup 문서 에는 이에 대한 완벽한 예가 있습니다).

결론적으로 두 컬렉션을 통합하려면이 경우 판매 된 총 사본이있는 플랫 사본 판매 필드를 사용하여 조금 더 작업해야합니다. 중간 컬렉션을 사용하면 수 밖으로 $ 최종 컬렉션.


안녕하세요, 친절하게 당신은 이와 같은 데이터를 관리하는 최적화 된 방법이 무엇인지 알 수 있습니까 : User, file.files 및 file.chunks는 세 가지 컬렉션입니다. 응답에 관련된 모든 파일을 가진 특정 사용자가 가능합니까? { "name": "batMan", "email": "bt@gmail.com", "files": [{file1}, {file2}, {file3}, ......]}
mfaisalhyder

위의 솔루션에 대한 공식 문서 예제는 여기에서 찾을 수 있습니다 : docs.mongodb.com/manual/reference/operator/aggregation/lookup을
야쿱 Czaplicki을

4
글쎄, 실제로 내 대답에는 공식 문서에 대한 세 개의 링크가 이미 있습니다. 어쨌든 기여해 주셔서 감사합니다. @JakubCzaplicki
Bruno Krebs

2
전체 뇌 기능 장애가있을 수 있지만 ( $lookuplocalField) 및 "foreignField"가 모두 "isbn"이 아니어야합니까? "_id"와 "isbn"이 아닙니까?
Dev01

13

mongodb에 대량 삽입이없는 경우의 모든 객체를 반복 small_collection하여 big_collection다음 에 하나씩 삽입합니다 .

db.small_collection.find().forEach(function(obj){ 
   db.big_collection.insert(obj)
});

db.colleciton.insert ([{}, {}, {}]) 삽입은 배열을 승인합니다.
augurone

2
이 작은 컬렉션에 대한 벌금을 작동하지만 :) 마이그레이션 인덱스에 잊지 마세요
세바스티앙 Lorber에게

12

$ lookup을 사용한 매우 기본적인 예입니다.

db.getCollection('users').aggregate([
    {
        $lookup: {
            from: "userinfo",
            localField: "userId",
            foreignField: "userId",
            as: "userInfoData"
        }
    },
    {
        $lookup: {
            from: "userrole",
            localField: "userId",
            foreignField: "userId",
            as: "userRoleData"
        }
    },
    { $unwind: { path: "$userInfoData", preserveNullAndEmptyArrays: true }},
    { $unwind: { path: "$userRoleData", preserveNullAndEmptyArrays: true }}
])

여기에 사용됩니다

 { $unwind: { path: "$userInfoData", preserveNullAndEmptyArrays: true }}, 
 { $unwind: { path: "$userRoleData", preserveNullAndEmptyArrays: true }}

대신에

{ $unwind:"$userRoleData"} 
{ $unwind:"$userRoleData"}

때문에 {$ 언 와인드 : "$ userRoleData"} 일치하는 기록이 조회 $로 찾을 수없는 경우이 비어 있거나 0 결과를 반환합니다.


11

'SQL UNION'방식으로 MongoDB에서 통합을 수행하는 것은 단일 쿼리에서 조회와 함께 집계를 사용하여 가능합니다. 다음은 MongoDB 4.0에서 작동하는 테스트 한 예입니다.

// Create employees data for testing the union.
db.getCollection('employees').insert({ name: "John", type: "employee", department: "sales" });
db.getCollection('employees').insert({ name: "Martha", type: "employee", department: "accounting" });
db.getCollection('employees').insert({ name: "Amy", type: "employee", department: "warehouse" });
db.getCollection('employees').insert({ name: "Mike", type: "employee", department: "warehouse"  });

// Create freelancers data for testing the union.
db.getCollection('freelancers').insert({ name: "Stephany", type: "freelancer", department: "accounting" });
db.getCollection('freelancers').insert({ name: "Martin", type: "freelancer", department: "sales" });
db.getCollection('freelancers').insert({ name: "Doug", type: "freelancer", department: "warehouse"  });
db.getCollection('freelancers').insert({ name: "Brenda", type: "freelancer", department: "sales"  });

// Here we do a union of the employees and freelancers using a single aggregation query.
db.getCollection('freelancers').aggregate( // 1. Use any collection containing at least one document.
  [
    { $limit: 1 }, // 2. Keep only one document of the collection.
    { $project: { _id: '$$REMOVE' } }, // 3. Remove everything from the document.

    // 4. Lookup collections to union together.
    { $lookup: { from: 'employees', pipeline: [{ $match: { department: 'sales' } }], as: 'employees' } },
    { $lookup: { from: 'freelancers', pipeline: [{ $match: { department: 'sales' } }], as: 'freelancers' } },

    // 5. Union the collections together with a projection.
    { $project: { union: { $concatArrays: ["$employees", "$freelancers"] } } },

    // 6. Unwind and replace root so you end up with a result set.
    { $unwind: '$union' },
    { $replaceRoot: { newRoot: '$union' } }
  ]);

작동 방식에 대한 설명은 다음과 같습니다.

  1. 인스턴스화 aggregate어느 것이 적어도 하나의 문서를 가지고 데이터베이스의 수집. 데이터베이스 컬렉션이 비어 있지 않다고 보장 할 수없는 경우 데이터베이스에서 통합 쿼리를 수행하기 위해 하나의 빈 문서가 포함 된 일종의 '더미'컬렉션을 만들어이 문제를 해결할 수 있습니다.

  2. 파이프 라인의 첫 단계를로 만드십시오 { $limit: 1 }. 그러면 첫 번째 문서를 제외한 컬렉션의 모든 문서가 제거됩니다.

  3. $project스테이지 를 사용하여 나머지 문서의 모든 필드를 제거하십시오 .

    { $project: { _id: '$$REMOVE' } }
  4. 이제 집계에 하나의 빈 문서가 포함됩니다. 이제 통합하려는 각 컬렉션에 대한 조회를 추가해야합니다. 이 pipeline필드를 사용하여 특정 필터링을 수행하거나 전체 컬렉션과 일치하도록 localFieldforeignFieldnull을 그대로 둘 수 있습니다.

    { $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } },
    { $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } },
    { $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } }
  5. 이제 다음과 같이 3 개의 배열을 포함하는 단일 문서를 포함하는 집계가 있습니다.

    {
        Collection1: [...],
        Collection2: [...],
        Collection3: [...]
    }

    그런 다음 집계 연산자 $project와 함께 스테이지를 사용하여 단일 배열로 병합 할 수 있습니다 $concatArrays.

    {
      "$project" :
      {
        "Union" : { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
      }
    }
  6. 이제 단일 문서를 포함하는 집합이 있으며 여기에는 컬렉션 집합이 포함 된 배열이 있습니다. 해야 할 일은 배열을 별도의 문서로 나누기 위해 $unwind$replaceRoot단계를 추가하는 것입니다 .

    { $unwind: "$Union" },
    { $replaceRoot: { newRoot: "$Union" } }
  7. Voilà. 이제 결합하려는 콜렉션이 포함 된 결과 세트가 있습니다. 그런 다음 더 많은 단계를 추가하여 필터링하고 정렬하고 skip () 및 limit ()을 적용 할 수 있습니다. 당신이 원하는 거의 모든 것.


"$ projection에는 최소한 하나의 출력 필드가 필요합니다"라는 메시지와 함께 쿼리가 실패합니다.
abhishek_ganta

@abhishek 만약 당신이 그것을 얻는다면 당신은 단일 프로젝션 단계에서 단일 문서에서 모든 필드를 제거하려고했기 때문입니다. MongoDB는 이것을 허용하지 않습니다. 이 문제를 해결하려면 두 번째 연속 투영을 수행해야합니다. 첫 번째 투영은 _id 이외의 모든 것을 제거하고 두 번째 투영은 나머지 _id를 제거합니다.
sboisse

@abhishek '$$ REMOVE'변수를 사용하는 단일 프로젝트에서 $ project 스테이지를 바꾸어 더 많은 쿼리를 단순화했습니다. 또한 쿼리 테스터에서 직접 복사하여 붙여 넣을 수있는 구체적인 예를 추가하여 작동하는지 확인했습니다.
sboisse

@ sboisse,이 솔루션은 더 작은 컬렉션에 작동하지만 큰 컬렉션 (100,000 + 문서) 에서이 작업을 수행하려면 "collectionToUnion1의 총 문서 크기가 최대 문서 크기를 초과합니다"라는 문제가 발생합니다. 문서에서는 큰 중간 문서 작성을 피하기 위해 $ lookup 바로 뒤에 $ unwind를 배치하는 것이 좋습니다. 해당 방법을 사용하여이 솔루션을 수정하지 못했습니다. 이 문제가 발생하여 해당 방법을 사용해야 했습니까? 내가 심판에있어 워드 프로세서 링크 : [링크] ( docs.mongodb.com/manual/core/aggregation-pipeline-optimization/... )
lucky7samson

@ lucky7samson 불행히도 내가 처리해야 할 데이터의 양은 그리 크지 않았습니다. 그래서 나는 당신이 말하는 문제에 직면 할 필요가 없었습니다. 필자의 경우 레코드를 나머지와 병합하기 전에 컬렉션에 필터링을 적용하여 조회 할 수 있으므로 통합 할 데이터의 양이 상당히 적었습니다.
sboisse

9

집계에서 여러 콜렉션에 대해 여러 $ lookup 사용

질문:

db.getCollection('servicelocations').aggregate([
  {
    $match: {
      serviceLocationId: {
        $in: ["36728"]
      }
    }
  },
  {
    $lookup: {
      from: "orders",
      localField: "serviceLocationId",
      foreignField: "serviceLocationId",
      as: "orders"
    }
  },
  {
    $lookup: {
      from: "timewindowtypes",
      localField: "timeWindow.timeWindowTypeId",
      foreignField: "timeWindowTypeId",
      as: "timeWindow"
    }
  },
  {
    $lookup: {
      from: "servicetimetypes",
      localField: "serviceTimeTypeId",
      foreignField: "serviceTimeTypeId",
      as: "serviceTime"
    }
  },
  {
    $unwind: "$orders"
  },
  {
    $unwind: "$serviceTime"
  },
  {
    $limit: 14
  }
])

결과:

{
    "_id" : ObjectId("59c3ac4bb7799c90ebb3279b"),
    "serviceLocationId" : "36728",
    "regionId" : 1.0,
    "zoneId" : "DXBZONE1",
    "description" : "AL HALLAB REST EMIRATES MALL",
    "locationPriority" : 1.0,
    "accountTypeId" : 1.0,
    "locationType" : "SERVICELOCATION",
    "location" : {
        "makani" : "",
        "lat" : 25.119035,
        "lng" : 55.198694
    },
    "deliveryDays" : "MTWRFSU",
    "timeWindow" : [ 
        {
            "_id" : ObjectId("59c3b0a3b7799c90ebb32cde"),
            "timeWindowTypeId" : "1",
            "Description" : "MORNING",
            "timeWindow" : {
                "openTime" : "06:00",
                "closeTime" : "08:00"
            },
            "accountId" : 1.0
        }, 
        {
            "_id" : ObjectId("59c3b0a3b7799c90ebb32cdf"),
            "timeWindowTypeId" : "1",
            "Description" : "MORNING",
            "timeWindow" : {
                "openTime" : "09:00",
                "closeTime" : "10:00"
            },
            "accountId" : 1.0
        }, 
        {
            "_id" : ObjectId("59c3b0a3b7799c90ebb32ce0"),
            "timeWindowTypeId" : "1",
            "Description" : "MORNING",
            "timeWindow" : {
                "openTime" : "10:30",
                "closeTime" : "11:30"
            },
            "accountId" : 1.0
        }
    ],
    "address1" : "",
    "address2" : "",
    "phone" : "",
    "city" : "",
    "county" : "",
    "state" : "",
    "country" : "",
    "zipcode" : "",
    "imageUrl" : "",
    "contact" : {
        "name" : "",
        "email" : ""
    },
    "status" : "ACTIVE",
    "createdBy" : "",
    "updatedBy" : "",
    "updateDate" : "",
    "accountId" : 1.0,
    "serviceTimeTypeId" : "1",
    "orders" : [ 
        {
            "_id" : ObjectId("59c3b291f251c77f15790f92"),
            "orderId" : "AQ18O1704264",
            "serviceLocationId" : "36728",
            "orderNo" : "AQ18O1704264",
            "orderDate" : "18-Sep-17",
            "description" : "AQ18O1704264",
            "serviceType" : "Delivery",
            "orderSource" : "Import",
            "takenBy" : "KARIM",
            "plannedDeliveryDate" : ISODate("2017-08-26T00:00:00.000Z"),
            "plannedDeliveryTime" : "",
            "actualDeliveryDate" : "",
            "actualDeliveryTime" : "",
            "deliveredBy" : "",
            "size1" : 296.0,
            "size2" : 3573.355,
            "size3" : 240.811,
            "jobPriority" : 1.0,
            "cancelReason" : "",
            "cancelDate" : "",
            "cancelBy" : "",
            "reasonCode" : "",
            "reasonText" : "",
            "status" : "",
            "lineItems" : [ 
                {
                    "ItemId" : "BNWB020",
                    "size1" : 15.0,
                    "size2" : 78.6,
                    "size3" : 6.0
                }, 
                {
                    "ItemId" : "BNWB021",
                    "size1" : 20.0,
                    "size2" : 252.0,
                    "size3" : 11.538
                }, 
                {
                    "ItemId" : "BNWB023",
                    "size1" : 15.0,
                    "size2" : 285.0,
                    "size3" : 16.071
                }, 
                {
                    "ItemId" : "CPMW112",
                    "size1" : 3.0,
                    "size2" : 25.38,
                    "size3" : 1.731
                }, 
                {
                    "ItemId" : "MMGW001",
                    "size1" : 25.0,
                    "size2" : 464.375,
                    "size3" : 46.875
                }, 
                {
                    "ItemId" : "MMNB218",
                    "size1" : 50.0,
                    "size2" : 920.0,
                    "size3" : 60.0
                }, 
                {
                    "ItemId" : "MMNB219",
                    "size1" : 50.0,
                    "size2" : 630.0,
                    "size3" : 40.0
                }, 
                {
                    "ItemId" : "MMNB220",
                    "size1" : 50.0,
                    "size2" : 416.0,
                    "size3" : 28.846
                }, 
                {
                    "ItemId" : "MMNB270",
                    "size1" : 50.0,
                    "size2" : 262.0,
                    "size3" : 20.0
                }, 
                {
                    "ItemId" : "MMNB302",
                    "size1" : 15.0,
                    "size2" : 195.0,
                    "size3" : 6.0
                }, 
                {
                    "ItemId" : "MMNB373",
                    "size1" : 3.0,
                    "size2" : 45.0,
                    "size3" : 3.75
                }
            ],
            "accountId" : 1.0
        }, 
        {
            "_id" : ObjectId("59c3b291f251c77f15790f9d"),
            "orderId" : "AQ137O1701240",
            "serviceLocationId" : "36728",
            "orderNo" : "AQ137O1701240",
            "orderDate" : "18-Sep-17",
            "description" : "AQ137O1701240",
            "serviceType" : "Delivery",
            "orderSource" : "Import",
            "takenBy" : "KARIM",
            "plannedDeliveryDate" : ISODate("2017-08-26T00:00:00.000Z"),
            "plannedDeliveryTime" : "",
            "actualDeliveryDate" : "",
            "actualDeliveryTime" : "",
            "deliveredBy" : "",
            "size1" : 28.0,
            "size2" : 520.11,
            "size3" : 52.5,
            "jobPriority" : 1.0,
            "cancelReason" : "",
            "cancelDate" : "",
            "cancelBy" : "",
            "reasonCode" : "",
            "reasonText" : "",
            "status" : "",
            "lineItems" : [ 
                {
                    "ItemId" : "MMGW001",
                    "size1" : 25.0,
                    "size2" : 464.38,
                    "size3" : 46.875
                }, 
                {
                    "ItemId" : "MMGW001-F1",
                    "size1" : 3.0,
                    "size2" : 55.73,
                    "size3" : 5.625
                }
            ],
            "accountId" : 1.0
        }, 
        {
            "_id" : ObjectId("59c3b291f251c77f15790fd8"),
            "orderId" : "AQ110O1705036",
            "serviceLocationId" : "36728",
            "orderNo" : "AQ110O1705036",
            "orderDate" : "18-Sep-17",
            "description" : "AQ110O1705036",
            "serviceType" : "Delivery",
            "orderSource" : "Import",
            "takenBy" : "KARIM",
            "plannedDeliveryDate" : ISODate("2017-08-26T00:00:00.000Z"),
            "plannedDeliveryTime" : "",
            "actualDeliveryDate" : "",
            "actualDeliveryTime" : "",
            "deliveredBy" : "",
            "size1" : 60.0,
            "size2" : 1046.0,
            "size3" : 68.0,
            "jobPriority" : 1.0,
            "cancelReason" : "",
            "cancelDate" : "",
            "cancelBy" : "",
            "reasonCode" : "",
            "reasonText" : "",
            "status" : "",
            "lineItems" : [ 
                {
                    "ItemId" : "MMNB218",
                    "size1" : 50.0,
                    "size2" : 920.0,
                    "size3" : 60.0
                }, 
                {
                    "ItemId" : "MMNB219",
                    "size1" : 10.0,
                    "size2" : 126.0,
                    "size3" : 8.0
                }
            ],
            "accountId" : 1.0
        }
    ],
    "serviceTime" : {
        "_id" : ObjectId("59c3b07cb7799c90ebb32cdc"),
        "serviceTimeTypeId" : "1",
        "serviceTimeType" : "nohelper",
        "description" : "",
        "fixedTime" : 30.0,
        "variableTime" : 0.0,
        "accountId" : 1.0
    }
}

1

Mongorestore는 데이터베이스에 이미있는 것 외에이 기능을 추가하므로이 동작은 두 컬렉션을 결합하는 데 사용될 수 있습니다.

  1. mongodump collection1
  2. collection2.rename (컬렉션 1)
  3. 몽고

아직 시도하지는 않았지만 map / reduce 방식보다 더 빠르게 수행 될 수 있습니다.


1

시작 Mongo 4.4하여 새로운 $unionWith집계 단계와 $group새로운 $accumulator연산자 를 결합하여 집계 파이프 라인 내에서이 결합을 달성 할 수 있습니다 .

// > db.users.find()
//   [{ user: 1, name: "x" }, { user: 2, name: "y" }]
// > db.books.find()
//   [{ user: 1, book: "a" }, { user: 1, book: "b" }, { user: 2, book: "c" }]
// > db.movies.find()
//   [{ user: 1, movie: "g" }, { user: 2, movie: "h" }, { user: 2, movie: "i" }]
db.users.aggregate([
  { $unionWith: "books"  },
  { $unionWith: "movies" },
  { $group: {
    _id: "$user",
    user: {
      $accumulator: {
        accumulateArgs: ["$name", "$book", "$movie"],
        init: function() { return { books: [], movies: [] } },
        accumulate: function(user, name, book, movie) {
          if (name) user.name = name;
          if (book) user.books.push(book);
          if (movie) user.movies.push(movie);
          return user;
        },
        merge: function(userV1, userV2) {
          if (userV2.name) userV1.name = userV2.name;
          userV1.books.concat(userV2.books);
          userV1.movies.concat(userV2.movies);
          return userV1;
        },
        lang: "js"
      }
    }
  }}
])
// { _id: 1, user: { books: ["a", "b"], movies: ["g"], name: "x" } }
// { _id: 2, user: { books: ["c"], movies: ["h", "i"], name: "y" } }
  • $unionWith지정된 수집의 레코드를 이미 집계 파이프 라인에있는 문서 내에 결합합니다. 2 개의 노조 단계 이후, 우리는 파이프 라인 내에 모든 사용자, 서적 및 영화 기록을 가지고 있습니다.

  • 그런 다음 연산자를 사용하여 항목 을 $group기록 $user하고 누적하여 $accumulator문서를 그룹화 할 때 사용자 지정 문서를 누적 할 수 있습니다.

    • 우리가 축적하고 싶은 분야는 accumulateArgs .
    • init 요소를 그룹화 할 때 누적 될 상태를 정의합니다.
    • accumulate기능을 사용하면 누적 된 상태를 구축하기 위해 그룹화되는 레코드로 사용자 정의 조치를 수행 할 수 있습니다. 예를 들어, 그룹화되는 항목에 book필드가 정의 되어 있으면 books상태 의 일부를 업데이트합니다 .
    • merge두 개의 내부 상태를 병합하는 데 사용됩니다. 샤드 클러스터에서 실행되거나 작업이 메모리 제한을 초과하는 경우에만 사용됩니다.

비슷한 결과를 검색 할 수 있습니다 : 4.2.6 버전
0:38의 Nixit Patel

0

예, 가능합니다 : 오늘 작성한이 유틸리티 기능을 사용하십시오.

function shangMergeCol() {
  tcol= db.getCollection(arguments[0]);
  for (var i=1; i<arguments.length; i++){
    scol= db.getCollection(arguments[i]);
    scol.find().forEach(
        function (d) {
            tcol.insert(d);
        }
    )
  }
}

이 함수에는 여러 컬렉션을 전달할 수 있으며 첫 번째 컬렉션은 대상 컬렉션이됩니다. 나머지 모든 콜렉션은 소스로 전송 될 소스입니다.


-1

코드 스 니펫. 이것을 포함하여 스택 오버플로에 대한 다수의 게시물.

 db.cust.drop();
 db.zip.drop();
 db.cust.insert({cust_id:1, zip_id: 101});
 db.cust.insert({cust_id:2, zip_id: 101});
 db.cust.insert({cust_id:3, zip_id: 101});
 db.cust.insert({cust_id:4, zip_id: 102});
 db.cust.insert({cust_id:5, zip_id: 102});

 db.zip.insert({zip_id:101, zip_cd:'AAA'});
 db.zip.insert({zip_id:102, zip_cd:'BBB'});
 db.zip.insert({zip_id:103, zip_cd:'CCC'});

mapCust = function() {
    var values = {
        cust_id: this.cust_id
    };
    emit(this.zip_id, values);
};

mapZip = function() {
    var values = {
    zip_cd: this.zip_cd
    };
    emit(this.zip_id, values);
};

reduceCustZip =  function(k, values) {
    var result = {};
    values.forEach(function(value) {
    var field;
        if ("cust_id" in value) {
            if (!("cust_ids" in result)) {
                result.cust_ids = [];
            }
            result.cust_ids.push(value);
        } else {
    for (field in value) {
        if (value.hasOwnProperty(field) ) {
                result[field] = value[field];
        }
         };  
       }
      });
       return result;
};


db.cust_zip.drop();
db.cust.mapReduce(mapCust, reduceCustZip, {"out": {"reduce": "cust_zip"}});
db.zip.mapReduce(mapZip, reduceCustZip, {"out": {"reduce": "cust_zip"}});
db.cust_zip.find();


mapCZ = function() {
    var that = this;
    if ("cust_ids" in this.value) {
        this.value.cust_ids.forEach(function(value) {
            emit(value.cust_id, {
                zip_id: that._id,
                zip_cd: that.value.zip_cd
            });
        });
    }
};

reduceCZ = function(k, values) {
    var result = {};
    values.forEach(function(value) {
        var field;
        for (field in value) {
            if (value.hasOwnProperty(field)) {
                result[field] = value[field];
            }
        }
    });
    return result;
};
db.cust_zip_joined.drop();
db.cust_zip.mapReduce(mapCZ, reduceCZ, {"out": "cust_zip_joined"}); 
db.cust_zip_joined.find().pretty();


var flattenMRCollection=function(dbName,collectionName) {
    var collection=db.getSiblingDB(dbName)[collectionName];

    var i=0;
    var bulk=collection.initializeUnorderedBulkOp();
    collection.find({ value: { $exists: true } }).addOption(16).forEach(function(result) {
        print((++i));
        //collection.update({_id: result._id},result.value);

        bulk.find({_id: result._id}).replaceOne(result.value);

        if(i%1000==0)
        {
            print("Executing bulk...");
            bulk.execute();
            bulk=collection.initializeUnorderedBulkOp();
        }
    });
    bulk.execute();
};


flattenMRCollection("mydb","cust_zip_joined");
db.cust_zip_joined.find().pretty();

-2

애플리케이션 계층에서 그렇게해야합니다. ORM을 사용하는 경우 다른 컬렉션에 존재하는 참조를 가져 오기 위해 주석 (또는 유사한 것)을 사용할 수 있습니다. 나는 단지 일한 모르핀@Reference조회 할 때 내가 코드에서 그것을 자신을 일을 피할 수 있어요, 그래서 주석, 참조 된 개체를 가져옵니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.