MongoDB: Kết hợp dữ liệu từ nhiều bộ sưu tập thành một..làm thế nào?


229

Làm cách nào tôi (trong MongoDB) kết hợp dữ liệu từ nhiều bộ sưu tập thành một bộ sưu tập?

Tôi có thể sử dụng map-less và nếu vậy thì thế nào?

Tôi sẽ đánh giá rất cao một số ví dụ như tôi là một người mới.


18
Bạn chỉ muốn sao chép tài liệu từ các bộ sưu tập khác nhau vào một bộ sưu tập duy nhất hoặc kế hoạch của bạn là gì? Bạn có thể chỉ định "kết hợp"? Nếu bạn chỉ muốn sao chép qua mongo shell a db.collection1.find().forEach(function(doc){db.collection2.save(doc)});là đủ. Vui lòng chỉ định trình điều khiển đã sử dụng của bạn (java, php, ...) nếu bạn không sử dụng trình bao mongo.
pro xấpus

vì vậy tôi có một bộ sưu tập (nói người dùng) hơn các bộ sưu tập khác nói bộ sưu tập sổ địa chỉ, danh sách các bộ sưu tập sách, v.v. ?
dùng697697

Câu trả lời:


147

Mặc dù bạn không thể thực hiện việc này theo thời gian thực, nhưng bạn có thể chạy giảm bản đồ nhiều lần để hợp nhất dữ liệu với nhau bằng cách sử dụng tùy chọn "rút gọn" trong bản đồ / thu nhỏ MongoDB 1.8+ (xem http://www.mongodb.org/ hiển thị / DOCS / MapReduce # MapReduce-Outputoptions ). Bạn cần có một số khóa trong cả hai bộ sưu tập mà bạn có thể sử dụng làm _id.

Ví dụ: giả sử bạn có một usersbộ sưu tập và một commentsbộ sưu tập và bạn muốn có một bộ sưu tập mới có một số thông tin nhân khẩu học cho mỗi bình luận.

Giả sử usersbộ sưu tập có các trường sau:

  • _Tôi
  • tên đầu tiên
  • Họ
  • Quốc gia
  • giới tính
  • tuổi tác

Và sau đó commentsbộ sưu tập có các trường sau:

  • _Tôi
  • tên người dùng
  • bình luận
  • tạo

Bạn sẽ làm bản đồ này / giảm:

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

Tại thời điểm này, bạn sẽ có một bộ sưu tập mới có tên users_commentschứa dữ liệu được hợp nhất và bây giờ bạn có thể sử dụng dữ liệu đó. Tất cả các bộ sưu tập được giảm này đều _idlà khóa mà bạn đang phát ra trong các chức năng bản đồ của mình và sau đó tất cả các giá trị là một đối tượng phụ bên trong valuekhóa - các giá trị không ở cấp cao nhất của các tài liệu giảm này.

Đây là một ví dụ đơn giản. Bạn có thể lặp lại điều này với nhiều bộ sưu tập nhiều như bạn muốn tiếp tục xây dựng bộ sưu tập giảm. Bạn cũng có thể thực hiện tóm tắt và tổng hợp dữ liệu trong quy trình. Có khả năng bạn sẽ định nghĩa nhiều hơn một hàm giảm vì logic để tổng hợp và bảo tồn các trường hiện có trở nên phức tạp hơn.

Bạn cũng sẽ lưu ý rằng hiện có một tài liệu cho mỗi người dùng với tất cả các nhận xét của người dùng đó trong một mảng. Nếu chúng ta hợp nhất dữ liệu có mối quan hệ một-một thay vì một-nhiều, nó sẽ không thay đổi và bạn chỉ cần sử dụng hàm rút gọn như thế này:

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;
};

Nếu bạn muốn làm phẳng users_commentsbộ sưu tập để nó là một tài liệu cho mỗi bình luận, hãy chạy thêm:

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"});

