node.js - MongoDB 聚合查找和展开

标签 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' }]

问题是:如何在 Transfer 集合中按服务名称进行过滤?我知道 Mongoose 的填充,但这不允许通过外部数据进行过滤(另外,我的函数需要分页(我使用 Mongoose 分页插件),因此,我在执行后放弃过滤,因为这可以从少量数据中过滤宇宙)。

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

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"
        }
      ]
}

最佳答案

这是一个解决方案 - 现在不确定它是否是最漂亮的,但它肯定可以完成工作:

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" ] } }
                    ]
                }
            }
        }
    }
}])

我从未真正比较过两个版本的性能,但我怀疑第二个版本更快一点。

关于node.js - MongoDB 聚合查找和展开,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52562112/

相关文章:

node.js - 如何使用名称前没有 `node` 的 Powershell 执行 node.js 文件?

javascript - Mac的angular.js教程

performance - mongoose 在这种情况下子文档和全局之间的性能有什么区别吗?

javascript - 为什么这个partialFilterExpression会抛出错误?

mysql - 抛出新错误时, Sequelize 回滚不起作用

javascript - util.inherits 如何影响原型(prototype)链?

mongodb - 有没有一个好的库可以让 Kotlin 协程异步访问 Mongo?

database - 如何将环境变量传递给 mongo docker-entrypoint-initdb.d?

node.js - 使用 MongoDB 删除 Node.js 中上传的文件

javascript - 将 Mongoose 数据库中的内容上传到 React Component