A Guide to Queries in Spring Data MongoDB – Spring Data MongoDB中的查询指南

最后修改: 2015年 8月 11日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

This tutorial will focus on building out different types of queries in Spring Data MongoDB.

本教程将重点介绍在Spring Data MongoDB中构建不同的类型的查询

We’re going to be looking at querying documents with Query and Criteria classes, auto-generated query methods, JSON queries, and QueryDSL.

我们将研究用QueryCriteria类、自动生成的查询方法、JSON查询和QueryDSL来查询文档。

For the Maven setup, have a look at our introductory article.

关于Maven设置,请看我们的介绍性文章

2. Documents Query

2.文件查询

One of the more common ways to query MongoDB with Spring Data is by making use of the Query and Criteria classes, which very closely mirror native operators.

使用Spring Data查询MongoDB的一种更常见的方式是利用QueryCriteria类,它们非常接近本地操作符。

2.1. Is

2.1.

This is simply a criterion using equality. Let’s see how it works.

这只是一个使用平等的标准。让我们看看它是如何工作的。

In the following example, we’ll look for users named Eric.

在下面的例子中,我们将寻找名为Eric的用户。

Let’s look at our database:

让我们看一下我们的数据库。

[
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581907"),
        "_class" : "org.baeldung.model.User",
        "name" : "Eric",
        "age" : 45
    },
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581908"),
        "_class" : "org.baeldung.model.User",
        "name" : "Antony",
        "age" : 55
    }
}

Now let’s look at the query code:

现在我们来看看查询代码。

Query query = new Query();
query.addCriteria(Criteria.where("name").is("Eric"));
List<User> users = mongoTemplate.find(query, User.class);

As expected, this logic returns:

正如预期的那样,这种逻辑返回。

{
    "_id" : ObjectId("55c0e5e5511f0a164a581907"),
    "_class" : "org.baeldung.model.User",
    "name" : "Eric",
    "age" : 45
}

2.2. Regex

2.2. Regex

A more flexible and powerful type of query is the regex. This creates a criterion using a MongoDB $regex that returns all records suitable for the regex for this field.

一个更灵活和强大的查询类型是regex。这使用MongoDB $regex创建一个标准,返回适合该字段regex的所有记录。

It works similarly to startingWith and endingWith operations.

它的工作原理类似于startingWithendingWith操作。

In this example, we’ll look for all users that have names starting with A.

在这个例子中,我们将寻找所有名字以A开头的用户。

Here’s the state of the database:

这里是数据库的状态。

[
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581907"),
        "_class" : "org.baeldung.model.User",
        "name" : "Eric",
        "age" : 45
    },
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581908"),
        "_class" : "org.baeldung.model.User",
        "name" : "Antony",
        "age" : 33
    },
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581909"),
        "_class" : "org.baeldung.model.User",
        "name" : "Alice",
        "age" : 35
    }
]

Now let’s create the query:

现在我们来创建查询。

Query query = new Query();
query.addCriteria(Criteria.where("name").regex("^A"));
List<User> users = mongoTemplate.find(query,User.class);

This runs and returns 2 records:

这样运行并返回2条记录。

[
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581908"),
        "_class" : "org.baeldung.model.User",
        "name" : "Antony",
        "age" : 33
    },
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581909"),
        "_class" : "org.baeldung.model.User",
        "name" : "Alice",
        "age" : 35
    }
]

Here’s another quick example, this time looking for all users that have names ending with c:

这里是另一个快速的例子,这次是寻找所有名字以c结尾的用户。

Query query = new Query();
query.addCriteria(Criteria.where("name").regex("c$"));
List<User> users = mongoTemplate.find(query, User.class);

So the result will be:

因此,结果将是。

{
    "_id" : ObjectId("55c0e5e5511f0a164a581907"),
    "_class" : "org.baeldung.model.User",
    "name" : "Eric",
    "age" : 45
}

2.3. Lt and gt

2.3.Ltgt

These operators create a criterion using the $lt (less than) and $gt (greater than) operators.

这些运算符使用$lt(小于)和$gt(大于)运算符创建一个标准。

Let’s take a quick example where we’re looking for all users between the ages of 20 and 50.

让我们举一个简单的例子,我们正在寻找所有年龄在20至50岁之间的用户。

The database is:

该数据库是。

[
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581907"),
        "_class" : "org.baeldung.model.User",
        "name" : "Eric",
        "age" : 45
    },
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581908"),
        "_class" : "org.baeldung.model.User",
        "name" : "Antony",
        "age" : 55
    }
}

The query code:

查询代码。

Query query = new Query();
query.addCriteria(Criteria.where("age").lt(50).gt(20));
List<User> users = mongoTemplate.find(query,User.class);

And the results for all users with an age of greater than 20 and less than 50:

以及所有年龄大于20岁和小于50岁的用户的结果。

{
    "_id" : ObjectId("55c0e5e5511f0a164a581907"),
    "_class" : "org.baeldung.model.User",
    "name" : "Eric",
    "age" : 45
}

2.4. Sort

2.4.排序

Sort is used to specify a sort order for the results.

Sort用于为结果指定一个排序顺序。

The example below returns all the users sorted by age in ascending order.

下面的例子是以升序方式返回所有按年龄排序的用户。

First, here’s the existing data:

首先,这里是现有的数据。

[
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581907"),
        "_class" : "org.baeldung.model.User",
        "name" : "Eric",
        "age" : 45
    },
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581908"),
        "_class" : "org.baeldung.model.User",
        "name" : "Antony",
        "age" : 33
    },
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581909"),
        "_class" : "org.baeldung.model.User",
        "name" : "Alice",
        "age" : 35
    }
]

After executing sort:

在执行了sort之后。

Query query = new Query();
query.with(Sort.by(Sort.Direction.ASC, "age"));
List<User> users = mongoTemplate.find(query,User.class);

And here’s the result of the query, nicely sorted by age:

这里是查询的结果,按年龄很好地进行了排序。

[
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581908"),
        "_class" : "org.baeldung.model.User",
        "name" : "Antony",
        "age" : 33
    },
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581909"),
        "_class" : "org.baeldung.model.User",
        "name" : "Alice",
        "age" : 35
    },
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581907"),
        "_class" : "org.baeldung.model.User",
        "name" : "Eric",
        "age" : 45
    }
]

2.5. Pageable

2.5. Pageable

Let’s look at a quick example using pagination.

让我们看一个使用分页的快速例子。

Here’s the state of the database:

这里是数据库的状态。

[
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581907"),
        "_class" : "org.baeldung.model.User",
        "name" : "Eric",
        "age" : 45
    },
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581908"),
        "_class" : "org.baeldung.model.User",
        "name" : "Antony",
        "age" : 33
    },
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581909"),
        "_class" : "org.baeldung.model.User",
        "name" : "Alice",
        "age" : 35
    }
]

Now here’s the query logic, simply asking for a page of size 2:

现在,这里是查询逻辑,只是要求一个大小为2的页面。

final Pageable pageableRequest = PageRequest.of(0, 2);
Query query = new Query();
query.with(pageableRequest);

And the result, the 2 documents, as expected:

而结果,这2个文件,正如预期的那样。

[
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581907"),
        "_class" : "org.baeldung.model.User",
        "name" : "Eric",
        "age" : 45
    },
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581908"),
        "_class" : "org.baeldung.model.User",
        "name" : "Antony",
        "age" : 33
    }
]

3. Generated Query Methods

3.生成的查询方法

Now let’s explore the more common type of query that Spring Data usually provides, auto-generated queries out of method names.

现在让我们来探索Spring Data通常提供的更常见的查询类型,即从方法名中自动生成的查询。

The only thing we need to do to leverage these kinds of queries is to declare the method on the repository interface:

我们需要做的唯一一件事就是在存储库接口上声明该方法,以利用这类查询。

public interface UserRepository 
  extends MongoRepository<User, String>, QueryDslPredicateExecutor<User> {
    ...
}

3.1. FindByX

3.1.FindByX

We’ll start simple, by exploring the findBy type of query. In this case, we’ll use find by name:

我们将从简单的开始,通过探索findBy类型的查询。在这种情况下,我们将使用按名称查找。

List<User> findByName(String name);

Just like in the previous section, 2.1, the query will have the same results, finding all users with the given name:

就像上一节中的2.1一样,查询会有同样的结果,找到所有具有给定名称的用户。

List<User> users = userRepository.findByName("Eric");

3.2. StartingWith and endingWith

3.2.StartingWithendingWith

In section 2.2, we explored a regex based query. Starts and ends with are of course less powerful, but nevertheless quite useful, especially if we don’t have to actually implement them.

在第2.2节中,我们探讨了一个基于regex的查询。Starts和ends with当然没有那么强大,但还是相当有用的,尤其是当我们不必实际实现它们的时候。

Here’s a quick example of what the operations would look like:

下面是一个快速的例子,说明这些操作会是什么样子。

List<User> findByNameStartingWith(String regexp);
List<User> findByNameEndingWith(String regexp);

The example of actually using this would, of course, be very simple:

当然,实际使用这个的例子会非常简单。

List<User> users = userRepository.findByNameStartingWith("A");
List<User> users = userRepository.findByNameEndingWith("c");

And the results are exactly the same.

而结果是完全一样的。

3.3. Between

3.3.之间

Similar to section 2.3, this will return all users with ages between ageGT and ageLT:

与2.3节类似,这将返回所有年龄在ageGTageLT:
之间的用户。