Kỹ thuật này chắc chắn không nên được thực hiện trên bay. Nó phù hợp cho một công việc định kỳ hoặc một cái gì đó tương tự cập nhật dữ liệu hợp nhất theo định kỳ. Bạn có thể muốn chạy ensureIndextrên bộ sưu tập mới để đảm bảo các truy vấn bạn thực hiện đối với nó chạy nhanh (hãy nhớ rằng dữ liệu của bạn vẫn nằm trong valuekhóa, vì vậy nếu bạn lập chỉ mục comments_with_demographicsvề createdthời gian nhận xét , thì đó sẽ làdb.comments_with_demographics.ensureIndex({"value.created": 1});


1
Tôi có thể sẽ không bao giờ làm điều đó trong phần mềm sản xuất, nhưng nó vẫn là một kỹ thuật tuyệt vời.
Dave Griffith

3
Cảm ơn, Dave. Tôi đã sử dụng kỹ thuật này để tạo bảng xuất và báo cáo cho một trang web có lưu lượng truy cập cao trong sản xuất trong 3 tháng qua mà không gặp sự cố. Đây là một bài viết khác mô tả việc sử dụng kỹ thuật tương tự: tebros.com/2011/07/ trên
rmarscher

1
Cảm ơn @rmarscher chi tiết bổ sung của bạn thực sự giúp tôi hiểu rõ hơn mọi thứ.
benstr

5
Tôi nên cập nhật câu trả lời này bằng một ví dụ sử dụng đường dẫn tổng hợp và thao tác tra cứu $ mới. Đề cập đến nó ở đây cho đến khi tôi có thể viết một bài viết thích hợp. docs.mongodb.org/manual/reference/operator/aggregation/lookup
rmarscher

1
FYI cho những người muốn nhanh chóng tìm hiểu những gì nó làm, đây là những gì trong users_commentsbộ sưu tập sau khối mã đầu tiên gist.github.com/nolanamy/83d7fb6a9bf92482a1c4311ad9c78835
Nolan Amy

127

MongoDB 3.2 hiện cho phép một người kết hợp dữ liệu từ nhiều bộ sưu tập thành một qua giai đoạn tổng hợp tra cứu $ . Như một ví dụ thực tế, giả sử rằng bạn có dữ liệu về sách được chia thành hai bộ sưu tập khác nhau.

Bộ sưu tập đầu tiên, được gọi books, có dữ liệu sau:

{
    "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"
}

Và bộ sưu tập thứ hai, được gọi books_selling_data, có dữ liệu sau:

{
    "_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
}

Để hợp nhất cả hai bộ sưu tập chỉ là vấn đề sử dụng $ tra cứu theo cách sau:

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

Sau tập hợp này, booksbộ sưu tập sẽ trông như sau:

{
    "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
        }
    ]
}

Điều quan trọng cần lưu ý một số điều:

  1. Bộ sưu tập "từ", trong trường hợp này books_selling_data, không thể bị loại bỏ.
  2. Trường "as" sẽ là một mảng, như ví dụ trên.
  3. Cả hai tùy chọn "localField" và "ForeignField" trong giai đoạn tra cứu $ sẽ được coi là null cho các mục đích phù hợp nếu chúng không tồn tại trong các bộ sưu tập tương ứng của chúng ( tài liệu $ lookup có một ví dụ hoàn hảo về điều đó).

Vì vậy, như một kết luận, nếu bạn muốn hợp nhất cả hai bộ sưu tập, trong trường hợp này, một trường copy_sold phẳng với tổng số bản sao được bán, bạn sẽ phải làm việc nhiều hơn một chút, có thể sử dụng bộ sưu tập trung gian, sau đó, được $ ra bộ sưu tập cuối cùng.


xin chào, xin vui lòng bạn có thể cho biết đâu sẽ là cách tối ưu hóa để quản lý dữ liệu như thế này: Người dùng, file.files và file.chunks là ba bộ sưu tập, tôi muốn người dùng cụ thể có tất cả các tệp liên quan trong một phản hồi có khả thi không? {"name": "batMan", "email ':" bt@gmail.com "," files ": [{file1}, {file2}, {file3}, .... v.v.]
mfaisalhyder

Các ví dụ tài liệu chính thức cho giải pháp trên có thể được tìm thấy ở đây: docs.mongodb.com/manual/reference/operator/aggregation/lookup
Jakub Czaplicki

4
Vâng, thực sự câu trả lời của tôi đã có ba liên kết đến tài liệu chính thức. Nhưng dù sao cũng cảm ơn sự đóng góp của bạn. @JakubCzaplicki
Bruno Krebs

2
Tôi có thể bị trục trặc toàn bộ não (rất có thể) nhưng $lookupkhông phải cả "localField" và "ForeignField" đều bằng "isbn"? không phải "_id" và "isbn"?
Dev01

13

Nếu không có chèn số lượng lớn vào mongodb, chúng ta lặp tất cả các đối tượng trong small_collectionvà chèn từng cái một vào big_collection:

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

db.colleciton.insert ([{}, {}, {}]) Chèn chấp nhận mảng.
augurone

2
điều này hoạt động tốt cho các bộ sưu tập nhỏ, nhưng đừng quên di chuyển các chỉ mục :)
Sebastien Lorber

