$facet(聚合)
定义
$facet
版本 3.4 中的新增功能。
在单个阶段内处理同一组输入文档上的多个聚合管道。每个子管道在输出文档中都有自己的字段,其结果存储为文档数组。
$facet
阶段支持创建多分面聚合,能够在单个聚合阶段跨多个维度或分面描述数据特征。多分面聚合提供多个过滤器和分类方式来指导数据浏览和分析。零售商通常利用分面来创建产品价格、制造商、尺寸等过滤器以缩小搜索结果范围。输入文档仅传递到
$facet
阶段一次。$facet
使能够对同一组输入文档进行各种聚合,而无需多次检索输入文档。
兼容性
可以使用 $facet
查找托管在以下环境中的部署:
MongoDB Atlas:用于云中 MongoDB 部署的完全托管服务
MongoDB Enterprise:基于订阅、自我管理的 MongoDB 版本
MongoDB Community:源代码可用、免费使用且可自行管理的 MongoDB 版本
语法
$facet
阶段具有以下形式:
{ $facet: { <outputField1>: [ <stage1>, <stage2>, ... ], <outputField2>: [ <stage1>, <stage2>, ... ], ... } }
为每个指定管道指定输出字段名称。
Considerations
执行 $facet
中的每个阶段时,生成的文档限制为 100 MB。请注意,allowDiskUse 标记不会影响 100 MB 的大小限制,因为 $facet
不会溢出到磁盘。
最终输出文档受 16 兆字节 BSON 文档大小限制。如果超过 16 兆字节,聚合就会产生错误。
行为
与分面相关的聚合阶段对传入文档进行分类和分组。在不同的 $facet
子管道的 <stage>
中指定以下任何与分面相关的阶段,以执行多分面聚合:
其他聚合阶段也可以与 $facet
一起使用,但以下情况除外:
$facet
内的每个子管道都会被传递完全相同的输入文档集。这些子管道彼此完全独立,每个子管道输出的文档数组都存储在输出文档的单独字段中。在同一 $facet
阶段中,一个子管道的输出不能用作另一子管道的输入。如需进一步聚合,请在 $facet
之后添加附加阶段,并指定所需子管道输出的字段名 <outputField>
。
索引使用
管道顺序决定了 $facet
阶段使用索引的方式。
如果
$facet
阶段是管道中的第一个阶段,则该阶段将执行COLLSCAN
。如果$facet
阶段是管道中的第一个阶段,则不使用索引。如果
$facet
阶段出现在管道中较晚的阶段,并且较早的阶段已使用索引,则$facet
在执行期间不会触发COLLSCAN
。
例如,在 $facet
阶段之前的 $match
或 $sort
阶段可以使用索引,而 $facet
不会触发 COLLSCAN
。
有关优化建议,请参阅聚合管道优化。
例子
考虑一个在线商店,其库存存储在以下 artwork
集合中:
{ "_id" : 1, "title" : "The Pillars of Society", "artist" : "Grosz", "year" : 1926, "price" : NumberDecimal("199.99"), "tags" : [ "painting", "satire", "Expressionism", "caricature" ] } { "_id" : 2, "title" : "Melancholy III", "artist" : "Munch", "year" : 1902, "price" : NumberDecimal("280.00"), "tags" : [ "woodcut", "Expressionism" ] } { "_id" : 3, "title" : "Dancer", "artist" : "Miro", "year" : 1925, "price" : NumberDecimal("76.04"), "tags" : [ "oil", "Surrealism", "painting" ] } { "_id" : 4, "title" : "The Great Wave off Kanagawa", "artist" : "Hokusai", "price" : NumberDecimal("167.30"), "tags" : [ "woodblock", "ukiyo-e" ] } { "_id" : 5, "title" : "The Persistence of Memory", "artist" : "Dali", "year" : 1931, "price" : NumberDecimal("483.00"), "tags" : [ "Surrealism", "painting", "oil" ] } { "_id" : 6, "title" : "Composition VII", "artist" : "Kandinsky", "year" : 1913, "price" : NumberDecimal("385.00"), "tags" : [ "oil", "painting", "abstract" ] } { "_id" : 7, "title" : "The Scream", "artist" : "Munch", "year" : 1893, "tags" : [ "Expressionism", "painting", "oil" ] } { "_id" : 8, "title" : "Blue Flower", "artist" : "O'Keefe", "year" : 1918, "price" : NumberDecimal("118.42"), "tags" : [ "abstract", "painting" ] }
以下操作使用 MongoDB 的分面功能为客户提供按多个维度(如标签、价格和创建年份)分类的商店库存。此 $facet
阶段有三个子管道,分别使用 $sortByCount
、$bucket
或 $bucketAuto
来执行此次分面聚合。artwork
中的输入文档只在操作开始时从数据库中获取一次:
db.artwork.aggregate( [ { $facet: { "categorizedByTags": [ { $unwind: "$tags" }, { $sortByCount: "$tags" } ], "categorizedByPrice": [ // Filter out documents without a price e.g., _id: 7 { $match: { price: { $exists: 1 } } }, { $bucket: { groupBy: "$price", boundaries: [ 0, 150, 200, 300, 400 ], default: "Other", output: { "count": { $sum: 1 }, "titles": { $push: "$title" } } } } ], "categorizedByYears(Auto)": [ { $bucketAuto: { groupBy: "$year", buckets: 4 } } ] } } ])
该操作将返回以下文档:
{ "categorizedByYears(Auto)" : [ // First bucket includes the document without a year, e.g., _id: 4 { "_id" : { "min" : null, "max" : 1902 }, "count" : 2 }, { "_id" : { "min" : 1902, "max" : 1918 }, "count" : 2 }, { "_id" : { "min" : 1918, "max" : 1926 }, "count" : 2 }, { "_id" : { "min" : 1926, "max" : 1931 }, "count" : 2 } ], "categorizedByPrice" : [ { "_id" : 0, "count" : 2, "titles" : [ "Dancer", "Blue Flower" ] }, { "_id" : 150, "count" : 2, "titles" : [ "The Pillars of Society", "The Great Wave off Kanagawa" ] }, { "_id" : 200, "count" : 1, "titles" : [ "Melancholy III" ] }, { "_id" : 300, "count" : 1, "titles" : [ "Composition VII" ] }, { // Includes document price outside of bucket boundaries, e.g., _id: 5 "_id" : "Other", "count" : 1, "titles" : [ "The Persistence of Memory" ] } ], "categorizedByTags" : [ { "_id" : "painting", "count" : 6 }, { "_id" : "oil", "count" : 4 }, { "_id" : "Expressionism", "count" : 3 }, { "_id" : "Surrealism", "count" : 2 }, { "_id" : "abstract", "count" : 2 }, { "_id" : "woodblock", "count" : 1 }, { "_id" : "woodcut", "count" : 1 }, { "_id" : "ukiyo-e", "count" : 1 }, { "_id" : "satire", "count" : 1 }, { "_id" : "caricature", "count" : 1 } ] }