[英]Aggregate Merge Lookup Result to Main Document
我創建了一個函數來縫合來自不同集合的2條記錄:
集合1的記錄:
{
_id: objectId(1231242331233),
acc: '12390',
val2: 'asdasdas'
}
集合2記錄:
{
_id: objectId(989232382302308),
isValid: '1',
tf: '098789928',
acc: '12390'
}
為此,我想出了以下帶有$ lookup的聚合函數。
Collection2.aggregate([
{
$lookup:
{
from: "Collection1",
localField: "acc",
foreignField: "acc",
as: "acc_record"
}
}
{
$out: 'Collection3'
}
]);
這將生成具有以下結構的記錄的Collection3:
{
_id: objectId(989232382302308),
isValid: '1',
tf: '098789928',
acc: '12390',
acc_record:[
{
_id: objectId(1231242331233),
acc: '12390',
val2: 'asdasdas'
}
]
}
合並這2個記錄的聚合函數是什么,但是不是將Collection1記錄放置在json對象的更深層次中,而是僅將不相等的元素放入並合並具有相同名稱的元素?
因此最終記錄結果將是:
{
_id: objectId(989232382302308),
isValid: '1',
tf: '098789928',
acc: '12390',
val2: 'asdasdas'
}
在$project
添加:
Collection2.aggregate([
{ "$lookup":{
"from": "Collection1",
"localField": "acc",
"foreignField": "acc",
"as": "acc_record"
}},
{ "$project": {
"isValid": 1,
"tf": 1,
"acc": { "$arrayElemAt": ["$acc_record.acc",0] },
"val2": { "$arrayElemAt": ["$acc_record.val2", 0] }
}},
{ "$out": "Collection3" }
]);
並使用$arrayElemAt
引用數組中的值,並將其提升為頂級對象中的值。
如果您始終知道結果是“一對一”的並且可以簡單地從返回的第一個數組元素中獲取值,那很好。 如果它們是“一對多”的,則還可以應用$unwind
:
Collection2.aggregate([
{ "$lookup":{
"from": "Collection1",
"localField": "acc",
"foreignField": "acc",
"as": "acc_record"
}},
{ "$unwind": "$acc_record" },
{ "$project": {
"_id": 0,
"isValid": 1,
"tf": 1,
"acc": "$acc_record.acc",
"val2": "$acc_record.val2"
}},
{ "$out": "Collection3" }
]);
這對於“一對一”也是完全有效的,但是您應該注意,在這里_id
被“故意”刪除了。 原因是對於$lookup
結果中返回的每個數組成員, $unwind
使用“許多”結果時會生成父文檔的“多個”副本。 由於_id
是“主鍵”,因此您不能在“多個文檔”中將該值保持相同。
因此,丟棄主鍵的目的是使$out
可以在寫入時創建一個新值,並且不會因“重復鍵錯誤”而失敗。 或者,如果您想將此保留為“引用”,則只需將"$_id"
值重命名為$project
另一個字段。
為了獲得更大的輸出,我們可以使用一些技巧來“合並”您的MongoDB支持這些功能的位置。 在目前的版本中,這些都是$arrayToObject
和$objectToArray
從MongoDB的3.4.4及以上:
Collection2.aggregate([
{ "$lookup":{
"from": "Collection1",
"localField": "acc",
"foreignField": "acc",
"as": "acc_record"
}},
{ "$unwind": "$acc_record" },
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": {
"$concatArrays": [
{ "$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$not": { "$in": ["$$this.k", ["_id", "acc_record"] } }
}},
{ "$filter": {
"input": { "$objectToArray": "$acc_record" },
"cond": { "$ne": ["$$this.k", "acc"] }
}}
]
}
}
}},
{ "$out": "Collection3" }
])
訣竅是將“ ROOT”文檔和子數組內容都轉換為單獨的數組,過濾掉重疊的鍵,然后應用$concatArrays
使其成為一個數組。 然后,您可以在“加入的”結果上應用$arrayToObject
,並通過$replaceRoot
將其轉換為根文檔。
MongoDB 3.6使它變得更加容易,並引入了$mergeObjects
,因此您實際上可以做一些簡單的事情:
Collection2.aggregate([
{ "$lookup":{
"from": "Collection1",
"localField": "acc",
"foreignField": "acc",
"as": "acc_record"
}},
{ "$unwind": "$acc_record" },
{ "$replaceRoot": {
"newRoot": {
"$mergeObjects": [
{ "$arrayToObject": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$not": { "$in": ["$$this.k", ["_id", "acc_record","acc"] } },
}
},
"$acc_record"
]
}
}},
{ "$out": "Collection3" }
]);
通常,您仍然需要$filter
不需要的鍵,例如$lookup
的目標字段名稱,以及最有可能的"localField"
或"foreignField"
值。 因此,除非您准備添加另一個聚合階段以完全刪除該子鍵,否則您不能只使用"$ROOT"
與子鍵的內容合並。
因此,一般來說, $mergeObjects
在這里並沒有增加太多,當然,除了運算符的命名可以使代碼的意圖清晰明了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.