12

Ví dụ rất cơ bản với $ tra cứu.

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 }}
])

Ở đây được sử dụng

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

Thay vì

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

Bởi vì {$ bung ra: "$ userRoleData"}, điều này sẽ trả về kết quả trống hoặc 0 nếu không tìm thấy bản ghi phù hợp với $ tra cứu.


11

Có thể thực hiện các kết hợp trong MongoDB theo kiểu 'SQL UNION' bằng cách sử dụng các kết hợp cùng với tra cứu, trong một truy vấn duy nhất. Đây là một ví dụ tôi đã thử nghiệm hoạt động với 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' } }
  ]);

Dưới đây là lời giải thích về cách thức hoạt động của nó:

  1. Khởi tạo một aggregatera khỏi bất kỳ tập hợp các cơ sở dữ liệu của bạn có ít nhất một tài liệu trong đó. Nếu bạn không thể đảm bảo bất kỳ bộ sưu tập cơ sở dữ liệu nào của bạn sẽ không trống, bạn có thể giải quyết vấn đề này bằng cách tạo trong cơ sở dữ liệu của mình một bộ sưu tập 'giả' chứa một tài liệu trống trong đó sẽ có sẵn để thực hiện các truy vấn hợp nhất.

  2. Làm cho giai đoạn đầu tiên của đường ống của bạn được { $limit: 1 }. Điều này sẽ loại bỏ tất cả các tài liệu của bộ sưu tập trừ cái đầu tiên.

  3. Loại bỏ tất cả các trường của tài liệu còn lại bằng cách sử dụng một $projectgiai đoạn:

    { $project: { _id: '$$REMOVE' } }
  4. Tổng hợp của bạn bây giờ chứa một tài liệu trống duy nhất. Đã đến lúc thêm tra cứu cho mỗi bộ sưu tập bạn muốn kết hợp với nhau. Bạn có thể sử dụng pipelinetrường để thực hiện một số bộ lọc cụ thể hoặc để lại localFieldforeignFieldlà null để khớp với toàn bộ bộ sưu tập.

    { $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } },
    { $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } },
    { $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } }
  5. Bây giờ bạn có một tập hợp chứa một tài liệu duy nhất chứa 3 mảng như thế này:

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

    Sau đó, bạn có thể hợp nhất chúng lại với nhau thành một mảng bằng cách sử dụng một $projectgiai đoạn cùng với $concatArraystoán tử tổng hợp:

    {
      "$project" :
      {
        "Union" : { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
      }
    }
  6. Bây giờ bạn có một tập hợp chứa một tài liệu duy nhất, trong đó nằm trong một mảng chứa liên kết các bộ sưu tập của bạn. Những gì còn lại phải làm là thêm một$unwind và một $replaceRootgiai đoạn để phân chia mảng của bạn thành các tài liệu riêng biệt:

    { $unwind: "$Union" },
    { $replaceRoot: { newRoot: "$Union" } }
  7. Võngà. Bây giờ bạn có một tập kết quả chứa các bộ sưu tập bạn muốn kết hợp với nhau. Sau đó, bạn có thể thêm nhiều giai đoạn để lọc thêm, sắp xếp nó, áp dụng Skip () và giới hạn (). Khá nhiều thứ bạn muốn.


Truy vấn không thành công với thông báo "$ chiếu yêu cầu ít nhất một trường đầu ra".
abhishek_ganta

@abhishek Nếu bạn hiểu điều đó là do bạn đã cố gắng loại bỏ tất cả các trường ra khỏi tài liệu duy nhất trong một giai đoạn chiếu. MongoDB sẽ không cho phép bạn làm điều này. Để giải quyết vấn đề này, bạn cần thực hiện 2 phép chiếu liên tiếp trong đó phép chiếu thứ nhất loại bỏ mọi thứ trừ _id và lần thứ hai loại bỏ _id còn lại.
sboisse

@abhishek Tôi đã đơn giản hóa hơn nữa truy vấn bằng cách thay thế các giai đoạn dự án $ trong một giai đoạn duy nhất sử dụng biến '$$ XÓA'. Tôi cũng đã thêm một ví dụ cụ thể rằng bạn chỉ có thể sao chép và dán trực tiếp vào trình kiểm tra truy vấn của mình để thấy rằng nó hoạt động.
sboisse

@sboisse, giải pháp này hoạt động cho các bộ sưu tập nhỏ hơn, tuy nhiên, nếu tôi muốn thực hiện điều này trên các bộ sưu tập lớn, (hơn 100.000 tài liệu), tôi chạy vào "Tổng kích thước tài liệu trong bộ sưu tậpToUnion1 vượt quá kích thước tài liệu tối đa". Trong các tài liệu, nó gợi ý đặt $ bung trực tiếp sau khi tra cứu $ để tránh tạo các tài liệu trung gian lớn. Tôi đã không thành công trong việc sửa đổi giải pháp này bằng phương pháp đó. Bạn đã gặp phải vấn đề này và phải sử dụng phương pháp đó? Liên kết đến các tài liệu mà tôi tham khảo: [link] ( docs.mongodb.com/manual/core/aggregation-pipeline-optimization/ trộm )
lucky7samson 15/07/19

@ lucky7samson thật không may, lượng dữ liệu tôi phải xử lý không lớn. Vì vậy, tôi không phải đối mặt với vấn đề mà bạn đang đề cập. Trong trường hợp của tôi, tôi có thể áp dụng tính năng lọc trên bộ sưu tập để tra cứu trước khi hợp nhất các bản ghi với phần còn lại, vì vậy lượng dữ liệu cho liên minh là khá nhỏ.
sboisse

9

sử dụng nhiều $ tra cứu cho nhiều bộ sưu tập trong tổng hợp

truy vấn:

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
  }
])

