Spring Data (数据)MongoDB(二)

时间:2022-11-21 18:12:57

Spring Data (数据)MongoDB(二)

10.6. 查询文档

您可以使用 theandclasses 来表达您的查询。它们具有反映本机MongoDB运算符名称的方法名称,例如,,,等。Theandclasses 遵循流畅的 API 样式,因此您可以将多个方法条件和查询链接在一起,同时具有易于理解的代码。为了提高可读性,静态导入允许您避免使用“new”关键字来创建和实例。您还可以使用从纯 JSON 字符串创建实例,如以下示例所示:​​Query​​​​Criteria​​​​lt​​​​lte​​​​is​​​​Query​​​​Criteria​​​​Query​​​​Criteria​​​​BasicQuery​​​​Query​

例 66。从纯 JSON 字符串创建查询实例

BasicQuery query = new BasicQuery("{ age : { $lt : 50 }, accounts.balance : { $gt : 1000.00 }}");
List<Person> result = mongoTemplate.find(query, Person.class);

Spring MongoDB还支持GeoSpatial Query(参见GeoSpatial Query部分)和Map-Reduce操作(参见Map-Reduce部分)。

10.6.1. 查询集合中的文档

前面,我们看到了如何使用 theand 方法来检索单个文档。这些方法返回单个域对象。我们还可以查询要作为域对象列表返回的文档集合。假设我们有许多对象的名称和期限作为文档存储在集合中,并且每个人都有一个带有余额的嵌入式帐户文档,我们现在可以使用以下代码运行查询:​​findOne​​​​findById​​​​MongoTemplate​​​​Person​

例 67。使用 MongoTemplate 查询文档

// ...

List<Person> result = template.query(Person.class)
.matching(query(where("age").lt(50).and("accounts.balance").gt(1000.00d)))
.all();

所有查找方法都将对象作为参数。此对象定义用于执行查询的条件和选项。条件是通过使用具有名为实例化新对象的静态工厂方法的对象来指定的。我们建议使用 static 导入 forand,以使查询更具可读性。​​Query​​​​Criteria​​​​where​​​​Criteria​​​​org.springframework.data.mongodb.core.query.Criteria.where​​​​Query.query​

查询应返回满足指定条件的对象列表。本节的其余部分列出了与MongoDB中提供的运算符相对应的theand类的方法。大多数方法返回对象,以便为 API 提供流畅的样式。​​Person​​​​Criteria​​​​Query​​​​Criteria​

条件类的方法

Theclass 提供了以下方法,所有这些方法都对应于 MongoDB 中的运算符:​​Criteria​

  • ​Criteria​​ 使用运算符创建条件(Object o)$all
  • ​Criteria​​ 将一个链接与指定的 添加到当前并返回新创建的(String key)CriteriakeyCriteria
  • ​Criteria​​ 和操作员使用运算符为所有提供的条件创建 and 查询(需要 MongoDB 2.0 或更高版本)(Criteria…​ criteria)$and
  • ​Criteria​​ 和操作员使用运算符为所有提供的条件创建 and 查询(需要 MongoDB 2.0 或更高版本)(Collection<Criteria> criteria)$and
  • ​Criteria​​ elemMatch使用运算符创建条件(Criteria c)$elemMatch
  • ​Criteria​​ 存在使用运算符创建条件(boolean b)$exists
  • ​Criteria​​ 燃气轮机使用运算符创建条件(Object o)$gt
  • ​Criteria​​ GTE使用运算符创建条件(Object o)$gte
  • ​Criteria​​ 使用 varargs 参数的运算符创建条件。(Object…​ o)$in
  • ​Criteria​​ 使用集合使用运算符创建条件(Collection<?> collection)$in
  • ​Criteria​​ 使用字段匹配 () 创建条件。如果指定的值是文档,则字段中字段的顺序和文档中的完全相等性很重要。(Object o){ key:value }
  • ​Criteria​​ 使用运算符创建条件(Object o)$lt
  • ​Criteria​​ 电信使用运算符创建条件(Object o)$lte
  • ​Criteria​​ 国防部使用运算符创建条件(Number value, Number remainder)$mod
  • ​Criteria​​ 使用运算符创建条件(Object o)$ne
  • ​Criteria​​ 使用运算符创建条件(Object…​ o)$nin
  • ​Criteria​​ norOperator使用运算符为所有提供的条件创建 nor 查询(Criteria…​ criteria)$nor
  • ​Criteria​​ norOperator使用运算符为所有提供的条件创建 nor 查询(Collection<Criteria> criteria)$nor
  • ​Criteria​​ 使用 themeta 运算符创建一个条件,该运算符直接影响紧随其后的子句()$not
  • ​Criteria​​ 或运算符使用运算符为所有提供的条件创建 or 查询(Criteria…​ criteria)$or
  • ​Criteria​​ 或运算符使用运算符为所有提供的条件创建 or 查询(Collection<Criteria> criteria)$or
  • ​Criteria​​ 正则表达式使用(String re)$regex
  • ​Criteria​​ 采样率使用运算符创建条件(double sampleRate)$sampleRate
  • ​Criteria​​ 大小使用运算符创建条件(int s)$size
  • ​Criteria​​ 类型使用运算符创建条件(int t)$type
  • ​Criteria​​ 匹配文档结构使用JSON 架构条件的运算符创建条件。只能应用于查询的顶层,而不能应用于特定于属性。使用架构的属性与嵌套字段匹配。(MongoJsonSchema schema)$jsonSchema$jsonSchemaproperties
  • ​Criteria​​ bits()是MongoDB按位查询运算符的网关。$bitsAllClear

Criteria 类还为地理空间查询提供了以下方法(请参阅地理空间查询部分以查看它们的实际操作):

  • ​Criteria​​ 使用运算符创建地理空间条件。(Circle circle)$geoWithin $center
  • ​Criteria​​ 使用操作创建地理空间条件。(Box box)$geoWithin $box
  • ​Criteria​​ 在球体内使用运算符创建地理空间条件。(Circle circle)$geoWithin $center
  • ​Criteria​​ 使用操作创建地理空间条件(Point point)$near
  • ​Criteria​​ 近球体使用操作创建地理空间条件。这仅适用于MongoDB 1.7及更高版本。(Point point)$nearSphere$center
  • ​Criteria​​ 最小距离使用操作创建地理空间条件,以便与$near一起使用。(double minDistance)$minDistance
  • ​Criteria​​ 最大距离使用操作创建地理空间条件,以便与$near一起使用。(double maxDistance)$maxDistance
查询类的方法

该类有一些其他方法,为查询提供选项:​​Query​

  • ​Query​​ add用于向查询添加其他条件的条件(Criteria criteria)
  • ​Field​​ 用于定义要包含在查询结果中的字段的字段()
  • ​Query​​ 限制用于将返回结果的大小限制为提供的限制(用于分页)(int limit)
  • ​Query​​ skip用于跳过结果中提供的文档数(用于分页)(int skip)
  • ​Query​​ 用于为结果提供排序定义(Sort sort)
选择字段

MongoDB支持投影查询返回的字段。 投影可以根据字段的名称包含和排除字段(除非显式排除,否则始终包含字段)。​​_id​

例 68。选择结果字段

public class Person {

@Id String id;
String firstname;

@Field("last_name")
String lastname;

Address address;
}

query.fields().include("lastname");

query.fields().exclude("id").include("lastname")

query.fields().include("address")

query.fields().include("address.city")

结果将包含两者和过孔。​​_id​​​​last_name​​​​{ "last_name" : 1 }​

结果将仅包含过孔。​​last_name​​​​{ "_id" : 0, "last_name" : 1 }​

结果将包含和整个对象通过。​​_id​​​​address​​​​{ "address" : 1 }​

结果将包含仅包含字段 via 的 and 和对象。​​_id​​​​address​​​​city​​​​{ "address.city" : 1 }​

从MongoDB 4.4开始,您可以使用聚合表达式进行字段投影,如下所示:

例 69。使用表达式计算结果字段

query.fields()
.project(MongoExpression.create("'$toUpper' : '$last_name'"))
.as("last_name");

query.fields()
.project(StringOperators.valueOf("lastname").toUpper())
.as("last_name");

query.fields()
.project(AggregationSpELExpression.expressionOf("toUpper(lastname)"))
.as("last_name");

使用本机表达式。使用的字段名称必须引用数据库文档中的字段名称。

指定表达式结果投影到的字段名称。生成的字段名称不会针对域模型进行映射。

使用一个。除本机名称外,字段名称将映射到域模型中使用的字段名称。​​AggregationExpression​​​​MongoExpression​

使用 SpEL 和 anto 调用表达式函数。字段名称将映射到域模型中使用的字段名称。​​AggregationExpression​

​@Query(fields="…")​​允许在基于MongoDB JSON 的查询方法和字段限制中所述的级别使用表达式字段投影。​​Repository​

10.6.2. 查询文档的方法

查询方法需要指定返回的目标类型,并且对于应对返回类型指示的集合以外的集合进行操作的查询,它们使用显式集合名称进行重载。以下查询方法允许您查找一个或多个文档:​​T​

  • findAll:查询集合中的类型对象列表。T
  • findOne:将集合上的即席查询结果映射到指定类型的对象的单个实例。
  • findById:返回给定 ID 和目标类的对象。
  • find:将集合上的即席查询结果映射到指定类型的 a。List
  • findAndRemove:将集合上的即席查询结果映射到指定类型的对象的单个实例。将返回与查询匹配的第一个文档,并从数据库中的集合中删除。

10.6.3. 查询非重复值

MongoDB提供了一个操作,通过使用来自结果文档中的查询来获取单个字段的不同值。 结果值不需要具有相同的数据类型,该功能也不限于简单类型。 对于检索,实际结果类型对于转换和键入确实很重要。以下示例演示如何查询非重复值:

例 70。检索非重复值

template.query(Person.class)  
.distinct("lastname")
.all();

查询集合。​​Person​

选择字段的不同值。字段名称根据域类型属性声明进行映射,同时考虑潜在的注释。​​lastname​​​​@Field​

检索所有非重复值作为 aof(由于未指定显式结果类型)。​​List​​​​Object​

将非重复值检索到 aofis 是最灵活的方法,因为它尝试确定域类型的属性值并将结果转换为所需的类型或映射结构。​​Collection​​​​Object​​​​Document​

有时,当所需字段的所有值都固定为某个类型时,直接获取正确键入的值会更方便,如以下示例所示:​​Collection​

例 71。检索强类型非重复值

template.query(Person.class)  
.distinct("lastname")
.as(String.class)
.all();

查询集合。​​Person​

选择字段的不同值。字段名称根据域类型属性声明进行映射,同时考虑潜在的注释。​​lastname​​​​@Field​

检索到的值将转换为所需的目标类型 — 在本例中为。如果存储的字段包含文档,也可以将值映射到更复杂的类型。​​String​

检索所有非重复值作为 aof。如果无法将类型转换为所需的目标类型,则此方法将引发 a。​​List​​​​String​​​​DataAccessException​

10.6.4. 地理空间查询

MongoDB通过使用诸如,,,和)等运算符来支持GeoSpatial 查询。特定于地理空间查询的方法可在类上找到。还有一些形状类(、和)与地理空间相关方法结合使用。​​$near​​​​$within​​​​geoWithin​​​​$nearSphere​​​​Criteria​​​​Box​​​​Circle​​​​Point​​​​Criteria​

在 MongoDB 事务中使用时,使用地理空间查询需要注意,请参阅事务内的特殊行为。

若要了解如何执行 GeoSpatial 查询,请考虑以下类(取自集成测试并依赖于丰富的类):​​Venue​​​​MappingMongoConverter​

@Document(collection="newyork")
public class Venue {

@Id
private String id;
private String name;
private double[] location;

@PersistenceConstructor
Venue(String name, double[] location) {
super();
this.name = name;
this.location = location;
}

public Venue(String name, double x, double y) {
super();
this.name = name;
this.location = new double[] { x, y };
}

public String getName() {
return name;
}

public double[] getLocation() {
return location;
}

@Override
public String toString() {
return "Venue [id=" + id + ", name=" + name + ", location="
+ Arrays.toString(location) + "]";
}
}

若要查找 中的位置,可以使用以下查询:​​Circle​

Circle circle = new Circle(-73.99171, 40.738868, 0.01);
List<Venue> venues =
template.find(new Query(Criteria.where("location").within(circle)), Venue.class);

要在球面坐标内查找场地,可以使用以下查询:​​Circle​

Circle circle = new Circle(-73.99171, 40.738868, 0.003712240453784);
List<Venue> venues =
template.find(new Query(Criteria.where("location").withinSphere(circle)), Venue.class);

若要查找 中的场地,可以使用以下查询:​​Box​

//lower-left then upper-right
Box box = new Box(new Point(-73.99756, 40.73083), new Point(-73.988135, 40.741404));
List<Venue> venues =
template.find(new Query(Criteria.where("location").within(box)), Venue.class);

要查找 a 附近的场地,您可以使用以下查询:​​Point​

Point point = new Point(-73.99171, 40.738868);
List<Venue> venues =
template.find(new Query(Criteria.where("location").near(point).maxDistance(0.01)), Venue.class);
Point point = new Point(-73.99171, 40.738868);
List<Venue> venues =
template.find(new Query(Criteria.where("location").near(point).minDistance(0.01).maxDistance(100)), Venue.class);

要查找球面坐标附近的场地,可以使用以下查询:​​Point​

Point point = new Point(-73.99171, 40.738868);
List<Venue> venues =
template.find(new Query(
Criteria.where("location").nearSphere(point).maxDistance(0.003712240453784)),
Venue.class);
地理邻近查询


在 2.2 中更改!
​MongoDB 4.2​删除了对以前用于运行的命令的支持。​​geoNear​​​​NearQuery​



Spring Data MongoDB 2.2使用聚合​而不是命令来运行一个。​​MongoOperations#geoNear​​​​$geoNear​​​​geoNear​​​​NearQuery​



以前在包装器类型中返回的计算距离(使用 geoNear 命令时)现在嵌入在包装器类型中 到生成的文档中。 如果给定的域类型已包含具有该名称的属性,则计算的距离 使用可能随机的后缀命名。​​dis​​​​calculated-distance​



