【问题标题】:Lookup with array of Objects使用对象数组查找
【发布时间】:2017-12-13 13:19:36
【问题描述】:

我正在尝试填充我的文档中引用另一个集合的两个对象数组。然而,它总是在结果中显示为一个空数组,这让我感到惊讶。我在这里做错了什么?有没有更好的方法来“填充”uploaded_filesfile_history

这是我的总结:

    Project.aggregate(
        { $match: {"project_id": projectId}},
        { $addFields: {
            uploaded_files: {
                $filter: {
                    input: '$uploaded_files',
                    as: 'uploadedFile',
                    cond: {$eq: ['$$uploadedFile.upload_id', uploadId]}
                }
            },
            file_history: {
                $filter: {
                    input: '$file_history',
                    as: 'revision',
                    cond: {$eq: ['$$revision.upload_id', uploadId]}
                }
            },
        }},
        { $lookup: {
            from: 'users',
            localField: 'owner',
            foreignField: '_id',
            as: 'owner'
        }},
        { $lookup: {
            from: 'files',
            localField: 'uploaded_files.file',
            foreignField: '_id',
            as: 'test'
        }},
        { $lookup: {
            from: 'files',
            localField: 'file_history.file',
            foreignField: '_id',
            as: 'test2'
        }},
        { $unwind: '$owner' },
        { $limit: 1 }
    ).then(projects => {
        if (projects.length == 0)
            return res.status(404).send({ error: "Couldn't find a project with this id" })

        let project = projects[0]
})

我的文档如下所示:

{
    "_id" : ObjectId("5935a41f12f3fac949a5f925"),
    "project_id" : 13,
    "updated_at" : ISODate("2017-07-09T19:41:51.396Z"),
    "created_at" : ISODate("2017-06-05T18:34:07.150Z"),
    "owner" : ObjectId("591eea4439e1ce33b47e73c3"),
    "name" : "Demo project",
    "uploaded_files" : [ 
        {
            "display_name" : "001.jpg",
            "file" : ObjectId("596286ff7d3a594ed4797848"),
            "upload_id" : ObjectId("596286ff7d3a594ed4797849"),
            "created_at" : ISODate("2017-07-09T19:41:51.000Z")
        }
    ],
    "file_history" : [ 
        {
            "display_name" : "001.jpg",
            "file" : ObjectId("596286ff7d3a594ed4797848"),
            "upload_id" : ObjectId("596286ff7d3a594ed4797849"),
            "created_at" : ISODate("2017-07-09T19:41:51.000Z")
        }
    ]
}

【问题讨论】:

    标签: mongodb mongoose mongodb-query aggregation-framework


    【解决方案1】:

    你基本上需要先$unwind数组。 MongoDB 尚不能将数组中对象的“内部”属性用作$lookup 的源。

    另外为了效率我们真的应该先使用$concatArrays来“加入”数组源,然后只做一个 $lookup操作:

    Project.aggregate([
      { "$match": { "project_id": projectId} },
      { "$project": {
        "project_id": 1,
        "updated_at": 1,
        "created_at": 1,
        "owner": 1,
        "name": 1,
        "combined": {
          "$concatArrays": [
            { "$map": {
              "input": {
                "$filter": {
                  "input": "$uploaded_files",
                  "as": "uf",
                  "cond": { "$eq": ["$$uf.upload_id", uploadId ] }
                }
              },
              "as": "uf",
              "in": {
                "$arrayToObject": {
                  "$concatArrays": [
                    { "$objectToArray": "$$uf" },
                    [{ "k": "type", "v": "uploaded_files" }]
                  ]
                }
              }
            }},
            { "$map": {
              "input": {
                "$filter": {
                  "input": "$file_history",
                  "as": "fh",
                  "cond": { "$eq": ["$$fh.upload_id", uploadId ] }
                }
              },
              "as": "fh",
              "in": {
                "$arrayToObject": {
                  "$concatArrays": [
                    { "$objectToArray": "$$fh" },
                    [{ "k": "type", "v": "file_history" }]
                  ]
                }
              }
            }}
          ]
        }
      }},
      { "$unwind": "$combined" },
      { "$lookup": {
        "from": "files",
        "localField": "combined.file",
        "foreignField": "_id",
        "as": "combined.file"
      }},
      { "$unwind": "$combined.file" },
      { "$lookup": {
        "from": "users",
        "localField": "owner",
        "foreignField": "_id",
        "as": "owner"
      }},
      { "$unwind": "$owner" },
      { "$group": {
        "_id": "$_id",
        "project_id": { "$first": "$project_id" },
        "updated_at": { "$first": "$updated_at" },
        "created_at": { "$first": "$created_at" },
        "owner": { "$first": "$owner" },
        "name": { "$first": "$name" },
        "combined": { "$push": "$combined" }
      }},
      { "$project": {
        "project_id": 1,
        "updated_at": 1,
        "created_at": 1,
        "owner": 1,
        "name": 1,
        "uploaded_files": {
          "$filter": {
            "input": "$combined",
            "as": "cf",
            "cond": { "$eq": [ "$$cf.type", "uploaded_files" ] }
          }    
        },
        "file_history": {
          "$filter": {
            "input": "$combined",
            "as": "cf",
            "cond": { "$eq": [ "$$cf.type", "file_history" ] }
          }    
        }
      }}
    ])
    

    简而言之

    1. 将两个数组放在源中并标记它们,然后首先$unwind

    2. 在组合细节上执行$lookup$unwindthat

    3. 在另一个外国来源上执行$lookup$unwind 那个

    4. $group 将文档与单个数组一起返回。

    5. $filter 通过我们添加的“标签名称”或“类型”字段来“分隔”数组。

    您可以遵循相同的过程,只需在每个数组上使用$unwind,然后执行“加入”并重新组合在一起。但实际上这需要更多的步骤,而不是一开始就简单地“组合”。

    另请注意,$lookup 后跟$unwind 在服务器上处理时实际上被视为一个管道阶段。详情见Aggregation Pipeline Optimization

    【讨论】:

    • 我认为没有必要放松,因为他们已经应用了这个改进:jira.mongodb.org/browse/SERVER-22881,因此我还没有这样做。
    • @kentor 这仅适用于诸如“仅”ObjectId 值的数组之类的东西,无论要提供什么键。对于“嵌入式对象”,您仍然需要先$unwind
    • 大查询好像有问题,看这里的错误信息:ghostbin.com/paste/gj4tm
    • @kentor 只是拼写错误。已更正。
    • 数组中带有虚线路径的本地字段错误已在 3.4.6 中解决。 jira.mongodb.org/browse/SERVER-28717
    猜你喜欢
    • 1970-01-01
    • 2018-03-04
    • 1970-01-01
    • 1970-01-01
    • 2020-09-15
    • 1970-01-01
    • 2022-08-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多