我有一个包含项目列表的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_type
是STANDARD_INDEX_LENS
或RX_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}
}
}
}
]
}
我的查询中的逻辑错误是什么?我应该使用什么构造?我是否需要在客户端进行这种过滤?
答案 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显示的技术“保留”原始文档的形式。
但“依赖”满足您的需求。似乎第一个方法应该适合您的需求,但至少您有一些尝试。