目标类型可能包含以返回的距离命名的属性,以(另外)将其直接读回域类型,如下所示。




GeoResults<VenueWithDisField> = template.query(Venue.class) 
.as(VenueWithDisField.class)
.near(NearQuery.near(new GeoJsonPoint(-73.99, 40.73), KILOMETERS))
.all();



用于标识目标集合和潜在查询映射的域类型。

目标类型包含类型的字段。​​dis​​​​Number​

MongoDB支持在数据库中查询地理位置,同时计算与给定原点的距离。使用邻近地理位置查询,您可以表达诸如“查找周围 10 英里内的所有餐馆”之类的查询。为此,请提供采用 aas 参数的方法(以及已经熟悉的实体类型和集合),如以下示例所示:​​MongoOperations​​​​geoNear(…)​​​​NearQuery​

Point location = new Point(-73.99171, 40.738868);
NearQuery query = NearQuery.near(location).maxDistance(new Distance(10, Metrics.MILES));

GeoResults<Restaurant> = operations.geoNear(query, Restaurant.class);

我们使用构建器 API 来设置一个查询,以将给定区域周围的所有实例返回到 10 英里。这里使用的 theenum 实际上实现了一个接口,以便其他指标也可以插入到远处。AI 由乘数支持,可将给定指标的距离值转换为本机距离。此处显示的示例将 10 视为英里。使用其中一个内置指标(英里和公里)会自动触发要在查询上设置的球形标志。如果要避免这种情况,请将普通值传递到。有关更多信息,请参阅JavaDocofand。​​NearQuery​​​​Restaurant​​​​Point​​​​Metrics​​​​Metric​​​​double​​​​maxDistance(…)​​​​NearQuery​​​​Distance​

地理邻近操作返回封装实例的包装器对象。换行允许访问所有结果的平均距离。单个对象携带找到的实体及其与原点的距离。​​GeoResults​​​​GeoResult​​​​GeoResults​​​​GeoResult​

10.6.5. 地理 JSON 支持

MongoDB支持GeoJSON和地理空间数据的简单(传统)坐标对。这些格式既可用于存储数据,也可用于查询数据。请参阅MongoDB关于GeoJSON支持的手册,以了解要求和限制。

域类中的地理 JSON 类型

在域类中使用GeoJSON类型非常简单。包包含诸如,和其他类型。这些类型是现有类型的扩展。以下示例使用:​​org.springframework.data.mongodb.core.geo​​​​GeoJsonPoint​​​​GeoJsonPolygon​​​​org.springframework.data.geo​​​​GeoJsonPoint​

public class Store {

String id;

/**
* location is stored in GeoJSON format.
* {
* "type" : "Point",
* "coordinates" : [ x, y ]
* }
*/
GeoJsonPoint location;
}


如果 GeoJSON 对象表示纬度和经对,则经度首先是纬度
因此被视为经度纬度。​​coordinates​​​​GeoJsonPoint​​​​getX()​​​​getY()​


存储库查询方法中的地理 JSON 类型

使用 GeoJSON 类型作为存储库查询参数会在创建查询时强制使用运算符,如以下示例所示:​​$geometry​

public interface StoreRepository extends CrudRepository<Store, String> {

List<Store> findByLocationWithin(Polygon polygon);

}

/*
* {
* "location": {
* "$geoWithin": {
* "$geometry": {
* "type": "Polygon",
* "coordinates": [
* [
* [-73.992514,40.758934],
* [-73.961138,40.760348],
* [-73.991658,40.730006],
* [-73.992514,40.758934]
* ]
* ]
* }
* }
* }
* }
*/
repo.findByLocationWithin(
new GeoJsonPolygon(
new Point(-73.992514, 40.758934),
new Point(-73.961138, 40.760348),
new Point(-73.991658, 40.730006),
new Point(-73.992514, 40.758934)));

/*
* {
* "location" : {
* "$geoWithin" : {
* "$polygon" : [ [-73.992514,40.758934] , [-73.961138,40.760348] , [-73.991658,40.730006] ]
* }
* }
* }
*/
repo.findByLocationWithin(
new Polygon(
new Point(-73.992514, 40.758934),
new Point(-73.961138, 40.760348),
new Point(-73.991658, 40.730006)));

使用公共类型的存储库方法定义允许使用 GeoJSON 和旧格式调用它。

使用 GeoJSON 类型来使用运算符。​​$geometry​

请注意,GeoJSON 多边形需要定义一个闭合环。

使用旧格式运算符。​​$polygon​

指标和距离计算

然后MongoDB运算符允许使用GeoJSON Point或遗留坐标对。​​$geoNear​

NearQuery.near(new Point(-73.99171, 40.738868))
{
"$geoNear": {
//...
"near": [-73.99171, 40.738868]
}
}
NearQuery.near(new GeoJsonPoint(-73.99171, 40.738868))
{
"$geoNear": {
//...
"near": { "type": "Point", "coordinates": [-73.99171, 40.738868] }
}
}

尽管语法不同,但无论集合中的目标文档采用何种格式,服务器都可以接受两者 正在使用。


距离计算存在巨大差异。使用旧格式操作 在类似地球的球体上,而GeoJSON格式使用

为避免严重头痛,请确保将测量单位设置为所需的计量单位,以确保 要正确计算的距离。​​Metric​

换句话说:

假设您有 5 个文档,如下所示:

{
"_id" : ObjectId("5c10f3735d38908db52796a5"),
"name" : "Penn Station",
"location" : { "type" : "Point", "coordinates" : [ -73.99408, 40.75057 ] }
}
{
"_id" : ObjectId("5c10f3735d38908db52796a6"),
"name" : "10gen Office",
"location" : { "type" : "Point", "coordinates" : [ -73.99171, 40.738868 ] }
}
{
"_id" : ObjectId("5c10f3735d38908db52796a9"),
"name" : "City Bakery ",
"location" : { "type" : "Point", "coordinates" : [ -73.992491, 40.738673 ] }
}
{
"_id" : ObjectId("5c10f3735d38908db52796aa"),
"name" : "Splash Bar",
"location" : { "type" : "Point", "coordinates" : [ -73.992491, 40.738673 ] }
}
{
"_id" : ObjectId("5c10f3735d38908db52796ab"),
"name" : "Momofuku Milk Bar",
"location" : { "type" : "Point", "coordinates" : [ -73.985839, 40.731698 ] }
}

获取 400 米半径内的所有文档如下所示 GeoJSON:​​[-73.99171, 40.738868]​

例 72。GeoNear with GeoJSON

{
"$geoNear": {
"maxDistance": 400,
"num": 10,
"near": { type: "Point", coordinates: [-73.99171, 40.738868] },
"spherical":true,
"key": "location",
"distanceField": "distance"
}
}

返回以下 3 个文档:

{
"_id" : ObjectId("5c10f3735d38908db52796a6"),
"name" : "10gen Office",
"location" : { "type" : "Point", "coordinates" : [ -73.99171, 40.738868 ] }
"distance" : 0.0
}
{
"_id" : ObjectId("5c10f3735d38908db52796a9"),
"name" : "City Bakery ",
"location" : { "type" : "Point", "coordinates" : [ -73.992491, 40.738673 ] }
"distance" : 69.3582262492474
}
{
"_id" : ObjectId("5c10f3735d38908db52796aa"),
"name" : "Splash Bar",
"location" : { "type" : "Point", "coordinates" : [ -73.992491, 40.738673 ] }
"distance" : 69.3582262492474
}


到中心点的最大距离(以米为单位)。


GeoJSON 始终在球体上运行。


与中心点的距离,单位为米

现在,当使用传统坐标对时,如前所述,对弧度进行操作。所以我们使用命令。确保正确设置距离乘数。​​Metrics#KILOMETERS when constructing the `$geoNear​​​​Metric​

例 73。具有传统坐标对的地理邻近

{
"$geoNear": {
"maxDistance": 0.0000627142377,
"distanceMultiplier": 6378.137,
"num": 10,
"near": [-73.99171, 40.738868],
"spherical":true,
"key": "location",
"distanceField": "distance"
}
}

返回 3 个文档,就像 GeoJSON 变体一样:

{
"_id" : ObjectId("5c10f3735d38908db52796a6"),
"name" : "10gen Office",
"location" : { "type" : "Point", "coordinates" : [ -73.99171, 40.738868 ] }
"distance" : 0.0
}
{
"_id" : ObjectId("5c10f3735d38908db52796a9"),
"name" : "City Bakery ",
"location" : { "type" : "Point", "coordinates" : [ -73.992491, 40.738673 ] }
"distance" : 0.0693586286032982
}
{
"_id" : ObjectId("5c10f3735d38908db52796aa"),
"name" : "Splash Bar",
"location" : { "type" : "Point", "coordinates" : [ -73.992491, 40.738673 ] }
"distance" : 0.0693586286032982
}

弧度为单位的距中心点的最大距离。

距离乘数,因此我们得到公里作为结果距离。

确保我们在2d_sphere索引上运行。

与中心点的距离(以公里为单位) - 乘以 1000 以匹配 GeoJSON 变体的

GeoJSON Jackson Modules

通过使用Web支持,Spring Data注册了额外的Jacksons,用于对常见的Spring Data域类型进行反序列化。 请参考Spring 数据杰克逊模块部分,了解有关此功能的基础架构设置的更多信息。​​Modules​​​​ObjectMapper​

MongoDB模块还通过它注册了以下GeoJSON类型。​​JsonDeserializer​​​​GeoJsonConfiguration​​​​GeoJsonModule​

org.springframework.data.mongodb.core.geo.GeoJsonPoint
org.springframework.data.mongodb.core.geo.GeoJsonMultiPoint
org.springframework.data.mongodb.core.geo.GeoJsonLineString
org.springframework.data.mongodb.core.geo.GeoJsonMultiLineString
org.springframework.data.mongodb.core.geo.GeoJsonPolygon
org.springframework.data.mongodb.core.geo.GeoJsonMultiPolygon


唯一的寄存器!
要配备一组对称的,您需要手动配置这些或提供自定义配置暴露为春豆。​​​GeoJsonModule​​​​JsonDeserializer​​​​ObjectMapper​​​​JsonSerializer​​​​ObjectMapper​​​​SpringDataJacksonModules​​​​GeoJsonModule.serializers()​




class GeoJsonConfiguration implements SpringDataJacksonModules {

@Bean
public Module geoJsonSerializers() {
return GeoJsonModule.serializers();
}
}




默认情况下,下一个主要版本 () 将为 GeoJSON 类型注册 和 。​​4.0​​​​JsonDeserializer​​​​JsonSerializer​


10.6.6. 全文查询

从 MongoDB 的 2.6 版开始,您可以使用运算符运行全文查询。特定于全文查询的方法和操作在 and 中可用。执行全文搜索时,请参阅MongoDB 参考,了解其行为和限制。​​$text​​​​TextQuery​​​​TextCriteria​

全文搜索

在实际使用全文搜索之前,必须正确设置搜索索引。有关如何创建索引结构的更多详细信息,请参阅文本索引。下面的示例演示如何设置全文搜索:

db.foo.createIndex(
{
title : "text",
content : "text"
},
{
weights : {
title : 3
}
}
)

可以按如下方式定义和运行搜索查询:​​coffee cake​

例 74。全文查询

Query query = TextQuery
.queryText(new TextCriteria().matchingAny("coffee", "cake"));

List<Document> page = template.find(query, Document.class);

根据用途按相关性对结果进行排序。​​weights​​​​TextQuery.sortByScore​

例 75。全文查询 - 按分数排序

Query query = TextQuery
.queryText(new TextCriteria().matchingAny("coffee", "cake"))
.sortByScore()
.includeScore();

List<Document> page = template.find(query, Document.class);

使用 score 属性按触发的相关性对结果进行排序。​​.sort({'score': {'$meta': 'textScore'}})​

用于在结果中包含计算的相关性。​​TextQuery.includeScore()​​​​Document​

您可以通过使用 withor 前缀来排除搜索词,如以下示例所示(请注意,这两行具有相同的效果,因此是多余的):​​-​​​​notMatching​

// search for 'coffee' and not 'cake'
TextQuery.queryText(new TextCriteria().matching("coffee").matching("-cake"));
TextQuery.queryText(new TextCriteria().matching("coffee").notMatching("cake"));

