我有一台MongoDB 3.2服务器。我的收藏包含以下文件:
{
"name": "string",
"explicitMods": [
"+48 to Blah",
"-13% to Blah",
"12 to 18 to Blah"
]
}
如果我写这个:
myCollection.find({" explicitMods":/ bad string /})
我得到零结果,正如所料。
但是,如果我这样写:
myCollection.find({" explicitMods":/ \ d + to \ d + /}}
我收集了该集合中的所有文件。这是意料之外的,因为我实际上想要包含像12 to 18
这样的子串的文档。如果我将正则表达式更改为/\d+ to \d+z/
,则它正确匹配任何内容。
答案 0 :(得分:1)
您发出的查询“正确”会返回实际符合您要求的条件的文档。这就是您正在测试的属性中的“至少一个”数组元素实际上与查询中的条件匹配。
由此我们可以推测出两种可能的结果:
您的意图是仅返回所有数组条目满足条件的文档。
您的意图是“过滤”文档中“数组”中的条目,只返回满足条件的结果。
从中可以看出各种方法。首先,实际上没有MongoDB的这样的查询运算符要求“全部”数组元素必须通过“常规查询”由给定条件满足。因此,您需要以不同的形式应用逻辑。
一个这样的选择是以检查数组内容的方式使用$where
的JavaScript评估。在这里,除了常规查询过滤器之外,您还可以应用Array.every()
来测试您的情况,因为这实际上是在做一些有用的工作。
给出源文件,如:
/* 1 */
{
"_id" : ObjectId("5993a35be38f41729f1d6501"),
"name" : "string",
"explicitMods" : [
"+48 to Blah",
"-13% to Blah",
"12 to 18 to Blah"
]
}
/* 2 */
{
"_id" : ObjectId("5993a35be38f41729f1d6502"),
"name" : "string",
"explicitMods" : [
"12 to 18 to Blah"
]
}
如果您的目的只是返回与“所有”数组元素匹配的“文档”,则发出声明:
db.myCollection.find({
"explicitMods": /\d+ to \d+/,
"$where": function() { return this.explicitMods.every(e => /\d+ to \d+/.test(e)) }
}
})
仅返回匹配的文档:
{
"_id" : ObjectId("5993a35be38f41729f1d6502"),
"name" : "string",
"explicitMods" : [
"12 to 18 to Blah"
]
}
在使用$where
的替代情况下,MongoDB的聚合框架允许使用“本机编码运算符”的表达式,这些运算符通常比JavaScript解释表达式应用更快。但是,实际上没有SERVER-11947的$regex
等效的“逻辑运算符”适用于$redact
等聚合操作。
因此,此处可用的唯一方法是使用$match
使用常规查询条件“之后”使用$unwind
对数组元素进行非规范化:
db.myCollection.aggregate([
// Match "possible" documents
{ "$match": { "explicitMods": /\d+ to \d+/ } },
// unwind to denormalize
{ "$unwind": "$explicitMods" },
// Match on the "array" items now as documents
{ "$match": { "explicitMods": /\d+ to \d+/ } },
// Optionally "re-group" back to documents with only matching array items
{ "$group": {
"_id": "$_id",
"name": { "$first": "$name" },
"explicitMods": { "$push": "$explicitMods" }
}}
])
那个将返回“both”文档,但只返回那些匹配数组项的文档:
/* 1 */
{
"_id" : ObjectId("5993a35be38f41729f1d6501"),
"name" : "string",
"explicitMods" : [
"12 to 18 to Blah"
]
}
/* 2 */
{
"_id" : ObjectId("5993a35be38f41729f1d6502"),
"name" : "string",
"explicitMods" : [
"12 to 18 to Blah"
]
}
当然,您可以对该主题应用“变体”,并根据过滤条件“测试数组的长度”,以决定返回哪个文档:
db.myCollection.aggregate([
{ "$match": { "explicitMods": /\d+ to \d+/ } },
{ "$addFields": { "origSize": { "$size": "$explicitMods" } } },
{ "$unwind": "$explicitMods" },
{ "$match": { "explicitMods": /\d+ to \d+/ } },
{ "$group": {
"_id": "$_id",
"name": { "$first": "$name" },
"origSize": { "$first": "$origSize" },
"explicitMods": { "$push": "$explicitMods" },
}},
{ "$redact": {
"$cond": {
"if": {
"$eq": [
{ "$size": "$explicitMods" },
"$origSize"
]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
虽然它与$where
使用“本地运营商”的原始选项做同样的事情,但$unwind
这样的操作的一般成本使其效用值得怀疑,因此可能需要更多产生结果的时间和资源比原始查询。