簡體   English   中英

將合並查找結果匯總到主文檔

[英]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

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM