MongoDB聚合查找和展开

时间:2018-09-28 20:14:13

标签: node.js mongodb mongoose aggregation-framework lookup

我有这个问题:

我需要进行查询,以获取外国数据(通过查询),“多对多”和“一对多”。

我的数据如下:

转移收藏集:

{
  "_id": 1,
  "requests": [
    {
       "service": 1,
       "foo": "foo1",
       "bar": "bar1"
    },
    {
       "service": 2,
       "foo": "foo2",
       "bar": "bar2"
    }
  ]
}

因此,“服务”字段是另一个集合“服务”的外部ID。

服务集合:

[{ _id: 1, name: 'Service 1" }, { _id: 2, name: 'Service 2' }]

问题是:如何在“转移”集合中按服务名称过滤?我知道猫鼬的填充,但是这不允许按外部数据进行过滤(而且,我的函数需要分页(我使用mongoose-pagination插件),因此,我在执行后将其丢弃以进行过滤,因为这可以从少量数据中进行过滤宇宙)。

我认为最好的选择是使用聚合。 但是,如果我做这个...

db.transfers.aggregate([
  { 
    $lookup: { 
      from: 'services', 
      localField: 'requests.service', 
      foreignField: '_id', 
      as: 'requests.service'
    } 
  }
])

我明白了:

{
    "_id" : 1,
    "requests" : {
        "service" : [ 
            {
                "_id" : 1,
                "name" : "Service 1"
            }, 
            {
                "_id" : 1,
                "name" : "Service 2"
            }
        ]
    }
}

如何在不更改查询的情况下将服务数据放入主体对象中?

显然,聚合函数在“请求”(很多)上添加了“平仓”,但没有在“服务”(一个)上添加“平仓”

无论如何,我需要此返回的数据:

{
    "_id" : 1,
    "requests": [
        {
           "service": {
                "_id" : 1,
                "name" : "Service 1"
            },
           "foo": "foo1",
           "bar": "bar1"
        },
        {
           "service": {
               "_id" : 2,
               "name" : "Service 2"
            },
           "foo": "foo2",
           "bar": "bar2"
        }
      ]
}

1 个答案:

答案 0 :(得分:2)

这是一种解决方案-暂时不确定这是否是最漂亮的方法,但肯定可以完成工作:

db.transfers.aggregate([{
    $lookup: {
        from: 'services',
        localField: 'requests.service',
        foreignField: '_id',
        as: 'requests2'
    }
}, {
    $project: {
        "requests": {
            $map: {
                "input": {
                    $zip: {
                        "inputs": [ "$requests", "$requests2" ]
                    }
                },
                "as": "this",
                "in": {
                    $mergeObjects: [
                        { $arrayElemAt: [ "$$this", 0 ] },
                        { "service": { $arrayElemAt: [ "$$this", 1 ] } }
                    ]
                }
            }
        }
    }
}])

第二种方法是这样做:

db.transfers.aggregate([{
    $lookup: {
        from: 'services',
        localField: 'requests.service',
        foreignField: '_id',
        as: 'requests2'
    }
}, {
    $project: {
        "requests": {
            $map: {
                "input": {
                    $range: [ 0, { $size: "$requests2" } ]
                },
                "as": "index",
                "in": {
                    $mergeObjects: [
                        { $arrayElemAt: [ "$requests", "$$index" ] },
                        { "service": { $arrayElemAt: [ "$requests2", "$$index" ] } }
                    ]
                }
            }
        }
    }
}])

我从来没有真正比较过两个版本的性能,但是我怀疑第二个版本会更快。