【问题标题】:Regex inside array in mongoDBMongoDB中数组内的正则表达式
【发布时间】:2014-05-19 11:58:41
【问题描述】:

我想用正则表达式在 mongodb 的数组中进行查询,集合有这样的文档:

{
"_id" : ObjectId("53340d07d6429d27e1284c77"),
"company" : "New Company",
"worktypes" : [ 
    {
        "name" : "Pompas",
        "works" : [ 
            {
                "name" : "name 2",
                "code" : "A00011",
                "price" : "22,22"
            }, 
            {
                "name" : "name 3",
                "code" : "A00011",
                "price" : "22,22"
            }, 
            {
                "name" : "name 4",
                "code" : "A00011",
                "price" : "22,22"
            }, 
            {
                "code" : "asdasd",
                "name" : "asdads",
                "price" : "22"
            }, 
            {
                "code" : "yy",
                "name" : "yy",
                "price" : "11"
            }
        ]
    }, 
    {
        "name" : "name 4",
        "works" : [ 
            {
                "code" : "A112",
                "name" : "Nombre",
                "price" : "11,2"
            }
        ]
    },          
    {
        "name" : "ee",
        works":[

            {
                "code" : "aa",
                "name" : "aa",
                "price" : "11"
            }, 
            {
                "code" : "A00112",
                "name" : "Nombre",
                "price" : "12,22"
            }
              ]
    }
]

}

然后我需要按公司名称查找文档,并且其中的任何工作都与代码或名称工作中的正则表达式匹配。 我有这个:

var companyquery = { "company": "New Company"};
var regQuery = new RegExp('^A0011.*$', 'i');

db.categories.find({$and: [companyquery,
            {$or: [
                {"worktypes.works.$.name": regQuery},
                {"worktypes.works.$.code": regQuery}
            ]}]})

但不返回任何结果..我认为错误是尝试使用 de dot 和 $.. 有什么想法吗?

编辑:

有了这个:

db.categories.find({$and: [{"company":"New Company"},
            {$or: [
                {"worktypes.works.name": {"$regex": "^A00011$|^a00011$"}},
                {"worktypes.works.code": {"$regex": "^A00011$|^a00011$"}}
            ]}]})

这是结果:

{
    "_id" : ObjectId("53340d07d6429d27e1284c77"),
    "company" : "New Company",
    "worktypes" : [ 
        {
            "name" : "Pompas",
            "works" : [ 
                {
                    "name" : "name 2",
                    "code" : "A00011",
                    "price" : "22,22"
                }, 
                {
                    "code" : "aa",
                    "name" : "aa",
                    "price" : "11"
                }, 
                {
                    "code" : "A00112",
                    "name" : "Nombre",
                    "price" : "12,22"
                }, 
                {
                    "code" : "asdasd",
                    "name" : "asdads",
                    "price" : "22"
                }, 
                {
                    "code" : "yy",
                    "name" : "yy",
                    "price" : "11"
                }
            ]
        }, 
        {
            "name" : "name 4",
            "works" : [ 
                {
                    "code" : "A112",
                    "name" : "Nombre",
                    "price" : "11,2"
                }
            ]
        }, 
        {
            "name" : "Bombillos"
        }, 
        {
            "name" : "Pompas"
        }, 
        {
            "name" : "Bombillos 2"
        }, 
        {
            "name" : "Other type"
        }, 
        {
            "name" : "Other new type"
        }
    ]
}

正则表达式不显示结果 ok..

