【问题标题】:MongoDb Except equivalentMongoDb 等价物除外
【发布时间】:2019-02-19 05:34:31
【问题描述】:

我在尝试对文档集合使用 $setDifference 时遇到了一个问题。

我想要的只是根 1 中包含的所有文档,并根据“reference.id”删除根 2 中也包含的所有文档。

我的集合代表两个树结构,基本上如下所示:

/* Tree Root 1 */
{
    "_id" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "name" : "Root 1",
    "children" : [ 
        LUUID("ca01f1ab-7c32-4e6b-a07a-e0ee9d8ec5ac"), 
        LUUID("6dd8c8ed-4a60-41ca-abf1-a4d795a0c213")
    ]
},
/* Child 1 - Root 1 */
{
    "_id" : LUUID("ca01f1ab-7c32-4e6b-a07a-e0ee9d8ec5ac"),
    "parentId" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "reference" : {
        "type" : "someType",
        "id" : LUUID("331503FB-C4D1-4F7A-A461-933C701EF9AB")
    },
    "rootReferenceId" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "name" : "Child 1 (Root 1)"
}
/* Child 2 - Root 1 */
{
    "_id" : LUUID("6dd8c8ed-4a60-41ca-abf1-a4d795a0c213"),
    "parentId" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "reference" : {
        "type" : "someType",
        "id" : LUUID("23E8B540-3EFB-455A-AA5C-2B67D6B59943")
    },
    "rootReferenceId" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "displayName" : "Child 2 (Root 1)"
}
/* Tree Root 2 */
{
    "_id" : LUUID("27f2b4a6-5471-406a-a39b-1e0b0f8c4eb9"),
    "name" : "Root 2",
    "children" : [ 
        LUUID("ad4ad076-322e-4c26-8855-91c9b1912d1f"), 
        LUUID("66452420-dd2f-4d27-91c9-78bd0990817c")
    ]
},
/* Child 1 - Root 2 */
{
    "_id" : LUUID("ad4ad076-322e-4c26-8855-91c9b1912d1f"),
    "parentId" : LUUID("27f2b4a6-5471-406a-a39b-1e0b0f8c4eb9"),
    "reference" : {
        "type" : "someType",
        "id" : LUUID("331503FB-C4D1-4F7A-A461-933C701EF9AB")
    },
    "rootReferenceId" : LUUID("27f2b4a6-5471-406a-a39b-1e0b0f8c4eb9"),
    "displayName" : "Child 1 (Root 2)"
}

也就是说我到底想拥有文件:

/* Child 2 - Root 1 */
{
    "_id" : LUUID("6dd8c8ed-4a60-41ca-abf1-a4d795a0c213"),
    "parentId" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "reference" : {
        "type" : "someType",
        "id" : LUUID("23E8B540-3EFB-455A-AA5C-2B67D6B59943")
    },
    "rootReferenceId" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "displayName" : "Child 2 (Root 1)"
}

因为它的reference.id包含在Root 1中而不是Root 2中(所以它不会像Child 1一样被排除在结果集中)

我已经编写了一个聚合阶段来对“reference.id”进行分组,如下所示:

db.getCollection('test').aggregate([
    {
        $match: {
            rootReferenceId: { $ne: null }
        }
    },
    {
        $group: {
            _id: "$rootReferenceId",
            referenceIds: { $addToSet: "$reference.id" } 
        }
    }
])

这是什么让我回味:

/* 1 */
{
    "_id" : LUUID("27f2b4a6-5471-406a-a39b-1e0b0f8c4eb9"),
    "referenceIds" : [ 
        LUUID("331503fb-c4d1-4f7a-a461-933c701ef9ab")
    ]
}

/* 2 */
{
    "_id" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "referenceIds" : [ 
        LUUID("23e8b540-3efb-455a-aa5c-2b67d6b59943"), 
        LUUID("331503fb-c4d1-4f7a-a461-933c701ef9ab")
    ]
}

有谁知道我如何将其 $project 转换为 $setDifference 接受的格式?

我认为它需要看起来像这样:

{
    LUUID("27f2b4a6-5471-406a-a39b-1e0b0f8c4eb9") : [ 
        LUUID("331503fb-c4d1-4f7a-a461-933c701ef9ab")
    ]
    LUUID("9f3a73df-bca7-48b7-b111-285359e50a02") : [ 
        LUUID("23e8b540-3efb-455a-aa5c-2b67d6b59943"), 
        LUUID("331503fb-c4d1-4f7a-a461-933c701ef9ab")
    ]
}

或者它有完全不同的方式来实现这一点我不知道?

感谢任何帮助!

编辑解决方案:

现在的解决方案就像 dnickless 建议的那样。真的很不错!非常感谢!

【问题讨论】:

    标签: mongodb except set-theory set-difference


    【解决方案1】:

    这是您可以在不以字符串格式存储重复值的情况下执行的操作。这个解决方案的好处在于

    a) 它返回您感兴趣的整个文档,因此您不需要第二次查询(如果您不需要整个文档,则可以简单地将 $filter 运算符替换为 $setDifference 位)

    b) 它由非常少且便宜的阶段组成(没有分组!),并且将利用 rootReferenceId 字段上的索引(如果有我会推荐的)。

    db.getCollection('test').aggregate([
      { "$facet": {
        "allInRoot1": [{
          "$match": { "rootReferenceId": LUUID("9f3a73df-bca7-48b7-b111-285359e50a02") }
        }],
        "allInRoot2": [{
          "$match": { "rootReferenceId": LUUID("27f2b4a6-5471-406a-a39b-1e0b0f8c4eb9") }
        }]
      }}, {
        "$project": {
          "difference": {
            "$filter": {
                "input": "$allInRoot1",
                "as": "this",
                "cond": { "$in": [ "$$this.reference.id", { "$setDifference": [ "$allInRoot1.reference.id", "$allInRoot2.reference.id" ] } ] }
            }
          }
        }
      }
    ])
    

    【讨论】:

    • 这也很好。非常感谢您提供此解决方案!我根本不知道 $facet 聚合阶段。
    【解决方案2】:

    您可以在 mongodb 3.6 及更高版本中尝试以下聚合。

     db.getCollection('test').aggregate([
      { "$match": { "rootReferenceId": { "$ne": null }}},
      { "$group": {
        "_id": "$rootReferenceId",
        "referenceIds": { "$addToSet": "$reference.id" } 
      }},
      { "$group": {
        "_id": null,
        "data": {
          "$push": { "k": { "$toString": "$_id" }, "v": "$referenceIds" }
        }
      }},
      { "$replaceRoot": { "newRoot": { "$arrayToObject": "$data" }}}
    ])
    

    【讨论】:

    • 哇,非常感谢。但似乎 $arrayToObject 不接受 UUID 作为键值。有没有办法在聚合中将它们转换为字符串?
    • @StefanO 你用的是哪个版本的mongodb?
    • 目前我使用的是 4.0.也许我需要降级到 3.6,但我希望这没关系。
    • 我已经更新了答案,请看看它是否适用于 4.0
    • $toString 不支持从 UUID 到字符串的转换。如果 UUID 和 Mongodb 变得更复杂一点,它真的不能很好地协同工作。
    猜你喜欢
    • 2011-04-22
    • 2020-12-22
    • 1970-01-01
    • 2018-07-04
    • 2011-04-17
    • 2014-01-04
    • 1970-01-01
    • 2012-05-18
    • 1970-01-01
    相关资源
    最近更新 更多