List<User> findByAgeBetween(int ageGT, int ageLT);

Calling the method will result in exactly the same documents being found:

调用该方法的结果将是找到完全相同的文件。

List<User> users = userRepository.findByAgeBetween(20, 50);

3.4. Like and OrderBy

3.4.LikeOrderBy

Let’s have a look at a more advanced example this time, combining two types of modifiers for the generated query.

这次我们来看看一个更高级的例子,结合两种类型的修改器来生成查询。

We’re going to be looking for all users that have names containing the letter A, and we’re also going to order the results by age, in ascending order:

我们将寻找所有名字中含有字母A,的用户,我们还将按年龄对结果进行排序,以升序排列。

List<User> users = userRepository.findByNameLikeOrderByAgeAsc("A");

For the database we used in section 2.4, the result will be:

对于我们在2.4节中使用的数据库,结果将是。

[
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581908"),
        "_class" : "org.baeldung.model.User",
        "name" : "Antony",
        "age" : 33
    },
    {
        "_id" : ObjectId("55c0e5e5511f0a164a581909"),
        "_class" : "org.baeldung.model.User",
        "name" : "Alice",
        "age" : 35
    }
]

4. JSON Query Methods

4.JSON查询方法

If we can’t represent a query with the help of a method name or criteria, we can do something more low level, use the @Query annotation.

如果我们不能借助方法名或标准来表示一个查询,我们可以做一些更低级的事情,使用@Query注解

With this annotation, we can specify a raw query as a Mongo JSON query string.

通过这个注解,我们可以将原始查询指定为Mongo JSON查询字符串。

4.1. FindBy

4.1.FindBy

Let’s start simple and look at how we would represent a find by type of method first:

让我们从简单的开始,先看一下我们如何表示一个通过类型的方法

@Query("{ 'name' : ?0 }")
List<User> findUsersByName(String name);

This method should return users by name. The placeholder ?0 references the first parameter of the method.

这个方法应该按名字返回用户。占位符 ?0引用该方法的第一个参数。

List<User> users = userRepository.findUsersByName("Eric");

4.2. $regex

4.2. $regex

We can also look at a regex driven query, which of course produces the same result as in sections 2.2 and 3.2:

我们也可以看一下一个regex驱动的查询,这当然会产生与2.2和3.2节中相同的结果。

@Query("{ 'name' : { $regex: ?0 } }")
List<User> findUsersByRegexpName(String regexp);

The usage is also exactly the same:

使用方法也完全相同。

List<User> users = userRepository.findUsersByRegexpName("^A");
List<User> users = userRepository.findUsersByRegexpName("c$");

4.3. $lt and $gt

4.3.$lt$gt

Now let’s implement the lt and gt query:

现在我们来实现ltgt查询。

@Query("{ 'age' : { $gt: ?0, $lt: ?1 } }")
List<User> findUsersByAgeBetween(int ageGT, int ageLT);

Now that the method has 2 parameters, we’re referencing each of these by index in the raw query, ?0 and ?1:

现在,该方法有2个参数,我们在原始查询中通过索引来引用每个参数,?0?1:

List<User> users = userRepository.findUsersByAgeBetween(20, 50);

5. QueryDSL Queries

5 查询DSL查询

MongoRepository has good support for the QueryDSL project, so we can leverage that nice, type-safe API here as well.

MongoRepositoryQueryDSL项目有很好的支持,所以我们也可以在这里利用这个漂亮的、类型安全的 API。

5.1. The Maven Dependencies

5.1.Maven的依赖性

First, let’s make sure we have the correct Maven dependencies defined in the pom:

首先,让我们确保在pom中定义了正确的Maven依赖项。

<dependency>
    <groupId>com.mysema.querydsl</groupId>
    <artifactId>querydsl-mongodb</artifactId>
    <version>4.3.1</version>
</dependency>
<dependency>
    <groupId>com.mysema.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <version>4.3.1</version>
</dependency>

5.2. Q-classes

5.2.Q

QueryDSL used Q-classes for creating queries, but since we don’t really want to create these by hand, we need to generate them somehow.

QueryDSL使用Q-classes来创建查询,但由于我们并不希望手工创建这些查询,我们需要以某种方式来生成它们

We’re going to use the apt-maven-plugin to do that:

我们将使用apt-maven-plugin来实现这一目标。

<plugin>    
    <groupId>com.mysema.maven</groupId>
    <artifactId>apt-maven-plugin</artifactId>
    <version>1.1.3</version>
    <executions>
        <execution>
            <goals>
                <goal>process</goal>
            </goals>
            <configuration>
                <outputDirectory>target/generated-sources/java</outputDirectory>
                <processor>
                  org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor
                </processor>
            </configuration>
        </execution>
     </executions>
</plugin>

