Spring Data MongoDB 教程

分面分类(Faceted Classification)

MongoDB 从 3.4 版本开始,通过使用聚合框架支持分面分类(Faceted Classification)。分面分类将多个语义类别(常规的或特定的主题)组合在一起以创建完整的分类条目。凡是流经聚合管道的文档将被分类到存储桶(Buckets)中,多方面的分类支持对同一组输入文档进行各种聚合,而无需多次检索输入文档。

存储桶(Buckets)

存储桶操作根据指定的表达式和存储桶边界将传入文档分类为组(称为存储桶)。存储桶操作需要分组字段或分组表达式。你可以使用 Aggregate 类的 bucket() 和 bucketAuto() 方法来定义它们。BucketOperation 和 BucketAutoOperation 可以根据输入文档的聚合表达式累积。您可以使用 with...() 和 andOutput(String) 方法。还可以使用 as(String) 方法为操作设置别名。每个存储桶在输出中均表示为一个文档。

BucketOperation 采用一组定义的边界将传入文档分组到这些类别中。需要对边界进行排序。以下显示了存储桶操作的一些示例:

// generates {$bucket: {groupBy: $price, boundaries: [0, 100, 400]}}
bucket("price").withBoundaries(0, 100, 400);

// generates {$bucket: {groupBy: $price, default: "Other" boundaries: [0, 100]}}
bucket("price").withBoundaries(0, 100).withDefault("Other");

// generates {$bucket: {groupBy: $price, boundaries: [0, 100], output: { count: { $sum: 1}}}}
bucket("price").withBoundaries(0, 100).andOutputCount().as("count");

// generates {$bucket: {groupBy: $price, boundaries: [0, 100], 5, output: { titles: { $push: "$title"}}}
bucket("price").withBoundaries(0, 100).andOutput("title").push().as("titles");

BucketAutoOperation 确定边界,试图将文档平均分配到指定数量的桶中。BucketAutoOperation 可以选择取一个粒度值,指定使用的首选数字系列,以确保计算的边界边缘在首选的整数或10的幂上结束。下面列出了桶操作的例子:

// generates {$bucketAuto: {groupBy: $price, buckets: 5}}
bucketAuto("price", 5)

// generates {$bucketAuto: {groupBy: $price, buckets: 5, granularity: "E24"}}
bucketAuto("price", 5).withGranularity(Granularities.E24).withDefault("Other");

// generates {$bucketAuto: {groupBy: $price, buckets: 5, output: { titles: { $push: "$title"}}}
bucketAuto("price", 5).andOutput("title").push().as("titles");

为了在桶中创建输出字段,桶操作可以通过 andOutput() 使用 AggregationExpression,通过 andOutputExpression() 使用 SpEL 表达。

多面聚合

多个聚合管道可用于创建多方面的聚合,以表示单个聚合阶段中多个维度(或分面)的数据。多方面的聚合提供多个筛选器和分类来指导数据浏览和分析。分面的一个常见实现是,许多在线零售商通过应用产品价格、制造商、尺寸和其他因素的过滤器,提供缩小搜索结果的方法。

您可以使用聚合类的 facet() 方法定义 FacetOperation。可以使用 and() 方法使用多个聚合管道对其进行自定义。每个子管道在输出文档中都有自己的字段,其结果存储为文档数组。

子管道可以在分组之前投影和过滤输入文档。常见的用例包括在分类之前提取日期部分或进行计算。以下列表显示了方面操作示例:

// generates {$facet: {categorizedByPrice: [ { $match: { price: {$exists : true}}}, { $bucketAuto: {groupBy: $price, buckets: 5}}]}}
facet(match(Criteria.where("price").exists(true)), bucketAuto("price", 5)).as("categorizedByPrice"))

// generates {$facet: {categorizedByCountry: [ { $match: { country: {$exists : true}}}, { $sortByCount: "$country"}]}}
facet(match(Criteria.where("country").exists(true)), sortByCount("country")).as("categorizedByCountry"))

// generates {$facet: {categorizedByYear: [
//     { $project: { title: 1, publicationYear: { $year: "publicationDate"}}},
//     { $bucketAuto: {groupBy: $price, buckets: 5, output: { titles: {$push:"$title"}}}
// ]}}
facet(project("title").and("publicationDate").extractYear().as("publicationYear"),
     bucketAuto("publicationYear", 5).andOutput("title").push().as("titles"))
 .as("categorizedByYear"))

按计数(Count)排序

按计数排序操作根据指定的表达式的值对输入的文档进行分组,计算每个不同组中的文档计数(Count),并按计数对结果进行排序。它提供了在使用分面分类时应用排序的快捷方式。按计数排序操作需要分组字段或分组表达式。

下面显示了一个按计数排序的示例:

// generates { $sortByCount: "$country" }
sortByCount("country");

按次数排序的操作等同于以下BSON(二进制JSON):

{ $group: { _id: <expression>, count: { $sum: 1 } } },
{ $sort: { count: -1 } }

投影表达式中的Spring表达式支持

我们通过 ProjectionOperation 和 BucketOperation 类的 andExpression() 方法支持在投影表达式中使用 SpEL 表达式。这个功能让你把所需的表达式定义为 SpEL 表达式。在运行查询时,SpEL 表达式被翻译成相应的 MongoDB 投影表达式部分,这使得表达复杂的计算变得更加容易。

用 SpEL 表达式进行复杂计算

考虑以下 SpEL 表达式:

1 + (q + 1) / (q - 1)

上面的表达式将被翻译成下面的 MongoDB 投影表达式:

{ "$add" : [ 1, {
   "$divide" : [ {
       "$add":["$q", 1]}, {
       "$subtract":[ "$q", 1]}
   ]
}]}

下表显示了 Spring Data MongoDB 所支持的 SpEL 转换:

SpEL表达式Mongo表达式
a == b{ $eq : [$a, $b] }
a != b{ $ne : [$a , $b] }
a > b{ $gt : [$a, $b] }
a >= b{ $gte : [$a, $b] }
a < b{ $lt : [$a, $b] }
a ⇐ b{ $lte : [$a, $b] }
a + b{ $add : [$a, $b] }
a - b{ $subtract : [$a, $b] }
a * b{ $multiply : [$a, $b] }
a / b{ $divide : [$a, $b] }
a^b{ $pow : [$a, $b] }
a % b{ $mod : [$a, $b] }
a && b{ $and : [$a, $b] }
a || b{ $or : [$a, $b] }
!a{ $not : [$a] }

除了上述的转换之外,你还可以使用标准的 SpEL 操作,例如使用 new 来创建数组,并通过它们的名字引用表达式。下面的例子显示了如何以这种方式创建一个数组:

// { $setEquals : [$a, [5, 8, 13] ] }
.andExpression("setEquals(a, new int[]{5, 8, 13})");
说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号