kết quả:

{
    "_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 có tính năng nối thêm vào bất cứ thứ gì đã có trong cơ sở dữ liệu, vì vậy hành vi này có thể được sử dụng để kết hợp hai bộ sưu tập:

  1. bộ sưu tập mongodump1
  2. bộ sưu tập2.rename (bộ sưu tập1)
  3. mongorore

Chưa thử, nhưng nó có thể thực hiện nhanh hơn phương pháp tiếp cận bản đồ / thu nhỏ.


1

Bắt đầu Mongo 4.4, chúng ta có thể đạt được sự tham gia này trong một đường ống tổng hợp bằng cách ghép $unionWithgiai đoạn tổng hợp mới với toán tử $groupmới $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" } }
  • $unionWithkết hợp các bản ghi từ bộ sưu tập đã cho trong các tài liệu đã có trong đường ống tổng hợp. Sau 2 giai đoạn kết hợp, chúng tôi có tất cả hồ sơ người dùng, sách và phim trong đường ống dẫn.

  • Sau đó, chúng tôi $groupghi lại $uservà tích lũy các mục bằng cách sử dụng $accumulatortoán tử cho phép tích lũy tùy chỉnh các tài liệu khi chúng được nhóm lại:

    • các lĩnh vực chúng tôi quan tâm đến việc tích lũy được xác định với accumulateArgs .
    • init định nghĩa trạng thái sẽ được tích lũy khi chúng ta nhóm các phần tử.
    • các accumulatechức năng cho phép thực hiện một hành động tùy chỉnh với mức kỷ lục được nhóm lại để xây dựng nhà nước lũy kế. Ví dụ: nếu mục được nhóm có booktrường được xác định, thì chúng tôi cập nhậtbooks phần trạng thái.
    • mergeđược sử dụng để hợp nhất hai trạng thái nội bộ. Nó chỉ được sử dụng cho các tập hợp chạy trên các cụm bị phân mảnh hoặc khi hoạt động vượt quá giới hạn bộ nhớ.

có thể truy xuất đầu ra tương tự cho: phiên bản 4.2.6
Nixit Patel

0

Có bạn có thể: Thực hiện chức năng tiện ích này mà tôi đã viết ngày hôm nay:

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);
        }
    )
  }
}

Bạn có thể chuyển đến chức năng này bất kỳ số lượng bộ sưu tập nào, bộ sưu tập đầu tiên sẽ là mục tiêu. Tất cả các bộ sưu tập còn lại là các nguồn sẽ được chuyển đến mục tiêu.


-1

Đoạn mã. Lịch sự - Nhiều bài đăng trên stack stack bao gồm cả bài này.

 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

Bạn đã làm điều đó trong lớp ứng dụng của bạn. Nếu bạn đang sử dụng ORM, nó có thể sử dụng các chú thích (hoặc một cái gì đó tương tự) để kéo các tham chiếu tồn tại trong các bộ sưu tập khác. Tôi chỉ làm việc với Mor chếch@Referencechú thích tìm nạp thực thể được tham chiếu khi được truy vấn, vì vậy tôi có thể tránh tự làm điều đó trong mã.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.