Let’s look at the User class, focusing specifically on the @QueryEntity annotation:

让我们看看User类,特别关注@QueryEntity注解。

@QueryEntity 
@Document
public class User {
 
    @Id
    private String id;
    private String name;
    private Integer age;
 
    // standard getters and setters
}

After running the process goal of the Maven lifecycle (or any other goal after that one), the apt plugin will generate the new classes under target/generated-sources/java/{your package structure}:

运行Maven生命周期的process 目标(或之后的任何其他目标)后,apt插件将在target/generated-sources/java/{your package structure}下生成新类。

/**
 * QUser is a Querydsl query type for User
 */
@Generated("com.mysema.query.codegen.EntitySerializer")
public class QUser extends EntityPathBase<User> {

    private static final long serialVersionUID = ...;

    public static final QUser user = new QUser("user");

    public final NumberPath<Integer> age = createNumber("age", Integer.class);

    public final StringPath id = createString("id");

    public final StringPath name = createString("name");

    public QUser(String variable) {
        super(User.class, forVariable(variable));
    }

    public QUser(Path<? extends User> path) {
        super(path.getType(), path.getMetadata());
    }

    public QUser(PathMetadata<?> metadata) {
        super(User.class, metadata);
    }
}

It’s because of this class that we don’t need to create our queries.

正是因为有了这个类,我们才不需要创建我们的查询。

As a side note, if we’re using Eclipse, introducing this plugin will generate the following warning in pom:

顺便提一下,如果我们使用Eclipse,引入这个插件会在pom中产生以下警告。

You need to run build with JDK or have tools.jar on the classpath. If this occurs during eclipse build make sure you run eclipse under JDK as well (com.mysema.maven:apt-maven-plugin:1.1.3:process:default:generate-sources

你需要用JDK运行构建,或者在classpath上有tools.jar。如果在eclipse构建时出现这种情况,请确保在JDK下运行eclipse(com.mysema.maven:apt-maven-plugin:1.1.3:process:default:generate-sources

The Maven install works fine and the QUser class is generated, but a plugin is highlighted in the pom.

Maven安装工作正常,QUser类被生成,但pom中突出显示了一个插件。

A quick fix is to manually point to the JDK in eclipse.ini:

一个快速的解决方案是在eclipse.ini中手动指向JDK。

...
-vm
{path_to_jdk}\jdk{your_version}\bin\javaw.exe

5.3. The New Repository

5.3.新存储库

Now we need to actually enable QueryDSL support in our repositories, which is done by simply extending the QueryDslPredicateExecutor interface:

现在我们需要在我们的存储库中实际启用QueryDSL支持,这可以通过简单的扩展QueryDslPredicateExecutor接口来完成。

public interface UserRepository extends 
  MongoRepository<User, String>, QuerydslPredicateExecutor<User>

5.4. Eq

5.4.Eq

With support enabled, let’s now implement the same queries as the ones we illustrated before.

启用支持后,现在让我们实现与我们之前说明的相同的查询

We’ll start with simple equality:

我们将从简单的平等开始。

QUser qUser = new QUser("user");
Predicate predicate = qUser.name.eq("Eric");
List<User> users = (List<User>) userRepository.findAll(predicate);

5.5. StartingWith and EndingWith

5.5.StartingWithEndingWith

Similarly, let’s implement the previous queries and find users with names that are starting with A:

同样,让我们实现前面的查询,找到名字是以A开头的用户。

QUser qUser = new QUser("user");
Predicate predicate = qUser.name.startsWith("A");
List<User> users = (List<User>) userRepository.findAll(predicate);

As well as ending with c:

以及以c结尾。

QUser qUser = new QUser("user");
Predicate predicate = qUser.name.endsWith("c");
List<User> users = (List<User>) userRepository.findAll(predicate);

The result is the same as in sections 2.2, 3.2 and 4.2.

其结果与2.2、3.2和4.2节相同。

5.6. Between

5.6.中间

The next query will return users with ages between 20 and 50, similar to the previous sections:

下一个查询将返回年龄在20至50岁之间的用户,与前几节类似。

QUser qUser = new QUser("user");
Predicate predicate = qUser.age.between(20, 50);
List<User> users = (List<User>) userRepository.findAll(predicate);

6. Conclusion

6.结论

In this article, we explored the many ways we can query using Spring Data MongoDB.

在这篇文章中,我们探讨了使用Spring Data MongoDB进行查询的多种方式。

It’s interesting to take a step back and see all the powerful ways we have to query MongoDB, varying from limited control all the way to full control with raw queries.

退一步讲,看看我们有哪些强大的方法来查询MongoDB,从有限的控制一直到完全控制的原始查询,这很有意思。

The implementation of all of these examples and code snippets can be found in the GitHub project.

所有这些例子和代码片段的实现都可以在GitHub项目中找到。