ฉันจะ (ใน MongoDB) รวมข้อมูลจากหลาย ๆ คอลเลคชันเป็นหนึ่งคอลเล็กชันได้อย่างไร
ฉันสามารถใช้แผนที่ลดขนาดและถ้าเป็นเช่นนั้นได้อย่างไร
ฉันจะซาบซึ้งอย่างมากในขณะที่ฉันเป็นสามเณร
ฉันจะ (ใน MongoDB) รวมข้อมูลจากหลาย ๆ คอลเลคชันเป็นหนึ่งคอลเล็กชันได้อย่างไร
ฉันสามารถใช้แผนที่ลดขนาดและถ้าเป็นเช่นนั้นได้อย่างไร
ฉันจะซาบซึ้งอย่างมากในขณะที่ฉันเป็นสามเณร
คำตอบ:
แม้ว่าคุณจะไม่สามารถทำตามเวลาจริงได้ แต่คุณสามารถเรียกใช้แผนที่ลดหลาย ๆ ครั้งเพื่อรวมข้อมูลเข้าด้วยกันโดยใช้ตัวเลือก "ลด" ออกใน MongoDB 1.8+ แผนที่ / ลด (ดูhttp://www.mongodb.org/ display / DOCS / MapReduce # MapReduce-Outputoptions ) คุณต้องมีคีย์ในคอลเลกชันทั้งสองที่คุณสามารถใช้เป็น _id ได้
ตัวอย่างเช่นสมมติว่าคุณมีusers
คอลเล็กชันและcomments
คอลเล็กชันและคุณต้องการมีคอลเล็กชันใหม่ที่มีข้อมูลกลุ่มผู้ใช้บางส่วนสำหรับความคิดเห็นแต่ละรายการ
สมมติว่าusers
คอลเล็กชันมีฟิลด์ต่อไปนี้:
แล้วcomments
คอลเลกชันมีฟิลด์ต่อไปนี้:
คุณจะทำแผนที่นี้ / ลด:
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"});
เทคนิคนี้ไม่ควรดำเนินการทันที มันเหมาะสำหรับงาน cron หรือบางอย่างเช่นที่ปรับปรุงข้อมูลที่ผสานเป็นระยะ คุณอาจต้องการเรียกใช้ensureIndex
คอลเลกชันใหม่เพื่อให้แน่ใจว่าคำสั่งที่คุณดำเนินการกับมันทำงานได้อย่างรวดเร็ว (โปรดทราบว่าข้อมูลของคุณยังคงอยู่ในvalue
คีย์ดังนั้นหากคุณต้องจัดทำดัชนีcomments_with_demographics
ในcreated
เวลาแสดงความคิดเห็นdb.comments_with_demographics.ensureIndex({"value.created": 1});
users_comments
คอลเล็กชันหลังจากบล็อกแรกของรหัสgist.github.com/nolanamy/83d7fb6a9bf92482a1c4311ad9c78835
MongoDB 3.2 ในขณะนี้ช่วยให้หนึ่งในการรวมข้อมูลจากคอลเลกชันเป็นหนึ่งในหลายผ่าน$ ค้นหาเวทีรวม เป็นตัวอย่างที่ใช้งานได้จริงสมมติว่าคุณมีข้อมูลเกี่ยวกับหนังสือแบ่งออกเป็นสองกลุ่มแตกต่างกัน
การรวบรวมแรกเรียกว่า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
}
]
}
เป็นสิ่งสำคัญที่ควรทราบบางสิ่ง:
books_selling_data
ไม่สามารถคัดลอกคอลเลกชัน "จาก" ในกรณีนี้ได้ดังนั้นโดยสรุปหากคุณต้องการรวมทั้งคอลเลกชันที่มีในกรณีนี้เขตคัดลอกแบนที่มียอดขายรวมสำเนาคุณจะต้องทำงานอีกเล็กน้อยอาจจะใช้คอลเลกชันตัวกลางที่จะแล้ว เป็น$ outสำหรับคอลเล็กชันสุดท้าย
$lookup
ทั้ง "localField" และ "foreignField" เท่ากับ "isbn" ไม่ควรใช่หรือไม่ ไม่ใช่ "_id" และ "isbn"
หากไม่มีการแทรกขนาดใหญ่ลงใน mongodb เราจะวนลูปวัตถุทั้งหมดในsmall_collection
และแทรกทีละหนึ่งเข้าไปในbig_collection
:
db.small_collection.find().forEach(function(obj){
db.big_collection.insert(obj)
});
ตัวอย่างพื้นฐานที่มีการค้นหา $
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 ผลลัพธ์หากไม่พบระเบียนที่ตรงกันที่มีการค้นหา $
การทำสหภาพใน MongoDB ในรูปแบบ 'SQL UNION' เป็นไปได้โดยใช้การรวมตัวพร้อมกับการค้นหาในแบบสอบถามเดียว นี่คือตัวอย่างที่ฉันได้ทดสอบที่ทำงานกับ 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' } }
]);
นี่คือคำอธิบายวิธีการทำงาน:
ยกตัวอย่างaggregate
จากการรวบรวมฐานข้อมูลใด ๆของคุณที่มีเอกสารอย่างน้อยหนึ่งรายการ หากคุณไม่สามารถรับประกันได้ว่าการรวบรวมฐานข้อมูลใด ๆ ของคุณจะไม่ว่างเปล่าคุณสามารถแก้ไขปัญหานี้ได้โดยสร้างในฐานข้อมูลของคุณบางคอลเลกชัน 'ดัมมี่' ที่มีเอกสารเปล่าฉบับเดียวอยู่ในนั้น
{ $limit: 1 }
ทำให้ขั้นตอนแรกของท่อของคุณจะ สิ่งนี้จะตัดเอกสารทั้งหมดของการรวบรวมยกเว้นเอกสารแรก
ตัดฟิลด์ทั้งหมดของเอกสารที่เหลือโดยใช้ส$project
เตจ:
{ $project: { _id: '$$REMOVE' } }
ตอนนี้การรวมของคุณมีเอกสารเปล่าหนึ่งฉบับ ได้เวลาเพิ่มการค้นหาสำหรับแต่ละคอลเล็กชันที่คุณต้องการรวมเข้าด้วยกัน คุณอาจใช้pipeline
ข้อมูลที่จะทำกรองเฉพาะบางส่วนหรือออกlocalField
และforeignField
เป็นโมฆะเพื่อให้ตรงกับคอลเลกชันทั้งหมด
{ $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } },
{ $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } },
{ $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } }
ตอนนี้คุณมีการรวมที่มีเอกสารเดียวที่มี 3 อาร์เรย์ดังนี้:
{
Collection1: [...],
Collection2: [...],
Collection3: [...]
}
จากนั้นคุณสามารถรวมเข้าด้วยกันเป็นอาร์เรย์เดียวโดยใช้ส$project
เตจพร้อมกับ$concatArrays
ตัวดำเนินการรวม:
{
"$project" :
{
"Union" : { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
}
}
ตอนนี้คุณมีการรวมที่มีเอกสารเดียวซึ่งอยู่ในอาร์เรย์ที่มีการรวมกลุ่มของคุณ สิ่งที่ต้องทำคือการเพิ่ม$unwind
และ$replaceRoot
ขั้นตอนในการแบ่งอาร์เรย์ของคุณเป็นเอกสารแยกต่างหาก:
{ $unwind: "$Union" },
{ $replaceRoot: { newRoot: "$Union" } }
voila ตอนนี้คุณมีชุดผลลัพธ์ที่มีคอลเลกชันที่คุณต้องการรวมเข้าด้วยกัน จากนั้นคุณสามารถเพิ่มขั้นตอนเพิ่มเติมเพื่อกรองเพิ่มเติมเรียงลำดับใช้ข้าม () และ จำกัด () สวยทุกอย่างที่คุณต้องการ
ใช้การค้นหาหลาย ๆ$สำหรับการรวบรวมหลาย ๆ
แบบสอบถาม:
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
}
}
Mongorestore มีคุณสมบัตินี้ต่อท้ายสิ่งที่มีอยู่แล้วในฐานข้อมูลดังนั้นพฤติกรรมนี้สามารถใช้สำหรับการรวมสองคอลเลกชัน:
ยังไม่ได้ลอง แต่อาจทำงานได้เร็วกว่าแผนที่ / ลดการเข้าใกล้
เริ่มต้น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
ใช้เพื่อผสานสถานะภายในสองสถานะ มันใช้สำหรับการรวมที่ทำงานบนกลุ่มที่มีเศษซากหรือเมื่อการดำเนินการเกินขีด จำกัด หน่วยความจำใช่คุณทำได้: ใช้ฟังก์ชั่นยูทิลิตี้ที่ฉันเขียนวันนี้:
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);
}
)
}
}
คุณสามารถส่งไปยังฟังก์ชันนี้ได้ไม่ว่าจะเป็นคอลเล็กชั่นใดก็ตามอันแรกจะเป็นเป้าหมายหนึ่ง คอลเลกชันที่เหลือทั้งหมดเป็นแหล่งที่จะโอนไปยังเป้าหมาย
ข้อมูลโค้ด มารยาท - โพสต์หลายรายการในสแต็คล้นรวมถึงโพสต์นี้
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();
คุณต้องทำอย่างนั้นในเลเยอร์แอปพลิเคชันของคุณ หากคุณใช้ ORM อาจใช้คำอธิบายประกอบ (หรือสิ่งที่คล้ายกัน) เพื่อดึงข้อมูลอ้างอิงที่มีอยู่ในคอลเล็กชันอื่น ๆ ฉันเพิ่งทำงานร่วมกับMorphiaเท่านั้นและ@Reference
คำอธิบายประกอบจะดึงเอนทิตีที่อ้างอิงเมื่อมีการสอบถามดังนั้นฉันจึงสามารถหลีกเลี่ยงได้ด้วยตนเองในโค้ด
db.collection1.find().forEach(function(doc){db.collection2.save(doc)});
ก็เพียงพอแล้ว โปรดระบุไดร์เวอร์ที่ใช้แล้วของคุณ (java, php, ... ) ถ้าคุณไม่ใช้เชลล์ mongo