Spring Data MongoDB 教程

查询集合中的文档

在前面,我们看到了如何使用 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 类的方法

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 如下:

TypeNumberAlias
Double1“double”
String2“string”
Object3“object”
Array4“array”
Binary data5“binData”
Undefined6“undefined”
ObjectId7“objectId”
Boolean8“bool”
Date9“date”
Null10“null”
Regular Expression11“regex”
DBPointer12“dbPointer”
JavaScript13“javascript”
Symbol14“symbol”
JavaScript(with scope)15“javascriptWithScope”
32-bit integer16“int”
Timestamp17“timestamp”
64-bit integer18“long”
Decimal12819“decimal”
Min key-1“minKey”
Max key127“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 类有一些额外的方法,可以为查询提供选项:

  • 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 的查询方法和字段限制中所述。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号