MongoDB聚合运算符:$zip
$zip
用于将输入数组的元素重新组合转换为新的数组,重组转换的的规则是使用输入数组的第一个元素重组为第一个数组,使用第二个元素重组为第二个数组,以此类推。
例如,[ [ 1, 2, 3 ], [ "a", "b", "c" ] ]
转换后为[ [ 1, "a" ], [ 2, "b" ], [ 3, "c" ] ]
语法
{
$zip: {
inputs: [ <array expression1>, ... ],
useLongestLength: <boolean>,
defaults: <array expression>
}
}
inputs
:表达式数组,数组的每个表达式可解析为数组。这些输入数组的元素组合成输出数组。
如果任何一个输入数组的解析值为空或指向一个缺失字段,$zip
将返回空值。
如果输入数组中的任何一个没有解析为数组或空值,或者指向一个缺失字段,$zip
将返回错误信息。useLongestLength
:布尔值,用于指定最长数组的长度是否决定输出数组中数组的个数。
默认值为 false,最短数组的长度决定输出数组中数组的个数。- 如果输入数组的长度不同,则使用默认元素值数组。必须同时指定
useLongestLength: true
,否则$zip
将返回错误。
如果useLongestLength: true
,但defaults
为空或未指定,$zip
将使用null
作为默认值。
如果指定了非空defaults
,则必须为每个输入数组指定默认值,否则$zip
返回错误。
使用
输入数组的长度不要求相同。默认情况下,输出数组的长度为最短输入数组的长度,但useLongestLength
选项可以指定$zip
输出与最长输入数组的长度是否一致。
参考下面的例子来说明:
例1:
{ $zip: { inputs: [ [ "a" ], [ "b" ], [ "c" ] ] }}
结果:
[ [ "a", "b", "c" ] ]
例2:
{ $zip: { inputs: [ [ "a" ], [ "b", "c" ] ] } }
结果:
[ [ "a", "b" ] ]
例3:
{
$zip: {
inputs: [ [ 1 ], [ 2, 3 ] ],
useLongestLength: true
}
}
结果:
[ [ 1, 2 ], [ null, 3 ] ]
例4:
{
$zip: {
inputs: [ [ 1 ], [ 2, 3 ], [ 4 ] ],
useLongestLength: true,
defaults: [ "a", "b", "c" ]
}
}
结果:
因为useLongestLength: true
,$zip
会用缺省值元素填充较短的输入数组。
[ [ 1, 2, 4 ], [ "a", 3, "c" ] ]
举例
矩阵转置
使用下面的脚本创建matrices
集合:
db.matrices.insertMany([
{ matrix: [[1, 2], [2, 3], [3, 4]] },
{ matrix: [[8, 7], [7, 6], [5, 4]] },
])
要计算这个集合中每个 3x2 矩阵的转置,可以使用下面的聚合运算:。
db.matrices.aggregate([{
$project: {
_id: false,
transposed: {
$zip: {
inputs: [
{ $arrayElemAt: [ "$matrix", 0 ] },
{ $arrayElemAt: [ "$matrix", 1 ] },
{ $arrayElemAt: [ "$matrix", 2 ] },
]
}
}
}
}])
执行的结果为:
{ "transposed" : [ [ 1, 2, 3 ], [ 2, 3, 4 ] ] }
{ "transposed" : [ [ 8, 7, 5 ], [ 7, 6, 4 ] ] }
过滤并保留索引
可以使用$zip
和$filter
来获取数组中的元素子集,同时保存每个元素的原始索引。
使用脚本创建pages
集合:
db.pages.insertOne( {
"category": "unix",
"pages": [
{ "title": "awk for beginners", reviews: 5 },
{ "title": "sed for newbies", reviews: 0 },
{ "title": "grep made simple", reviews: 2 },
] } )
下面的聚合首先把pages
数组中的元素连同其索引一起压缩,然后只筛选出至少有一条评论的页面::
db.pages.aggregate([{
$project: {
_id: false,
pages: {
$filter: {
input: {
$zip: {
inputs: [ "$pages", { $range: [0, { $size: "$pages" }] } ]
}
},
as: "pageWithIndex",
cond: {
$let: {
vars: {
page: { $arrayElemAt: [ "$$pageWithIndex", 0 ] }
},
in: { $gte: [ "$$page.reviews", 1 ] }
}
}
}
}
}
}])
结果为:
{
"pages" : [
[ { "title" : "awk for beginners", "reviews" : 5 }, 0 ],
[ { "title" : "grep made simple", "reviews" : 2 }, 2 ] ]
}