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 } ])数据集中刻意混入了大小写混合的BoWL、boTtLe,便于演示$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 = 0、14 % 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.MaxInt64、math.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 选项:i、m、s
$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选项后,boTtLe与bottle都被视为匹配,这正是"大小写不敏感"的直观体现。
选项行为与嵌套字段的源码验证
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"的多行字符串,用于验证m与s选项的真实效果。
非法正则与错误选项的处理
当正则表达式本身非法、或$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面向文本字段,按模式匹配过滤,配合i、m、s选项可覆盖大小写不敏感、多行、跨行匹配等常见场景,也可用于嵌套字段路径。
两者的边界行为(非法数组、非数值、非法正则、非法选项)都已在 FerretDB 仓库的兼容性测试中与 MongoDB 逐一对齐验证。你可以基于本文示例数据直接在本机 FerretDB 中运行验证,再将这些模式迁移到真实业务查询中。
【免费下载链接】FerretDBA truly Open Source MongoDB alternative项目地址: https://gitcode.com/gh_mirrors/fe/FerretDB
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考