对所有动态引用字段如果存在执行查找

问题描述

我有一个相当复杂的情况需要解决我有一个非常动态的模型,它允许引用其他对象(在同一个集合中)。引用可以是单个值或引用数组。此外,这些名称事先并不知道(在这个例子中,我将它们命名为 ref1 和 ref2,但它们可以是任何东西)。标识引用对象的是具有键referenceId 的字段存在。我实际上确实解决了那部分(见下文)。示例数据看起来像这样(为了简单起见,我选择了字符串而不是 ObjectIds):

[
  {
    "id": "1","name": "First"
  },{
    "id": "2","name": "Second","ref1": {
      "referenceId": "3"
    },"ref2": [
      {
        "referenceId": "1",},{
        "referenceId": "3"
      }
    ]
  },{
    "id": "3"
    "name": "Third",}
]

期望的结果是(在执行查找并且结果对象被内联替换之后)——这里是 id = 2 的对象:

[
  {
    "id": "2","ref1":   {
      "id": "1","name": "First"
    },"ref2": [
      {
        "id": "1","name": "First"
      },{
        "id": "3"
        "name": "Third",}
    ]
  }
]

经过大量试验后,我想出了一个聚合管道的想法,它产生以下输出

[
  {
    "id": "2","ref1": {
      "id": "3","name": "Third"
    },"ref2": [
      {
        "referenceId": "1"
      },"ref2": {
      "id": "1","name": "First"
    }
  },"ref2": {
      "id": "3","name": "Third"
    }
  }
]

这非常接近我想要的,但是,我在这个阶段遇到了以下问题:

  • 如何删除不需要的字段?由于 $lookup 的 $unwinding,我在最终结果中有重复的字段(查找 ref2 的 ref1 和 ref1 的 ref2)
  • 如何通过动态(又名不可预测)字段名称(在本例中为 ref1 和 ref2?)将结果分组到一个对象中

这在 MongoDB 中可行吗?我是在正确的道路上还是我应该考虑在客户端(如后端)代码中做这件事?我更愿意在聚合中解决这个问题,因为客户端代码会影响性能

以下是我目前采取的步骤:

Mongo playground

  1. 通过 $objectToArray 将结果转换为数组,并通过检查是否存在 referenceId 键来仅获取我感兴趣的键。如果存在,返回 [key,value] 对,否则返回 null: (匹配上面数据中的第二个对象)
  {
    $match: {
      id: "2"
    }
  }
  {
    $addFields: {
      "references": {
        $map: {
          input: {
            "$objectToArray": "$$ROOT"
          },as: "item",in: {
            $cond: {
              if: {
                $ne: [
                  "$$item.v.referenceId",undefined
                ]
              },then: [
                "$$item.k","$$item.v.referenceId"
              ],else: null
            }
          }
        }
      }
    }
  }
  1. 为后续映射过滤掉空值:
  {
    "$addFields": {
      "references": {
        $filter: {
          input: "$references",cond: {
            $ne: [
              "$$this",null
            ]
          }
        }
      }
    }
  }
  1. 对于查找,我需要固定的字段名称,因此将结果转换为 {"k":,"v": "} 的数组
{
    "$addFields": {
      "references": {
        $map: {
          input: "$references",in: {
            k: {
              "$arrayElemAt": [
                "$$item",0
              ]
            },v: {
              "$arrayElemAt": [
                "$$item",1
              ]
            }
          }
        }
      }
    }
  }
  1. 展开结果以使用 v 字段执行查找:
  {
    $unwind: "$references"
  },{
    $unwind: "$references.v"
  }
  1. 在“已解析”字段中执行查找,展开它,然后将其转换为名称为 (~ ref1,ref2 在我上面的示例中) 的对象,并将其添加到结果文档中。
{
    $lookup: {
      "from": "collection","localField": "references.v","foreignField": "id","as": "resolved"
    }
  },{
    $unwind: "$resolved"
  },{
    "$addFields": {
      "k": "$references.k","v": "$resolved"
    }
  },

最后删除临时字段

  {
    $project: {
      resolved: 0,references: 0,}
  },

有人愿意接受挑战还是我太努力了? ;-)

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...