2016-08-10 35 views
1

我已经知道MongoDB不支持连接操作,但我必须模拟$lookup(来自聚合框架)和mapReduce范例。在MongoDB中使用MapReduce加入两个集合

我的两个集合是:

// Employees sample 
{ 
    "_id" : "1234", 
    "first_name" : "John", 
    "last_name" : "Bush", 
    "departments" : 
    [ 
    { "dep_id" : "d001", "hire_date" : "date001" }, 
    { "dep_id" : "d004", "hire_date" : "date004" } 
    ] 
} 
{ 
    "_id" : "5678", 
    "first_name" : "Johny", 
    "last_name" : "Cash", 
    "departments" : [ { "dep_id" : "d001", "hire_date" : "date03" } ] 
} 
{ 
    "_id" : "9012", 
    "first_name" : "Susan", 
    "last_name" : "Bowdy", 
    "departments" : [ { "dep_id" : "d004", "hire_date" : "date04" } ] 
} 

// Departments sample 
{ 
    "_id" : "d001", 
    "dep_name" : "Sales", 
    "employees" : [ "1234", "5678" ] 
}, 
{ 
    "_id" : "d004", 
    "name" : "Quality M", 
    "employees" : [ "1234", "9012" ] 
} 

而实际上,我想有这样的结果:

{ 
    "_id" : "1234", 
    "value" : 
    { 
    "first_name" : "John", 
    "departments" : 
    [ 
     { "dep_id" : "d001", "dep_name" : "Sales" }, 
     { "dep_id" : "d004", "dep_name" : "Quality M" } 
    ] 
    } 
} 
{ 
    "_id" : "5678", 
    "value" : 
    { 
    "first_name" : "Johnny", 
    "departments" : [ { "dep_id" : "d001", "dep_name" : "Sales" } ] 
    } 
} 
{ 
    "_id" : "9012", 
    "value" : 
    { 
    "first_name" : "Susan", 
    "departments" : [ { "dep_id" : "d004", "dep_name" : "Quality M" } ] 
    } 
} 

公共领域是dep_id(从员工)和_id(从部门)。

我的代码是下一个,但它不工作,因为我需要。

var mapD = function() { 
    for (var i=0; i<this.employees.length; i++) { 
    emit(this.employees[i], { dep_id: 0, dep_name: this.dep_name }); 
    } 
} 

var mapE = function() { 
    for (var i=0; i<this.departments.length; i++) { 
    emit(this._id, { dep_id: this.departments[i].dep_id, dep_name: 0 }); 
    } 
} 

var reduceLookUp = function(key, values) { 
    var result = {dep_id: 0, dep_name: 0}; 
    values.forEach(function(value) { 
    if (value.dep_name !== null && value.dep_name !== undefined) { 
     result.dep_name = values.dep_name; 
    } 
    if (value.dep_id !== null && value.dep_id !== undefined) { 
     result.dep_id = value.dep_id; 
    } 
    }); 
    return result; 
}; 

db.Departments.mapReduce(mapD, reduceLookUp, { out: { reduce: "joined" } }); 
db.Employees.mapReduce(mapE, reduceLookUp, { out: { reduce: "joined" } }); 

我真的会赞赏你的帮助!提前致谢。

回答

5

在你的问题first_name只能从Employees集合中提取,而dep_name只能从Departments集合中提取。

您可以使用MapReduce和聚合框架来实现它。

1的MapReduce解决方案

如果修改您的地图和减少功能如下

var mapD = function() { 
    for (var i=0; i<this.employees.length; i++) 
    emit(this.employees[i], { dep_id: this._id, dep_name: this.dep_name }); 
} 

var mapE = function() { emit(this._id, { first_name: this.first_name }); } 

var reduceLookUp = function(key, values) { 
    var results = {}; 
    var departments = []; 
    values.forEach(function(value) { 
    var department = {}; 
    if (value.dep_id !== undefined) department["dep_id"] = value.dep_id; 
    if (value.dep_name !== undefined) department["dep_name"] = value.dep_name; 
    if (Object.keys(department).length > 0) departments.push(department); 
    if (value.first_name !== undefined) results["first_name"] = value.first_name; 
    if (value.departments !== undefined) results["departments"] = value.departments; 
    }); 
    if (Object.keys(departments).length > 0) results["departments"] = departments; 
    return results; 
} 

然后第一MapReduce的通话

db.Departments.mapReduce(mapD, reduceLookUp, { out: { reduce: "joined" } }); 

将插入joined收集

{ 
    "_id" : "1234", 
    "value" : 
    { 
    "departments" : 
    [ 
     { "dep_id" : "d001", "dep_name" : "Sales" }, 
     { "dep_id" : "d004", "dep_name" : "Quality M" } 
    ] 
    } 
} 

,而第二个电话

db.Employees.mapReduce(mapE, reduceLookUp, { out: { reduce: "joined" } }); 

应该插入

{ "_id" : "1234", "value" : { "first_name" : "John" } } 

,但根据documentationreduce输出选项将

合并新结果与现有的结果,如果输出收集 已经存在。如果现有的文件具有相同的密钥作为新 结果,应用减少功能,无论是新的和现有的 文件,并用结果覆盖现有文件

因此,减少函数会被调用再次,你的情况与参数

key = "1234", 
values = 
[ 
    { 
    "departments" : 
    [ 
     { "dep_id" : "d001", "dep_name" : "Sales" }, 
     { "dep_id" : "d004", "dep_name" : "Quality M" } 
    ] 
    }, 
    { "first_name" : "John" } 
] 

和最终的结果是

{ 
    "_id" : "1234", 
    "value" : 
    { 
    "first_name" : "John", 
    "departments" : 
    [ 
     { "dep_id" : "d001", "dep_name" : "Sales" }, 
     { "dep_id" : "d004", "dep_name" : "Quality M" } 
    ] 
    } 
} 

2.聚合框架解决方案

针对您的问题的更好的解决方案是使用aggregation framework而不是Map-Reduce。在这里,你会用$lookup阶段从Employees

db.Departments.aggregate([ 
    { $unwind: "$employees" }, 
    { 
    $lookup: 
     { 
     from: "Employees", 
     localField: "employees", 
     foreignField: "_id", 
     as: "employee" 
     } 
    }, 
    { $unwind: "$employee" }, 
    { 
    $group: 
     { 
     "_id": "$employees", 
     "first_name": { $first: "$employee.first_name" }, 
     "departments": { $push: { dep_id: "$_id", dep_name: "$dep_name" } } 
     } 
    } 
]); 

获取的一些数据,这将导致到

{ 
    "_id" : "1234", 
    "first_name" : "John", 
    "departments" : 
    [ 
     { "dep_id" : "d001", "dep_name" : "Sales" }, 
     { "dep_id" : "d004", "dep_name" : "Quality M" } 
    ] 
} 
+0

应如何如果结果我想员工的FIRST_NAME领域呢? – IvanG4Life

+0

检查更新的答案 – tarashypka

+0

对不起,但我需要'map'和'reduce'功能。 :( – IvanG4Life