Wie kann ich (in MongoDB) Daten aus mehreren Sammlungen in einer Sammlung kombinieren?
Kann ich Map-Reduce verwenden und wenn ja, wie?
Ich würde mich sehr über ein Beispiel freuen, da ich ein Neuling bin.
Wie kann ich (in MongoDB) Daten aus mehreren Sammlungen in einer Sammlung kombinieren?
Kann ich Map-Reduce verwenden und wenn ja, wie?
Ich würde mich sehr über ein Beispiel freuen, da ich ein Neuling bin.
Antworten:
Obwohl Sie dies nicht in Echtzeit tun können, können Sie Map-Reduce mehrmals ausführen, um Daten zusammenzuführen, indem Sie die Option "Reduzieren" in MongoDB 1.8+ Map / Reduce verwenden (siehe http://www.mongodb.org/). display / DOCS / MapReduce # MapReduce-Outputoptions ). Sie benötigen einen Schlüssel in beiden Sammlungen, den Sie als _id verwenden können.
Angenommen, Sie haben eine users
Sammlung und eine comments
Sammlung und möchten eine neue Sammlung mit einigen demografischen Benutzerinformationen für jeden Kommentar.
Angenommen, die users
Sammlung enthält die folgenden Felder:
Und dann hat die comments
Sammlung die folgenden Felder:
Sie würden diese Karte machen / reduzieren:
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
Zu diesem Zeitpunkt haben Sie eine neue Sammlung mit dem Namen users_comments
, die die zusammengeführten Daten enthält, und Sie können diese jetzt verwenden. Diese reduzierten Sammlungen haben alle _id
den Schlüssel, den Sie in Ihren Kartenfunktionen ausgegeben haben, und dann sind alle Werte ein Unterobjekt innerhalb des value
Schlüssels - die Werte befinden sich nicht auf der obersten Ebene dieser reduzierten Dokumente.
Dies ist ein etwas einfaches Beispiel. Sie können dies mit mehr Sammlungen wiederholen, so oft Sie die reduzierte Sammlung weiter aufbauen möchten. Sie können dabei auch Zusammenfassungen und Aggregationen von Daten erstellen. Wahrscheinlich würden Sie mehr als eine Reduzierungsfunktion definieren, da die Logik zum Aggregieren und Beibehalten vorhandener Felder komplexer wird.
Sie werden auch feststellen, dass es jetzt für jeden Benutzer ein Dokument mit allen Kommentaren dieses Benutzers in einem Array gibt. Wenn wir Daten zusammenführen würden, die eher eine Eins-zu-Eins-Beziehung als eine Eins-zu-Viele-Beziehung haben, wäre dies flach und Sie könnten einfach eine Reduzierungsfunktion wie die folgende verwenden:
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;
};
Wenn Sie die users_comments
Sammlung reduzieren möchten, sodass es sich um ein Dokument pro Kommentar handelt, führen Sie zusätzlich Folgendes aus:
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"});
Diese Technik sollte definitiv nicht im laufenden Betrieb durchgeführt werden. Es eignet sich für einen Cron-Job oder ähnliches, bei dem die zusammengeführten Daten regelmäßig aktualisiert werden. Möglicherweise möchten Sie ensureIndex
die neue Sammlung ausführen, um sicherzustellen, dass Abfragen, die Sie für sie ausführen, schnell ausgeführt werden (denken Sie daran, dass sich Ihre Daten noch in einem value
Schlüssel befinden. Wenn Sie also comments_with_demographics
die Kommentarzeit indizieren created
, ist dies der Falldb.comments_with_demographics.ensureIndex({"value.created": 1});
users_comments
Sammlung nach dem ersten Codeblock gist.github.com/nolanamy/83d7fb6a9bf92482a1c4311ad9c78835
Mit MongoDB 3.2 können jetzt Daten aus mehreren Sammlungen über die Aggregationsphase $ lookup zu einer zusammengefasst werden . Nehmen wir als praktisches Beispiel an, Sie haben Daten zu Büchern, die in zwei verschiedene Sammlungen aufgeteilt sind.
Erste Sammlung, genannt books
, mit folgenden Daten:
{
"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"
}
Und die zweite Sammlung books_selling_data
mit den folgenden Daten:
{
"_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
}
Um beide Sammlungen zusammenzuführen, müssen Sie $ lookup nur folgendermaßen verwenden:
db.books.aggregate([{
$lookup: {
from: "books_selling_data",
localField: "isbn",
foreignField: "isbn",
as: "copies_sold"
}
}])
Nach dieser Aggregation books
sieht die Sammlung folgendermaßen aus:
{
"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
}
]
}
Es ist wichtig, einige Dinge zu beachten:
books_selling_data
nicht gesplittert werden.Wenn Sie also beide Sammlungen konsolidieren möchten und in diesem Fall ein flaches Feld copy_sold mit den insgesamt verkauften Kopien haben, müssen Sie ein wenig mehr arbeiten, wahrscheinlich mit einer Zwischensammlung, die dann sein $ aus der endgültigen Sammlung.
$lookup
sollten nicht alle "localField" und "ForeignField" gleich "isbn" sein? nicht "_id" und "isbn"?
Wenn es keine Masseneinfügung in mongodb gibt, schleifen wir alle Objekte in die small_collection
und fügen sie einzeln in die ein big_collection
:
db.small_collection.find().forEach(function(obj){
db.big_collection.insert(obj)
});
Sehr einfaches Beispiel mit $ 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 }}
])
Hier wird verwendet
{ $unwind: { path: "$userInfoData", preserveNullAndEmptyArrays: true }},
{ $unwind: { path: "$userRoleData", preserveNullAndEmptyArrays: true }}
Anstatt
{ $unwind:"$userRoleData"}
{ $unwind:"$userRoleData"}
Aufgrund von {$ unwind: "$ userRoleData"} wird ein leeres oder 0-Ergebnis zurückgegeben, wenn mit $ lookup kein übereinstimmender Datensatz gefunden wurde.
Das Ausführen von Gewerkschaften in MongoDB in einer 'SQL UNION'-Weise ist möglich, indem Aggregationen zusammen mit Suchvorgängen in einer einzigen Abfrage verwendet werden. Hier ist ein Beispiel, das ich getestet habe und das mit MongoDB 4.0 funktioniert:
// 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' } }
]);
Hier ist die Erklärung, wie es funktioniert:
Instanziieren Sie ein aggregate
Out aus einer Sammlung Ihrer Datenbank, die mindestens ein Dokument enthält. Wenn Sie nicht garantieren können, dass eine Sammlung Ihrer Datenbank nicht leer ist, können Sie dieses Problem umgehen, indem Sie in Ihrer Datenbank eine Art "Dummy" -Sammlung erstellen, die ein einzelnes leeres Dokument enthält, das speziell für die Durchführung von Gewerkschaftsabfragen vorgesehen ist.
Machen Sie die erste Stufe Ihrer Pipeline { $limit: 1 }
. Dadurch werden alle Dokumente der Sammlung mit Ausnahme des ersten entfernt.
Entfernen Sie alle Felder des verbleibenden Dokuments mithilfe einer $project
Stufe:
{ $project: { _id: '$$REMOVE' } }
Ihr Aggregat enthält jetzt ein einzelnes, leeres Dokument. Es ist Zeit, Lookups für jede Sammlung hinzuzufügen, die Sie zusammenführen möchten. Sie können die verwendenpipeline
Feld verwenden, um eine bestimmte Filterung durchzuführen, oder lassen localField
und foreignField
als Null, um mit der gesamten Sammlung übereinzustimmen.
{ $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } },
{ $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } },
{ $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } }
Sie haben jetzt ein Aggregat, das ein einzelnes Dokument enthält, das drei Arrays wie folgt enthält:
{
Collection1: [...],
Collection2: [...],
Collection3: [...]
}
Sie können sie dann mit a zu einem einzigen Array zusammenführen $project
Stufe zusammen mit dem $concatArrays
Aggregationsoperator zusammenführen :
{
"$project" :
{
"Union" : { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
}
}
Sie haben jetzt ein Aggregat mit einem einzelnen Dokument, in dem sich ein Array befindet, das Ihre Vereinigung von Sammlungen enthält. Was noch zu tun bleibt, ist das Hinzufügen eines$unwind
und einer $replaceRoot
Stufe, um Ihr Array in separate Dokumente aufzuteilen:
{ $unwind: "$Union" },
{ $replaceRoot: { newRoot: "$Union" } }
Voilà. Sie haben jetzt eine Ergebnismenge mit den Sammlungen, die Sie zusammenführen möchten. Sie können dann weitere Stufen hinzufügen, um es weiter zu filtern, zu sortieren, skip () und limit () anzuwenden. So ziemlich alles was du willst.
Verwenden Sie mehrere $ lookup für mehrere Sammlungen in Aggregation
Abfrage:
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
}
])
Ergebnis:
{
"_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 verfügt über diese Funktion zum Anhängen an das, was bereits in der Datenbank vorhanden ist. Daher kann dieses Verhalten zum Kombinieren von zwei Sammlungen verwendet werden:
Ich habe es noch nicht ausprobiert, aber es funktioniert möglicherweise schneller als der Map / Reduce-Ansatz.
Ab Mongo 4.4
sofort können wir diesen Join innerhalb einer Aggregationspipeline erreichen, indem wir die neue $unionWith
Aggregationsstufe mit $group
dem neuen $accumulator
Operator koppeln:
// > 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
kombiniert Datensätze aus der angegebenen Sammlung in Dokumenten, die sich bereits in der Aggregationspipeline befinden. Nach den beiden Gewerkschaftsphasen haben wir somit alle Benutzer-, Buch- und Filmaufzeichnungen in der Pipeline.
Wir $group
zeichnen dann $user
Elemente auf und akkumulieren sie mithilfe des $accumulator
Operators, sodass benutzerdefinierte Akkumulationen von Dokumenten möglich sind, sobald sie gruppiert werden:
accumulateArgs
.init
definiert den Zustand, der beim Gruppieren von Elementen akkumuliert wird.accumulate
Funktion können Sie eine benutzerdefinierte Aktion ausführen, wobei ein Datensatz gruppiert wird, um den akkumulierten Status zu erstellen. Wenn für das zu gruppierende Element beispielsweise das book
Feld definiert ist, aktualisieren wir den books
Teil des Status.merge
wird verwendet, um zwei interne Zustände zusammenzuführen. Es wird nur für Aggregationen verwendet, die auf Sharded-Clustern ausgeführt werden oder wenn der Vorgang die Speichergrenzen überschreitet.Ja, Sie können: Nehmen Sie diese Dienstprogrammfunktion, die ich heute geschrieben habe:
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);
}
)
}
}
Sie können an diese Funktion eine beliebige Anzahl von Sammlungen übergeben, wobei die erste die Zielsammlung sein wird. Alle übrigen Sammlungen sind Quellen, die an die Zielsammlung übertragen werden sollen.
Code-Auszug. Courtesy-Mehrere Beiträge zum Stapelüberlauf, einschließlich dieses.
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();
Sie müssen dies in Ihrer Anwendungsschicht tun. Wenn Sie ein ORM verwenden, können Anmerkungen (oder ähnliches) verwendet werden, um Referenzen abzurufen, die in anderen Sammlungen vorhanden sind. Ich habe nur mit Morphia gearbeitet , und die @Reference
Anmerkung ruft die referenzierte Entität ab, wenn sie abgefragt wird, sodass ich es vermeiden kann, dies selbst im Code zu tun.
db.collection1.find().forEach(function(doc){db.collection2.save(doc)});
reicht a aus. Bitte geben Sie Ihren verwendeten Treiber (Java, PHP, ...) an, wenn Sie keine Mongo-Shell verwenden.