在前面,我们看到了如何使用 MongoTemplate 上的 findOne() 和 findById() 方法检索单个文档,这些方法返回一个领域对象。
我们还可以通过查询返回一个文档集合,作为域对象列表返回。假设我们有许多 Person 对象,名称(name)和年龄(age)作为文档存储在一个集合中,我们可以使用以下代码运行查询:
(1)Person 实体
package com.hxstrive.springdata.mongodb.entity; import lombok.Builder; import lombok.Data; import lombok.ToString; import org.springframework.data.annotation.TypeAlias; import org.springframework.data.annotation.Version; import org.springframework.data.mongodb.core.mapping.Document; /** * 用户 * @author hxstrive.com */ @Document("person") @TypeAlias("pers") @Data @Builder @ToString public class Person extends Contact { /** ID,自动映射到 MongoDB 的 _id 字段 */ private String id; /** 姓名 */ private String name; /** 年龄 */ private int age; /** 版本 */ @Version private long version; }
(2)查询代码,如下:
package com.hxstrive.springdata.mongodb.query; import com.hxstrive.springdata.mongodb.entity.Person; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.mongodb.core.MongoTemplate; import java.util.List; import static org.springframework.data.mongodb.core.query.Criteria.where; import static org.springframework.data.mongodb.core.query.Query.query; @SpringBootTest public class QueryDocumentDemo { @Autowired private MongoTemplate mongoTemplate; @Test public void contextLoads() { // 删除集合,清空数据 mongoTemplate.dropCollection(Person.class); // 准备数据 mongoTemplate.insert(Person.builder().id("1").name("Tom").age(22).build()); mongoTemplate.insert(Person.builder().id("2").name("Helen").age(29).build()); mongoTemplate.insert(Person.builder().id("3").name("Bill").age(37).build()); mongoTemplate.insert(Person.builder().id("4").name("Joe").age(48).build()); // 查询数据 // 年龄小于30岁,且名称中包含 “l” 字符 List<Person> result = mongoTemplate.query(Person.class) .matching(query(where("age").lt(30).and("name").regex("l"))) .all(); for(Person person : result) { System.out.println(person); } } }
运行代码,输出如下:
Person(id=2, name=Helen, age=29, version=1)
所有的查找方法都需要一个查询对象作为参数,这个对象定义了用于执行查询的标准和选项。查询标准是通过使用一个 Criteria 对象来指定的,该对象有一个静态工厂方法 where(),用来实例化一个新的 Criteria 对象。推荐使用 org.springframework.data.mongodb.core.query.Criteria.where 和 org.springframework.data.mongodb.core.query.Query.query 的静态导入,使查询更易读。
上面示例中,查询应该返回一个符合指定条件的 Person 对象的列表。下面将列出 Criteria 和 Query 类的方法,这些方法对应 MongoDB 中提供的操作符。大多数方法都返回 Criteria 对象,以便为 API 提供链式风格的查询。
Criteria 类提供以下方法,所有这些方法都对应于 MongoDB 中的操作符:
Criteria all(Object o) 使用 $all 操作符创建条件,匹配所有指定元素的文档。
Criteria and(String key) 在当前的 Criteria 中添加一个带有指定键的链式 Criteria,并返回新创建的 Criteria。“与” 条件查询。
Criteria andOperator(Criteria… criteria) 使用 $and 操作符为所有提供的条件创建 and 查询(需要 MongoDB 2.0 或更高版本)
Criteria andOperator(Collection<Criteria> criteria) 使用 $and 操作符为所有提供的条件创建 and 查询(需要 MongoDB 2.0 或更高版本)
Criteria elemMatch(Criteria c) 使用 $elemMatch 操作符创建条件,匹配内嵌文档或数组中的部分字段。
Criteria exists(boolean b) 使用 $exists 操作符创建条件,查询存在指定字段的文档。
Criteria gt(Object o) 使用 $gt 操作符创建条件,匹配大于(>)指定值的文档。
Criteria gte(Object o) 使用 $gte 操作符创建条件,匹配大于等于(>=)指定值的文档。
Criteria in(Object… o) 使用可变参数的 $in 操作符创建条件,匹配数组中的任一值。
Criteria in(Collection<?> collection) 使用集合使用 $in 操作符创建条件。
Criteria is(Object o) 使用匹配字段({ key:value })创建一个条件。如果指定的值是一个文档,那么字段的顺序和文档中的完全相等就很重要。
Criteria lt(Object o) 使用 $lt 操作符创建条件,匹配小于(<)指定值的文档。
Criteria lte(Object o) 使用 $lte 操作符创建条件,匹配小于等于(<=)指定值的文档。
Criteria mod(Number value, Number remainder) 使用 $mod 操作符创建条件,取余条件查询。
Criteria ne(Object o) 使用 $ne 操作符创建条件,匹配不等于(≠)指定值的文档。
Criteria nin(Object… o) 使用 $nin 操作符创建条件,不匹配数组中的值。
Criteria norOperator(Criteria… criteria) 使用 $nor 操作符为所有提供的条件创建 nor 查询。
Criteria norOperator(Collection<Criteria> criteria) 使用 $nor 操作符为所有提供的条件创建 nor 查询。
Criteria not() 使用 $not 操作符创建一个条件,该操作符直接影响紧随其后的子句,查询与表达式不匹配的文档。
Criteria orOperator(Criteria… criteria) 使用 $or 操作符为所有提供的条件创建 or 查询
Criteria orOperator(Collection<Criteria> criteria) 使用 $or 操作符为所有提供的条件创建 or 查询
Criteria regex(String re) 使用 $regex 操作符创建条件,正则表达式查询。
Criteria sampleRate(double sampleRate) 使用 $sampleRate 操作符创建条件。
Criteria size(int s) 使用 $size 操作符创建条件,匹配数组长度为指定大小的文档。
Criteria type(int t) 使用 $type 操作符创建条件,查询类型为指定类型的文档,3.2 版本添加 alias 别名,各种类型的 Number 及 Alias 如下:
Number | Alias | |
---|---|---|
Double | 1 | “double” |
String | 2 | “string” |
Object | 3 | “object” |
Array | 4 | “array” |
Binary data | 5 | “binData” |
Undefined | 6 | “undefined” |
ObjectId | 7 | “objectId” |
Boolean | 8 | “bool” |
Date | 9 | “date” |
Null | 10 | “null” |
Regular Expression | 11 | “regex” |
DBPointer | 12 | “dbPointer” |
JavaScript | 13 | “javascript” |
Symbol | 14 | “symbol” |
JavaScript(with scope) | 15 | “javascriptWithScope” |
32-bit integer | 16 | “int” |
Timestamp | 17 | “timestamp” |
64-bit integer | 18 | “long” |
Decimal128 | 19 | “decimal” |
Min key | -1 | “minKey” |
Max key | 127 | “maxKey” |
Criteria matchingDocumentStructure(MongoJsonSchema schema) 使用 $jsonSchema 操作符为 JSON 模式标准创建一个条件。$jsonSchema 只能应用于查询的顶层,而不是特定的属性,使用模式的属性来匹配嵌套字段。
Criteria bits() 是通往 MongoDB 位数查询操作符的网关,如:$bitsAllClear。
Criteria 类还为地理空间查询提供了以下方法:
Criteria within(Circle circle) 使用 $geoWithin、$center 操作符创建地理空间条件。
Criteria within(Box box) 使用 $geoWithin、$box 操作符创建地理空间条件。
Criteria withinSphere(Circle circle) 使用 $geoWithin、$center 操作符创建地理空间条件。
Criteria near(Point point) 使用 $near 操作符创建地理空间条件
Criteria nearSphere(Point point) 使用 $nearSphere$center 操作符创建地理空间条件,这仅适用于 MongoDB 1.7 及更高版本。
Criteria minDistance(double minDistance) 使用 $minDistance 操作符创建地理空间条件,以便与 $near 一起使用。
Criteria maxDistance(double maxDistance) 使用 $maxDistance 操作符创建地理空间条件,以便与 $near 一起使用。
Query 类有一些额外的方法,可以为查询提供选项:
Query addCriteria(Criteria criteria) 用于向查询添加其他条件
Field fields() 用于定义要包含在查询结果中的字段
Query limit(int limit) 用于将返回结果的大小限制为提供的限制(用于分页)
Query skip(int skip) 用于跳过结果中提供的文档数(用于分页)
Query with(Sort sort) 用于为结果提供排序定义
MongoDB支持投影查询返回的字段。投影可以根据字段的名称包含和排除字段(除非显式排除,否则始终包含 _id 字段):
package com.hxstrive.springdata.mongodb.query; import com.hxstrive.springdata.mongodb.entity.Person; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Query; import java.util.List; import static org.springframework.data.mongodb.core.query.Criteria.where; import static org.springframework.data.mongodb.core.query.Query.query; @SpringBootTest public class QueryDocumentDemo2 { @Autowired private MongoTemplate mongoTemplate; @Test public void contextLoads() { // 删除集合,清空数据 mongoTemplate.dropCollection(Person.class); // 准备数据 mongoTemplate.insert(Person.builder().id("1").name("Tom").age(22).build()); mongoTemplate.insert(Person.builder().id("2").name("Helen").age(29).build()); mongoTemplate.insert(Person.builder().id("3").name("Bill").age(37).build()); mongoTemplate.insert(Person.builder().id("4").name("Joe").age(48).build()); // 查询数据 Query query = query(where("age").gte(20)); // 手动指定只查询 id、name 字段 query.fields().include("id", "name"); List<Person> result = mongoTemplate.query(Person.class) .matching(query) .all(); for(Person person : result) { System.out.println(person); } } }
运行代码,输出如下:
Person(id=1, name=Tom, age=null, version=null) Person(id=2, name=Helen, age=null, version=null) Person(id=3, name=Bill, age=null, version=null) Person(id=4, name=Joe, age=null, version=null)
更多选择字段的代码,如下:
// 结果将通过{"last_name":1}同时包含 _id 和 last_name。 query.fields().include("lastname"); // 结果将仅通过{"_id":0, "last_name":1}包含 last_name。 query.fields().exclude("id").include("lastname"); // 结果将通过{"address":1}包含 _id 和整个 address 对象。 query.fields().include("address"); // 结果将包含通过{"address.city":1}仅包含城市字段的 id 和 address 对象。 query.fields().include("address.city");
从 MongoDB 4.4 开始,您可以使用聚合表达式进行字段投影,如下所示:
query.fields() // 使用一个本地表达式,使用的字段名必须是指数据库文件中的字段名。 .project(MongoExpression.create("'$toUpper' : '$last_name'")) // 指定表达式结果被投射到的字段名,由此产生的字段名不会针对域模型进行映射 .as("last_name"); query.fields() // 使用 AggregationExpression。除了本地 MongoExpression外,字段名被映射到域模型中使用的字段名 .project(StringOperators.valueOf("lastname").toUpper()) .as("last_name"); query.fields() // 将 SpEL与 AggregationExpression 一起使用来调用表达式函数,字段名被映射到领域模型中使用的字段名 .project(AggregationSpELExpression.expressionOf("toUpper(lastname)")) .as("last_name");
示例代码:
package com.hxstrive.springdata.mongodb.query; import com.hxstrive.springdata.mongodb.entity.Person; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.mongodb.MongoExpression; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Query; import java.util.List; import static org.springframework.data.mongodb.core.query.Criteria.where; import static org.springframework.data.mongodb.core.query.Query.query; @SpringBootTest public class QueryDocumentDemo3 { @Autowired private MongoTemplate mongoTemplate; @Test public void contextLoads() { // 删除集合,清空数据 mongoTemplate.dropCollection(Person.class); // 准备数据 mongoTemplate.insert(Person.builder().id("1").name("Tom").age(22).build()); mongoTemplate.insert(Person.builder().id("2").name("Helen").age(29).build()); mongoTemplate.insert(Person.builder().id("3").name("Bill").age(37).build()); mongoTemplate.insert(Person.builder().id("4").name("Joe").age(48).build()); // 查询数据 Query query = query(where("age").gte(20)); // 手动指定只查询 id、name 字段 query.fields() // 使用一个本地表达式,使用的字段名必须是指数据库文件中的字段名 // 将 name 的值转换成大写值 .project(MongoExpression.create("'$toUpper' : '$name'")) // 指定表达式结果被投射到 name 字段 .as("name"); List<Person> result = mongoTemplate.query(Person.class) .matching(query) .all(); for(Person person : result) { System.out.println(person); } } }
运行代码,输出如下:
Person(id=1, name=TOM, age=null, version=null) Person(id=2, name=HELEN, age=null, version=null) Person(id=3, name=BILL, age=null, version=null) Person(id=4, name=JOE, age=null, version=null)
注意,@Query(fields="...") 允许在 Repository 级别使用表达式字段投影,如 MongoDB 基于 JSON 的查询方法和字段限制中所述。