​TextCriteria.matching​​按原样采用提供的期限。因此,您可以通过将短语放在双引号之间来定义短语(例如,或使用 by以下示例显示了定义短语的两种方法:​​\"coffee cake\")​​​​TextCriteria.phrase.​

// search for phrase 'coffee cake'
TextQuery.queryText(new TextCriteria().matching("\"coffee cake\""));
TextQuery.queryText(new TextCriteria().phrase("coffee cake"));

您可以使用相应的方法设置标志。请注意,这两个可选标志已在MongoDB 3.2中引入,除非显式设置,否则不会包含在查询中。​​$caseSensitive​​​​$diacriticSensitive​​​​TextCriteria​

10.6.7. 排序规则

从版本3.4开始,MongoDB支持用于收集和索引创建的排序规则以及各种查询操作。排序规则基于ICU 排序规则定义字符串比较规则。归类文档由封装在其中的各种属性组成,如下面的清单所示:​​Collation​

Collation collation = Collation.of("fr")         

.strength(ComparisonLevel.secondary()
.includeCase())

.numericOrderingEnabled()

.alternate(Alternate.shifted().punct())

.forwardDiacriticSort()

.normalizationEnabled();

​Collation​​​需要创建区域设置。这可以是区域设置的字符串表示形式,a(考虑语言、国家/地区和变体)或 a。区域设置是创建所必需的。​​Locale​​​​CollationLocale​

排序规则强度定义表示字符之间差异的比较级别。您可以配置各种选项(区分大小写、大小写排序等),具体取决于所选强度。

指定是将数字字符串作为数字还是字符串进行比较。

指定排序规则是否应将空格和标点符号视为用于比较的基字符。

指定带有音调符号的字符串是否从字符串的后面排序,例如使用某些法语词典排序。

指定是否检查文本是否需要规范化以及是否执行规范化。

排序规则可用于创建集合和索引。如果创建指定排序规则的集合,则 排序规则应用于索引创建和查询,除非您指定了不同的排序规则。排序规则对 整个操作,不能按字段指定。

与其他元数据一样,排序规则可以通过注释的属性从域类型派生,并将在运行查询、创建集合或索引时直接应用。​​collation​​​​@Document​

当MongoDB在第一次交互时自动创建集合时,将不会使用带注释的排序规则。这将 需要额外的商店交互,延迟整个过程。请在这些情况下使用。​​MongoOperations.createCollection​

Collation french = Collation.of("fr");
Collation german = Collation.of("de");

template.createCollection(Person.class, CollectionOptions.just(collation));

template.indexOps(Person.class).ensureIndex(new Index("name", Direction.ASC).collation(german));

MongoDB使用简单的二进制比较,如果没有指定排序规则()。​​Collation.simple()​

将排序规则与收集操作结合使用是在查询或操作选项中指定实例的问题,如以下两个示例所示:​​Collation​

例 76。使用排序规则​​find​

Collation collation = Collation.of("de");

Query query = new Query(Criteria.where("firstName").is("Amél")).collation(collation);

List<Person> results = template.find(query, Person.class);

例 77。使用排序规则​​aggregate​

Collation collation = Collation.of("de");

AggregationOptions options = AggregationOptions.builder().collation(collation).build();

Aggregation aggregation = newAggregation(
project("tags"),
unwind("tags"),
group("tags")
.count().as("count")
).withOptions(options);

AggregationResults<TagCount> results = template.aggregate(aggregation, "tags", TagCount.class);


仅当用于操作的排序规则与索引排序规则匹配时,才使用索引。

​MongoDB 存储库​​支持通过注释的属性。​​Collations​​​​collation​​​​@Query​

例 78。存储库的排序规则支持

public interface PersonRepository extends MongoRepository<Person, String> {

@Query(collation = "en_US")
List<Person> findByFirstname(String firstname);

@Query(collation = "{ 'locale' : 'en_US' }")
List<Person> findPersonByFirstname(String firstname);

@Query(collation = "?1")
List<Person> findByFirstname(String firstname, Object collation);

@Query(collation = "{ 'locale' : '?1' }")
List<Person> findByFirstname(String firstname, String collation);

List<Person> findByFirstname(String firstname, Collation collation);

@Query(collation = "{ 'locale' : 'en_US' }")
List<Person> findByFirstname(String firstname, @Nullable Collation collation);
}

静态排序规则定义生成。​​{ 'locale' : 'en_US' }​

静态排序规则定义生成。​​{ 'locale' : 'en_US' }​

动态排序规则取决于第二个方法参数。允许的类型包括(例如。“en_US”),(例如 Locacle.US) 和(例如,新文档(“区域设置”、“en_US”))​​String​​​​Locacle​​​​Document​

动态排序规则取决于第二个方法参数。

将方法参数应用于查询。​​Collation​

方法参数将覆盖默认值,如果不是空。​​Collation​​​​collation​​​​@Query​

如果您为存储库查找器方法启用了自动索引创建,则可能是静态排序规则定义, 如 (1) 和 (2) 所示,将在创建索引时包含在内。

最具体的规则可能定义了其他规则。这意味着方法参数超过查询方法注释而不是域类型注释。​​Collation​

为了简化整个代码库中排序规则属性的使用,还可以使用注释,它用作上述注释的元注释。 相同的规则和位置适用,此外,直接使用 将取代在 上定义的任何排序规则值和其他批注。 这意味着,如果一个排序规则被声明为 via,并且另外通过,那么 fromis 就会被选中。​​@Collation​​​​@Collation​​​​@Query​​​​@Query​​​​@Collation​​​​@Collation​

例 79。用​​@Collation​

@Collation("en_US") 
class Game {
// ...
}

interface GameRepository extends Repository<Game, String> {

@Collation("en_GB")
List<Game> findByTitle(String title);

@Collation("de_AT")
@Query(collation="en_GB")
List<Game> findByDescriptionContaining(String keyword);
}

而不是。​​@Document(collation=…)​

而不是。​​@Query(collation=…)​

偏爱超过元用法。​​@Collation​

JSON 架构

从版本3.6开始,MongoDB支持根据提供的JSON模式验证文档的集合。 创建集合时,可以定义架构本身以及验证操作和级别,如以下示例所示:

例 80。示例 JSON 架构

{
"type": "object",

"required": [ "firstname", "lastname" ],

"properties": {

"firstname": {
"type": "string",
"enum": [ "luke", "han" ]
},
"address": {
"type": "object",
"properties": {
"postCode": { "type": "string", "minLength": 4, "maxLength": 5 }
}
}
}
}

JSON 架构文档始终从根目录描述整个文档。架构是架构对象本身,可以包含 描述属性和子文档的嵌入式架构对象。

​required​​​是一个属性,用于描述文档中需要哪些属性。可以选择指定它,以及其他 架构约束。请参阅MongoDB关于可用关键字的文档。

​properties​​​与描述 AnType 的架构对象相关。它包含特定于属性的架构约束。​​object​

​firstname​​​指定文档中字段的约束。在这里,它是一个基于字符串的元素声明 可能的字段值。​​firsname​​​​properties​

​address​​​是定义其字段中值的架构的子文档。​​postCode​

您可以通过指定模式文档(即使用 API 解析或构建文档对象)或使用 Spring Data 的 JSON 模式实用程序 in.is 所有 JSON 模式相关操作的入口点来构建它,从而提供模式。以下示例演示如何使用创建 JSON 架构:​​Document​​​​org.springframework.data.mongodb.core.schema​​​​MongoJsonSchema​​​​MongoJsonSchema.builder()​

例 81。创建 JSON 架构

MongoJsonSchema.builder()                                                    
.required("lastname")

.properties(
required(string("firstname").possibleValues("luke", "han")),

object("address")
.properties(string("postCode").minLength(4).maxLength(5)))

.build();

获取架构生成器以使用流畅的 API 配置架构。

直接配置所需属性(如此处所示)或使用更多详细信息(如 3 所示)。

配置所需的字符串类型字段,仅允许和值。属性可以是类型化的,也可以是非类型化的。使用静态导入 of 使语法稍微紧凑一些,并获取入口点,例如。​​firstname​​​​luke​​​​han​​​​JsonSchemaProperty​​​​string(…)​

生成架构对象。使用架构创建集合或查询文档。

已经有一些预定义和强类型的架构对象(和)可用 通过网关接口上的静态方法。 但是,您可能需要构建自定义属性验证规则,这些规则可以通过生成器 API 创建,如以下示例所示:​​JsonSchemaObject​​​​JsonSchemaProperty​

// "birthdate" : { "bsonType": "date" }
JsonSchemaProperty.named("birthdate").ofType(Type.dateType());

// "birthdate" : { "bsonType": "date", "description", "Must be a date" }
JsonSchemaProperty.named("birthdate").with(JsonSchemaObject.of(Type.dateType()).description("Must be a date"));

​CollectionOptions​​提供集合的架构支持的入口点,如以下示例所示:

例 82。创建集合使用​​$jsonSchema​

MongoJsonSchema schema = MongoJsonSchema.builder().required("firstname", "lastname").build();

template.createCollection(Person.class, CollectionOptions.empty().schema(schema));
生成架构

设置架构可能是一项耗时的任务,我们鼓励每个决定这样做的人真正花时间。 这很重要,架构更改可能很困难。 然而,有时人们可能不想犹豫不决,这就是发挥作用的地方。​​JsonSchemaCreator​

​JsonSchemaCreator​​其默认实现会生成映射基础结构提供的域类型元数据。 这意味着,系统会考虑带注释的属性以及潜在的自定义转化。​​MongoJsonSchema​

例 83。从域类型生成 JSON 架构

public class Person {

private final String firstname;
private final int age;
private Species species;
private Address address;
private @Field(fieldType=SCRIPT) String theForce;
private @Transient Boolean useTheForce;

public Person(String firstname, int age) {

this.firstname = firstname;
this.age = age;
}

// gettter / setter omitted
}

MongoJsonSchema schema = MongoJsonSchemaCreator.create(mongoOperations.getConverter())
.createSchemaFor(Person.class);

template.createCollection(Person.class, CollectionOptions.empty().schema(schema));
{
'type' : 'object',
'required' : ['age'],
'properties' : {
'firstname' : { 'type' : 'string' },
'age' : { 'bsonType' : 'int' }
'species' : {
'type' : 'string',
'enum' : ['HUMAN', 'WOOKIE', 'UNKNOWN']
}
'address' : {
'type' : 'object'
'properties' : {
'postCode' : { 'type': 'string' }
}
},
'theForce' : { 'type' : 'javascript'}
}
}

简单对象属性被视为常规属性。

基元类型被视为必需属性

枚举仅限于可能的值。

检查对象类型属性并将其表示为嵌套文档。

​String​​​由转换器转换为的类型属性。​​Code​

​@Transient​​生成架构时省略属性。

​_id​​​使用可转换为 Likeo 的类型的属性映射到除非通过注释提供更具体的信息。​​ObjectId​​​​String​​​​{ type : 'object' }​​​​@MongoId​

表 2.专用架构生成规则

爪哇岛

架构类型

笔记

​Object​

​type : object​

如果元数据可用。​​properties​

​Collection​

​type : array​

-

​Map​

​type : object​

-

​Enum​

​type : string​

包含保存可能的枚举值的属性。​​enum​

​array​

​type : array​

简单类型数组,除非它是​​byte[]​

​byte[]​

​bsonType : binData​

-

上面的示例演示了如何从非常精确的类型化源派生架构。 在域模型中使用多态元素可能会导致 和泛型类型的架构表示不准确,泛型类型可能表示为没有进一步的规范。允许定义其他详细信息,例如呈现架构时应考虑的嵌套文档类型。​​Object​​​​<T>​​​​{ type : 'object' }​​​​MongoJsonSchemaCreator.property(…)​

例 84。为属性指定其他类型

class Root {
Object value;
}

class A {
String aValue;
}

class B {
String bValue;
}
MongoJsonSchemaCreator.create()
.property("value").withTypes(A.class, B.class)
{
'type' : 'object',
'properties' : {
'value' : {
'type' : 'object',
'properties' : {
'aValue' : { 'type' : 'string' },
'bValue' : { 'type' : 'string' }
}
}
}
}

给定类型的属性将合并到一个元素中。

MongoDB的无模式方法允许将不同结构的文档存储在一个集合中。 这些可以建模为具有公共基类。 无论选择哪种方法,都可以帮助避免将多个架构合并为一个模式的需要。​​MongoJsonSchemaCreator.merge(…)​

例 85。将多个架构合并到单个架构定义中

abstract class Root {
String rootValue;
}

class A extends Root {
String aValue;
}

class B extends Root {
String bValue;
}

MongoJsonSchemaCreator.mergedSchemaFor(A.class, B.class)
{
'type' : 'object',
'properties' : {
'rootValue' : { 'type' : 'string' },
'aValue' : { 'type' : 'string' },
'bValue' : { 'type' : 'string' }
}
}
}

给定类型的属性(及其继承的属性)组合到一个架构中。


具有相同名称的属性需要引用相同的 JSON 架构才能组合。 下面的示例演示由于数据类型不匹配而无法自动合并的定义。 在这种情况下,必须提供给。​​ConflictResolutionFunction​​​​MongoJsonSchemaCreator​




class A extends Root {
String value;
}

class B extends Root {
Integer value;
}



查询集合以匹配 JSON 架构

可以使用架构在任何集合中查询与 JSON 架构定义的给定结构匹配的文档,如以下示例所示:

例 86。查询与 匹配的文档​​$jsonSchema​

MongoJsonSchema schema = MongoJsonSchema.builder().required("firstname", "lastname").build();

template.find(query(matchingDocumentStructure(schema)), Person.class);
加密字段

MongoDB 4.2字段​​级​​加密允许直接加密单个属性。

设置 JSON 架构时,可以将属性包装在加密属性中,如下例所示。

例 87。通过 JSON 架构进行客户端字段级加密

MongoJsonSchema schema = MongoJsonSchema.builder()
.properties(
encrypted(string("ssn"))
.algorithm("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic")
.keyId("*key0_id")
).build();

无需手动定义加密字段,而是可以利用注释,如下面的代码片段所示。​​@Encrypted​

例 88。通过 JSON 架构进行客户端字段级加密

