$expr

允许在查询语言中使用聚合表达式

$expr 具有以下语法:

1
{ $expr: { <expression> } }

参数可以是任何有效的聚合表达式。有关更多信息,请参见表达式

特性

$expr可以构建查询表达式,以比较$match阶段中同一文档中的字段。

如果$match阶段是阶段的一部分$lookup,则 $expr可以使用let变量比较字段。有关示例,请参见 使用$lookup指定多个联接条件。

$expr不支持多键索引。

例子

比较单个文档中的两个字段

考虑包含monthlyBudget以下文档的集合:

1
2
3
4
5
{ "_id" : 1, "category" : "food", "budget": 400, "spent": 450 }
{ "_id" : 2, "category" : "drinks", "budget": 100, "spent": 150 }
{ "_id" : 3, "category" : "clothes", "budget": 100, "spent": 50 }
{ "_id" : 4, "category" : "misc", "budget": 500, "spent": 300 }
{ "_id" : 5, "category" : "travel", "budget": 200, "spent": 650 }

以下操作用于$expr查找spent超过budget的文档:

1
db.monthlyBudget.find( { $expr: { $gt: [ "$spent" , "$budget" ] } } )

该操作返回以下结果:

1
2
3
{ "_id" : 1, "category" : "food", "budget" : 400, "spent" : 450 }
{ "_id" : 2, "category" : "drinks", "budget" : 100, "spent" : 150 }
{ "_id" : 5, "category" : "travel", "budget" : 200, "spent" : 650 }

$expr与条件语句一起使用

某些查询要求在定义查询过滤器时能够执行条件逻辑。聚合框架为$cond运算符提供了 表达条件语句的方法。通过$expr$cond运算符一起使用 ,可以为查询语句指定条件过滤器。

supplies使用以下文档创建样本集合:

1
2
3
4
5
6
7
db.supplies.insertMany([
   { "_id" : 1, "item" : "binder", "qty" : NumberInt("100"), "price" : NumberDecimal("12") },
   { "_id" : 2, "item" : "notebook", "qty" : NumberInt("200"), "price" : NumberDecimal("8") },
   { "_id" : 3, "item" : "pencil", "qty" : NumberInt("50"), "price" : NumberDecimal("6") },
   { "_id" : 4, "item" : "eraser", "qty" : NumberInt("150"), "price" : NumberDecimal("3") },
   { "_id" : 5, "item" : "legal pad", "qty" : NumberInt("42"), "price" : NumberDecimal("10") }
])

假设要在下个月进行的销售,您需要对价格进行折价,使得:

  • 如果qty大于或等于100,则折扣价price为0.5。
  • 如果qty小于100,则折价price为的0.75。

在应用折扣之前,您想知道supplies集合中的哪些项目的折扣价小于5。

以下示例使用$expr$cond来基于qty和$lt计算折扣价,并返回其折扣价小于NumberDecimal(“5”)的文档:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Aggregation expression to calculate discounted price

let discountedPrice = {
   $cond: {
      if: { $gte: ["$qty", 100] },
      then: { $multiply: ["$price", NumberDecimal("0.50")] },
      else: { $multiply: ["$price", NumberDecimal("0.75")] }
   }
};

// Query the supplies collection using the aggregation expression

db.supplies.find( { $expr: { $lt:[ discountedPrice,  NumberDecimal("5") ] } });

下表显示了每个文档的折扣价以及折扣价是否小于NumberDecimal(“5”)(即,文档是否满足查询条件)。

即使$cond计算出有效的折价,该价格也不会反映在返回的数据中。而是返回的文档以其原始状态表示匹配的文档。查找操作未返回legal,binder文件,因为它们的折扣价大于5。

$jsonSchema

$jsonSchema匹配满足指定JSON Schema文档。

https://docs.mongodb.com/manual/reference/operator/query/jsonSchema/

$mod

选择filed除以除数的字段的值具有指定余数的文档(即执行模运算以选择文档)。要指定$mod表达式,请使用以下语法:

1
{ field: { $mod: [ divisor, remainder ] } }

$mod当传递的元素少于或多于两个的数组时,运算符会出错。

例子

使用$mod来选择文件

考虑inventory包含以下文档的集合:

1
2
3
{ "_id" : 1, "item" : "abc123", "qty" : 0 }
{ "_id" : 2, "item" : "xyz123", "qty" : 5 }
{ "_id" : 3, "item" : "ijk123", "qty" : 12 }

然后,以下查询选择inventory集合中qty模 取值4等于的那些文档 0:

1
db.inventory.find( { qty: { $mod: [ 4, 0 ] } } )

该查询返回以下文档:

