2016-08-10 61 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