@Document
@Encrypted(keyId = "xKVup8B1Q+CkHaVRx+qa+g==", algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Random")
static class Patient {

@Id String id;
String name;

@Encrypted
String bloodType;

@Encrypted(algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic")
Integer ssn;
}

将为其设置的默认加密设置。​​encryptMetadata​

使用默认加密设置的加密字段。

覆盖默认加密算法的加密字段。


TheAnnotation 支持通过 SpEL 表达式解析 keyId。 为此,需要并且必须提供其他环境元数据(通过)。​​@Encrypted​​​​MappingContext​




@Document
@Encrypted(keyId = "#{mongocrypt.keyId(#target)}")
static class Patient {

@Id String id;
String name;

@Encrypted(algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Random")
String bloodType;

@Encrypted(algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic")
Integer ssn;
}

MongoJsonSchemaCreator schemaCreator = MongoJsonSchemaCreator.create(mappingContext);
MongoJsonSchema patientSchema = schemaCreator
.filter(MongoJsonSchemaCreator.encryptedOnly())
.createSchemaFor(Patient.class);




该函数是通过下面代码片段中显示的 anas 定义的。 提供自定义扩展提供了最灵活的计算 keyId 的方法。​​mongocrypt.keyId​​​​EvaluationContextExtension​




public class EncryptionExtension implements EvaluationContextExtension {

@Override
public String getExtensionId() {
return "mongocrypt";
}

@Override
public Map<String, Function> getFunctions() {
return Collections.singletonMap("keyId", new Function(getMethod("computeKeyId", String.class), this));
}

public String computeKeyId(String target) {
// ... lookup via target element name
}
}




To combine derived encryption settings with in a Spring Boot application use the .​​AutoEncryptionSettings​​​​MongoClientSettingsBuilderCustomizer​




@Bean
MongoClientSettingsBuilderCustomizer customizer(MappingContext mappingContext) {
return (builder) -> {

// ... keyVaultCollection, kmsProvider, ...

MongoJsonSchemaCreator schemaCreator = MongoJsonSchemaCreator.create(mappingContext);
MongoJsonSchema patientSchema = schemaCreator
.filter(MongoJsonSchemaCreator.encryptedOnly())
.createSchemaFor(Patient.class);

AutoEncryptionSettings autoEncryptionSettings = AutoEncryptionSettings.builder()
.keyVaultNamespace(keyVaultCollection)
.kmsProviders(kmsProviders)
.extraOptions(extraOpts)
.schemaMap(Collections.singletonMap("db.patient", patientSchema.schemaDocument().toBsonDocument()))
.build();

builder.autoEncryptionSettings(autoEncryptionSettings);
};
}



确保将驱动程序设置为使用客户端加密。MongoDB不支持对所有字段类型进行加密。特定数据类型需要确定性加密以保留相等比较功能。​​com.mongodb.AutoEncryptionSettings​

JSON 架构类型

下表显示了支持的 JSON 架构类型:

表 3.支持的 JSON 架构类型

架构类型

爪哇类型

架构属性

​untyped​

-

​description​​​生成​​description​​​​enum​​​​allOf​​​​anyOf​​​​oneOf​​​​not​

​object​

​Object​

​required​​​, , , , , ​​additionalProperties​​​​properties​​​​minProperties​​​​maxProperties​​​​patternProperties​

​array​

任何数组,除了​​byte[]​

​uniqueItems​​​, , , , ​​additionalItems​​​​items​​​​minItems​​​​maxItems​

​string​

​String​

​minLength​​​, , ​​maxLentgth​​​​pattern​

​int​

​int​​​, ​​Integer​

​multipleOf​​​, , , , ​​minimum​​​​exclusiveMinimum​​​​maximum​​​​exclusiveMaximum​

​long​

​long​​​, ​​Long​

​multipleOf​​​, , , , ​​minimum​​​​exclusiveMinimum​​​​maximum​​​​exclusiveMaximum​

​double​

​float​​​, , , ​​Float​​​​double​​​​Double​

​multipleOf​​​, , , , ​​minimum​​​​exclusiveMinimum​​​​maximum​​​​exclusiveMaximum​

​decimal​

​BigDecimal​

​multipleOf​​​, , , , ​​minimum​​​​exclusiveMinimum​​​​maximum​​​​exclusiveMaximum​

​number​

​Number​

​multipleOf​​​, , , , ​​minimum​​​​exclusiveMinimum​​​​maximum​​​​exclusiveMaximum​

​binData​

​byte[]​

(无)

​boolean​

​boolean​​​, ​​Boolean​

(无)

​null​

​null​

(无)

​objectId​

​ObjectId​

(无)

​date​

​java.util.Date​

(无)

​timestamp​

​BsonTimestamp​

(无)

​regex​

​java.util.regex.Pattern​

(无)


​untyped​​​是由所有类型化架构类型继承的泛型类型。它为类型化架构类型提供所有架构属性。​​untyped​

有关详细信息,请参阅​​$jsonSchema​​。

10.6.8. 流畅的模板接口

当涉及到与MongoDB进行更多低级交互时,接口是核心组件之一。它提供了广泛的方法,涵盖了从集合创建、索引创建和 CRUD 操作到更高级的功能(如 Map-Reduce 和聚合)的需求。 您可以找到每种方法的多个重载。它们中的大多数涵盖 API 的可选或可为空的部分。​​MongoOperations​

​FluentMongoOperations​​为常用方法提供了更窄的接口,并提供了更具可读性、更流畅的 API。 入口点(,,,和其他)遵循基于要运行的操作的自然命名架构。从入口点继续前进,API 旨在仅提供依赖于上下文的方法,这些方法会导致调用实际对应项的终止方法 — 在以下示例中为该方法:​​MongoOperations​​​​insert(…)​​​​find(…)​​​​update(…)​​​​MongoOperations​​​​all​

List<SWCharacter> all = ops.find(SWCharacter.class)
.inCollection("star-wars")
.all();

如果使用类名作为集合名,则跳过此步骤,如果定义集合withor,这很好。​​SWCharacter​​​​@Document​

有时,MongoDB中的集合包含不同类型的实体,例如在集合中。 若要对 and 返回值映射使用不同的类型,可以使用不同的方式映射结果,如以下示例所示:​​Jedi​​​​SWCharacters​​​​Query​​​​as(Class<?> targetType)​

List<Jedi> all = ops.find(SWCharacter.class)    
.as(Jedi.class)
.matching(query(where("jedi").is(true)))
.all();

查询字段将针对类型进行映射。​​SWCharacter​

生成的文档将映射到。​​Jedi​

您可以通过通过以下方式提供目标类型直接将投影​应用于结果文档。​​as(Class<?>)​

使用投影允许通过将实际响应限制为所需字段来优化结果映射 按投影目标类型。只要其本身不包含任何字段限制并且 目标类型是封闭接口或 DTO 投影。​​MongoTemplate​​​​Query​

投影不得应用于DBRefs。

您可以通过终止方法在检索单个实体和检索多个实体之间切换:,,, or。​​List​​​​Stream​​​​first()​​​​one()​​​​all()​​​​stream()​

编写地理空间查询时,终止方法的数量将更改为仅包括在MongoDB中运行命令的有效方法(获取实体作为awithin),如以下示例所示:​​near(NearQuery)​​​​geoNear​​​​GeoResult​​​​GeoResults​

GeoResults<Jedi> results = mongoOps.query(SWCharacter.class)
.as(Jedi.class)
.near(alderaan) // NearQuery.near(-73.9667, 40.78).maxDis…
.all();

10.6.9. Kotlin 的类型安全查询

Kotlin 通过其语言语法和扩展系统拥抱特定于领域的语言创建。 Spring Data MongoDB 附带了一个 Kotlin 扩展,用于使用Kotlin 属性引用来构建类型安全的查询。 使用此扩展的查询通常受益于改进的可读性。 大多数关键字都有一个匹配的 Kotlin 扩展名,例如 and。​​Criteria​​​​Criteria​​​​inValues​​​​regex​

请考虑以下示例,说明类型安全查询:

mongoOperations.find<Book>(
Query(Book::title isEqualTo "Moby-Dick")
)

mongoOperations.find<Book>(
Query(titlePredicate = Book::title exists true)
)

mongoOperations.find<Book>(
Query(
Criteria().andOperator(
Book::price gt 5,
Book::price lt 10
))
)

// Binary operators
mongoOperations.find<BinaryMessage>(
Query(BinaryMessage::payload bits { allClear(0b101) })
)

// Nested Properties (i.e. refer to "book.author")
mongoOperations.find<Book>(
Query(Book::author / Author::name regex "^H")
)

​isEqualTo()​​​是一个带有接收器类型的中缀扩展函数返回。​​KProperty<T>​​​​Criteria​

对于按位运算符,传递一个 lambda 参数,您可以在其中调用 的方法之一。​​Criteria.BitwiseCriteriaOperators​

若要构造嵌套属性,请使用字符(重载运算符)。​​/​​​​div​

10.6.10. 其他查询选项

MongoDB提供了多种将元信息(如注释或批大小)应用于查询的方法。使用接口 直接有几种方法可用于这些选项。​​Query​

Query query = query(where("firstname").is("luke"))
.comment("find luke")
.batchSize(100)

注释传播到 MongoDB 配置文件日志。

每个响应批处理中要返回的文档数。

在存储库级别,注释提供了以声明性方式添加查询选项的方法。​​@Meta​

@Meta(comment = "find luke", batchSize = 100, flags = { SLAVE_OK })
List<Person> findByFirstname(String firstname);

Spring Data (数据)MongoDB(二)

10.7. 示例查询

10.7.1. 简介

本章介绍按示例查询并说明如何使用它。

按示例查询 (QBE) 是一种用户友好的查询技术,具有简单的界面。 它允许创建动态查询,并且不需要您编写包含字段名称的查询。 事实上,按示例查询根本不要求您使用特定于存储的查询语言编写查询。

10.7.2. 用法

按示例查询 API 由四个部分组成:

  • 探测器:具有填充字段的域对象的实际示例。
  • ​ExampleMatcher​​:包含有关如何匹配特定字段的详细信息。 它可以在多个示例中重复使用。ExampleMatcher
  • ​Example​​:由探头和探头组成。 它用于创建查询。ExampleExampleMatcher
  • ​FetchableFluentQuery​​:提供流畅的 API,允许进一步自定义从 . 使用流畅的 API 可以为查询指定排序投影和结果处理。FetchableFluentQueryExample

按示例查询非常适合多种用例:

  • 使用一组静态或动态约束查询数据存储。
  • 频繁重构域对象,无需担心中断现有查询。
  • 独立于基础数据存储 API 工作。

按示例查询也有几个限制:

  • 不支持嵌套或分组属性约束,例如。firstname = ?0 or (firstname = ?1 and lastname = ?2)
  • 仅支持字符串的开始/包含/结束/正则表达式匹配和其他属性类型的精确匹配。

在开始使用按示例查询之前,您需要有一个域对象。 首先,请为存储库创建一个接口,如以下示例所示:

例 89。示例人员对象

public class Person {

@Id
private String id;
private String firstname;
private String lastname;
private Address address;

// … getters and setters omitted
}

前面的示例显示了一个简单的域对象。 您可以使用它来创建. 默认情况下,将忽略具有值的字段,并使用特定于存储的默认值匹配字符串。​​Example​​​​null​

将属性包含在“按示例查询”条件中基于可空性。 始终包含使用基元类型 (,, ...) 的属性,除非ExampleMatcher忽略属性路径​。​​int​​​​double​

可以使用工厂方法或使用ExampleMatcher.is 不可变来构建示例。 下面的清单显示了一个简单的示例:​​of​​​​Example​

例 90。简单示例

Person person = new Person();                         
person.setFirstname("Dave");

Example<Person> example = Example.of(person);

创建域对象的新实例。

设置要查询的属性。

创建。​​Example​

您可以使用存储库运行示例查询。 为此,请让您的存储库界面扩展。 以下清单显示了界面的摘录:​​QueryByExampleExecutor<T>​​​​QueryByExampleExecutor​

例 91。这​​QueryByExampleExecutor​

public interface QueryByExampleExecutor<T> {

<S extends T> S findOne(Example<S> example);

<S extends T> Iterable<S> findAll(Example<S> example);

// … more functionality omitted.
}

10.7.3. 匹配器示例

示例不限于默认设置。 您可以使用 指定自己的字符串匹配、null 处理和特定于属性的设置的默认值,如以下示例所示:​​ExampleMatcher​

例 92。具有自定义匹配的示例匹配器

Person person = new Person();                          
person.setFirstname("Dave");

ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnorePaths("lastname")
.withIncludeNullValues()
.withStringMatcher(StringMatcher.ENDING);

Example<Person> example = Example.of(person, matcher);

创建域对象的新实例。

设置属性。

创建 anto 期望所有值都匹配。 即使没有进一步的配置,它也可以在此阶段使用。​​ExampleMatcher​

构造一个新忽略属性路径。​​ExampleMatcher​​​​lastname​

构造一个新以忽略属性路径并包含空值。​​ExampleMatcher​​​​lastname​

构造一个 newto 忽略属性路径,以包含 null 值,并执行后缀字符串匹配。​​ExampleMatcher​​​​lastname​

创建一个基于域对象和配置的 new。​​Example​​​​ExampleMatcher​

默认情况下,期望探测器上设置的所有值都匹配。 如果要获取与隐式定义的任何谓词匹配的结果,请使用。​​ExampleMatcher​​​​ExampleMatcher.matchingAny()​

您可以为单个属性指定行为(例如“名字”和“姓氏”,或者对于嵌套属性,可以指定“address.city”)。 您可以使用匹配选项和区分大小写来调整它,如以下示例所示:

例 93。配置匹配器选项

ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("firstname", endsWith())
.withMatcher("lastname", startsWith().ignoreCase());
}

配置匹配器选项的另一种方法是使用 lambda(在 Java 8 中引入)。 此方法创建一个回调,要求实现者修改匹配器。 您无需返回匹配器,因为配置选项保存在匹配器实例中。 以下示例显示了使用 lambda 的匹配器:

例 94。使用 lambda 配置匹配器选项

ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("firstname", match -> match.endsWith())
.withMatcher("firstname", match -> match.startsWith());
}

使用配置的合并视图创建的查询。 可以在级别设置默认匹配设置,而可以将单个设置应用于特定属性路径。 设置的设置由属性路径设置继承,除非显式定义它们。 属性修补程序上的设置具有比默认设置更高的优先级。 下表描述了各种设置的范围:​​Example​​​​ExampleMatcher​​​​ExampleMatcher​​​​ExampleMatcher​

表 4.设置的范围​​ExampleMatcher​

设置

范围

空处理

​ExampleMatcher​

字符串匹配

​ExampleMatcher​​和属性路径

忽略属性

属性路径

区分大小写

​ExampleMatcher​​和属性路径

价值转型

属性路径

10.7.4. 流畅的接口

​QueryByExampleExecutor​​提供了另一种方法,到目前为止我们没有提到: 与其他方法一样,它执行从 . 但是,使用第二个参数,您可以控制该执行的某些方面,否则无法动态控制。 为此,可以在第二个参数中调用各种方法。用于指定结果的排序。用于指定要将结果转换为的类型。限制查询的属性。,,,,,,,,并定义获得的结果类型以及当可用结果数超过预期数时查询的行为方式。​​<S extends T, R> R findBy(Example<S> example, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction)​​​​Example​​​​FetchableFluentQuery​​​​sortBy​​​​as​​​​project​​​​first​​​​firstValue​​​​one​​​​oneValue​​​​all​​​​page​​​​stream​​​​count​​​​exists​

例 95。使用流畅的 API 获取可能许多结果中的最后一个,按姓氏排序。

Optional<Person> match = repository.findBy(example,
q -> q
.sortBy(Sort.by("lastname").descending())
.first()
);

10.7.5. 运行示例

以下示例显示了在使用存储库(在本例中为 ofobjects)时如何按示例进行查询:​​Person​

例 96。使用存储库按示例查询

public interface PersonRepository extends QueryByExampleExecutor<Person> {

}

public class PersonService {

@Autowired PersonRepository personRepository;

public List<Person> findPeople(Person probe) {
return personRepository.findAll(Example.of(probe));
}
}

包含非类型化使用存储库类型及其集合名称。类型实例使用其类型作为结果类型和实例中的集合名称。​​Example​​​​ExampleSpec​​​​ExampleSpec​​​​Repository​

当包含值时,Spring Data Mongo使用嵌入式文档匹配而不是点表示法属性匹配。这样做会强制对嵌入文档中的所有属性值和属性顺序进行精确匹配。​​null​​​​ExampleSpec​

Spring Data MongoDB支持以下匹配选项:

表 5.选项​​StringMatcher​

匹配

逻辑结果

​DEFAULT​​(区分大小写)

​{"firstname" : firstname}​

​DEFAULT​​(不区分大小写)

​{"firstname" : { $regex: firstname, $options: 'i'}}​

​EXACT​​(区分大小写)

​{"firstname" : { $regex: /^firstname$/}}​

​EXACT​​(不区分大小写)

​{"firstname" : { $regex: /^firstname$/, $options: 'i'}}​

​STARTING​​(区分大小写)

​{"firstname" : { $regex: /^firstname/}}​

​STARTING​​(不区分大小写)

​{"firstname" : { $regex: /^firstname/, $options: 'i'}}​

​ENDING​​(区分大小写)

​{"firstname" : { $regex: /firstname$/}}​

​ENDING​​(不区分大小写)

​{"firstname" : { $regex: /firstname$/, $options: 'i'}}​

​CONTAINING​​(区分大小写)

​{"firstname" : { $regex: /.*firstname.*/}}​

​CONTAINING​​(不区分大小写)

​{"firstname" : { $regex: /.*firstname.*/, $options: 'i'}}​

​REGEX​​(区分大小写)

​{"firstname" : { $regex: /firstname/}}​

​REGEX​​(不区分大小写)

​{"firstname" : { $regex: /firstname/, $options: 'i'}}​

10.7.6. 非类型化示例

默认情况下是严格类型的。这意味着映射的查询具有包含的类型匹配,将其限制为探测可分配的类型。例如,当坚持使用默认类型键 () 时,查询具有诸如 () 之类的限制。​​Example​​​​_class​​​​_class : { $in : [ com.acme.Person] }​

通过使用,可以绕过默认行为并跳过类型限制。因此,只要字段名称匹配,几乎任何域类型都可以用作创建引用的探测器,如以下示例所示:​​UntypedExampleMatcher​

例 97。非类型化示例查询

class JustAnArbitraryClassWithMatchingFieldName {
@Field("lastname") String value;
}

JustAnArbitraryClassWithMatchingFieldNames probe = new JustAnArbitraryClassWithMatchingFieldNames();
probe.value = "stark";

Example example = Example.of(probe, UntypedExampleMatcher.matching());

Query query = new Query(new Criteria().alike(example));
List<Person> result = template.find(query, Person.class);


​UntypedExampleMatcher​​​如果要在单个集合中存储不同的实体或选择不编写类型提示,则可能是您的正确选择。



另外,请记住,使用需要预先初始化。为此,请配置为以确保读取操作的别名解析正确。​​@TypeAlias​​​​MappingContext​​​​initialEntitySet​


10.8. 计数文档

在 SpringData MongoDB 的 3.x 之前版本中,计数操作使用 MongoDB 的内部集合统计信息。 随着MongoDB事务的引入,这不再可能,因为统计数据无法正确反映需要基于聚合的计数方法的事务期间的潜在变化。 因此,在 2.x 版本中,如果没有正在进行事务,将使用集合统计信息,如果是,则使用聚合变体。​​MongoOperations.count()​

从Spring Data MongoDB 3.x开始,任何操作都使用,无论是否存在过滤条件,都可以通过MongoDBs进行基于聚合的计数方法。 如果应用程序对处理收集统计信息的限制很好,则提供了另一种选择。​​count​​​​countDocuments​​​​MongoOperations.estimatedCount()​


通过设置为MongoTemplate#count(...)只要没有活动事务且模板未绑定到会话​,使用空筛选器查询的操作就会委派给。 仍然可以通过以下途径获得确切的数字,但可能会加快速度。​​MongoTemplate#useEstimatedCount(…)​​​​true​​​​estimatedCount​​​​MongoTemplate#exactCount​



MongoDB的原生方法和聚合,不支持和但需要以及不支持(见https://jira.mongodb.org/browse/SERVER-37043​)。​​countDocuments​​​​$match​​​​$near​​​​$nearSphere​​​​$geoWithin​​​​$center​​​​$centerSphere​​​​$minDistance​



因此,给定将使用-/来绕过如下所示的问题来重写操作。​​Query​​​​count​​​​Reactive​​​​MongoTemplate​




{ location : { $near : [-73.99171, 40.738868], $maxDistance : 1.1 } } 
{ location : { $geoWithin : { $center: [ [-73.99171, 40.738868], 1.1] } } }

{ location : { $near : [-73.99171, 40.738868], $minDistance : 0.1, $maxDistance : 1.1 } }
{$and :[ { $nor :[ { location :{ $geoWithin :{ $center :[ [-73.99171, 40.738868 ], 0.01] } } } ]}, { location :{ $geoWithin :{ $center :[ [-73.99171, 40.738868 ], 1.1] } } } ] }



使用对源查询进行计数。​​$near​

现在使用重写的查询。​​$geoWithin​​​​$center​

使用与和对源查询进行计数。​​$near​​​​$minDistance​​​​$maxDistance​

重写的查询现在是 critierias 的组合,以解决不支持的问题。​​$nor​​​​$geowithin​​​​$minDistance​

10.9. 地图化简操作

您可以使用 Map-Reduce 查询 MongoDB,这对于批处理、数据聚合以及查询语言不能满足您的需求非常有用。

Spring通过提供简化Map-Reduce操作的创建和运行的方法,提供了与MongoDB的Map-Reduce的集成。它可以将Map-Reduce操作的结果转换为POJO,并与Spring的资源抽象集成。这使您可以将JavaScript文件放在文件系统,类路径,HTTP服务器或任何其他Spring Resource实现上,然后通过简单的URI样式语法引用JavaScript资源 - 例如,。在文件中外部化 JavaScript 代码通常比将它们作为 Java 字符串嵌入到代码中更可取。请注意,如果您愿意,您仍然可以将 JavaScript 代码作为 Java 字符串传递。​​MongoOperations​​​​classpath:reduce.js;​

10.9.1. 用法示例

为了理解如何执行Map-Reduce操作,我们使用书中的一个例子,MongoDB - 权威指南[​​1​​]。在此示例中,我们创建了三个分别具有值 [a,b]、[b,c] 和 [c,d] 的文档。每个文档中的值都与键“x”相关联,如以下示例所示(假设这些文档位于名为的集合中):​​jmr1​

{ "_id" : ObjectId("4e5ff893c0277826074ec533"), "x" : [ "a", "b" ] }
{ "_id" : ObjectId("4e5ff893c0277826074ec534"), "x" : [ "b", "c" ] }
{ "_id" : ObjectId("4e5ff893c0277826074ec535"), "x" : [ "c", "d" ] }

以下映射函数计算每个文档的数组中每个字母的出现次数:

function () {
for (var i = 0; i < this.x.length; i++) {
emit(this.x[i], 1);
}
}

下面的reduce函数汇总了所有文档中每个字母的出现次数:

function (key, values) {
var sum = 0;
for (var i = 0; i < values.length; i++)
sum += values[i];
return sum;
}

运行上述函数将生成以下集合:

{ "_id" : "a", "value" : 1 }
{ "_id" : "b", "value" : 2 }
{ "_id" : "c", "value" : 2 }
{ "_id" : "d", "value" : 1 }

假设 map 和 reduce 函数位于 jar 中并捆绑在一起,因此它们在类路径中可用,您可以运行 Map-Reduce 操作,如下所示:​​map.js​​​​reduce.js​

MapReduceResults<ValueObject> results = mongoOperations.mapReduce("jmr1", "classpath:map.js", "classpath:reduce.js", ValueObject.class);
for (ValueObject valueObject : results) {
System.out.println(valueObject);
}

前面的示例生成以下输出:

ValueObject [id=a, value=1.0]
ValueObject [id=b, value=2.0]
ValueObject [id=c, value=2.0]
ValueObject [id=d, value=1.0]

该类实现并提供对原始输出以及计时和计数统计信息的访问。以下清单显示了类:​​MapReduceResults​​​​Iterable​​​​ValueObject​

public class ValueObject {

private String id;
private float value;

public String getId() {
return id;
}

public float getValue() {
return value;
}

public void setValue(float value) {
this.value = value;
}

@Override
public String toString() {
return "ValueObject [id=" + id + ", value=" + value + "]";
}
}

默认情况下,使用 of 的输出类型,因此无需指定输出集合。若要指定其他 Map-Reduce 选项,请使用采用额外参数的重载方法。该类具有流畅的 API,因此可以在紧凑的语法中添加其他选项。以下示例将输出集合设置为 (请注意,仅设置输出集合假定默认输出类型为 ):​​INLINE​​​​MapReduceOptions​​​​MapReduceOptions​​​​jmr1_out​​​​REPLACE​

MapReduceResults<ValueObject> results = mongoOperations.mapReduce("jmr1", "classpath:map.js", "classpath:reduce.js",
new MapReduceOptions().outputCollection("jmr1_out"), ValueObject.class);

还有一个静态导入 (),可用于使语法稍微紧凑一些,如以下示例所示:​​import static org.springframework.data.mongodb.core.mapreduce.MapReduceOptions.options;​

MapReduceResults<ValueObject> results = mongoOperations.mapReduce("jmr1", "classpath:map.js", "classpath:reduce.js",
options().outputCollection("jmr1_out"), ValueObject.class);

您还可以指定一个查询来减少输入到 Map-Reduce 操作中的数据集。以下示例将包含 [a,b] 的文档从 Map-Reduce 操作的考虑范围中删除:

Query query = new Query(where("x").ne(new String[] { "a", "b" }));
MapReduceResults<ValueObject> results = mongoOperations.mapReduce(query, "jmr1", "classpath:map.js", "classpath:reduce.js",
options().outputCollection("jmr1_out"), ValueObject.class);

请注意,您可以在查询上指定其他限制和排序值,但不能跳过值。

10.10. 脚本操作


MongoDB 4.2​删除了对所用命令的支持 由。
删除的功能没有替代品。​​​eval​​​​ScriptOperations​


MongoDB允许通过直接发送脚本或调用存储的脚本在服务器上运行JavaScript函数。下面的示例展示了如何给我们类:​​ScriptOperations​​​​MongoTemplate​​​​JavaScript​​​​ScriptOperations​

ScriptOperations scriptOps = template.scriptOps();

ExecutableMongoScript echoScript = new ExecutableMongoScript("function(x) { return x; }");
scriptOps.execute(echoScript, "directly execute script");

scriptOps.register(new NamedMongoScript("echo", echoScript));
scriptOps.call("echo", "execute script via name");

直接运行脚本,而不将函数存储在服务器端。

使用“echo”作为其名称存储脚本。给定名称标识脚本并允许稍后调用它。

使用提供的参数运行名称为“echo”的脚本。

10.11. 集团运营

作为使用 Map-Reduce 执行数据聚合的替代方法,您可以使用组操作,这感觉类似于使用 SQL 的按查询样式分组,因此与使用 Map-Reduce 相比,它可能感觉更平易近人。使用组操作确实有一些限制,例如,它在共享环境中不受支持,并且它在单个 BSON 对象中返回完整的结果集,因此结果应该很小,少于 10,000 个键。

Spring 通过在 MongoOperations 上提供方法来简化组操作的创建和运行,从而提供了与 MongoDB 的组操作的集成。它可以将组操作的结果转换为POJO,还可以与Spring的资源抽象抽象集成。这将允许您将JavaScript文件放在文件系统,类路径,http服务器或任何其他Spring Resource实现上,然后通过简单的URI样式语法引用JavaScript资源,例如'classpath:reduce.js;。在文件中外部化 JavaScript 代码(如果通常比将它们作为 Java 字符串嵌入到代码中)更可取。请注意,如果您愿意,您仍然可以将 JavaScript 代码作为 Java 字符串传递。

10.11.1. 用法示例

为了理解组操作的工作原理,使用了以下示例,该示例有些人为。有关更现实的示例,请参阅“MongoDB - 权威指南”一书。名为“已创建的”集合,包含以下行。​​group_test_collection​

{ "_id" : ObjectId("4ec1d25d41421e2015da64f1"), "x" : 1 }
{ "_id" : ObjectId("4ec1d25d41421e2015da64f2"), "x" : 1 }
{ "_id" : ObjectId("4ec1d25d41421e2015da64f3"), "x" : 2 }
{ "_id" : ObjectId("4ec1d25d41421e2015da64f4"), "x" : 3 }
{ "_id" : ObjectId("4ec1d25d41421e2015da64f5"), "x" : 3 }
{ "_id" : ObjectId("4ec1d25d41421e2015da64f6"), "x" : 3 }

我们想按每行中唯一的字段分组,该字段并汇总每个特定值出现的次数。为此,我们需要创建一个包含我们的 count 变量的初始文档,以及一个 reduce 函数,该函数将在每次遇到时递增它。运行组操作的 Java 代码如下所示​​x​​​​x​

GroupByResults<XObject> results = mongoTemplate.group("group_test_collection",
GroupBy.key("x").initialDocument("{ count: 0 }").reduceFunction("function(doc, prev) { prev.count += 1 }"),
XObject.class);

第一个参数是运行组操作的集合的名称,第二个参数是一个流畅的 API,它通过 aclass 指定组操作的属性。在这个例子中,我们只使用和方法。您还可以指定密钥函数以及终结器作为流畅 API 的一部分。如果要作为分组依据的多个键,则可以传入以逗号分隔的键列表。​​GroupBy​​​​intialDocument​​​​reduceFunction​

组操作的原始结果是一个 JSON 文档,如下所示

{
"retval" : [ { "x" : 1.0 , "count" : 2.0} ,
{ "x" : 2.0 , "count" : 1.0} ,
{ "x" : 3.0 , "count" : 3.0} ] ,
"count" : 6.0 ,
"keys" : 3 ,
"ok" : 1.0
}

“retval”字段下的文档映射到组方法中的第三个参数,在本例中为 XObject,如下所示。

public class XObject {

private float x;

private float count;


public float getX() {
return x;
}

public void setX(float x) {
this.x = x;
}

public float getCount() {
return count;
}

public void setCount(float count) {
this.count = count;
}

@Override
public String toString() {
return "XObject [x=" + x + " count = " + count + "]";
}
}

您还可以通过调用类上的方法获取原始结果。​​Document​​​​getRawResults​​​​GroupByResults​

组方法还有一个附加方法重载,它允许您指定用于选择行子集的对象。下面显示了一个使用aobject的示例,使用静态导入的一些语法糖,以及通过Spring Resource字符串引用键函数和reduce函数javascript文件。​​MongoOperations​​​​Criteria​​​​Criteria​

import static org.springframework.data.mongodb.core.mapreduce.GroupBy.keyFunction;
import static org.springframework.data.mongodb.core.query.Criteria.where;

GroupByResults<XObject> results = mongoTemplate.group(where("x").gt(0),
"group_test_collection",
keyFunction("classpath:keyFunction.js").initialDocument("{ count: 0 }").reduceFunction("classpath:groupReduce.js"), XObject.class);

10.12. 聚合框架支持

Spring Data MongoDB为2.2版中引入MongoDB的聚合框架提供支持。

有关更多信息,请参阅 MongoDB 的聚合框架和其他数据聚合工具的完整参考文档。

10.12.1. 基本概念

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

  • ​Aggregation​
    An表示MongoDB操作并保存聚合管道指令的描述。聚合是通过调用 theclass 的适当静态工厂方法创建的,该方法采用 of 列表和可选的输入类。AggregationaggregatenewAggregation(…)AggregationAggregateOperation


    实际的聚合操作由 的方法运行,该方法将所需的输出类作为参数。aggregateMongoTemplate
  • ​TypedAggregation​
    A 与 an 一样,包含聚合管道的指令和对输入类型的引用,该类型用于将域属性映射到实际文档字段。TypedAggregationAggregation


    在运行时,根据给定的输入类型检查字段引用,考虑潜在的注释。@Field

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

  • ​AggregationDefinition​
    表示 MongoDB 聚合管道操作,并描述应在此聚合步骤中执行的处理。尽管您可以手动创建 an,但我们建议使用 theclass 提供的静态工厂方法来构造 an。AggregationDefinitionAggregationDefinitionAggregateAggregateOperation
  • ​AggregationResults​
    ​AggregationResults​​是聚合操作结果的容器。它以ato的形式提供对原始聚合结果的访问,映射的对象和有关聚合的其他信息。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​

10.12.2. 支持的聚合操作

MongoDB聚合框架提供以下类型的聚合操作:

  • 管道聚合运算符
  • 组/累加器聚合运算符
  • 布尔聚合运算符
  • 比较聚合运算符
  • 算术聚合运算符
  • 字符串聚合运算符
  • 日期聚合运算符
  • 数组聚合运算符
  • 条件聚合运算符
  • 查找聚合运算符
  • 转换聚合运算符
  • 对象聚合运算符
  • 脚本聚合运算符

在撰写本文时,我们为Spring Data MongoDB中的以下聚合操作提供支持:

表 6.Spring Data MongoDB 目前支持的聚合操作

管道聚合运算符

​bucket​​​, , , , , , , , , , , , , , , ​​bucketAuto​​​​count​​​​facet​​​​geoNear​​​​graphLookup​​​​group​​​​limit​​​​lookup​​​​match​​​​project​​​​rand​​​​replaceRoot​​​​skip​​​​sort​​​​unwind​

设置聚合运算符

​setEquals​​​, , , , , , ​​setIntersection​​​​setUnion​​​​setDifference​​​​setIsSubset​​​​anyElementTrue​​​​allElementsTrue​

组/累加器聚合运算符

​addToSet​​​, , , , , , , , , , , , , , , , , , (*), , ​​bottom​​​​bottomN​​​​covariancePop​​​​covarianceSamp​​​​expMovingAvg​​​​first​​​​firstN​​​​last​​​​lastN​​​​max​​​​maxN​​​​min​​​​minN​​​​avg​​​​push​​​​sum​​​​top​​​​topN​​​​count​​​​stdDevPop​​​​stdDevSamp​

算术聚合运算符

​abs​​​,,,(* via),,,,,,,,,,,,,,,,,,,,,,(* via),,,,,​​acos​​​​acosh​​​​add​​​​plus​​​​asin​​​​asin​​​​atan​​​​atan2​​​​atanh​​​​ceil​​​​cos​​​​cosh​​​​derivative​​​​divide​​​​exp​​​​floor​​​​integral​​​​ln​​​​log​​​​log10​​​​mod​​​​multiply​​​​pow​​​​round​​​​sqrt​​​​subtract​​​​minus​​​​sin​​​​sinh​​​​tan​​​​tanh​​​​trunc​

字符串聚合运算符

​concat​​​,,,,,,,,,,,,分裂',,,,,,​​substr​​​​toLower​​​​toUpper​​​​strcasecmp​​​​indexOfBytes​​​​indexOfCP​​​​regexFind​​​​regexFindAll​​​​regexMatch​​​​replaceAll​​​​replaceOne​​​​strLenBytes​​​​strLenCP​​​​substrCP​​​​trim​​​​ltrim​​​​rtim​

比较聚合运算符

​eq​​​(* 通过),,,,,​​is​​​​gt​​​​gte​​​​lt​​​​lte​​​​ne​

数组聚合运算符

​arrayElementAt​​​,,,,,,,,,范围',,,,,,​​arrayToObject​​​​concatArrays​​​​filter​​​​first​​​​in​​​​indexOfArray​​​​isArray​​​​last​​​​reverseArray​​​​reduce​​​​size​​​​sortArray​​​​slice​​​​zip​

文字运算符

​literal​

日期聚合运算符

​dateSubstract​​​, , , , , , , , , , , , , , , , , , , , , , ​​dateTrunc​​​​dayOfYear​​​​dayOfMonth​​​​dayOfWeek​​​​year​​​​month​​​​week​​​​hour​​​​minute​​​​second​​​​millisecond​​​​dateAdd​​​​dateDiff​​​​dateToString​​​​dateFromString​​​​dateFromParts​​​​dateToParts​​​​isoDayOfWeek​​​​isoWeek​​​​isoWeekYear​​​​tsIncrement​​​​tsSecond​

变量运算符

​map​

条件聚合运算符

​cond​​​, , ​​ifNull​​​​switch​

类型聚合运算符

​type​

转换聚合运算符

​convert​​​, , , , , , , , , ​​degreesToRadians​​​​toBool​​​​toDate​​​​toDecimal​​​​toDouble​​​​toInt​​​​toLong​​​​toObjectId​​​​toString​

对象聚合运算符

​objectToArray​​​, , , ​​mergeObjects​​​​getField​​​​setField​

脚本聚合运算符

​function​​​, ​​accumulator​

* 该操作由 Spring Data MongoDB 映射或添加。

请注意,Spring Data MongoDB目前不支持此处未列出的聚合操作。比较聚合运算符表示为表达式。​​Criteria​



不支持的聚合操作/运算符(如​​MongoDB Atlas$search​​​可以通过提供其JSON或表示来实现 eitheror.is 注册管道阶段的快捷方式来提供。​​AggregationOperation​​​​AggregationExpression​​​​Aggregation.stage​​​​Bson​




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



10.12.3. 投影表达式

投影表达式用于定义作为特定聚合步骤结果的字段。投影表达式可以通过类的方法定义,通过传递对象列表或聚合框架对象。投影可以通过流畅的 API 使用其他字段进行扩展,方法是使用方法进行别名。 请注意,您还可以使用聚合框架的静态工厂方法定义带有别名的字段,然后可以使用该方法构造 newinstance。在后续聚合阶段对投影字段的引用仅对包含字段的字段名称或其别名(包括新定义的字段及其别名)有效。投影中未包含的字段无法在以后的聚合阶段中引用。以下清单显示了投影表达式的示例:​​project​​​​Aggregation​​​​String​​​​Fields​​​​and(String)​​​​as(String)​​​​Fields.field​​​​Fields​

例 98。投影表达式示例

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

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

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

可以在类中找到有关项目操作的更多示例。请注意,有关投影表达式的更多详细信息可以在MongoDB聚合框架参考文档的相应部分找到。​​AggregationTests​

10.12.4. 分面分类

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

存储桶操作根据指定的表达式和存储桶边界将传入文档分类为组(称为存储桶)。存储桶操作需要分组字段或分组表达式。您可以使用 theclass 的 and 方法来定义它们,并且可以基于输入文档的聚合表达式公开累积。您可以使用方法和方法通过流畅的 API 使用其他参数扩展存储桶操作。可以使用该方法为操作设置别名。每个存储桶在输出中表示为一个文档。​​bucket()​​​​bucketAuto()​​​​Aggregate​​​​BucketOperation​​​​BucketAutoOperation​​​​with…()​​​​andOutput(String)​​​​as(String)​

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

例 100。铲斗操作示例

// 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​​确定边界以尝试将文档均匀分布到指定数量的存储桶中。(可选)采用一个粒度值,该值指定用于确保计算的边界边以首选整数或 10 的幂结束的首选数字序列。以下清单显示了存储桶操作的示例:​​BucketAutoOperation​

例 101。铲斗操作示例

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

要在存储桶中创建输出字段,存储桶操作可以使用SpEL 表达式通过。​​AggregationExpression​​​​andOutput()​​​​andOutputExpression()​

请注意,有关存储桶表达式的更多详细信息可以在MongoDB聚合框架参考文档的$bucket节和$bucketAuto节中找到。

多方面聚合

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

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

子管道可以在分组之前投影和筛选输入文档。常见用例包括在分类之前提取日期部分或计算。以下清单显示了分面操作示例:

例 102.小平面操作示例

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

请注意,有关分面操作的更多详细信息可以在MongoDB聚合框架参考文档的$facet部分找到。

按计数排序

按计数排序操作根据指定表达式的值对传入文档进行分组,计算每个不同组中的文档计数,并按计数对结果进行排序。它提供了一个方便的快捷方式,用于在使用分面分类时应用排序。按计数排序操作需要分组字段或分组表达式。下面的清单显示了按计数排序的示例:

例 103.按计数示例排序

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

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

{ $group: { _id: <expression>, count: { $sum: 1 } } },
{ $sort: { count: -1 } }
投影表达式中的弹簧表达式支持

我们支持通过 theandclass 的方法在投影表达式中使用 SpEL 表达式。此功能允许您将所需的表达式定义为 SpEL 表达式。在运行查询时,SpEL 表达式将转换为相应的 MongoDB 投影表达式部分。这种安排使表达复杂计算变得更加容易。​​andExpression​​​​ProjectionOperation​​​​BucketOperation​

使用 SpEL 表达式进行复杂计算

请考虑以下 SpEL 表达式:

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

上述表达式将转换为以下投影表达式部分:

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

可以在聚合框架示例 5 和聚合框架示例 6 中查看更多上下文中的示例。您可以在中找到支持的 SpEL 表达式构造的更多用法示例。下表显示了Spring Data MongoDB支持的SpEL转换:​​SpelExpressionTransformerUnitTests​

表 7.支持的 SpEL 转换

SpEL 表达式

蒙戈表达式部分

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] }

一||b

{ $or : [$a, $b] }

!一个

{ $not : [$a] }

除了上表中显示的转换之外,您还可以使用标准 SpEL 操作,例如(例如)通过数组名称创建数组和引用表达式(后跟要在括号中使用的参数)。下面的示例演示如何以这种方式创建数组:​​new​

// { $setEquals : [$a, [5, 8, 13] ] }
.andExpression("setEquals(a, new int[]{5, 8, 13})");
聚合框架示例

本节中的示例演示了带有Spring Data MongoDB的MongoDB聚合框架的使用模式。

聚合框架示例 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. 使用静态工厂方法创建一个新的聚合,我们将聚合操作列表传递给该方法。这些聚合操作定义了我们的聚合管道。newAggregationAggregation
  2. 使用该操作从输入集合中选择字段(字符串数组)。projecttags
  3. 使用该操作为数组中的每个标记生成一个新文档。unwindtags
  4. 使用操作为每个值定义一个组,我们聚合其发生计数(通过使用聚合运算符并在调用的新字段中收集结果)。grouptagscountn
  5. 选择该字段,并为从上一个组操作(因此调用 to)生成的 ID 字段创建一个别名,名称为 of。npreviousOperation()tag
  6. 使用该操作按标签出现次数降序对生成的标签列表进行排序。sort
  7. 调用该方法让MongoDB执行实际的聚合操作,创建为参数。aggregateMongoTemplateAggregation

请注意,输入集合被显式指定为 Method 的参数。如果未显式指定输入集合的名称,则它派生自作为第一个参数传递给该方法的输入类。​​tags​​​​aggregate​​​​newAggreation​

聚合框架示例 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);

请注意,该类映射给定输入集合的结构。该类以所需的输出格式定义结构。​​ZipInfo​​​​ZipInfoStats​

前面的清单使用以下算法:

  1. 使用该操作从输入集合定义组。分组条件是 theandfields 的组合,它构成了组的 ID 结构。我们使用运算符聚合分组元素的属性值,并将结果保存在字段中。groupstatecitypopulationsumpop
  2. 使用该操作按 、andfields 升序对中间结果进行排序,使得最小的城市位于结果的顶部,最大的城市位于结果的底部。请注意,排序onandis隐式地针对组ID字段(Spring Data MongoDB处理)执行。sortpopstatecitystatecity
  3. 再次使用操作对中间结果进行分组。请注意,再次隐式引用组 ID 字段。我们在操作中分别调用 theandoperator 来选择最大和最小城市的名称和人口计数。groupstatestatelast(…)first(…​)project
  4. 从上一个操作中选择字段。请注意,再次隐式引用组 ID 字段。由于我们不希望显示隐式生成的 ID,因此我们使用 ID 从前面的操作中排除该 ID。由于我们要在输出类中填充嵌套结构,因此必须使用嵌套方法发出相应的子文档。stategroupstateand(previousOperation()).exclude()City
  5. 在操作中按其状态名称的升序对结果列表进行排序。StateStatssort

请注意,我们从作为第一个参数传递给方法的类中派生输入集合的名称。​​ZipInfo​​​​newAggregation​

聚合框架示例 3

此示例基于 MongoDB 聚合框架文档中人口超过 1000 万的州示例。我们添加了额外的排序,以使用不同的MongoDB版本产生稳定的结果。在这里,我们希望使用聚合框架返回人口大于 1000 万的所有州。此示例演示分组、排序和匹配(筛选)。

class StateStats {
@Id String id;
String state;
@Field("totalPop") int totalPopulation;
}
TypedAggregation<ZipInfo> agg = newAggregation(ZipInfo.class,
group("state").sum("population").as("totalPop"),
sort(ASC, previousOperation(), "totalPop"),
match(where("totalPop").gte(10 * 1000 * 1000))
);

AggregationResults<StateStats> result = mongoTemplate.aggregate(agg, StateStats.class);
List<StateStats> stateStatsList = result.getMappedResults();

前面的清单使用以下算法:

  1. 按字段对输入集合进行分组,并计算字段的总和并将结果存储在新字段中。statepopulation"totalPop"
  2. 除了按升序排列的字段外,还按上一个组操作的 id 引用对中间结果进行排序。"totalPop"
  3. 通过使用接受查询作为参数的操作来筛选中间结果。matchCriteria

请注意,我们从作为第一个参数传递给方法的类派生输入集合的名称。​​ZipInfo​​​​newAggregation​

聚合框架示例 4

此示例演示如何在投影操作中使用简单的算术运算。

class Product {
String id;
String name;
double netPrice;
int spaceUnits;
}
TypedAggregation<Product> agg = newAggregation(Product.class,
project("name", "netPrice")
.and("netPrice").plus(1).as("netPricePlus1")
.and("netPrice").minus(1).as("netPriceMinus1")
.and("netPrice").multiply(1.19).as("grossPrice")
.and("netPrice").divide(2).as("netPriceDiv2")
.and("spaceUnits").mod(2).as("spaceUnitsMod2")
);

AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class);
List<Document> resultList = result.getMappedResults();