1
2
{ "_id" : 1, "item" : "abc123", "qty" : 0 }
{ "_id" : 3, "item" : "ijk123", "qty" : 12 }

没有足够的元素错误

$mod传递少于两个元素的数组时,运算符会出错。

单元素数组

以下操作错误地向$mod运算符传递了包含单个元素的数组:

1
db.inventory.find( { qty: { $mod: [ 4 ] } } )

该语句导致以下错误:

1
2
3
4
error: {
     "$err" : "bad query: BadValue malformed mod, not enough elements",
     "code" : 16810
}

空数组

以下操作错误地向$mod运算符传递了一个空数组:

1
db.inventory.find( { qty: { $mod: [ ] } } )

该语句导致以下错误:

1
2
3
4
error: {
     "$err" : "bad query: BadValue malformed mod, not enough elements",
     "code" : 16810
}

元素过多错误

$mod传递具有两个以上元素的数组时,运算符会出错。

例如,以下操作尝试将$mod 运算符与包含四个元素的数组一起使用:

1
2
3
4
error: {
     "$err" : "bad query: BadValue malformed mod, too many elements",
     "code" : 16810
}

$regex

为查询中的模式匹配字符串提供正则表达式功能 。MongoDB使用具有UTF-8支持的Perl兼容正则表达式(即“PCRE”)版本8.42。

要使用$regex,请使用以下语法之一:

1
2
3
{ <field>: { $regex: /pattern/, $options: '<options>' } }
{ <field>: { $regex: 'pattern', $options: '<options>' } }
{ <field>: { $regex: /pattern/<options> } }

在MongoDB中,您还可以使用正则表达式对象(即 /pattern/)来指定正则表达式:

1
{ <field>: /pattern/<options> }

$options

以下内容<options>可用于正则表达式。

