问题描述
我有一个相当复杂的情况需要解决。我有一个非常动态的模型,它允许引用其他对象(在同一个集合中)。引用可以是单个值或引用数组。此外,这些名称事先并不知道(在这个例子中,我将它们命名为 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 中可行吗?我是在正确的道路上还是我应该考虑在客户端(如后端)代码中做这件事?我更愿意在聚合中解决这个问题,因为客户端代码会影响性能。
以下是我目前采取的步骤:
- 通过 $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
}
}
}
}
}
}
- 为后续映射过滤掉空值:
{
"$addFields": {
"references": {
$filter: {
input: "$references",cond: {
$ne: [
"$$this",null
]
}
}
}
}
}
- 对于查找,我需要固定的字段名称,因此将结果转换为 {"k":,"v": "} 的数组
{
"$addFields": {
"references": {
$map: {
input: "$references",in: {
k: {
"$arrayElemAt": [
"$$item",0
]
},v: {
"$arrayElemAt": [
"$$item",1
]
}
}
}
}
}
}
- 展开结果以使用 v 字段执行查找:
{
$unwind: "$references"
},{
$unwind: "$references.v"
}
{
$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 (将#修改为@)