请注意,我们从作为第一个参数传递给方法的类派生输入集合的名称。​​Product​​​​newAggregation​

聚合框架示例 5

此示例演示如何在投影操作中使用从 SpEL 表达式派生的简单算术运算。

class Product {
String id;
String name;
double netPrice;
int spaceUnits;
}
TypedAggregation<Product> agg = newAggregation(Product.class,
project("name", "netPrice")
.andExpression("netPrice + 1").as("netPricePlus1")
.andExpression("netPrice - 1").as("netPriceMinus1")
.andExpression("netPrice / 2").as("netPriceDiv2")
.andExpression("netPrice * 1.19").as("grossPrice")
.andExpression("spaceUnits % 2").as("spaceUnitsMod2")
.andExpression("(netPrice * 0.8 + 1.2) * 1.19").as("grossPriceIncludingDiscountAndCharge")

);

AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class);
List<Document> resultList = result.getMappedResults();
聚合框架示例 6

此示例演示如何在投影操作中使用从 SpEL 表达式派生的复杂算术运算。

注意:传递给该方法的其他参数可以根据其位置使用索引器表达式进行引用。在此示例中,我们引用参数数组的第一个参数。当 SpEL 表达式转换为 MongoDB 聚合框架表达式时,外部参数表达式将替换为它们各自的值。​​addExpression​​​​[0]​

