Jak mogę (w MongoDB) łączyć dane z wielu kolekcji w jedną kolekcję?
Czy mogę użyć funkcji zmniejszania mapy, a jeśli tak, to w jaki sposób?
Byłbym bardzo wdzięczny za jakiś przykład, ponieważ jestem nowicjuszem.
Jak mogę (w MongoDB) łączyć dane z wielu kolekcji w jedną kolekcję?
Czy mogę użyć funkcji zmniejszania mapy, a jeśli tak, to w jaki sposób?
Byłbym bardzo wdzięczny za jakiś przykład, ponieważ jestem nowicjuszem.
Odpowiedzi:
Chociaż nie można tego robić w czasie rzeczywistym, można wielokrotnie uruchamiać funkcję zmniejszania mapy w celu scalenia danych za pomocą opcji „zmniejszania” w MongoDB 1.8+ map / zmniejsz (patrz http://www.mongodb.org/ display / DOCS / MapReduce # MapReduce-Outputoptions ). Musisz mieć klucz w obu kolekcjach, którego możesz użyć jako _id.
Załóżmy na przykład, że masz users
kolekcję i comments
kolekcję i chcesz mieć nową kolekcję, która zawiera informacje demograficzne użytkownika dla każdego komentarza.
Powiedzmy, że users
kolekcja ma następujące pola:
A następnie comments
kolekcja ma następujące pola:
Zrobiłbyś tę mapę / zmniejszyć:
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
W tym momencie będziesz mieć nową kolekcję o nazwie, users_comments
która zawiera scalone dane i możesz teraz z niej korzystać. Wszystkie te zredukowane kolekcje mają _id
klucz, który emitowałeś w funkcjach mapy, a następnie wszystkie wartości są podkluczem wewnątrz value
klucza - wartości nie znajdują się na najwyższym poziomie tych zredukowanych dokumentów.
To jest dość prosty przykład. Możesz to powtórzyć z większą liczbą kolekcji, tak długo jak chcesz kontynuować tworzenie zmniejszonej kolekcji. Możesz także tworzyć podsumowania i agregacje danych. Prawdopodobnie zdefiniowałbyś więcej niż jedną funkcję redukującą, ponieważ logika agregacji i zachowania istniejących pól staje się bardziej złożona.
Zauważysz również, że dla każdego użytkownika jest teraz jeden dokument z wszystkimi komentarzami tego użytkownika w tablicy. Gdybyśmy scalali dane, które mają relację jeden-do-jednego, a nie jeden-do-wielu, byłoby to płaskie i można po prostu użyć funkcji redukcji w ten sposób:
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;
};
Jeśli chcesz spłaszczyć users_comments
kolekcję, aby był to jeden dokument na komentarz, uruchom dodatkowo:
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"});
Ta technika zdecydowanie nie powinna być wykonywana w locie. Nadaje się do zadania cron lub czegoś takiego, co okresowo aktualizuje scalone dane. Prawdopodobnie będziesz chciał uruchomić ensureIndex
nową kolekcję, aby upewnić się, że zapytania, które wykonujesz przeciwko niej, działają szybko (pamiętaj, że twoje dane są nadal w value
kluczu, więc jeśli indeksowałbyś czas comments_with_demographics
komentowania created
, byłoby todb.comments_with_demographics.ensureIndex({"value.created": 1});
users_comments
kolekcji po pierwszym bloku kodu gist.github.com/nolanamy/83d7fb6a9bf92482a1c4311ad9c78835
MongoDB 3.2 pozwala teraz łączyć dane z wielu kolekcji w jedną poprzez etap agregacji wyszukiwania $ . Jako praktyczny przykład, powiedzmy, że masz dane o książkach podzielone na dwie różne kolekcje.
Pierwszy zbiór, nazywany books
, zawierający następujące dane:
{
"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"
}
I druga kolekcja, nazywana books_selling_data
, zawierająca następujące dane:
{
"_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
}
Aby połączyć obie kolekcje, wystarczy po prostu użyć $ lookup w następujący sposób:
db.books.aggregate([{
$lookup: {
from: "books_selling_data",
localField: "isbn",
foreignField: "isbn",
as: "copies_sold"
}
}])
Po tej agregacji books
kolekcja będzie wyglądać następująco:
{
"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
}
]
}
Ważne jest, aby zwrócić uwagę na kilka rzeczy:
books_selling_data
nie można podzielić kolekcji „z” .Podsumowując, jeśli chcesz skonsolidować obie kolekcje, mając w tym przypadku płaskie pole copy_sold z całkowitą liczbą sprzedanych kopii, będziesz musiał pracować trochę więcej, prawdopodobnie używając kolekcji pośredniej, która następnie być $ się do ostatecznego odbioru.
$lookup
czy wszystkie „localField” i „ForeignField” nie powinny być równe „isbn”? nie „_id” i „isbn”?
Jeśli nie ma wstawiania zbiorczego do mongodb, zapętlamy wszystkie obiekty w small_collection
i wstawiamy je jeden po drugim do big_collection
:
db.small_collection.find().forEach(function(obj){
db.big_collection.insert(obj)
});
Bardzo prosty przykład z wyszukiwaniem $.
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 }}
])
Tutaj jest używany
{ $unwind: { path: "$userInfoData", preserveNullAndEmptyArrays: true }},
{ $unwind: { path: "$userRoleData", preserveNullAndEmptyArrays: true }}
Zamiast
{ $unwind:"$userRoleData"}
{ $unwind:"$userRoleData"}
Ponieważ {$ unwind: "$ userRoleData"} zwróci pusty lub wynik 0, jeśli nie znaleziono pasującego rekordu przy wyszukiwaniu $.
Tworzenie związków w MongoDB w sposób „SQL UNION” jest możliwe przy użyciu agregacji wraz z odnośnikami w jednym zapytaniu. Oto przykład, który przetestowałem, który działa z 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' } }
]);
Oto wyjaśnienie, jak to działa:
Instancję aggregate
Spośród każdej kolekcji swojej bazie danych, która ma co najmniej jeden dokument w nim. Jeśli nie możesz zagwarantować, że jakakolwiek kolekcja Twojej bazy danych nie będzie pusta, możesz obejść ten problem, tworząc w bazie danych pewnego rodzaju „obojętny” zbiór zawierający pojedynczy pusty dokument, który będzie tam specjalnie do wykonywania zapytań dotyczących związków.
Zrób pierwszy etap swojego rurociągu { $limit: 1 }
. Spowoduje to usunięcie wszystkich dokumentów z kolekcji oprócz pierwszego.
Usuń wszystkie pola z pozostałego dokumentu za pomocą $project
etapu:
{ $project: { _id: '$$REMOVE' } }
Twój agregat zawiera teraz pojedynczy, pusty dokument. Czas dodać wyszukiwania dla każdej kolekcji, którą chcesz połączyć. Możesz użyć tego pipeline
pola, aby wykonać określone filtrowanie, lub pozostawić localField
i foreignField
zerować, aby dopasować całą kolekcję.
{ $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } },
{ $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } },
{ $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } }
Masz teraz agregat zawierający pojedynczy dokument zawierający 3 tablice takie jak to:
{
Collection1: [...],
Collection2: [...],
Collection3: [...]
}
Następnie możesz scalić je razem w jedną tablicę przy użyciu stołu $project
montażowego wraz z $concatArrays
operatorem agregacji:
{
"$project" :
{
"Union" : { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
}
}
Masz teraz agregat zawierający pojedynczy dokument, w którym znajduje się tablica zawierająca twoją unię kolekcji. Co pozostaje do zrobienia jest dodanie $unwind
i na $replaceRoot
scenę, aby podzielić swoją tablicę w oddzielnych dokumentach:
{ $unwind: "$Union" },
{ $replaceRoot: { newRoot: "$Union" } }
Voilà. Masz teraz zestaw wyników zawierający kolekcje, które chcesz połączyć ze sobą. Następnie możesz dodać kolejne etapy, aby dalej filtrować, sortować, stosować skip () i limit (). Prawie wszystko, co chcesz.
użyj wielu $ lookup dla wielu kolekcji w agregacji
pytanie:
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
}
])
wynik:
{
"_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 ma tę funkcję dołączania do wszystkiego, co jest już w bazie danych, więc to zachowanie można wykorzystać do łączenia dwóch kolekcji:
Nie próbowałem tego jeszcze, ale może działać szybciej niż podejście do mapy / zmniejszania.
Zaczynając Mongo 4.4
, możemy osiągnąć to połączenie w ramach potoku agregacji, łącząc nowy $unionWith
etap agregacji z $group
nowym $accumulator
operatorem:
// > 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
łączy rekordy z danej kolekcji w dokumentach już w potoku agregacji. Po 2 etapach związkowych mamy zatem wszystkich użytkowników, książki i nagrania filmowe w przygotowaniu.
Następnie $group
rejestrujemy $user
i gromadzimy elementy za pomocą $accumulator
operatora, umożliwiając niestandardowe gromadzenie dokumentów podczas ich grupowania:
accumulateArgs
.init
określa stan, który będzie się kumulował, gdy grupujemy elementy.accumulate
funkcja umożliwia wykonywanie akcja niestandardowa z rekordem są grupowane w celu zbudowania nagromadzony stan. Na przykład, jeśli element zgrupowany ma book
zdefiniowane pole, wówczas aktualizujemy books
część stanu.merge
służy do łączenia dwóch stanów wewnętrznych. Jest używany tylko w przypadku agregacji działających na dzielonych klastrach lub gdy operacja przekracza limity pamięci.Tak, możesz: skorzystaj z tej funkcji narzędzia, którą napisałem dzisiaj:
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);
}
)
}
}
Możesz przekazać do tej funkcji dowolną liczbę kolekcji, pierwsza będzie docelowa. Wszystkie pozostałe kolekcje są źródłami, które należy przenieść do kolekcji docelowej.
Fragment kodu. Dzięki uprzejmości wiele postów na przepełnieniu stosu, w tym ten.
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();
Musisz to zrobić w warstwie aplikacji. Jeśli używasz ORM, może użyć adnotacji (lub czegoś podobnego), aby pobrać odniesienia istniejące w innych kolekcjach. Pracowałem tylko z Morphia , a @Reference
adnotacja pobiera przywoływany byt, gdy jest pytany, więc jestem w stanie uniknąć robienia tego samemu w kodzie.
db.collection1.find().forEach(function(doc){db.collection2.save(doc)});
wystarczy. Podaj używany sterownik (java, php, ...), jeśli nie korzystasz z powłoki mongo.