使用$ elemMatch的多个条件

时间:2014-03-28 22:27:04

标签: mongodb mongodb-query

我有一个包含项目列表的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}
                }
            }
        }
    ]
}

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

1 个答案:

答案 0 :(得分:0)

逻辑问题在于,给定文档时,带有“STANDARD_INDEX_LENS”的类型与第二个条件匹配,匹配“RX_SUN_LENS”的项匹配第一个条件。因此,由于两者都为真,因此会返回文档。

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

{
    "$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运算符确保在检查“已发货”状态时,不对“处方”类型执行检查,并且仅对其他项目执行,因此无法在一个项目上评估为真这些“处方”。


正如我所说,如果您认为您的逻辑在此限制中更复杂,那么还有另一种方法可以做到这一点。那么你需要一种方法将条件应用于数组中所有元素。您可以使用.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显示的技术“保留”原始文档的形式。

“依赖”满足您的需求。似乎第一个方法应该适合您的需求,但至少您有一些尝试。