class Product {
String id;
String name;
double netPrice;
int spaceUnits;
}
double shippingCosts = 1.2;

TypedAggregation<Product> agg = newAggregation(Product.class,
project("name", "netPrice")
.andExpression("(netPrice * (1-discountRate) + [0]) * (1+taxRate)", shippingCosts).as("salesPrice")
);

AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class);
List<Document> resultList = result.getMappedResults();

请注意,我们还可以在 SpEL 表达式中引用文档的其他字段。

聚合框架示例 7

此示例使用条件投影。它派生自$cond参考文档。

public class InventoryItem {

@Id int id;
String item;
String description;
int qty;
}

public class InventoryItemProjection {

@Id int id;
String item;
String description;
int qty;
int discount
}
TypedAggregation<InventoryItem> agg = newAggregation(InventoryItem.class,
project("item").and("discount")
.applyCondition(ConditionalOperator.newBuilder().when(Criteria.where("qty").gte(250))
.then(30)
.otherwise(20))
.and(ifNull("description", "Unspecified")).as("description")
);

AggregationResults<InventoryItemProjection> result = mongoTemplate.aggregate(agg, "inventory", InventoryItemProjection.class);
List<InventoryItemProjection> stateStatsList = result.getMappedResults();

此一步聚合对集合使用投影操作。我们通过对所有大于或等于的库存项目使用条件操作来投影字段。对字段执行第二个条件投影。我们将描述应用于所有没有字段的项目或具有描述的项目。​​inventory​​​​discount​​​​qty​​​​250​​​​description​​​​Unspecified​​​​description​​​​null​

