2017-08-16 65 views
3

我有一个MongoDB 3.2服务器。我的集合包含如下列文件:为什么此RegExp查询返回所有结果?

{ 
    "name": "string", 
    "explicitMods": [ 
     "+48 to Blah", 
     "-13% to Blah", 
     "12 to 18 to Blah" 
    ] 
} 

如果我这样写:

myCollection.find({ “explicitMods”:/坏串/})

我得到零结果,如预期。

但是,如果我这样写:

myCollection.find({ “explicitMods”:/ \ d +为\ d + /})

我得到的集合中的所有文件。这是意想不到的,因为我实际上想要包含像12 to 18这样的子字符串的文档。如果我将正则表达式更改为/\d+ to \d+z/,则正确匹配任何内容。

+0

您是否可以更新您的问题以提供查询返回的示例文档,但您不希望包含该文档? – JohnnyHK

+1

您的查询似乎有效。那么你是否试图在那个文档中过滤'explicitMods'? – Mikey

+0

如果你不想在你的“12到18”之前或之后有任何其他字符 - 换句话说,你可能需要在开始时用'^'或者在表达式结尾的'$'来锚定你的正则表达式 - 换句话说,一个表达式,比如'/^\ d +到\ d + $ /'。这可能有帮助吗? –

回答

1

您正在发送的查询“正确”返回文档它实际上符合您要求的条件。这是因为您正在测试的属性中“至少有一个”数组元素实际上与查询中的条件匹配。

由此我们可以推测两种可能的结果:

  1. 你的意图是只返回那里所有数组项满足条件的文件。

  2. 您的意图是“过滤”来自“文档中的数组”的条目,只返回符合条件的结果。

从这些有不同的方法。首先是实际上MongoDB中没有这样的查询操作符,它要求“所有”数组元素必须满足给定的条件,使用“常规查询”。因此,您需要以不同的形式应用逻辑。

一种这样的选择是以检查数组内容的方式使用JavaScript评估$where。在这里你可以申请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" 
    ] 
} 

在使用的替代情况下,MongoDB的聚合框架允许使用“本地编码运算符”的表达式,这些运算符通常应用比JavaScript解释表达式更快。然而,实际上不存在$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" } 
    }} 
]) 

这一个会返回“这两个”文件,但只那些具有匹配数组项:

/* 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" 
    } 
    }} 
]) 

不过,虽然它使用“本地运营商”,这样的操作成本一般为$unwind使得它的效用值得怀疑,因此可能需要花费很多的时间做同样的事情与$where原来的选项和资源来生成结果比原始查询。

相关问题