【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

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置