FerretDB 求值查询运算符实战指南:`$mod` 取模与 `$regex` 正则匹配全解析
2026/9/24 14:47:16 网站建设 项目流程

FerretDB 求值查询运算符实战指南:$mod取模与$regex正则匹配全解析

【免费下载链接】FerretDBA truly Open Source MongoDB alternative项目地址: https://gitcode.com/gh_mirrors/fe/FerretDB

求值查询运算符(Evaluation Query Operators)是 FerretDB 查询过滤体系中的一类核心运算符,它根据对字段值执行指定表达式(取模运算、正则匹配等)的求值结果来决定文档是否命中。本文以 FerretDB 官方文档(v2.5)的求值运算符章节为主体,结合仓库中的兼容性测试与集成测试源码,系统讲解$mod$regex的语法、示例、边界行为与底层实现验证,读完即可在 FerretDB 中直接套用。

求值查询运算符是什么

求值查询运算符根据对指定表达式求值的结果来返回数据。与比较运算符($eq$gt等)、逻辑运算符($and$or等)不同,求值运算符的判定逻辑更偏向"对字段值执行一段程序化表达式",而非简单的值比较。

FerretDB 官方文档(evaluation-operators.md)中列出的求值查询运算符包括:

运算符说明
$mod匹配字段元素除以给定值后,余数为指定值的文档
$regex匹配字段满足指定正则表达式的文档

其中$mod完成数值取模过滤,$regex完成模式化文本匹配,两者是日常查询中最常用的求值运算符。如需了解其他运算符类别,可参见同目录下的 comparison-operators.md、logical-operators.md、element-operators.md、array-operators.md 与 bitwise-operators.md。

准备示例数据

本节所有示例都基于catalog集合。先向其中插入以下 5 条商品文档:

db.catalog.insertMany([ { product: 'bottle', price: 15, stock: 1 }, { product: 'spoon', price: 500, stock: 0 }, { product: 'cup', price: 100, stock: 14 }, { product: 'BoWL', price: 56, stock: 5 }, { product: 'boTtLe', price: 20, stock: 3 } ])

数据集中刻意混入了大小写混合的BoWLboTtLe,便于演示$regex的选项(flag)行为;stock字段包含1、0、14、5、3等不同数值,便于演示$mod的取模判定。

$mod 取模匹配

语法

$mod的语法形式为:

{ <field>: { $mod: [ <divisor-value>, <modulus> ] } }

其中<divisor-value>是除数,<modulus>是期望的余数。匹配的数学判定为:

field-value % divisor-value = modulus

即:字段值对除数取模的结果恰好等于指定的余数时,文档命中

示例:筛出 stock 能被 2 整除的文档

以下查询返回stock字段值能被 2 整除(即余数为 0)的所有文档:

db.catalog.find({ stock: { $mod: [2, 0] } })

输出结果为:

response = [ { _id: ObjectId('63e3ac0184f488929a3f737a'), product: 'spoon', price: 500, stock: 0 }, { _id: ObjectId('63e3ac0184f488929a3f737b'), product: 'cup', price: 100, stock: 14 } ]

验证一下:0 % 2 = 014 % 2 = 0均命中;而1、5、3对 2 取模不为 0,故不返回。

边界行为与注意事项

[!CAUTION]$mod表达式在以下三种情况下会返回错误:数组中只有一个元素、数组元素超过两个、数组为空。 此外,$mod会把小数的输入向下舍入到零,例如$mod: [ 3.5 , 2 ]会按$mod: [ 3 , 2 ]执行。

除了文档明确标注的上述限制,仓库中的兼容性测试 query_evaluation_compat_test.go(TestQueryEvaluationCompatMod)还系统验证了大量边界场景,可作为实际编码时的行为依据:

  • 数组长度非法bson.A{}(空数组)、bson.A{1}(单元素)、bson.A{1, 2, 3}(三元素)均不产生匹配结果;
  • 非数值输入:除数或余数为字符串("1""2")或nil时,不产生匹配结果;
  • 除数为零bson.A{0, 1}以及极小非零浮点数math.SmallestNonzeroFloat64作为除数时不产生匹配结果;
  • 无穷大:除数或余数为math.Inf(正负无穷)时不产生匹配结果;
  • 负数与浮点{-100, 89}{100, -89}{-100.5, 89.5}等正负组合与浮点组合均有对应测试,浮点输入按文档规则向下取整;
  • 大整数边界:测试覆盖了math.MaxInt64math.MinInt64及其浮点形式、溢出临界值(如9.223372036854776833e+18)等场景,超过 Int64 表示范围的数值不产生匹配。

这些用例同时用于 FerretDB 与 MongoDB 的兼容性对比(testQueryCompat),说明上述行为是 FerretDB 有意对齐 MongoDB 的结果。

$regex 正则匹配

语法

$regex提供三种等价语法形式:

{ <field>: { $regex: '<expression-string>', $options: '<flag>' } } { <field>: { $regex: /<expression-string>/, $options: '<flag>' } } { <field>: /<expression-string>/<flag> }

