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.
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.
Câu trả lời:
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 users
bộ sưu tập và một comments
bộ 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ử users
bộ sưu tập có các trường sau:
Và sau đó comments
bộ sưu tập có các trường sau:
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_comments
chứ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 _id
là 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 value
khó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_comments
bộ 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 ensureIndex
trê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 value
khóa, vì vậy nếu bạn lập chỉ mục comments_with_demographics
về created
thời gian nhận xét , thì đó sẽ làdb.comments_with_demographics.ensureIndex({"value.created": 1});
users_comments
bộ sưu tập sau khối mã đầu tiên gist.github.com/nolanamy/83d7fb6a9bf92482a1c4311ad9c78835
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, books
bộ 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:
books_selling_data
, không thể bị loại bỏ.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.
$lookup
không phải cả "localField" và "ForeignField" đều bằng "isbn"? không phải "_id" và "isbn"?
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_collection
và chèn từng cái một vào big_collection
:
db.small_collection.find().forEach(function(obj){
db.big_collection.insert(obj)
});
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.
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ó:
Khởi tạo một aggregate
ra 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.
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.
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 $project
giai đoạn:
{ $project: { _id: '$$REMOVE' } }
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 pipeline
trường để thực hiện một số bộ lọc cụ thể hoặc để lại localField
và foreignField
là 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' } }
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 $project
giai đoạn cùng với $concatArrays
toán tử tổng hợp:
{
"$project" :
{
"Union" : { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
}
}
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 $replaceRoot
giai đ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" } }
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.
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
}
}
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:
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ỏ.
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 $unionWith
giai đoạn tổng hợp mới với toán tử $group
mớ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" } }
$unionWith
kế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 $group
ghi lại $user
và tích lũy các mục bằng cách sử dụng $accumulator
toá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:
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ử.accumulate
chứ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ó book
trườ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ó 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.
Đ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();
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 và @Reference
chú 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ã.
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.