从MongoDB 3.6开始,可以使用条件表达式从投影中排除字段。

例 104.条件聚合投影

TypedAggregation<Book> agg = Aggregation.newAggregation(Book.class,
project("title")
.and(ConditionalOperators.when(ComparisonOperators.valueOf("author.middle")
.equalToValue(""))
.then("$$REMOVE")
.otherwiseValueOf("author.middle")
)
.as("author.middle"));

如果字段的值​​author.middle​

不包含值,

然后使用 $$REMOVE排除该字段。

否则,添加字段值 。​​author.middle​

10.13. 索引和集合管理

​MongoTemplate​​提供了一些管理索引和集合的方法。这些方法被收集到调用的帮助程序接口中。可以通过调用该方法并传入集合名称或实体(集合名称派生自 、按名称或从注释元数据派生)来访问这些操作。​​IndexOperations​​​​indexOps​​​​java.lang.Class​​​​.class​

以下清单显示了界面:​​IndexOperations​

public interface IndexOperations {

void ensureIndex(IndexDefinition indexDefinition);

void dropIndex(String name);

void dropAllIndexes();

void resetIndexCache();

List<IndexInfo> getIndexInfo();
}

10.13.1. 创建索引的方法

可以使用 MongoTemplate 类在集合上创建索引以提高查询性能,如以下示例所示:

mongoTemplate.indexOps(Person.class).ensureIndex(new Index().on("name",Order.ASCENDING));

​ensureIndex​​确保为集合提供索引定义存在索引。

可以使用 和类创建标准索引、地理空间索引和文本索引。例如,给定上一节中定义的类,可以声明地理空间查询,如以下示例所示:​​IndexDefinition​​​​GeoSpatialIndex​​​​TextIndexDefinition​​​​Venue​

mongoTemplate.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location"));

​Index​​​并支持排序规则​的配置。​​GeospatialIndex​

10.13.2. 访问索引信息

接口具有返回对象列表的方法。此列表包含在集合上定义的所有索引。下面的示例在具有属性的类上定义一个索引:​​IndexOperations​​​​getIndexInfo​​​​IndexInfo​​​​Person​​​​age​

template.indexOps(Person.class).ensureIndex(new Index().on("age", Order.DESCENDING).unique());

List<IndexInfo> indexInfoList = template.indexOps(Person.class).getIndexInfo();

// Contains
// [IndexInfo [fieldSpec={_id=ASCENDING}, name=_id_, unique=false, sparse=false],
// IndexInfo [fieldSpec={age=DESCENDING}, name=age_-1, unique=true, sparse=false]]

10.13.3. 使用集合的方法

以下示例演示如何创建集合:

例 105.使用​​MongoTemplate​

MongoCollection<Document> collection = null;
if (!mongoTemplate.getCollectionNames().contains("MyNewCollection")) {
collection = mongoTemplate.createCollection("MyNewCollection");
}

mongoTemplate.dropCollection("MyNewCollection");
  • getCollectionNames:返回一组集合名称。
  • 集合存在:检查具有给定名称的集合是否存在。
  • 创建集合:创建无上限集合
  • dropCollection:删除集合。
  • getCollection:按名称获取集合,如果集合不存在,则创建它。

集合创建允许自定义并支持排序规则​。​​CollectionOptions​

10.14. 运行命令

你可以通过使用方法获取MongoDB驱动程序的方法。这些方法还将异常转换为 Spring 的层次结构。​​MongoDatabase.runCommand( )​​​​executeCommand(…)​​​​MongoTemplate​​​​DataAccessException​

10.14.1. 运行命令的方法

  • ​Document​​ executeCommand:运行一个MongoDB命令。(Document command)
  • ​Document​​ executeCommand:使用给定的可为空的MongoDB运行MongoDB命令。(Document command, ReadPreference readPreference)ReadPreference
  • ​Document​​ executeCommand:运行表示为 JSON 字符串的 MongoDB 命令。(String jsonCommand)

10.15. 生命周期事件

MongoDB映射框架包括几个事件,您的应用程序可以通过在其中注册特殊的bean来响应这些事件。 基于Spring的事件基础设施使其他产品(如Spring Integration)能够轻松接收这些事件,因为它们是基于Spring的应用程序中众所周知的事件机制。​​org.springframework.context.ApplicationEvent​​​​ApplicationContext​​​​ApplicationContext​

实体生命周期事件的成本可能很高,在加载大型结果集时,您可能会注意到性能配置文件发生了变化。 您可以在模板 API 上禁用生命周期事件。

要在对象经历转换过程(这会将您的域对象转换为 )之前截获该对象,您可以注册一个覆盖该方法的子类。 调度事件时,侦听器在进入转换器之前被调用并传递域对象。 以下示例演示如何执行此操作:​​org.bson.Document​​​​AbstractMongoEventListener​​​​onBeforeConvert​

public class BeforeConvertListener extends AbstractMongoEventListener<Person> {
@Override
public void onBeforeConvert(BeforeConvertEvent<Person> event) {
... does some auditing manipulation, set timestamps, whatever ...
}
}

要在对象进入数据库之前截获该对象,可以注册一个覆盖该方法的子类。调度事件时,将调用侦听器并传递域对象和转换的对象。以下示例演示如何执行此操作:​​org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener​​​​onBeforeSave​​​​com.mongodb.Document​

public class BeforeSaveListener extends AbstractMongoEventListener<Person> {
@Override
public void onBeforeSave(BeforeSaveEvent<Person> event) {
… change values, delete them, whatever …
}
}

在 Spring ApplicationContext 中声明这些 bean 会导致每当调度事件时调用它们。

中存在以下回调方法:​​AbstractMappingEventListener​

  • ​onBeforeConvert​​:在对象转换为 aby a 之前调用 in、 和操作。MongoTemplateinsertinsertListsaveDocumentMongoConverter
  • ​onBeforeSave​​:在插入或保存数据库之前调用 、 和操作。MongoTemplateinsertinsertListsaveDocument
  • ​onAfterSave​​:在数据库中插入或保存调用 、 和操作。MongoTemplateinsertinsertListsaveDocument
  • ​onAfterLoad​​:从数据库中检索到之后的调用,,,和方法。MongoTemplatefindfindAndRemovefindOnegetCollectionDocument
  • ​onAfterConvert​​:调用,,,从数据库中检索到的方法转换为 POJO。MongoTemplatefindfindAndRemovefindOnegetCollectionDocument

仅针对根级别类型发出生命周期事件。在文档根目录中用作属性的复杂类型不受事件发布的影响,除非它们是带有批注的文档引用。​​@DBRef​

生命周期事件依赖于 an,在这种情况下可以配置一个,因此在处理事件时不提供任何保证。​​ApplicationEventMulticaster​​​​SimpleApplicationEventMulticaster​​​​TaskExecutor​

10.16. 实体回调

Spring 数据基础架构提供了用于在调用某些方法之前和之后修改实体的钩子。 这些所谓的实例提供了一种方便的方法,可以检查并可能以回调样式修改实体。
安看起来很像一个专业的人。 一些 Spring 数据模块发布存储允许修改给定实体的特定事件(例如)。在某些情况下,例如在使用不可变类型时,这些事件可能会导致麻烦。 此外,事件发布依赖于。如果使用异步配置它可能会导致不可预测的结果,因为事件处理可以分叉到线程上。​​EntityCallback​​​​EntityCallback​​​​ApplicationListener​​​​BeforeSaveEvent​​​​ApplicationEventMulticaster​​​​TaskExecutor​

实体回调为集成点提供同步和反应式 API,以保证在处理链中定义良好的检查点按顺序执行,返回可能修改的实体或反应式包装器类型。

实体回调通常按 API 类型分隔。这种分离意味着同步 API 仅考虑同步实体回调,而反应式实现仅考虑反应式实体回调。


实体回调 API 已在 Spring Data Commons 2.2 中引入。这是应用实体修改的推荐方法。 现有存储特定仍会在调用可能注册的实例之前发布。​​ApplicationEvents​​​​EntityCallback​


10.16.1. 实现实体回调

Anis 通过其泛型类型参数与其域类型直接关联。 每个 Spring 数据模块通常附带一组涵盖实体生命周期的预定义接口。​​EntityCallback​​​​EntityCallback​

例 106.剖析​​EntityCallback​

