原标题:Spring认证中国教育管理中心-Spring Data MongoDB教程六(内容来源:Spring中国教育管理中心)
JSON 模式类型
下表显示了支持的 JSON 架构类型:
untyped是由所有类型化架构类型继承的泛型类型。它为untyped类型化架构类型提供所有架构属性。
有关更多信息,请参阅$jsonSchema。
11.6.8.流畅的模板API
MongoOperations当涉及到与 MongoDB 的更多低级交互时,该接口是核心组件之一。它提供了广泛的方法,涵盖了从集合创建、索引创建和 CRUD 操作到更高级功能(例如 Map-Reduce 和聚合)的需求。您可以为每个方法找到多个重载。它们中的大多数涵盖了 API 的可选或可为空的部分。
FluentMongoOperations为通用方法提供更窄的接口,MongoOperations并提供更易读、更流畅的 API。入口点(insert(…)、find(…)、update(…)和其他)遵循基于要运行的操作的自然命名模式。从入口点开始,API 旨在仅提供依赖于上下文的方法,这些方法导致调用实际MongoOperations对应方的终止方法——all以下示例中的方法:
List<SWCharacter> all = ops.find(SWCharacter.class) .inCollection("star-wars") .all();
如果使用类名SWCharacter定义集合@Document或使用类名作为集合名,则跳过此步骤,这很好。
有时,在MongoDB中集持有不同的类型,如实体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<?>)。
使用投影允许MongoTemplate通过限制对投影目标类型所需字段的实际响应来优化结果映射。只要Query本身不包含任何字段限制并且目标类型是封闭接口或 DTO 投影,这适用。
可以检索单个实体和检索多个实体作为一个之间切换List或Stream通过终止方法:first(),one(),all(),或stream()。
使用 编写地理空间查询时near(NearQuery),终止方法的数量将更改为仅包括对geoNear在 MongoDB 中运行命令有效的方法(将实体作为GeoResult内获取GeoResults),如以下示例所示:
GeoResults<Jedi> results = mongoOps.query(SWCharacter.class) .as(Jedi.class) .near(alderaan) // NearQuery.near(-73.9667, 40.78).maxDis… .all();
11.6.9.Kotlin 的类型安全查询
Kotlin 通过其语言语法和扩展系统支持特定领域的语言创建。Spring Data MongoDB 附带一个 Kotlin 扩展,用于Criteria使用Kotlin 属性引用来构建类型安全查询。使用此扩展的查询通常受益于提高的可读性。大多数关键字Criteria都有匹配的 Kotlin 扩展名,例如inValues和regex。
考虑以下解释类型安全查询的示例:
import org.springframework.data.mongodb.core.query.* 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 operatorsmongoOperations.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)。
11.6.10.其他查询选项
MongoDB 提供了多种将元信息(如注释或批量大小)应用于查询的Query方法。直接使用API 有几种方法可以用于这些选项。
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);
11.7.按示例查询
11.7.1.介绍
本章介绍了 Query by Example 并解释了如何使用它。
示例查询 (QBE) 是一种用户友好的查询技术,具有简单的界面。它允许动态创建查询,并且不需要您编写包含字段名称的查询。事实上,Query by Example 根本不需要您使用特定于商店的查询语言编写查询。
11.7.2.用法
Query by Example API 由三部分组成:
探针:具有填充字段的域对象的实际示例。
ExampleMatcher:ExampleMatcher包含有关如何匹配特定字段的详细信息。它可以在多个示例中重复使用。
Example: AnExample由探针和ExampleMatcher. 它用于创建查询。
Query by Example 非常适合以下几个用例:
使用一组静态或动态约束查询您的数据存储。
频繁重构域对象而不必担心破坏现有查询。
独立于底层数据存储 API 工作。
Query by Example 也有几个限制:
不支持嵌套或分组的属性约束,例如firstname = ?0 or (firstname = ?1 and lastname = ?2).
仅支持字符串的开始/包含/结束/正则表达式匹配以及其他属性类型的精确匹配。
在开始使用 Query by Example 之前,您需要有一个域对象。首先,为您的存储库创建一个接口,如以下示例所示:
示例 91. 示例 Person 对象
public class Person { @Id private String id; private String firstname; private String lastname; private Address address; // … getters and setters omitted}
前面的示例显示了一个简单的域对象。您可以使用它来创建Example. 默认情况下,null忽略具有值的字段,并使用商店特定的默认值匹配字符串。
将属性包含在 Query by Example 标准中是基于可空性。除非忽略属性路径,否则始终包含使用原始类型 ( int, double, ...)的属性。
可以使用of工厂方法或使用ExampleMatcher. Example是不可变的。以下清单显示了一个简单的示例:
示例 92. 简单示例
Person person = new Person(); person.setFirstname("Dave"); Example<Person> example = Example.of(person);
创建域对象的新实例。
设置要查询的属性。
创建Example.
您可以使用存储库运行示例查询。为此,让您的存储库接口扩展QueryByExampleExecutor<T>. 以下清单显示了QueryByExampleExecutor界面的摘录:
例 93. 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.}
11.7.3.示例匹配器
示例不限于默认设置。您可以使用 为字符串匹配、空值处理和特定于属性的设置指定自己的默认值ExampleMatcher,如以下示例所示:
示例 94. 具有自定义匹配的示例匹配器
Person person = new Person(); person.setFirstname("Dave"); ExampleMatcher matcher = ExampleMatcher.matching() .withIgnorePaths("lastname") .withIncludeNullValues() .withStringMatcher(StringMatcher.ENDING); Example<Person> example = Example.of(person, matcher);
创建域对象的新实例。
设置属性。
创建一个ExampleMatcher以期望所有值匹配。即使没有进一步的配置,它也可以在这个阶段使用。
构造一个新ExampleMatcher的忽略lastname属性路径。
构造一个 newExampleMatcher以忽略lastname属性路径并包含空值。
构造一个 newExampleMatcher来忽略lastname属性路径,包含空值,并执行后缀字符串匹配。
创建一个新的Example基于域对象和配置上ExampleMatcher。
默认情况下,ExampleMatcher期望在探测器上设置的所有值都匹配。如果要获得与任何隐式定义的谓词匹配的结果,请使用
ExampleMatcher.matchingAny().
您可以为单个属性指定行为(例如“名字”和“姓氏”,或者对于嵌套属性,“address.city”)。您可以使用匹配选项和区分大小写来调整它,如以下示例所示:
示例 95. 配置匹配器选项
ExampleMatcher matcher = ExampleMatcher.matching() .withMatcher("firstname", endsWith()) .withMatcher("lastname", startsWith().ignoreCase()); }
另一种配置匹配器选项的方法是使用 lambdas(在 Java 8 中引入)。这种方法创建了一个回调,要求实现者修改匹配器。您不需要返回匹配器,因为配置选项保存在匹配器实例中。以下示例显示了使用 lambda 的匹配器:
示例 96. 使用 lambda 配置匹配器选项
ExampleMatcher matcher = ExampleMatcher.matching() .withMatcher("firstname", match -> match.endsWith()) .withMatcher("firstname", match -> match.startsWith()); }
Example使用配置的合并视图创建的查询。默认匹配设置可以在ExampleMatcher级别设置,而单独的设置可以应用于特定的属性路径。已设置上的设置ExampleMatcher由属性路径设置继承,除非它们被明确定义。属性补丁上的设置比默认设置具有更高的优先级。下表描述了各种ExampleMatcher设置的范围:
11.7.4.运行示例
以下示例显示了在使用存储库(Person在本例中为对象)时如何按示例进行查询:
示例 97. 使用存储库按示例查询
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实例的集合名称。
在 中包含null值时ExampleSpec,Spring Data Mongo 使用嵌入式文档匹配而不是点符号属性匹配。这样做会强制对嵌入文档中的所有属性值和属性顺序进行精确的文档匹配。
Spring Data MongoDB 支持以下匹配选项:
11.7.5.无类型示例
默认情况下Example是严格键入的。这意味着映射的查询具有包含的类型匹配,将其限制为探测可分配的类型。例如,当坚持使用默认类型键 ( _class) 时,查询具有诸如 ( _class : { $in : [ com.acme.Person] }) 之类的限制。
通过使用UntypedExampleMatcher,可以绕过默认行为并跳过类型限制。因此,只要字段名称匹配,几乎任何域类型都可以用作创建引用的探针,如以下示例所示:
示例 98. 无类型的示例查询
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为以确保读取操作的正确别名解析。
11.8.计数文件
在 SpringData MongoDB 3.x 之前的版本中,计数操作使用 MongoDB 的内部收集统计信息。随着MongoDB 事务的引入,这不再可能,因为统计数据无法正确反映需要基于聚合的计数方法的事务期间的潜在变化。因此,在 2.x 版本中,MongoOperations.count()如果没有正在进行的事务,将使用收集统计信息,如果是,则使用聚合变体。
从 Spring Data MongoDB 3.x 开始,任何count操作都使用通过 MongoDBs 的基于聚合的计数方法的过滤条件是否存在countDocuments。如果应用程序在处理收集统计数据的限制下很好,则
MongoOperations.estimatedCount()提供了一种替代方法。
MongoDBs本地countDocuments方法和$match聚集,不支持$near和$nearSphere但需要$geoWithin连同$center或$centerSphere不支持$minDistance(见
https://jira.mongodb.org/browse/SERVER-37043)。
因此,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。
现在使用$geoWithinwith重写查询$center。
使用$nearwith$minDistance和计算源查询$maxDistance。
重写的查询现在结合了$nor $geowithin标准来解决不受支持的$minDistance.
11.9.Map-Reduce 操作
您可以使用 Map-Reduce 查询 MongoDB,这对于批处理、数据聚合以及查询语言不能满足您的需求时非常有用。
Spring 通过提供方法MongoOperations来简化 Map-Reduce 操作的创建和运行,从而提供与 MongoDB 的 Map-Reduce 的集成。它可以将 Map-Reduce 操作的结果转换为 POJO,并与 Spring 的Resource 抽象集成。这让您可以放置您的文件系统、类路径、HTTP 服务器或任何其他 Spring 资源实现上的 JavaScript 文件,然后通过简单的 URI 样式语法引用 JavaScript 资源——例如,classpath:reduce.js;在文件中外部化 JavaScript 代码通常比将它们作为 Java 字符串嵌入到文件中更可取您的代码。请注意,如果您愿意,您仍然可以将 JavaScript 代码作为 Java 字符串传递。
11.9.1.示例用法
为了理解如何执行 Map-Reduce 操作,我们使用了MongoDB - The Definitive Guide [ 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" ] }
以下 map 函数计算每个文档的数组中每个字母的出现次数:
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 函数位于map.js和reduce.js并捆绑在您的 jar 中,因此它们在类路径上可用,您可以按如下方式运行 Map-Reduce 操作:
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并提供访问的原始输出和定时和下面列出了计数statistics.TheValueObject类:
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 + "]"; } }
默认情况下,使用的是输出类型,INLINE因此您无需指定输出集合。要指定额外的 Map-Reduce 选项,请使用带有额外MapReduceOptions参数的重载方法。该类MapReduceOptions具有流畅的 API,因此可以添加额外的选项以紧凑的语法完成。以下示例将输出集合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 操作的数据集。以下示例从 Map-Reduce 操作的考虑中删除包含 [a,b] 的文档:
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);
请注意,您可以在查询上指定额外的限制和排序值,但不能跳过值。