Comment puis-je (dans MongoDB) combiner les données de plusieurs collections en une seule collection?
Puis-je utiliser map-Reduce et si oui, comment?
J'apprécierais grandement un exemple car je suis novice.
Comment puis-je (dans MongoDB) combiner les données de plusieurs collections en une seule collection?
Puis-je utiliser map-Reduce et si oui, comment?
J'apprécierais grandement un exemple car je suis novice.
Réponses:
Bien que vous ne puissiez pas le faire en temps réel, vous pouvez exécuter plusieurs fois map-réduire pour fusionner les données en utilisant l'option "réduire" dans MongoDB 1.8+ map / Reduce (voir http://www.mongodb.org/ display / DOCS / MapReduce # MapReduce-Outputoptions ). Vous devez avoir une clé dans les deux collections que vous pouvez utiliser comme _id.
Par exemple, supposons que vous ayez une users
collection et une comments
collection et que vous souhaitiez avoir une nouvelle collection contenant des informations démographiques sur l'utilisateur pour chaque commentaire.
Supposons que la users
collection comporte les champs suivants:
Et puis la comments
collection a les champs suivants:
Vous feriez cette carte / réduisez:
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
À ce stade, vous aurez une nouvelle collection appelée users_comments
qui contient les données fusionnées et vous pouvez maintenant l'utiliser. Ces collections réduites ont toutes _id
la clé que vous émettiez dans vos fonctions de carte, puis toutes les valeurs sont un sous-objet à l'intérieur duvalue
clé - les valeurs ne sont pas au niveau supérieur de ces documents réduits.
Ceci est un exemple quelque peu simple. Vous pouvez répéter cela avec plus de collections autant que vous le souhaitez pour continuer à constituer la collection réduite. Vous pouvez également faire des résumés et des agrégations de données au cours du processus. Vous définiriez probablement plus d'une fonction de réduction, car la logique d'agrégation et de conservation des champs existants devient plus complexe.
Vous remarquerez également qu'il existe désormais un document pour chaque utilisateur avec tous les commentaires de cet utilisateur dans un tableau. Si nous fusionnions des données qui ont une relation un-à-un plutôt qu'un-à-plusieurs, ce serait plat et vous pourriez simplement utiliser une fonction de réduction comme celle-ci:
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;
};
Si vous souhaitez aplatir la users_comments
collection afin qu'elle soit un document par commentaire, exécutez également ceci:
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"});
Cette technique ne doit certainement pas être effectuée à la volée. Il convient à un travail cron ou quelque chose comme ça qui met à jour périodiquement les données fusionnées. Vous voudrez probablement exécuter ensureIndex
la nouvelle collection pour vous assurer que les requêtes que vous effectuez contre elle s'exécutent rapidement (gardez à l'esprit que vos données sont toujours dans une value
clé, donc si vous deviez indexer comments_with_demographics
l' created
heure du commentaire , ce seraitdb.comments_with_demographics.ensureIndex({"value.created": 1});
users_comments
collection après le premier bloc de code gist.github.com/nolanamy/83d7fb6a9bf92482a1c4311ad9c78835
MongoDB 3.2 permet désormais de combiner les données de plusieurs collections en une seule à travers l' étape d'agrégation $ lookup . À titre d'exemple pratique, disons que vous disposez de données sur les livres divisées en deux collections différentes.
Première collecte, appelée books
, contenant les données suivantes:
{
"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"
}
Et la deuxième collection, appelée books_selling_data
, contenant les données suivantes:
{
"_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
}
Pour fusionner les deux collections, il suffit d'utiliser la recherche $ de la manière suivante:
db.books.aggregate([{
$lookup: {
from: "books_selling_data",
localField: "isbn",
foreignField: "isbn",
as: "copies_sold"
}
}])
Après cette agrégation, la books
collection ressemblera à ceci:
{
"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
}
]
}
Il est important de noter quelques éléments:
books_selling_data
, la collection "from" ne peut pas être fragmentée.Donc, en conclusion, si vous voulez consolider les deux collections, ayant, dans ce cas, un champ plat copies_sold avec le total des copies vendues, vous devrez travailler un peu plus, probablement en utilisant une collection intermédiaire qui, alors, être $ à la collection finale.
$lookup
tous les deux "localField" et "foreignField" ne devrait-il pas être égal à "isbn"? pas "_id" et "isbn"?
S'il n'y a pas d'insertion en masse dans mongodb, nous bouclons tous les objets dans le small_collection
et les insérons un par un dans le big_collection
:
db.small_collection.find().forEach(function(obj){
db.big_collection.insert(obj)
});
Exemple très basique avec $ 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 }}
])
Ici est utilisé
{ $unwind: { path: "$userInfoData", preserveNullAndEmptyArrays: true }},
{ $unwind: { path: "$userRoleData", preserveNullAndEmptyArrays: true }}
Au lieu de
{ $unwind:"$userRoleData"}
{ $unwind:"$userRoleData"}
Parce que {$ unwind: "$ userRoleData"} cela retournera un résultat vide ou 0 si aucun enregistrement correspondant trouvé avec $ lookup.
Il est possible d'effectuer des unions dans MongoDB à la manière d'un 'SQL UNION' en utilisant des agrégations avec des recherches, dans une seule requête. Voici un exemple que j'ai testé qui fonctionne avec 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' } }
]);
Voici l'explication de son fonctionnement:
Instancier une aggregate
de toute collection de votre base de données qui a au moins un document en elle. Si vous ne pouvez garantir qu'une collection de votre base de données ne sera pas vide, vous pouvez contourner ce problème en créant dans votre base de données une sorte de collection `` factice '' contenant un seul document vide qui sera là spécifiquement pour effectuer des requêtes d'union.
Faites de la première étape de votre pipeline { $limit: 1 }
. Cela supprimera tous les documents de la collection, sauf le premier.
Supprimez tous les champs du document restant à l'aide d'une $project
étape:
{ $project: { _id: '$$REMOVE' } }
Votre agrégat contient désormais un seul document vide. Il est temps d'ajouter des recherches pour chaque collection que vous souhaitez réunir. Vous pouvez utiliser le pipeline
champ pour effectuer un filtrage spécifique, ou laisser localField
et foreignField
comme null pour correspondre à l'ensemble de la collection.
{ $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } },
{ $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } },
{ $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } }
Vous avez maintenant un agrégat contenant un seul document qui contient 3 tableaux comme celui-ci:
{
Collection1: [...],
Collection2: [...],
Collection3: [...]
}
Vous pouvez ensuite les fusionner ensemble dans un seul tableau à l'aide d'une $project
étape avec l' $concatArrays
opérateur d'agrégation:
{
"$project" :
{
"Union" : { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
}
}
Vous avez maintenant un agrégat contenant un seul document, dans lequel se trouve un tableau qui contient votre union de collections. Ce qui reste à faire est d'ajouter une $unwind
et une $replaceRoot
étape pour diviser votre tableau en documents séparés:
{ $unwind: "$Union" },
{ $replaceRoot: { newRoot: "$Union" } }
Voilà. Vous disposez maintenant d'un jeu de résultats contenant les collections que vous souhaitez réunir. Vous pouvez ensuite ajouter plus d'étapes pour le filtrer davantage, le trier, appliquer skip () et limit (). À peu près tout ce que vous voulez.
utiliser plusieurs recherches $ pour plusieurs collections en agrégation
requete:
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
}
])
résultat:
{
"_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 a cette fonctionnalité d'ajouter au-dessus de tout ce qui est déjà dans la base de données, donc ce comportement pourrait être utilisé pour combiner deux collections:
Je ne l'ai pas encore essayé, mais il pourrait fonctionner plus rapidement que l'approche carte / réduire.
En commençant Mongo 4.4
, nous pouvons réaliser cette jointure au sein d'un pipeline d'agrégation en couplant la nouvelle $unionWith
étape d'agrégation avec $group
le nouvel $accumulator
opérateur de:
// > 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
combine les enregistrements de la collection donnée dans des documents déjà dans le pipeline d'agrégation. Après les 2 étapes de l'union, nous avons donc tous les utilisateurs, livres et enregistrements de films dans le pipeline.
Nous $group
enregistrons ensuite par $user
et accumulons des éléments à l'aide de l' $accumulator
opérateur permettant des accumulations personnalisées de documents au fur et à mesure qu'ils sont regroupés:
accumulateArgs
.init
définit l'état qui sera accumulé lorsque nous regrouperons les éléments.accumulate
fonction permet d'effectuer une action personnalisée avec un enregistrement en cours de regroupement afin de construire l'état accumulé. Par exemple, si l'élément en cours de regroupement a le book
champ défini, nous mettons à jour la books
partie de l'état.merge
est utilisé pour fusionner deux états internes. Il n'est utilisé que pour les agrégations s'exécutant sur des clusters fragmentés ou lorsque l'opération dépasse les limites de la mémoire.Oui, vous pouvez: Prenez cette fonction utilitaire que j'ai écrite aujourd'hui:
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);
}
)
}
}
Vous pouvez passer à cette fonction n'importe quel nombre de collections, la première va être la cible. Toutes les autres collections sont des sources à transférer vers la cible.
Extrait de code. Courtoisie - Plusieurs messages sur le débordement de la pile, y compris celui-ci.
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();
Vous devez le faire dans votre couche d'application. Si vous utilisez un ORM, il pourrait utiliser des annotations (ou quelque chose de similaire) pour extraire des références qui existent dans d'autres collections. Je n'ai travaillé qu'avec Morphia , et l' @Reference
annotation récupère l'entité référencée lorsqu'elle est interrogée, donc je peux éviter de le faire moi-même dans le code.
db.collection1.find().forEach(function(doc){db.collection2.save(doc)});
suffit. Veuillez spécifier votre pilote utilisé (java, php, ...) si vous n'utilisez pas le shell mongo.