如何将不同数组中的两个匹配对象合并为一个对象?
Posted
技术标签:
【中文标题】如何将不同数组中的两个匹配对象合并为一个对象?【英文标题】:How to merge two matching objects from different array into one object? 【发布时间】:2018-06-08 00:09:40 【问题描述】:我有一种情况,我从聚合中得到一个结果,我以这种格式获取数据。
"_id" : ObjectId("5a42432d69cbfed9a410e8ad"),
"bacId" : "BAC0023444",
"cardId" : "2",
"defaultCardOrder" : "2",
"alias" : "Finance",
"label" : "Finance",
"for" : "",
"cardTooltip" :
"enable" : true,
"text" : ""
,
"dataBlocks" : [
"defaultBlockOrder" : "1",
"blockId" : "1",
"data" : "0"
,
"defaultBlockOrder" : "2",
"blockId" : "2",
"data" : "0"
,
"defaultBlockOrder" : "3",
"blockId" : "3",
"data" : "0"
],
"templateBlocks" : [
"blockId" : "1",
"label" : "Gross Profit",
"quarter" : "",
"data" : "",
"dataType" :
"typeId" : "2"
,
"tooltip" :
"enable" : true,
"text" : ""
,
"blockId" : "2",
"label" : "Profit Forecast",
"quarter" : "",
"data" : "",
"dataType" :
"typeId" : "2"
,
"tooltip" :
"enable" : true,
"text" : ""
,
"blockId" : "3",
"label" : "Resource Billing",
"quarter" : "",
"data" : "",
"dataType" :
"typeId" : "2"
,
"tooltip" :
"enable" : true,
"text" : ""
]
,
"_id" : ObjectId("5a42432d69cbfed9a410e8ad"),
"bacId" : "BAC0023444",
"cardId" : "3",
"defaultCardOrder" : "3",
"alias" : "Staffing",
"label" : "Staffing",
"for" : "",
"cardTooltip" :
"enable" : true,
"text" : ""
,
"dataBlocks" : [
"defaultBlockOrder" : "1",
"blockId" : "1",
"data" : "1212"
,
"defaultBlockOrder" : "2",
"blockId" : "2",
"data" : "1120"
,
"defaultBlockOrder" : "3",
"blockId" : "3",
"data" : "1200"
],
"templateBlocks" : [
"blockId" : "1",
"label" : "Staffing Planner",
"quarter" : "",
"data" : "",
"dataType" :
"typeId" : "1"
,
"tooltip" :
"enable" : true,
"text" : ""
,
"blockId" : "2",
"label" : "Baseline",
"quarter" : "",
"data" : "",
"dataType" :
"typeId" : "1"
,
"tooltip" :
"enable" : true,
"text" : ""
,
"blockId" : "3",
"label" : "Projected",
"quarter" : "",
"data" : "",
"dataType" :
"typeId" : "1"
,
"tooltip" :
"enable" : true,
"text" : ""
]
现在我想比较每一行的两个对象数组,在这种情况下,它的“dataBlocks”和“templateBlocks”基于“blockId”,我想得到以下格式的结果。
"_id" : ObjectId("5a42432d69cbfed9a410e8ad"),
"bacId" : "BAC0023444",
"cardId" : "2",
"defaultCardOrder" : "2",
"alias" : "Finance",
"label" : "Finance",
"for" : "",
"cardTooltip" :
"enable" : true,
"text" : ""
,
"blocks" : [
"defaultBlockOrder" : "1",
"blockId" : "1",
"data" : "0",
"label" : "Gross Profit",
"quarter" : "",
"dataType" :
"typeId" : "2"
,
"tooltip" :
"enable" : true,
"text" : ""
,
"defaultBlockOrder" : "2",
"blockId" : "2",
"data" : "0",
"label" : "Profit Forecast",
"quarter" : "",
"dataType" :
"typeId" : "2"
,
"tooltip" :
"enable" : true,
"text" : ""
,
"defaultBlockOrder" : "3",
"blockId" : "3",
"data" : "0",
"label" : "Resource Billing",
"quarter" : "",
"dataType" :
"typeId" : "2"
,
"tooltip" :
"enable" : true,
"text" : ""
]
,
"_id" : ObjectId("5a42432d69cbfed9a410e8ad"),
"bacId" : "BAC0023444",
"cardId" : "3",
"defaultCardOrder" : "3",
"alias" : "Staffing",
"label" : "Staffing",
"for" : "",
"cardTooltip" :
"enable" : true,
"text" : ""
,
"dataBlocks" : [
"defaultBlockOrder" : "1",
"blockId" : "1",
"data" : "1212",
"label" : "Staffing Planner",
"quarter" : "",
"dataType" :
"typeId" : "1"
,
"tooltip" :
"enable" : true,
"text" : ""
,
"defaultBlockOrder" : "2",
"blockId" : "2",
"data" : "1120",
"label" : "Baseline",
"quarter" : "",
"dataType" :
"typeId" : "1"
,
"tooltip" :
"enable" : true,
"text" : ""
,
"defaultBlockOrder" : "3",
"blockId" : "3",
"data" : "1200",
"label" : "Projected",
"quarter" : "",
"dataType" :
"typeId" : "1"
,
"tooltip" :
"enable" : true,
"text" : ""
]
是否可以使用 mongodb 完成它?我正在使用 3.4 并尝试使用聚合来实现这一点。
提前致谢。
【问题讨论】:
【参考方案1】:您可以在 3.6 中尝试以下聚合。
下面的查询迭代 dataBlocks 数组并将数据块元素与模板块元素合并。使用$indexofArray
查找模板块,它定位具有匹配块id 的数组索引和$arrayElemAt
以访问找到的索引处的元素。
db.collection_name.aggregate(["$addFields":
"blocks":
"$map":
"input":"$dataBlocks",
"in":
"$mergeObjects":[
"$$this",
"$arrayElemAt":[
"$templateBlocks",
"$indexOfArray":["$templateBlocks.blockId","$$this.blockId"]
]
]
])
对于 3.4,将 $mergeObjects
替换为 $arrayToObject
、$objectToArray
和 $concatArrays
的组合以合并两个数组中的每个数组元素。
db.collection_name.aggregate(["$addFields":
"blocks":
"$map":
"input":"$dataBlocks",
"in":
"$arrayToObject":
"$concatArrays":[
"$objectToArray":"$$this",
"$objectToArray":
"$arrayElemAt":[
"$templateBlocks",
"$indexOfArray":["$templateBlocks.blockId","$$this.blockId"]
]
]
])
您可以使用排除项目作为最后阶段从输出中删除数组字段。
"$project":"templateBlocks":0,"dataBlocks":0
【讨论】:
"$indexOfArray":["$templateBlocks","$$this.blockId"]
的部分不起作用,因为templateBlocks
包含对象而不是元素,因此它们与给定的blockId
不直接匹配,它们需要以某种方式映射,以便您可以引用每个templateBlock
的blockId
属性。
我建议对@Argento 提到的问题进行编辑。否则,这绝对应该是公认的答案。
@Argento,@Burawi,我刚刚添加了 "$indexOfArray":["$templateBlocks
.blockId ","$$this.blockId"]
,它似乎有效。【参考方案2】:
以下查询完成了这项工作:
db.merge.aggregate([
// unwind twice
$unwind: "$templateBlocks",
$unwind: "$dataBlocks",
// get rid of documents where dataBlocks.blockId and
// templateBlocks.blockId are not equal
$redact: $cond: [
$eq: [
"$dataBlocks.blockId",
"$templateBlocks.blockId"
]
,
"$$KEEP",
"$$PRUNE"
]
,
// merge dataBlocks and templateBlocks into a single document
$project:
bacId: 1,
cardId: 1,
defaultCardOrder: 1,
alias: 1,
label: 1,
for: 1,
cardTooltip: 1,
dataBlocks:
defaultBlockOrder: "$dataBlocks.defaultBlockOrder",
blockId: "$dataBlocks.blockId",
data: "$dataBlocks.data",
label: "$templateBlocks.label",
quarter: "$templateBlocks.quarter",
data: "$templateBlocks.data",
dataType: "$templateBlocks.dataType",
tooltip: "$templateBlocks.tooltip"
,
// group to put correspondent dataBlocks to an array
$group:
_id:
_id: "$_id",
bacId: "$bacId",
cardId: "$cardId",
defaultCardOrder: "$defaultCardOrder",
alias: "$alias",
label: "$label",
for: "$for",
cardTooltip: "$cardTooltip"
,
dataBlocks: $push: "$dataBlocks"
,
// remove the unnecessary _id object
$project:
_id: "$_id._id",
bacId: "$_id.bacId",
cardId: "$_id.cardId",
defaultCardOrder: "$_id.defaultCardOrder",
alias: "$_id.alias",
label: "$_id.label",
for: "$_id.for",
cardTooltip: "$_id.cardTooltip",
dataBlocks: "$dataBlocks"
])
考虑到性能取决于数据集的大小,因为查询展开两次,它可能会产生大量的中间文档。
【讨论】:
谢谢。它拯救了我的一天。以上是关于如何将不同数组中的两个匹配对象合并为一个对象?的主要内容,如果未能解决你的问题,请参考以下文章