初学 mongo 的时候就体验到了 aggregate 函数的强大。aggregate 函数也毫无疑问的是 mongo 的核心功能。
但是 aggregate 中存在两个很神秘的符号 $ 和 $$。下面着重讲一下 aggregate 的设定。
首先明确 mongo 中的一个概念:query 和 expression 的区别。
query 是查询语句,而 expression 是表达式。两者有基本不同的语法,mongo 的官方文档中对 query 和 expression 的语法都有很详细的定义。
不同的语法带来不同的功能。
query 只会用在查询中,表达了一种筛选条件,对结果集运行后就可以直接获得筛选结果。
expression 则在 aggregate 中广泛存在,而且通常都会产生一个返回值。
这里就产生了第一个很让人迷惑的点,因为绝大多数的操作符(例如 $in / $or / $lt 等等)同时存在于 query 和 expression 中,并且他们的写法是完全不同的。
以 $in 举例,query 的 $in 的用法是
fieldA: { $in: ["A", "D"] }
这表示查询所有 fieldA 值为 “A” 或 “D” 的文档
而在 expression 中 $in 的用法变成了
$in: [ "$fieldA", [ "A", "D" ] ]
表达 fieldA 值为 “A” 或 “D” 时返回 true
介绍完了 query 和 expression 的区别之后 我们开始介绍 $ 和 $$ 的含义。
$ 的含义很简单,在 query 中,如果想要选中一个变量可以直接写他的字段名 例如上面例子里的 fieldA。
而在 expression 中,如果你需要选中一个变量,为了和普通字符串区别开,你就得在他的开头写上 $ 符,如上例中的 $fieldA。
似乎这样已经很全面没什么问题了?
并不是,因为 expression 中有 $let 和 $map 等等操作符可以定义变量,甚至 filter 中的 as 别名也会参合进来搅屎。那这些变量如果和本身的字段冲突了怎么办?更何况语义里这两个就完全不是同一种东西啊。
解决方案是 $$ 符。
$$ 符的适用场景就是调用一些与文档无关的变量(包括系统变量和人为定义变量)时使用。举例来说。
$filter: { input: '$fieldA', as: 'fieldB', cond: { $in: ['$$fieldB', [ "A", "D" ]] } }
这时 $$fieldB 就指向了我们刚刚取了别名的 $fieldA。
另外,如果你闲的蛋疼。想要统一变量名,觉得什么 $ 啊 $$ 啊的太乱了。很简单,官方提供了一个系统变量 CURRENT。你可以把每一个 aggregate 中的变量都写成 $$CURRENT.xxx。例如 $fieldA 可以写成 $$CURRENT.fieldA。
总结:
以 fieldA 为例
fieldA:存在于 query 中,表示选定的字段名
$fieldA:存在于 expression 中,表示选定的字段名
$$fieldA:存在于 expression 中,表示 自定义变量/系统变量/别名
一些补充。
比较特殊的是 $match 表达式,他会在 $ 遍地的 aggregate 中引入一个 query。再加上 $match 表达式实在太常用了。所以 aggregate 函数中经常出现 fieldA / $fieldA / $$fieldA 三巨头共同出现的现象,可以说是很让人困扰了。
#EOF