【Spring连载】使用Spring Data访问 MongoDB----Aggregation Framework支持

本文主要是介绍【Spring连载】使用Spring Data访问 MongoDB----Aggregation Framework支持,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【Spring连载】使用Spring Data访问 MongoDB----聚合框架支持

  • 一、基础槪念
  • 二、投影表达式Projection Expressions
  • 三、分面分类法Faceted Classification
    • 3.1 桶Buckets
    • 3.2 多方面的聚合Multi-faceted Aggregation
    • 3.3 按计数排序Sort By Count
    • 3.4 投影表达式中的Spring表达式支持
      • 3.4.1 使用SpEL表达式的复杂计算
    • 3.5 聚合框架示例Aggregation Framework Examples
      • 3.5.1 Aggregation Framework 例1
      • 3.5.2 Aggregation Framework 例2
      • 3.5.3 Aggregation Framework 例3
      • 3.5.4 Aggregation Framework 例4
      • 3.5.5 Aggregation Framework 例5
      • 3.5.6 Aggregation Framework 例6
      • 3.5.7 Aggregation Framework 例7

Spring Data MongoDB为MongoDB 2.2版引入的聚合框架提供了支持。
有关更多信息,请参阅MongoDB的聚合框架和其他数据聚合工具的完整 参考文档。

一、基础槪念

Spring Data MongoDB中的聚合框架支持基于以下关键抽象:Aggregation、AggregationDefinition和AggregationResults。

  • 聚合Aggregation
    Aggregation表示MongoDB聚合操作,并保存聚合管道(aggregation pipeline)指令的描述。聚合是通过调用Aggregation类的相应newAggregation(…)静态工厂方法创建的,该方法采用AggregateOperation列表和一个可选输入类。
    实际的聚合操作由MongoTemplate的聚合方法运行,该方法将所需的输出类作为参数。
  • 类型聚合TypedAggregation
    TypedAggregation与Aggregation一样,包含聚合管道的指令和对输入类型的引用,用于将域属性映射到实际document字段。
    在运行时,考虑到潜在的@Field注解,根据给定的输入类型检查字段引用。

在3.2版本中做出更改,引用不存在的属性不再引发错误。要恢复以前的行为,请使用AggregationOptions的strictMapping选项。

  • 聚合定义AggregationDefinition
    AggregationDefinition表示MongoDB聚合管道操作,并描述该聚合步骤中应执行的处理。尽管你可以手动创建AggregationDefinition,但建议使用Aggregate类提供的静态工厂方法来构造AggregateOperation。
  • 聚合结果AggregationResults
    AggregationResults是聚合操作结果的容器。它以Document的形式提供对原始聚合结果的访问,以访问映射的对象和有关聚合的其他信息。

以下列表展示了使用Spring Data MongoDB对MongoDB聚合框架的支持的典型示例:

Aggregation agg = newAggregation(pipelineOP1(),pipelineOP2(),pipelineOPn()
);AggregationResults<OutputType> results = mongoTemplate.aggregate(agg, "INPUT_COLLECTION_NAME", OutputType.class);
List<OutputType> mappedResult = results.getMappedResults();

注意,如果你提供一个输入类作为newAggregation方法的第一个参数,那么MongoTemplate将从该类派生输入集合的名称。否则,如果不指定输入类,则必须显式地提供输入集合的名称。如果同时提供了输入类和输入集合,则后者优先。
支持的聚合操作和阶段
MongoDB聚合框架提供了以下类型的聚合阶段和操作:

  • addFields - AddFieldsOperation
  • bucket / bucketAuto - BucketOperation / BucketAutoOperation
  • count - CountOperation
  • densify - DensifyOperation
  • facet - FacetOperation
  • geoNear - GeoNearOperation
  • graphLookup - GraphLookupOperation
  • group - GroupOperation
  • limit - LimitOperation
  • lookup - LookupOperation
  • match - MatchOperation
  • merge - MergeOperation
  • project - ProjectionOperation
  • redact - RedactOperation
  • replaceRoot - ReplaceRootOperation
  • sample - SampleOperation
  • set - SetOperation
  • setWindowFields - SetWindowFieldsOperation
  • skip - SkipOperation
  • sort / sortByCount - SortOperation / SortByCountOperation
  • unionWith - UnionWithOperation
  • unset - UnsetOperation
  • unwind - UnwindOperation

