Come posso (in MongoDB) combinare i dati di più raccolte in una raccolta?
Posso usare map-ridurre e, in tal caso, come?
Gradirei molto qualche esempio dato che sono alle prime armi.
Come posso (in MongoDB) combinare i dati di più raccolte in una raccolta?
Posso usare map-ridurre e, in tal caso, come?
Gradirei molto qualche esempio dato che sono alle prime armi.
Risposte:
Sebbene non sia possibile farlo in tempo reale, è possibile eseguire più volte la riduzione della mappa per unire i dati utilizzando l'opzione "riduci" in MongoDB 1.8+ mappa / riduzione (vedere http://www.mongodb.org/ display / DOCS / MapReduce # MapReduce-Outputoptions ). È necessario disporre di alcune chiavi in entrambe le raccolte che è possibile utilizzare come _id.
Ad esempio, supponiamo che tu abbia una users
collezione e uncomments
raccolta e desideri disporre di una nuova raccolta con alcune informazioni demografiche dell'utente per ciascun commento.
Supponiamo che la users
raccolta abbia i seguenti campi:
E poi la comments
raccolta ha i seguenti campi:
Dovresti fare questa mappa / ridurre:
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
A questo punto, avrai una nuova raccolta chiamata users_comments
che contiene i dati uniti e ora puoi usarli. Queste raccolte ridotte hanno tutte _id
la chiave che stavi emettendo nelle funzioni della tua mappa e quindi tutti i valori sono un oggetto secondario all'interno della value
chiave - i valori non sono al livello più alto di questi documenti ridotti.
Questo è un esempio piuttosto semplice. Puoi ripeterlo con più raccolte tanto quanto vuoi continuare a costruire la raccolta ridotta. È inoltre possibile eseguire riepiloghi e aggregazioni di dati nel processo. Probabilmente definiresti più di una funzione di riduzione man mano che la logica di aggregazione e conservazione dei campi esistenti diventa più complessa.
Noterai anche che ora c'è un documento per ogni utente con tutti i commenti di quell'utente in un array. Se unessimo i dati che hanno una relazione uno a uno anziché uno a molti, sarebbero piatti e potresti semplicemente usare una funzione di riduzione come questa:
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;
};
Se vuoi appiattire la users_comments
raccolta in modo che sia un documento per commento, esegui anche questo:
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"});
Questa tecnica non dovrebbe assolutamente essere eseguita al volo. È adatto per un lavoro cron o qualcosa del genere che aggiorna periodicamente i dati uniti. Probabilmente vorrai eseguire ensureIndex
la nuova raccolta per assicurarti che le query che esegui contro di essa vengano eseguite rapidamente (tieni presente che i tuoi dati sono ancora all'interno di una value
chiave, quindi se dovessi indicizzare comments_with_demographics
il momento del commento created
, sarebbedb.comments_with_demographics.ensureIndex({"value.created": 1});
users_comments
collezione dopo il primo blocco di codice gist.github.com/nolanamy/83d7fb6a9bf92482a1c4311ad9c78835
MongoDB 3.2 ora consente di combinare i dati di più raccolte in una attraverso la fase di aggregazione $ lookup . A titolo di esempio pratico, supponiamo che i dati sui libri siano divisi in due diverse raccolte.
Prima raccolta, chiamata books
, con i seguenti dati:
{
"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"
}
E la seconda raccolta, chiamata books_selling_data
, con i seguenti dati:
{
"_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
}
Unire entrambe le raccolte è solo una questione di usare $ lookup nel modo seguente:
db.books.aggregate([{
$lookup: {
from: "books_selling_data",
localField: "isbn",
foreignField: "isbn",
as: "copies_sold"
}
}])
Dopo questa aggregazione, la books
raccolta sarà simile alla seguente:
{
"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
}
]
}
È importante notare alcune cose:
books_selling_data
, non può essere frammentata.Quindi, in conclusione, se vuoi consolidare entrambe le raccolte, avendo, in questo caso, un campo copie_venduto piatto con le copie totali vendute, dovrai lavorare un po 'di più, probabilmente usando una raccolta intermedia che, quindi, essere $ fuori alla raccolta finale.
$lookup
non dovrebbe essere tutto uguale "isbn" sia "localField" che "foreignField"? non "_id" e "isbn"?
Se non è presente un inserimento bulk in mongodb, eseguiamo il loop di tutti gli oggetti in small_collection
e li inseriamo uno per uno nel big_collection
:
db.small_collection.find().forEach(function(obj){
db.big_collection.insert(obj)
});
Esempio molto semplice con $ 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 }}
])
Qui è usato
{ $unwind: { path: "$userInfoData", preserveNullAndEmptyArrays: true }},
{ $unwind: { path: "$userRoleData", preserveNullAndEmptyArrays: true }}
Invece di
{ $unwind:"$userRoleData"}
{ $unwind:"$userRoleData"}
Perché {$ unwind: "$ userRoleData"} questo restituirà risultati vuoti o 0 se nessun record corrispondente trovato con $ lookup.
Fare unioni in MongoDB in modo "SQL UNION" è possibile usando aggregazioni e ricerche, in una singola query. Ecco un esempio che ho testato che funziona con 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' } }
]);
Ecco la spiegazione di come funziona:
Crea un'istanza aggregate
di qualsiasi raccolta del database che contiene almeno un documento. Se non puoi garantire che nessuna raccolta del tuo database non sarà vuota, puoi risolvere questo problema creando nel tuo database una sorta di raccolta "fittizia" contenente un singolo documento vuoto che sarà lì appositamente per fare query sindacali.
Prepara la prima fase della tua pipeline { $limit: 1 }
. Questo eliminerà tutti i documenti della raccolta tranne il primo.
Rimuovere tutti i campi del documento rimanente utilizzando uno $project
stage:
{ $project: { _id: '$$REMOVE' } }
Il tuo aggregato ora contiene un singolo documento vuoto. È tempo di aggiungere ricerche per ogni raccolta che desideri unire insieme. È possibile utilizzare il pipeline
campo per eseguire alcuni filtri specifici oppure lasciare localField
e foreignField
come null per abbinare l'intera raccolta.
{ $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } },
{ $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } },
{ $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } }
Ora hai un aggregato contenente un singolo documento che contiene 3 array come questo:
{
Collection1: [...],
Collection2: [...],
Collection3: [...]
}
È quindi possibile unirli insieme in un singolo array utilizzando uno $project
stage insieme $concatArrays
all'operatore di aggregazione:
{
"$project" :
{
"Union" : { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
}
}
Ora hai un aggregato contenente un singolo documento, in cui si trova un array che contiene l'unione di raccolte. Ciò che resta da fare è aggiungere una $unwind
e una $replaceRoot
fase per dividere l'array in documenti separati:
{ $unwind: "$Union" },
{ $replaceRoot: { newRoot: "$Union" } }
Ecco. Ora hai un set di risultati contenente le raccolte che desideri unire insieme. È quindi possibile aggiungere più fasi per filtrarlo ulteriormente, ordinarlo, applicare skip () e limit (). Praticamente tutto quello che vuoi.
usa più $ lookup per più raccolte in aggregazione
query:
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
}
])
risultato:
{
"_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 ha questa funzione di aggiunta in aggiunta a qualsiasi cosa sia già presente nel database, quindi questo comportamento potrebbe essere usato per combinare due raccolte:
Non l'ho ancora provato, ma potrebbe funzionare più velocemente dell'approccio mappa / riduzione.
Iniziando Mongo 4.4
, possiamo raggiungere questo join all'interno di una pipeline di aggregazione accoppiando la nuova $unionWith
fase di aggregazione con $group
il nuovo $accumulator
operatore:
// > 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
combina i record della raccolta data all'interno di documenti già presenti nella pipeline di aggregazione. Dopo le 2 fasi del sindacato, abbiamo quindi tutti gli utenti, i libri e i record di film in cantiere.
Quindi $group
registriamo $user
e accumuliamo articoli utilizzando l' $accumulator
operatore che consente l'accumulo personalizzato di documenti man mano che vengono raggruppati:
accumulateArgs
.init
definisce lo stato che verrà accumulato man mano che raggruppiamo gli elementi.accumulate
funzione consente di eseguire un'azione personalizzata con un record raggruppato per creare lo stato accumulato. Ad esempio, se l'elemento da raggruppare ha il book
campo definito, allora aggiorniamo la books
parte dello stato.merge
viene utilizzato per unire due stati interni. Viene utilizzato solo per aggregazioni in esecuzione su cluster frammentati o quando l'operazione supera i limiti di memoria.Sì, puoi: prendi questa funzione di utilità che ho scritto oggi:
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);
}
)
}
}
Puoi passare a questa funzione un numero qualsiasi di raccolte, la prima sarà quella di destinazione. Tutte le altre raccolte sono fonti da trasferire a quella di destinazione.
Snippet di codice. Per gentile concessione: più post in overflow dello stack, incluso questo.
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();
Devi farlo nel tuo livello di applicazione. Se stai usando un ORM, potrebbe usare le annotazioni (o qualcosa di simile) per estrarre i riferimenti esistenti in altre raccolte. Ho lavorato solo con Morphia e l' @Reference
annotazione recupera l'entità referenziata quando viene interrogata, quindi sono in grado di evitare di farlo da sola nel codice.
db.collection1.find().forEach(function(doc){db.collection2.save(doc)});
è sufficiente. Specificare il driver utilizzato (java, php, ...) se non si utilizza la shell mongo.