@FunctionalInterface
public interface BeforeSaveCallback<T> extends EntityCallback<T> {

/**
* Entity callback method invoked before a domain object is saved.
* Can return either the same or a modified instance.
*
* @return the domain object to be persisted.
*/
T onBeforeSave(T entity <2>, String collection <3>);
}

​BeforeSaveCallback​​在保存实体之前要调用的特定方法。返回可能已修改的实例。

持久之前的实体。

许多存储特定的参数,例如实体保存到的集合

例 107.反应性解剖​​EntityCallback​

@FunctionalInterface
public interface ReactiveBeforeSaveCallback<T> extends EntityCallback<T> {

/**
* Entity callback method invoked on subscription, before a domain object is saved.
* The returned Publisher can emit either the same or a modified instance.
*
* @return Publisher emitting the domain object to be persisted.
*/
Publisher<T> onBeforeSave(T entity <2>, String collection <3>);
}

​BeforeSaveCallback​​在保存实体之前,要在订阅上调用的特定方法。发出可能经过修改的实例。

持久之前的实体。

许多存储特定的参数,例如实体保存到的集合

可选的实体回调参数由实现 Spring 数据模块定义,并从调用站点推断。​​EntityCallback.callback()​

实现适合您的应用程序需求的接口,如以下示例所示:

例 108.例​​BeforeSaveCallback​

class DefaultingEntityCallback implements BeforeSaveCallback<Person>, Ordered {      

@Override
public Object onBeforeSave(Person entity, String collection) {

if(collection == "user") {
return // ...
}

return // ...
}

@Override
public int getOrder() {
return 100;
}
}


根据您的需求实现回调。


如果存在同一域类型的多个实体回调,则可能会对实体回调进行排序。排序遵循最低优先级。

10.16.2. 注册实体回调

​EntityCallback​​如果 bean 在 中注册,则由商店特定的实现拾取。 大多数模板 API 已经实现,因此可以访问​​ApplicationContext​​​​ApplicationContextAware​​​​ApplicationContext​

以下示例说明了有效实体回调注册的集合:

例 109.示例 Bean 注册​​EntityCallback​

@Order(1)                                                           
@Component
class First implements BeforeSaveCallback<Person> {

@Override
public Person onBeforeSave(Person person) {
return // ...
}
}

@Component
class DefaultingEntityCallback implements BeforeSaveCallback<Person>,
Ordered {

@Override
public Object onBeforeSave(Person entity, String collection) {
// ...
}

@Override
public int getOrder() {
return 100;
}
}

@Configuration
public class EntityCallbackConfiguration {

@Bean
BeforeSaveCallback<Person> unorderedLambdaReceiverCallback() {
return (BeforeSaveCallback<Person>) it -> // ...
}
}

@Component
class UserCallbacks implements BeforeConvertCallback<User>,
BeforeSaveCallback<User> {

@Override
public Person onBeforeConvert(User user) {
return // ...
}

@Override
public Person onBeforeSave(User user) {
return // ...
}
}


​BeforeSaveCallback​​​从注释中接收其命令。​​@Order​


​BeforeSaveCallback​​​通过接口实现接收其订单。​​Ordered​


​BeforeSaveCallback​​​使用 lambda 表达式。默认无序,最后调用。请注意,由 lambda 表达式实现的回调不会公开类型信息,因此使用不可分配的实体调用这些信息会影响回调吞吐量。使用 aorto 为回调 Bean 启用类型筛选。​​class​​​​enum​


在单个实现类中组合多个实体回调接口。

10.16.3. 存储特定的实体回调

Spring Data MongoDB使用API作为其审计支持,并对以下回调做出反应。​​EntityCallback​

表 8.支持的实体回调

回调

方法

描述

次序

反应式/转换前回调

​onBeforeConvert(T entity, String collection)​

在将域对象转换为之前调用。​​org.bson.Document​

​Ordered.LOWEST_PRECEDENCE​

反应式/转换后回调

​onAfterConvert(T entity, org.bson.Document target, String collection)​

加载域对象后调用。
可以从 读取域对象后对其进行修改。​​​org.bson.Document​

​Ordered.LOWEST_PRECEDENCE​

Reactive/AuditingEntityCallback

​onBeforeConvert(Object entity, String collection)​

标记已创建修改的可审核实体

100

反应式/保存前回调

​onBeforeSave(T entity, org.bson.Document target, String collection)​

在保存域对象之前调用。
可以修改目标,要持久化,包含所有映射的实体信息。​​​Document​

​Ordered.LOWEST_PRECEDENCE​

反应式/保存后回调

​onAfterSave(T entity, org.bson.Document target, String collection)​

在保存域对象之前调用。
可以修改域对象,保存后返回,包含所有映射的实体信息。​​​Document​

​Ordered.LOWEST_PRECEDENCE​

10.17. 异常翻译

Spring 框架为各种数据库和映射技术提供了异常转换。这传统上是针对JDBC和JPA的。Spring对MongoDB的支持通过提供接口的实现将此功能扩展到MongoDB数据库。​​org.springframework.dao.support.PersistenceExceptionTranslator​

映射到Spring一致的数据访问异常层次结构背后的动机是,然后您可以编写可移植的描述性异常处理代码,而无需针对MongoDB错误代码进行编码。Spring 的所有数据访问异常都是从根类继承而来的,因此您可以确保在单个 try-catch 块中捕获所有与数据库相关的异常。请注意,并非所有 MongoDB 驱动程序抛出的异常都继承自类。保留内部异常和消息,以便不会丢失任何信息。​​DataAccessException​​​​MongoException​

某些映射由错误代码 1003、12001、12010、12011 和 12012 执行。查看实现以获取有关映射的更多详细信息。​​MongoExceptionTranslator​​​​com.mongodb.Network to DataAccessResourceFailureException​​​​MongoException​​​​InvalidDataAccessApiUsageException​

10.18. 执行回调

所有 Spring 模板类的一个共同设计特征是所有功能都路由到模板的回调方法之一中。这样做有助于确保一致地执行异常和可能需要的任何资源管理。虽然JDBC和JMS比MongoDB更需要这个特性,但它仍然为异常转换和日志记录提供了一个单一的位置。因此,使用这些回调是访问 MongoDB 驱动程序的 sandobjects 以执行未作为方法公开的不常见操作的首选方法。​​execute​​​​execute​​​​MongoDatabase​​​​MongoCollection​​​​MongoTemplate​

以下列表描述了回调方法。​​execute​

  • ​<T> T​​ execute:为指定类的实体集合运行给定的。(Class<?> entityClass, CollectionCallback<T> action)CollectionCallback
  • ​<T> T​​ execute:运行给定名称的集合。(String collectionName, CollectionCallback<T> action)CollectionCallback
  • ​<T> T​​ execute:运行 DbCallback,根据需要转换任何异常。Spring Data MongoDB为2.2版中引入MongoDB的聚合框架提供支持。(DbCallback<T> action)
  • ​<T> T​​ execute:在给定名称的集合上运行,根据需要翻译任何异常。(String collectionName, DbCallback<T> action)DbCallback
  • ​<T> T​​ executeInSession:在与数据库的同一连接中运行给定的,以确保在写入密集型环境中的一致性,您可以在其中读取写入的数据。(DbCallback<T> action)DbCallback

以下示例使用 theto 返回有关索引的信息:​​CollectionCallback​

boolean hasIndex = template.execute("geolocation", new CollectionCallbackBoolean>() {
public Boolean doInCollection(Venue.class, DBCollection collection) throws MongoException, DataAccessException {
List<Document> indexes = collection.getIndexInfo();
for (Document document : indexes) {
if ("location_2d".equals(document.get("name"))) {
return true;
}
}
return false;
}
});

10.19. 网格FS支持

MongoDB支持将二进制文件存储在其文件系统GridFS中。Spring Data MongoDB提供了一个接口以及相应的实现,让你与文件系统进行交互。您可以通过将 aas 和 a 交给它来设置实例,如以下示例所示:​​GridFsOperations​​​​GridFsTemplate​​​​GridFsTemplate​​​​MongoDatabaseFactory​​​​MongoConverter​

爪哇岛

.XML

class GridFsConfiguration extends AbstractMongoClientConfiguration {

// … further configuration omitted

@Bean
public GridFsTemplate gridFsTemplate() {
return new GridFsTemplate(mongoDbFactory(), mappingMongoConverter());
}
}

现在可以注入模板并用于执行存储和检索操作,如以下示例所示:

例 110.使用 GridFsTemplate 存储文件

class GridFsClient {

@Autowired
GridFsOperations operations;

@Test
public void storeFileToGridFs() {

FileMetadata metadata = new FileMetadata();
// populate metadata
Resource file = … // lookup File or Resource

operations.store(file.getInputStream(), "filename.txt", metadata);
}
}

这些操作采用有关要存储的文件的文件名和(可选)元数据信息。元数据可以是任意对象,该对象将由配置的 封送。或者,您也可以提供 a.​​store(…)​​​​InputStream​​​​MongoConverter​​​​GridFsTemplate​​​​Document​

您可以通过这些方法从文件系统中读取文件。我们先来看看方法。您可以找到单个文件或与 a 匹配的多个文件。可以使用帮助程序类来定义查询。它提供静态工厂方法来封装默认元数据字段(如 and)或自定义元数据字段。下面的示例演示如何使用 to 查询文件:​​find(…)​​​​getResources(…)​​​​find(…)​​​​Query​​​​GridFsCriteria​​​​whereFilename()​​​​whereContentType()​​​​whereMetaData()​​​​GridFsTemplate​

例 111.使用 GridFsTemplate 查询文件

class GridFsClient {

@Autowired
GridFsOperations operations;

@Test
public void findFilesInGridFs() {
GridFSFindIterable result = operations.find(query(whereFilename().is("filename.txt")))
}
}


目前,MongoDB不支持在从GridFS检索文件时定义排序条件。因此,将忽略在传递给该方法的实例上定义的任何排序标准。​​Query​​​​find(…)​

从 GridF 读取文件的另一个选项是使用接口引入的方法。它们允许将 Ant 路径交给方法,从而可以检索与给定模式匹配的文件。以下示例演示如何使用读取文件:​​ResourcePatternResolver​​​​GridFsTemplate​

例 112.使用 GridFsTemplate 读取文件

class GridFsClient {

@Autowired
GridFsOperations operations;

@Test
public void readFilesFromGridFs() {
GridFsResources[] txtFiles = operations.getResources("*.txt");
}
}

​GridFsOperations​​extendsand允许(例如)插入到anto从MongoDB数据库中读取Spring Config文件。​​ResourcePatternResolver​​​​GridFsTemplate​​​​ApplicationContext​

10.20.具有可尾游标的无限流

默认情况下,当客户端用尽游标提供的所有结果时,MongoDB会自动关闭游标。 在耗尽时关闭光标会将流转换为有限流。对于有上限的集合, 您可以使用在客户端之后保持打开状态的可尾游标 使用了所有最初返回的数据。

可以使用创建有上限的集合。为此,请提供所需的。​​MongoOperations.createCollection​​​​CollectionOptions.empty().capped()…​

可尾游标可以与命令式和反应式MongoDB API一起使用。强烈建议使用 反应性变体,因为它的资源密集度较低。但是,如果您无法使用反应式 API,您仍然可以使用消息传递 这个概念在Spring生态系统中已经很普遍。

10.20.1. 可尾游标​​MessageListener​

使用同步驱动程序侦听有上限的集合会创建一个长时间运行的阻止任务,需要委派给 一个单独的组件。在这种情况下,我们需要首先创建一个,这将是主入口点 用于运行特定。Spring Data MongoDB已经附带了一个默认实现,该实现 操作 和 能够为 a 创建和运行实例。​​MessageListenerContainer​​​​SubscriptionRequest​​​​MongoTemplate​​​​Task​​​​TailableCursorRequest​

下面的示例演示如何将可尾游标与实例一起使用:​​MessageListener​

例 113.带有实例的可尾游标​​MessageListener​

MessageListenerContainer container = new DefaultMessageListenerContainer(template);
container.start();

MessageListener<Document, User> listener = System.out::println;

TailableCursorRequest request = TailableCursorRequest.builder()
.collection("orders")
.filter(query(where("value").lt(100)))
.publishTo(listener)
.build();

container.register(request, User.class);

// ...

container.stop();

启动容器会初始化资源并启动已注册实例的实例。启动后添加的请求将立即运行。​​Task​​​​SubscriptionRequest​

定义收到 ais 时调用的侦听器。将转换为请求的域类型。用于接收未经转换的原始结果。​​Message​​​​Message#getBody()​​​​Document​

设置要收听的集合。

为要接收的文档提供可选筛选器。

设置要将传入消息发布到的消息侦听器。​​Message​

注册请求。返回的可用于检查当前状态并取消它以释放资源。​​Subscription​​​​Task​

一旦确定不再需要容器,请不要忘记停止它。这样做会停止容器内所有正在运行的实例。​​Task​

10.20.2. 反应式可尾游标

使用具有反应式数据类型的可尾游标可以构造无限流。可尾游标在外部关闭之前保持打开状态。当新文档到达上限集合时,它会发出数据。

如果查询未返回匹配项,或者游标返回集合“末尾”的文档,并且应用程序随后删除该文档,则可尾游标可能会变为死游标或无效。下面的示例演示如何创建和使用无限流查询:

例 114.使用ReactiveMongoOperations的无限流查询

Flux<Person> stream = template.tail(query(where("name").is("Joe")), Person.class);

Disposable subscription = stream.doOnNext(person -> System.out.println(person)).subscribe();

// …

// Later: Dispose the subscription to close the stream
subscription.dispose();

Spring Data MongoDB 反应式存储库通过注释查询方法支持无限流。这适用于能够发出多个元素的 return 和其他反应式类型的方法,如以下示例所示:​​@Tailable​​​​Flux​

例 115.使用 ReactiveMongoRepository 的无限流查询

public interface PersonRepository extends ReactiveMongoRepository<Person, String> {

@Tailable
Flux<Person> findByFirstname(String firstname);

}

Flux<Person> stream = repository.findByFirstname("Joe");

Disposable subscription = stream.doOnNext(System.out::println).subscribe();

// …

// Later: Dispose the subscription to close the stream