不支持的聚合阶段(如MongoDB Atlas的$search)可以通过实现AggregationOperation或Bson表示来提供。Aggregation.stage是通过提供其JSON来注册pipeline stage的快捷方式。

Aggregation.stage("""{ $search : {"near": {"path": "released","origin": { "$date": { "$numberLong": "..." } } ,"pivot": 7}}}
""");

在撰写本文时,Spring Data MongoDB为以下聚合运算符提供支持:
表1:Spring Data MongoDB当前支持的聚合运算符

分类运算符
Set Aggregation OperatorssetEquals, setIntersection, setUnion, setDifference, setIsSubset, anyElementTrue, allElementsTrue
Group/Accumulator Aggregation OperatorsaddToSet, bottom, bottomN, covariancePop, covarianceSamp, expMovingAvg, first, firstN, last, lastN max, maxN, min, minN, avg, push, sum, top, topN, count (*), median, percentile, stdDevPop, stdDevSamp
Arithmetic Aggregation Operatorsabs, acos, acosh, add (* via plus), asin, asin, atan, atan2, atanh, ceil, cos, cosh, derivative, divide, exp, floor, integral, ln, log, log10, mod, multiply, pow, round, sqrt, subtract (* via minus), sin, sinh, tan, tanh, trunc
String Aggregation Operatorsconcat, substr, toLower, toUpper, strcasecmp, indexOfBytes, indexOfCP, regexFind, regexFindAll, regexMatch, replaceAll, replaceOne, split`, strLenBytes, strLenCP, substrCP, trim, ltrim, rtim
Comparison Aggregation Operatorseq (* via is), gt, gte, lt, lte, ne
Array Aggregation OperatorsarrayElementAt, arrayToObject, concatArrays, filter, first, in, indexOfArray, isArray, last, range`, reverseArray, reduce, size, sortArray, slice, zip
Literal Operatorsliteral
Date Aggregation OperatorsdateSubstract, dateTrunc, dayOfYear, dayOfMonth, dayOfWeek, year, month, week, hour, minute, second, millisecond, dateAdd, dateDiff, dateToString, dateFromString, dateFromParts, dateToParts, isoDayOfWeek, isoWeek, isoWeekYear, tsIncrement, tsSecond
Variable Operatorsmap
Conditional Aggregation Operatorscond, ifNull, switch
Type Aggregation Operatorstype
Convert Aggregation Operatorsconvert, degreesToRadians, toBool, toDate, toDecimal, toDouble, toInt, toLong, toObjectId, toString
Object Aggregation OperatorsobjectToArray, mergeObjects, getField, setField
Script Aggregation Operatorsfunction, accumulator

*操作由Spring Data MongoDB映射或添加。
请注意,此处未列出的聚合操作目前不被Spring Data MongoDB支持。比较聚合运算符表示为Criteria表达式。

二、投影表达式Projection Expressions

投影表达式用于定义特定聚合步骤的结果字段。投影表达式可以通过Aggregation类的project方法定义,也可以通过传递String对象列表或聚合框架Fields对象来定义。可以使用and(String)方法通过fluent API使用附加字段扩展投影,并使用as(String)方法对其起别名。请注意,你还可以使用聚合框架的Fields.field静态工厂方法定义具有别名的字段,然后可以使用该方法构造新的Fields实例。在后面的聚合阶段中对投影字段的引用仅对包含字段的字段名或其别名(包括新定义的字段及其别名)有效。未包含在投影中的字段不能在以后的聚合阶段中引用。以下列表展示了投影表达式的示例:
例1:投影表达式示例

// generates {$project: {name: 1, netPrice: 1}}
project("name", "netPrice")// generates {$project: {thing1: $thing2}}
project().and("thing1").as("thing2")// generates {$project: {a: 1, b: 1, thing2: $thing1}}
project("a","b").and("thing1").as("thing2")

例2。使用投影和排序的多阶段聚合