【问题讨论】:

  • 什么是regexQuery
  • 它是一个带有正则表达式值的字符串,我已经从 nodejs 客户端复制了我的代码,我将编辑我的问题。谢谢
  • 我认为问题之一是位置运算符$ 是投影/更新运算符,而不是查询运算符,尝试更接近:"worktypes.works.name" 而不是"worktypes.works.$.name"
  • 相同的结果..没什么可显示的 :(
  • 等等你想要什么结果?该文件确实填写了结果

标签: regex mongodb mongodb-query aggregation-framework


【解决方案1】:

我认为有一个错字:

正则表达式应该是:^A00011.*$

三重0而不是双0

【讨论】:

  • 不,同样的结果。我已经尝试了大多数代码和名称,但没有任何工作。
【解决方案2】:

您正在使用 JavaScript 原生 RegExp 对象作为正则表达式,但是为了让 mongo 处理正则表达式,它需要作为查询文档的一部分发送,这不是一回事。

此外,正则表达式将与您想要的值不匹配。精确匹配实际上可能是^A0111$,但是不区分大小写的匹配会导致问题,从而导致对可能的索引进行更大的扫描。所以有更好的写法。另请参阅文档链接以了解不区分大小写匹配的问题。

改用$regex 运算符:

db.categories.find({
    "$and": [
        {"company":"New Company"},
        { "$or": [
            { "worktypes.works.name": { "$regex": "^A00011$|^a00011$" }},
            { "worktypes.works.code": { "$regex": "^A00011$|^a00011$" }}
        ]}
    ]
})

同样位置$ 占位符对查询无效,它们仅用于投影或更新或查询找到的第一个匹配元素。

但您的实际问题似乎是您试图仅获取与您的条件“匹配”的数组元素。您不能使用 .find() 执行此操作,为此您需要改用 .aggregate()

db.categories.aggregate([

    // Always makes sense to match the actual documents
    { "$match": {
        "$and": [
            {"company":"New Company"},
            { "$or": [
                { "worktypes.works.name": { "$regex": "^A00011$|^a00011$" }},
                { "worktypes.works.code": { "$regex": "^A00011$|^a00011$" }}
            ]}
        ]
    }},

    // Unwind the worktypes array
    { "$unwind": "$worktypes" },

    // Unwind the works array
    { "$unwind": "$worktypes.works" },

    // Then use match to filter only the matching entries
    { "$match": {
       "$or": [
            { "worktypes.works.name": { "$regex": "^A00011$|^a00011$" } },
            { "worktypes.works.code": { "$regex": "^A00011$|^a00011$" } }
        ]
    }},

    /* Stop */
    // If you "really" need the arrays back then include all the following
    // Otherwise the steps up to here actually got you your results

    // First put the "works" array back together
    { "$group": {
        "_id": {
            "_id": "$_id",
            "company": "$company",
            "workname": "$worktypes.name"
        },
        "works": { "$push": "$worktypes.works" }
    }},

    // Then put the "worktypes" array back
    { "$group": {
        "_id": "$_id._id",
        "company": { "$first": "$_id.company" },
        "worktypes": {
            "$push": {
                "name": "$_id.workname",
                "works": "$works"
            } 
        } 
    }}
])

.aggregate() 对所有这些阶段所做的就是将数组元素分解为正常的文档形式,以便可以使用 $match 运算符对其进行过滤。这样,只返回“匹配”的元素。

“查找”正确的是匹配满足条件的“文档”。由于文档包含匹配的元素,因此它们被返回。这两个原则是非常不同的东西。

当您的意思是“过滤”时,请使用聚合。

【讨论】:

  • @colymore 结果中的文档确实包含与表达式匹配的元素。那么您是否真的试图“过滤”数组的结果以仅返回匹配项?那是另一回事。
  • 我试图只获取他的 worktype.work.code 或名称与正则表达式匹配的 worktypes.works,并丢弃不匹配的 worktypes.works..
  • RegExp 对象将被转换为 BSON 正则表达式对象,就像它在文档中显示的那样。 BSON 正则表达式对象不直接转换为 $regex 运算符。
  • @colymore 这就是我在评论中所说的。您实际上是在要求“找到”不做的事情。请参阅附加信息。
  • @colymore 所以我实际上已经花时间复制了您的数据并运行了给出的最终聚合表达式。编辑在响应中,整个链按预期工作。所以你没有理由不接受这一点。我已经指出了您原始方法的几个问题,并给出了实际过滤数组的正确解决方案。真正接受你的建议是不错的:)
【解决方案3】:

您可以尝试聚合方法和聚合数组运算符,因此从 MongoDB 4.2 开始将支持此查询,

  • $match 符合您的条件
  • $addFields 在文档中添加/编辑字段
  • $map 迭代 worktypes 数组的循环
  • $filter 迭代 works 数组的循环,它将根据提供的条件返回过滤结果
  • $regexMatch 匹配正则表达式,就像我们在$match 阶段所做的一样,它会返回一个布尔响应,所以我们在这里检查$or 条件,
  • $mergeObjects 合并worktypes 的当前对象并更新works 数组属性
  • 第二个$addFields 用于删除works 数组的空结果
  • $filter 迭代 worktypes 数组的循环并检查否定条件以删除空的 works 文档
db.categories.aggregate([
  {
    $match: {
      $and: [
        { "company": "New Company" },
        {
          $or: [
            { "worktypes.works.name": { "$regex": "^A00011$|^a00011$" } },
            { "worktypes.works.code": { "$regex": "^A00011$|^a00011$" } }
          ]
        }
      ]
    }
  },
  {
    $addFields: {
      worktypes: {
        $map: {
          input: "$worktypes",
          in: {
            $mergeObjects: [
              "$$this",
              {
                works: {
                  $filter: {
                    input: "$$this.works",
                    cond: {
                      $or: [
                        {
                          $regexMatch: {
                            input: "$$this.name",
                            regex: "^A00011$|^a00011$"
                          }
                        },
                        {
                          $regexMatch: {
                            input: "$$this.code",
                            regex: "^A00011$|^a00011$"
                          }
                        }
                      ]
                    }
                  }
                }
              }
            ]
          }
        }
      }
    }
  },
  {
    $addFields: {
      worktypes: {
        $filter: {
          input: "$worktypes",
          cond: { $ne: ["$$this.works", []] }
        }
      }
    }
  }
])

Playground

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-28
    • 1970-01-01
    • 2014-01-26
    • 2020-09-10
    • 2017-04-10
    • 1970-01-01
    相关资源
    最近更新 更多