选项 描述 语法限制
i 不区分大小写,以匹配大小写。
m 对于包含锚点的模式(即^开始, $结束),请在每行的开头或结尾匹配具有多行值的字符串。如果没有此选项,这些锚点将在字符串的开头或结尾匹配。如果模式不包含锚点,或者字符串值不包含换行符(例如\n),则该m选项无效。
x “扩展”功能可以忽略模式中的所有空白字符,$regex除非转义或包含在字符类中。此外,它会忽略中间的字符,包括未转义的井号/磅(#)字符和下一个新行,因此您可以在复杂模式中添加注释。这仅适用于数据字符;空格字符永远不会出现在图案的特殊字符序列中。该x选项不影响VT字符(即代码11)的处理。 需要$regex$options语法
s 允许点字符(即.)匹配所有字符,包括换行符。 需要$regex$options语法

特性

$ regex vs /pattern/

$in表达式

要在$in查询表达式中包含正则表达式,您只能使用JavaScript正则对象(即/pattern/ )。例如:

1
{ name: { $in: [ /^acme/i, /^ack/ ] } }

您不能在中使用$regex运算符表达式$in。

隐AND条件字段

要将正则表达式包含在该字段的查询条件的逗号分隔列表中,请使用$regex运算符。例如:

1
2
3
{ name: { $regex: /acme.*corp/i, $nin: [ 'acmeblahcorp' ] } }
{ name: { $regex: /acme.*corp/, $options: 'i', $nin: [ 'acmeblahcorp' ] } }
{ name: { $regex: 'acme.*corp', $options: 'i', $nin: [ 'acmeblahcorp' ] } }

x和s选项

要使用任一x选项或s选项,则必须使用 $regex与该$options。例如,要指定i和s选项,必须同时使用$options:

1
2
{ name: { $regex: /acme.*corp/, $options: "si" } }
{ name: { $regex: 'acme.*corp', $options: "si" } }

PCRE VS JavaScript

要在regex模式中使用JavaScript不支持的PCRE支持的功能,必须将$regex运算符表达式与模式一起用作字符串。例如,要在模式中使用(?i) 来为剩余的模式打开不区分大小写并为剩余的模式(?-i)打开区分大小写,您必须$regex将该模式的运算符用作字符串:

1
{ name: { $regex: '(?i)a(?-i)cme' } }

$regex和$not

从4.0.7版开始,$not可以对以下两种方式执行NOT逻辑操作:

正则表达式对象(即/pattern/)

例如:

1
db.inventory.find( { item: { $not: /^p.*/ } } )

$regex 运算符表达式(从MongoDB 4.0.7开始):

例如:

1
2
db.inventory.find( { item: { $not: { $regex: "^p.*" } } } )
db.inventory.find( { item: { $not: { $regex: /^p.*/ } } } )

在4.0.6及更低版本中,您可以将$not运算符与正则表达式对象(即/pattern/)一起使用,但不能与$regex运算符表达式一起使用。

索引使用

对于区分大小写的正则表达式查询,如果该字段存在索引,则MongoDB会将正则表达式与索引中的值进行匹配,这可能比集合扫描更快。如果正则表达式是“前缀表达式”,则可能会发生进一步的优化,这意味着所有潜在的匹配都以相同的字符串开头。这允许MongoDB从该前缀构造一个“范围”,并且仅与该范围内的索引值匹配。

如果正则表达式以尖号(^)或左锚(\A)开头,后跟一串简单符号,则为“前缀表达式” 。例如,/^abc.*/将仅通过匹配索引中以开头的值来优化正则表达式abc。

此外,虽然/^a//^a.*//^a.*$/匹配等效的字符串,但它们具有不同的性能特征。如果存在适当的索引,则所有这些表达式都使用索引;但是,/^a.*//^a.*$/较慢。/^a/匹配前缀后可以停止扫描。

不区分大小写的正则表达式查询通常不能有效地使用索引。该$regex实现不支持排序规则,并且无法使用不区分大小写的索引。

例子

以下示例将集合products与以下文档结合使用:

1
2
3
4
{ "_id" : 100, "sku" : "abc123", "description" : "Single line description." }
{ "_id" : 101, "sku" : "abc789", "description" : "First line\nSecond line" }
{ "_id" : 102, "sku" : "xyz456", "description" : "Many spaces before     line" }
{ "_id" : 103, "sku" : "xyz789", "description" : "Multiple\nline description" }

执行LIKE匹配

以下示例匹配sku字段类似的所有文档"%789":

1
db.products.find( { sku: { $regex: /789$/ } } )

该示例类似于以下SQL LIKE语句:

1
2
SELECT * FROM products
WHERE sku like "%789";

执行不区分大小写的正则表达式匹配

以下示例使用i选项对sku值以开头的文档执行 不区分大小写的匹配ABC。

1
db.products.find( { sku: { $regex: /^ABC/i } } )

该查询与以下文档匹配:

1
2
{ "_id" : 100, "sku" : "abc123", "description" : "Single line description." }
{ "_id" : 101, "sku" : "abc789", "description" : "First line\nSecond line" }

从指定模式开始的行的多行匹配

以下示例使用该m选项来匹配S多行字符串以字母开头的行:

1
db.products.find( { description: { $regex: /^S/, $options: 'm' } } )

该查询与以下文档匹配:

1
2
{ "_id" : 100, "sku" : "abc123", "description" : "Single line description." }
{ "_id" : 101, "sku" : "abc789", "description" : "First line\nSecond line" }

如果没有该m选项,则查询将仅匹配以下文档:

1
{ "_id" : 100, "sku" : "abc123", "description" : "Single line description." }

如果该$regex模式不包含锚,则该模式将与整个字符串匹配,如以下示例所示:

1
db.products.find( { description: { $regex: /S/ } } )

然后,$regex将匹配两个文档:

1
2
{ "_id" : 100, "sku" : "abc123", "description" : "Single line description." }
{ "_id" : 101, "sku" : "abc789", "description" : "First line\nSecond line" }

使用.点字符来匹配

以下示例使用允许点字符(即.)匹配所有字符(包括换行符)的s选项以及执行不区分大小写的匹配的i选项:

1
db.products.find( { description: { $regex: /m.*line/, $options: 'si' } } )

该查询与以下文档匹配:

1
2
{ "_id" : 102, "sku" : "xyz456", "description" : "Many spaces before     line" }
{ "_id" : 103, "sku" : "xyz789", "description" : "Multiple\nline description" }

如果没有该s选项,则查询将仅与以下文档匹配:

1
{ "_id" : 102, "sku" : "xyz456", "description" : "Many spaces before     line" }

忽略Pattern中的空格

以下示例使用x选项“忽略空白”和注释,在匹配模式中以#表示并以\n结尾:

1
2
var pattern = "abc #category code\n123 #item number"
db.products.find( { sku: { $regex: pattern, $options: "x" } } )

该查询与以下文档匹配:

1
{ "_id" : 100, "sku" : "abc123", "description" : "Single line description." }

$text

$text对用文本索引索引的字段的内容进行文本搜索。

https://docs.mongodb.com/manual/reference/operator/query/text/

$where

使用$where运算符将包含JavaScript表达式的字符串或完整的JavaScript函数传递给查询系统。在$where提供了更大的灵活性,但无法利用索引。因此,当您使用标准MongoDB运算符(例如$gt$in)来表达查询时,查询性能将会提高。

重要

在版本3.6中更改:$expr运算符允许在查询语言中使用聚合表达式。 $expr$where快,因为它不执行JavaScript,因此应尽可能首选。