// generates {$project: {name: 1, netPrice: 1}}, {$sort: {name: 1}}
project("name", "netPrice"), sort(ASC, "name")// generates {$project: {name: $firstname}}, {$sort: {name: 1}}
project().and("firstname").as("name"), sort(ASC, "name")// does not work
project().and("firstname").as("name"), sort(ASC, "firstname")

项目操作的更多示例可以在AggregationTests类中找到。请注意,关于投影表达式的更多细节可以在MongoDB Aggregation Framework参考文档的相应部分中找到。

三、分面分类法Faceted Classification

从3.4版本开始,MongoDB使用聚合框架支持分面分类(faceted classification)。分面分类使用语义类别(通用或特定主题),这些类别组合在一起创建完整的分类条目。流经聚合管道的Documents被分类到存储桶中。多方面分类允许对同一组输入documents进行各种聚合,而无需多次检索输入documents。

3.1 桶Buckets

Bucket操作根据指定的表达式和bucket边界将传入documents分类为组,称为buckets。Bucket操作需要分组字段或分组表达式。你可以使用Aggregate类的bucket()和bucketAuto()方法来定义它们。BucketOperation和BucketAutoOperation可以公开基于输入文档的聚合表达式的累积(accumulations)。你可以使用with…()方法和andOutput(String)方法,通过fluent API使用附加参数来扩展bucket操作。你可以使用as(String)方法对操作起别名。每个bucket在输出中表示为一个document。
BucketOperation采用一组定义的边界将传入documents分组到这些类别中。需要对边界进行排序。以下列表展示了bucket操作的一些示例:
例3:Bucket操作示例

// 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确定边界,试图将documents平均分配到指定数量的存储桶中。BucketAutoOperation可选地采用一个粒度值,该值指定要使用的首选数字序列,以确保计算的边界边以首选整数或10的幂结束。以下列表展示了bucket操作的示例:
例4:Bucket操作示例

// 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");

要在buckets中创建输出字段,bucket操作可以通过andOutput()使用AggregationExpression,通过andOutputExpression()使用SpEL表达式。
请注意,有关bucket表达式的更多详细信息可以在MongoDB Aggregation Framework参考文档的$ bucket部分和$bucketAuto部分找到。

3.2 多方面的聚合Multi-faceted Aggregation

多个聚合管道可用于创建多方面聚合,这些聚合在单个聚合阶段内表征多个维度(或方面)的数据。多面聚合提供了多个过滤器和分类,以指导数据浏览和分析。faceting的一个常见实现是,有多少在线零售商通过对产品价格、制造商、尺寸和其他因素应用过滤器来缩小搜索结果的范围。
你可以使用Aggregation类的facet()方法定义FacetOperation。你还可以使用and()方法使用多个聚合管道对其进行自定义。每个子管道在输出document中都有自己的字段,其结果存储为documents数组。
子管道可以在分组之前投影和过滤输入documents。常见的用例包括在分类之前提取日期部分或进行计算。以下列表展示了方面(facet)操作示例:
例5:Facet操作示例

// 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"))

请注意,关于facet操作的更多细节可以在MongoDB Aggregation Framework参考文档的$facet部分中找到。

3.3 按计数排序Sort By Count

按计数排序操作根据指定表达式的值对传入documents进行分组,计算每个distinct组中的documents数,并按计数对结果进行排序。当使用分面分类时,它提供了一个方便的快捷方式来应用排序。按计数排序操作需要分组字段或分组表达式。以下列表展示了按计数排序的示例:
例6:按计数排序示例

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

按计数排序操作相当于以下BSON(二进制JSON):

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

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

通过ProjectionOperation 和BucketOperation 类的andExpression方法,框架支持在投影表达式中使用SpEL表达式。该特性允许你将所需的表达式定义为SpEL表达式。在运行查询时,SpEL表达式被转换为对应的MongoDB投影表达式部分。这种安排使表达复杂的计算变得容易很多。

3.4.1 使用SpEL表达式的复杂计算

参见下面的SpEL表达式:

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

将上述表达式翻译成以下投影表达式部分:

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

