【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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数