我有一个MongoDB 3.2服务器。我的收藏包含以下文件:

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

如果我这样写:



正如预期的那样,我得到零结果。

但是,如果我这样写:



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

最佳答案

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

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

  • 您的意图是仅返回所有数组条目均满足条件的文档。
  • 您的意图是从“文档中的数组”中“过滤”条目,仅返回满足条件的结果。

  • 从这些中可以找到不同的方法。首先,实际上对于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" }
      }}
    ])
    

    那将返回“两个”文档,但仅返回那些具有匹配数组项的文档:
    /* 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 这样的运算的一般成本使其实用程序存在疑问,因此与原始查询相比,可能需要花费更多的时间和资源来产生结果。

    关于javascript - 为什么此RegExp查询返回所有结果?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45703608/

    10-11 10:27
    查看更多