第一种使用字符串表达正则,第二种使用正则字面量并显式给出$options,第三种直接把正则字面量与 flag 写在一起。三种形式最终行为一致,可按习惯选用。

示例:匹配以 "b" 开头的 product

以下查询返回product字段值以字母 "b" 开头的所有文档:

db.catalog.find({ product: { $regex: /^b/ } })

输出结果为:

response = [ { _id: ObjectId('63e4ce469695494b86bf2b2d'), product: 'bottle', price: 15, stock: 1 }, { _id: ObjectId('63e4ce469695494b86bf2b31'), product: 'boTtLe', price: 20, stock: 3 } ]

注意boTtLe虽然后续字符大小写混合,但首字符是小写b,因此同样命中;BoWL首字符为大写B,不命中。这说明正则默认区分大小写

$options 选项:ims

$options是可选参数,用于指定正则表达式 flag,常用取值包括:

  • i:大小写不敏感(case-insensitivity)
  • m:多行匹配(multi-line matching)
  • s:点号匹配任意字符,包括换行(dot character matching)

i选项为例,以下查询返回product字段值等于 "bottle"(忽略大小写)的所有文档:

db.catalog.find({ product: { $regex: /bottle/i } })

输出结果为:

response = [ { _id: ObjectId('63e3ac0184f488929a3f7379'), product: 'bottle', price: 15, stock: 1 }, { _id: ObjectId('63e3ac0184f488929a3f737d'), product: 'boTtLe', price: 20, stock: 3 } ]

加了i选项后,boTtLebottle都被视为匹配,这正是"大小写不敏感"的直观体现。

选项行为与嵌套字段的源码验证

FerretDB 的集成测试 query_evaluation_test.go(TestQueryEvaluationRegex)对上述选项行为做了逐一验证:

  • RegexWithOption{ $regex: Pattern: "42", Options: "i" }命中包含 "42"(任意大小写)的字符串文档;
  • RegexStringOptionMatchCaseInsensitive:以字符串形式写$regex: "foo"$options: "i",命中含 "foo"、"Foo" 等大小写变体的文档;
  • RegexStringOptionMatchMultiline$options: "m"^foo能匹配多行字符串"bar\nfoo"中第二行的开头;
  • RegexStringOptionMatchLineEnd$options: "s"b.*foo可以跨过换行符匹配"bar\nfoo"
  • RegexNested{ "v.foo.bar": { $regex: "quz" } }证明$regex支持点号嵌套字段路径。

测试数据中专门插入了_id: "multiline-string"、值为"bar\nfoo"的多行字符串,用于验证ms选项的真实效果。

非法正则与错误选项的处理

当正则表达式本身非法、或$options携带无效 flag 时,FerretDB 的行为在 query_evaluation_compat_test.go(TestQueryEvaluationCompatRegexErrors)中有明确覆盖,包括:缺失右括号(g(-z]+ng wrong regex)、缺失右方括号、非法转义(\uZ)、命名捕获组((?P<name>))、孤立右括号、尾部反斜杠、非法重复(a**)、孤立量词(*+?)、非法字符类区间([z-a])、非法 Perl 语法((?z))、超大重复次数((aa){3,10001})等。这些非法模式在兼容测试中均以EmptyResult断言(不产生匹配结果、不崩溃),而非法选项(如Options: "123")同样如此。这意味着在生产使用时应先在客户端校验正则合法性,避免查询静默返回空结果。

结合源码理解实现位置

$mod$regex的完整行为契约主要由以下两个测试文件固化:

  • integration/query_evaluation_test.go:FerretDB 侧的功能性集成测试,验证$regex的正常匹配、嵌套字段、i/m/s选项;
  • integration/query_evaluation_compat_test.go:与 MongoDB 的兼容性对比测试,覆盖$mod的数值边界与$regex的非法输入,确保 FerretDB 行为与 MongoDB 对齐。

当你在 FerretDB 中运行上述find查询时,查询会经由find命令的处理链路(参见 internal/handler/msg_find.go),最终落到底层存储引擎执行过滤。借助这两组测试,开发者可以在修改相关逻辑后快速回归验证$mod$regex的语义没有被破坏。

小结

$mod$regex是 FerretDB 求值查询运算符中两种互补的能力:

  • $mod面向数值字段,按"取模余数"精确过滤,适合周期性、分批性筛选(如偶数/奇数、库存分桶),使用时应牢记数组必须恰好两个元素、小数会被向下取整这两个关键约束;
  • $regex面向文本字段,按模式匹配过滤,配合ims选项可覆盖大小写不敏感、多行、跨行匹配等常见场景,也可用于嵌套字段路径。

两者的边界行为(非法数组、非数值、非法正则、非法选项)都已在 FerretDB 仓库的兼容性测试中与 MongoDB 逐一对齐验证。你可以基于本文示例数据直接在本机 FerretDB 中运行验证,再将这些模式迁移到真实业务查询中。

【免费下载链接】FerretDBA truly Open Source MongoDB alternative项目地址: https://gitcode.com/gh_mirrors/fe/FerretDB

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询