【问题标题】:Use multiple criteria with $elemMatch对 $elemMatch 使用多个条件
【发布时间】:2014-03-29 10:38:21
【问题描述】:

我有一个包含项目列表的 Mongo 文档。这些项目中的每一个都有一些与之相关的状态。

我想使用$elemMatch 返回状态符合特定条件的文档。

考虑以下文档。它代表一个订单,订单中的每一个商品都是不同的(它们代表眼镜):

{
    "_id" : ObjectId("5335dcd6888a4f21dd77657c"),
    "items" : [ 
        {
            "lens_type" : "STANDARD_INDEX_LENS",
            "status" : {}
        }, 
        {
            "lens_type" : "NO_LENS",
            "status" : {
                "shipped" : true
            }
        }, 
        {
            "lens_type" : "RX_SUN_LENS",
            "status" : {
                "received_from_lab" : true,
                "sent_to_lab" : true,
                "shipped" : true
            }
        }
    ]
}

我想查找所有“未发货”的项目 - 即 items.status.shipped 不存在。我想找到所有准备发货的物品。

但是,如果该物品有处方镜片 - lens_typeSTANDARD_INDEX_LENSRX_SUN_LENS - 那么我只有在从实验室收到它时才认为它未发货。 item.status.received_from_lab 存在。

我的查询不应返回上述文档。这是因为其中两件商品已发货,而另一件商品尚未发货但尚未发货received_from_lab

但是,我的查询确实返回了这个文档!

这是我的查询:

{
    "$and": [
        {
            "items": {
                "$elemMatch": {
                    "lens_type": {
                       "$in": [
                            "STANDARD_INDEX_LENS",
                            "RX_SUN_LENS"
                        ]
                    },
                    "status.received_from_lab": {"$exists": true}
                }
            }
        },
        {
            "items": {
                "$elemMatch": {
                    "status.shipped": {"$exists": false}
                }
            }
        }
    ]
}

我的查询中的逻辑错误是什么?我应该改用什么构造?我需要在客户端进行这种过滤吗?

【问题讨论】:

  • 我已经读了好几遍了。我看到了两种可能的答案,但这取决于您期望看到的结果。您能否将您的“结果”添加到问题中,或者至少确认您希望这个“整个”文档匹配。
  • @NeilLunn 你好!我的期望是该文档匹配。因为: 1. 第一项有STANDARD_INDEX_LENS,但没有密钥status.received_from_lab。因此,即使它也缺少密钥 status.shipped,它也不符合条件。 2. 第 2 件和第 3 件商品的密钥为status.shipped,因此也不符合发货条件。因为没有任何物品可以发货,所以文件不应该被退回。

标签: mongodb mongodb-query


【解决方案1】:

逻辑问题在于给定文档,带有“STANDARD_INDEX_LENS”的类型匹配第二个条件,而匹配“RX_SUN_LENS”的项目匹配第一个条件。所以既然 both 都是 true,那么文档就会被返回。

您没有做的是声明您的“处方”镜片可以评估这些条件中的任何一个。所以可能最简单的写法如下:

{
    "$and": [
        {
            "items": {
                "$elemMatch": {
                    "lens_type": {
                       "$in": [
                            "STANDARD_INDEX_LENS",
                            "RX_SUN_LENS"
                        ]
                    },
                    "status.received_from_lab": {"$exists": true}
                }
            }
        },
        {
            "items": {
                "$elemMatch": {
                    "lens_type": {
                       "$nin": [
                            "STANDARD_INDEX_LENS",
                            "RX_SUN_LENS"
                        ]
                    },
                    "status.shipped": {"$exists": false}
                }
            }
        }
    ]
}

因此,$nin 运营商确保在检查“已发货”状态时,不会对“处方”类型执行检查,并且仅对其他项目执行检查,因此它不能在其中一项上评估为真“处方”。


正如我所说,如果您认为您的逻辑在此限制中更复杂,还有另一种方法可以做到这一点。因此,您需要一种方法将条件应用于数组中的 all 元素。您可以使用 .aggregate() 来做到这一点:

db.order.aggregate([

    // Document level filtering still makes sense
    { "$match": {
        "$and": [
            {
                "items": {
                    "$elemMatch": {
                        "lens_type": {
                           "$in": [
                                "STANDARD_INDEX_LENS",
                                "RX_SUN_LENS"
                            ]
                        },
                        "status.received_from_lab": {"$exists": true}
                    }
                }
            },
            {
                "items": {
                    "$elemMatch": {
                        "status.shipped": {"$exists": false}
                    }
                }
            }
        ]
    }},

    // Unwind to de-normalize
    { "$unwind": "$items" },

    // Then actually "filter" each of the un-wound documents
    { "$match": {
        "$and": [
            {
                "items.lens_type": {
                    "$in": [
                        "STANDARD_INDEX_LENS",
                        "RX_SUN_LENS"
                    ]
                },
                "items.status.received_from_lab": {"$exists": true}
            },
            {
                "items.status.shipped": {"$exists": false}
            }
        ]          
    }}

    // Group back the results of any found "elements"
    { "$group": {
        "_id": "$_id",
        "items": { "$push": "$items" }
    }}

])

所以这基本上是在执行初始$match 之后,数组中的文档被“拆分”分开或通常使用$unwind“去规范化”,因此它们现在出现在单个文档中。

额外的$match 然后确保剩下的任何内容都不符合这些条件。或者您可能希望将逻辑更改为另一种情况。最后,您可以使用$group 将任何结果带回数组中。但是这个数组只包含没有被过滤的文档。所以文件的形式会被改变。

您可以使用here 所示的技术“保留”原始文档的形式。

但它“取决于”你的需求是什么。 first 方法似乎应该适合您的需求,但至少您有一些事情要尝试。

【讨论】:

    猜你喜欢
    • 2014-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-30
    • 1970-01-01
    • 1970-01-01
    • 2019-03-06
    • 2023-03-24
    相关资源
    最近更新 更多