你可以在聚合框架例5和聚合框架例6中看到更多上下文中的示例。你可以在SpelExpressionTransformerUnitTests中找到更多受支持的SpEL表达式构造的用法示例。
支持的SpEL转换

SpEL ExpressionMongo Expression Part
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})");

3.5 聚合框架示例Aggregation Framework Examples

本节中的示例演示了MongoDB Aggregation Framework与Spring Data MongoDB的使用模式。

3.5.1 Aggregation Framework 例1

在这个介绍性示例中,我们希望聚合一个标签列表,以从MongoDB集合(称为tags)中获得特定标签的出现次数,该集合按出现次数降序排序。此示例演示了分组、排序、投影(选择)和展开(结果拆分)的用法。

class TagCount {String tag;int n;
}
Aggregation agg = newAggregation(project("tags"),unwind("tags"),group("tags").count().as("n"),project("n").and("tag").previousOperation(),sort(DESC, "n")
);AggregationResults<TagCount> results = mongoTemplate.aggregate(agg, "tags", TagCount.class);
List<TagCount> tagCount = results.getMappedResults();

前面的列表使用以下算法:

  1. 使用newAggregation静态工厂方法创建一个新的聚合,我们将聚合操作列表传递给该方法。这些聚合操作定义了聚合的聚合管道。
  2. 使用投影操作从输入集合中选择tags字段(它是一个字符串数组)。
  3. 使用展开操作为tags数组中的每个tag生成一个新document。
  4. 使用group操作为每个tags值定义一个组,我们将为其聚合出现次数(通过使用count聚合运算符并将结果收集到一个名为n的新字段中)。
  5. 选择n字段,并为上一个group操作(因此调用previousOperation())生成的ID字段创建一个别名,名称为tag。
  6. 使用排序操作可以按tags的出现次数降序对结果列表进行排序。
  7. 调用MongoTemplate上的聚合方法,让MongoDB执行实际的聚合操作,并将创建的Aggregation作为参数。

请注意,输入集合被显式指定为聚合方法的tags参数。如果没有显式指定输入集合的名称,则它是从作为第一个参数传递给newAggregation方法的输入类派生的。

3.5.2 Aggregation Framework 例2

此示例基于MongoDB聚合框架文档中的各州最大和最小城市示例。这里添加了额外的排序,以在不同的MongoDB版本中产生稳定的结果。在这里,我们想通过使用聚合框架返回每个州按人口划分的最小和最大城市。这个例子演示了分组、排序和投影(选择)。

class ZipInfo {String id;String city;String state;@Field("pop") int population;@Field("loc") double[] location;
}class City {String name;int population;
}class ZipInfoStats {String id;String state;City biggestCity;City smallestCity;
}
TypedAggregation<ZipInfo> aggregation = newAggregation(ZipInfo.class,group("state", "city").sum("population").as("pop"),sort(ASC, "pop", "state", "city"),group("state").last("city").as("biggestCity").last("pop").as("biggestPop").first("city").as("smallestCity").first("pop").as("smallestPop"),project().and("state").previousOperation().and("biggestCity").nested(bind("name", "biggestCity").and("population", "biggestPop")).and("smallestCity").nested(bind("name", "smallestCity").and("population", "smallestPop")),sort(ASC, "state")
);AggregationResults<ZipInfoStats> result = mongoTemplate.aggregate(aggregation, ZipInfoStats.class);
ZipInfoStats firstZipInfoStats = result.getMappedResults().get(0);

3.5.3 Aggregation Framework 例3

3.5.4 Aggregation Framework 例4

3.5.5 Aggregation Framework 例5

3.5.6 Aggregation Framework 例6

3.5.7 Aggregation Framework 例7

这篇关于【Spring连载】使用Spring Data访问 MongoDB----Aggregation Framework支持的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/757499

相关文章

vue使用docxtemplater导出word

《vue使用docxtemplater导出word》docxtemplater是一种邮件合并工具,以编程方式使用并处理条件、循环,并且可以扩展以插入任何内容,下面我们来看看如何使用docxtempl... 目录docxtemplatervue使用docxtemplater导出word安装常用语法 封装导出方

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J