我有一个名为商店的系列。结构就像:
[
{
'_id' : id1,
'details' : {name: 'shopA'},
'products' : [{
_id: 'p1',
details: {
'name': 'product1'
}
},{
_id: 'p2',
details: {
'name': 'product2'
}
}, {
_id: 'p4',
details: {
'name': 'product4'
}
}
},{
'_id' : id2,
'details' : {name: 'shopB'},
'products' : [{
_id: 'p1',
details: {
'name': 'product1'
}
},{
_id: 'p4',
details: {
'name': 'product4'
}
}, {
_id: 'p5',
details: {
'name': 'product5'
}
}
},{
'_id' : id3,
'details' : {name: 'shopC'},
'products' : [{
_id: 'p1',
details: {
'name': 'product1'
}
},{
_id: 'p2',
details: {
'name': 'product2'
}
}, {
_id: 'p3',
details: {
'name': 'product3'
}
}
},{
'_id' : id4,
'details' : {name: 'shopOther'},
'products' : [{
_id: 'p10',
details: {
'name': 'product10'
}
},{
_id: 'p12',
details: {
'name': 'product12'
}
}, {
_id: 'p13',
details: {
'name': 'product13'
}
}
}
]
现在,用户可以从菜单中选择一些产品,并尝试找到那些商店。结果应该是所有提供至少一个选定项目的商店。
实施例,
假设用户选择['p1', 'p2', 'p3'] //ids
然后只有三家商店
将列出id1,id2,id3(id4没有这些项目),加上结构使得它从结果数组中的文档中删除商店的其余产品(未列出)。
有没有办法,我可以直接从mongodb得到这样的结果?
答案 0 :(得分:2)
既然你确实提出了很好的问题并且形成得很好,那么有一些考虑,类似的答案实际上可能不适合参考,特别是如果你的MongoDB产品的经验水平很低。
像$redact
这样的选项看起来很简单,而且它们通常很适合。但这不是你需要如何构造语句的情况:
db.collection.aggregate([
{ "$match": { "products._id": { "$in": ["p1","p2","p3"] } }},
{ "$redact": {
"$cond": {
"if": {
"$or": [
{ "$eq": [ "$_id", "p1" ] },
{ "$eq": [ "$_id", "p2" ] },
{ "$eq": [ "$_id", "p3" ] }
]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
])
这适用于聚合运算符中$or
的“不太明显”使用。至少在正确的语法和形式方面,它实际上是一个“完全失败”。原因是因为$redact
通常是一种“递归”操作,它检查文档的“所有级别”而不仅仅是在特定级别。因此,在“顶级”中,_id
断言将失败,因为同名的顶级字段不符合该条件。
关于这一点你真的没有其他任何事情可做,但考虑到数组中的_id
实际上是一个“唯一”元素,那么你总是可以在$project
阶段执行此操作在$map
和$setDifference
的帮助下:
db.collection.aggregate([
{ "$match": { "products._id": { "$in": ["p1","p2","p3"] } }},
{ "$project": {
"details": 1,
"products": {
"$setDifference": [
{ "$map": {
"input": "$products",
"as": "el",
"in": {
"$cond": {
"if": {
"$or": [
{ "$eq": [ "$$el._id", "p1" ] },
{ "$eq": [ "$$el._id", "p2" ] },
{ "$eq": [ "$$el._id", "p3" ] }
]
},
"then": "$$el",
"else": false
}
}
}},
[false]
]
}
}}
])
看起来很冗长,但实际上非常有效。 $map
运算符为每个文档处理数组“内联”,并对每个元素进行操作以生成新数组。在$cond
下根据条件不匹配的false
断言通过考虑与$setDifference
相比较的“设置”结果来平衡,false
有效地“过滤”了_id
来自结果数组的结果,只留下有效的匹配。
当然,db.collection.aggregate([
{ "$match": { "products._id": { "$in": ["p1","p2","p3"] } }},
{ "$unwind": "$products" },
{ "$match": { "products._id": { "$in": ["p1","p2","p3"] } }},
{ "$group": {
"_id": "$_id",
"details": { "$first": "$details" },
"products": { "$push": "$products" }
}}
])
值或整个对象并非真正“唯一”,那么“集合”将不再有效。考虑到这一点,以及所提到的运算符在2.6之前的MongoDB版本不可用的事实,那么更传统的方法是$unwind
数组成员然后通过{{3'过滤“它们操作。
$match
考虑到,根据其他示例,应首先在管道中执行$match
阶段,以减少与条件匹配的“可能”文档。 $unwind
的“第二”阶段在处于“非规范化”形式时对数组内的文档元素进行实际“过滤”。
由于数组是由db.collection.find(
{ "products._id": { "$in": ["p1","p2","p3"] },
{ "details": 1, "products.$": 1 }
)
“解构”的,$match
的目的是“重新构建”数组,从与条件不匹配的元素中“过滤”。
MongoDB还提供$group
运算符,以便从查询条件中选择匹配的数组元素。像这样:
{{1}}
但问题在于此运算符仅支持查询文档中提供的条件的“第一”匹配。这是一个设计意图,目前还没有严格的运算符语法来满足一个以上的匹配。
因此,您的最终方法是使用positional $
方法,以便在您想要的内部数组上实际实现匹配过滤。无论是过滤内容还是过滤内容都会在客户端代码中自行响应,具体取决于最终对您的满意程度。