Руководство по запросам в Spring Data MongoDB

Руководство по запросам в Spring Data MongoDB

1. обзор

В этой статье мы сосредоточимся на построении разныхtypes of queries in Spring Data MongoDB.

Мы рассмотрим запросы к документам с помощью классовQuery иCriteria, автоматически сгенерированные методы запросов, запросы JSON и QueryDSL.

Для настройки Maven взгляните на нашintroductory article.

2. Документы Запрос

Один из наиболее распространенных способов запроса MongoDB с помощью Spring Data - использование классовQuery иCriteria, которые очень точно отражают собственные операторы.

2.1. Isс

Это просто критерий равенства - давайте посмотрим, как он работает.

В следующем примере мы ищем пользователей с именемEric.

Давайте посмотрим на нашу базу данных:

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

Теперь посмотрим на код запроса:

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

Эта логика возвращает, как и ожидалось:

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

2.2. Regexс

Более гибкий и мощный тип запроса - это регулярное выражение. Этот c` ++ `повторяет критерий с использованием MongoDB$regex, который возвращает все записи, подходящие для этого регулярного выражения для этого поля.

Он работает аналогично операциямstartingWith, endingWith - давайте рассмотрим пример.

Теперь мы ищем всех пользователей, чьи имена начинаются сA.

Вот состояние базы данных:

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

Теперь создадим запрос:

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

Это запускает и возвращает 2 записи:

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

Вот еще один быстрый пример, на этот раз ищем всех пользователей, чьи имена заканчиваются наc:

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

Таким образом, результат будет:

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

2.3. Lt иgt

Эти операторы создают критерий, используя оператор$lt (меньше чем) и$gt (больше чем).

Давайте быстро рассмотрим пример - мы ищем всех пользователей в возрасте от 20 до 50 лет.

База данных:

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

Этот код запроса:

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

И в результате - все пользователи, которым от 20 и до 50 лет:

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

2.4. Sortс

Sort используется для указания порядка сортировки результатов.

В приведенном ниже примере возвращаются все пользователи, отсортированные по возрасту в порядке возрастания.

Во-первых, вот существующие данные:

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

После выполненияsort:

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

И вот результат запроса - красиво отсортированный поage:

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

2.5. Pageableс

Давайте посмотрим на быстрый пример использования нумерации страниц.

Вот состояние базы данных:

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

Теперь логика запросов, просто запрашивающая страницу размером 2:

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

И результат - 2 документа, как и ожидалось:

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

3. Сгенерированные методы запроса

Теперь давайте рассмотрим наиболее распространенный тип запросов, который обычно предоставляет Spring Data - автоматически сгенерированные запросы на основе имен методов.

Единственное, что нам нужно сделать, чтобы использовать такие запросы, - это объявить метод в интерфейсе репозитория:

public interface UserRepository
  extends MongoRepository, QueryDslPredicateExecutor {
    ...
}

3.1. FindByXс

Мы начнем с простого, с изучения типа запроса findBy - в данном случае поиск по имени: __

List findByName(String name);

Как и в предыдущем разделе - 2.1 - запрос будет иметь те же результаты, найдя всех пользователей с заданным именем:

List users = userRepository.findByName("Eric");

3.2. StartingWith и endWith.

В 2.2 мы исследовали запрос на основеregex. Начало и конец, конечно, менее эффективны, но тем не менее весьма полезны - особенно если нам не нужно их реализовывать.

Вот краткий пример того, как будут выглядеть операции: __

List findByNameStartingWith(String regexp);
List findByNameEndingWith(String regexp);

Пример реального использования этого, конечно, будет очень простым:

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

И результаты точно такие же.

3.3. Betweenс

Как и в 2.3, это вернет всех пользователей с возрастом отageGT доageLT:.

List findByAgeBetween(int ageGT, int ageLT);

Вызов метода приведет к тому, что будут найдены точно такие же документы:

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

3.4. Like иOrderBy

На этот раз давайте рассмотрим более сложный пример, сочетающий два типа модификаторов для сгенерированного запроса.

Мы будем искать всех пользователей, имена которых содержат буквуA, а также отсортировать результаты по возрасту в порядке возрастания:

List users = userRepository.findByNameLikeOrderByAgeAsc("A");

Для базы данных мы использовали в 2.4 - результат будет:

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

4. Методы запросов JSON

Если мы не можем представить запрос с помощью имени метода или критериев, мы можем сделать что-то более низкое -use the @Query annotation.

С помощью этой аннотации мы можем указать необработанный запрос - как строку запроса Mongo JSON.

4.1. FindByс

Давайте начнем с простого и сначала посмотрим, как мы представимa find by type of method:

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

Этот метод должен возвращать пользователей по имени - заполнитель?0 ссылается на первый параметр метода.

List users = userRepository.findUsersByName("Eric");

4.2 $regex

Давайте также посмотрим наa regex driven query - который, конечно, дает тот же результат, что и в 2.2 и 3.2:

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

Использование также точно так же:

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

4.3. $lt и$gt

Теперь давайте реализуем запросы lt иgt:

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

Теперь, как теперь, когда метод имеет 2 параметра, мы ссылаемся на каждый из них по индексу в необработанном запросе:?0 и?1.

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

5. Запросы QueryDSL

MongoRepository имеет хорошую поддержку для проектаQueryDSL, поэтому здесь мы также можем использовать этот красивый, безопасный для типов API.

5.1. Зависимости Maven

Во-первых, давайте убедитесь, что у нас есть правильные зависимости Maven, определенные в pom:


    com.mysema.querydsl
    querydsl-mongodb
    3.6.6


    com.mysema.querydsl
    querydsl-apt
    3.6.6

5.2. Q-классов

QueryDSL использовал Q-классы для создания запросов. Но, поскольку мы не хотим создавать их вручную,we need to generate them каким-то образом.

Для этого мы воспользуемся плагином apt-maven-plugin:


    com.mysema.maven
    apt-maven-plugin
    1.1.3
    
        
            
                process
            
            
                target/generated-sources/java
                
                  org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor
                
            
        
     

Давайте посмотрим на классUser, уделяя особое внимание аннотации@QueryEntity:

@QueryEntity
@Document
public class User {

    @Id
    private String id;
    private String name;
    private Integer age;

    // standard getters and setters
}

После выполнения целиprocess жизненного цикла Maven (или любой другой цели после этой) - плагин aptwill generate the new classes под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 {

    private static final long serialVersionUID = ...;

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

    public final NumberPath 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 path) {
        super(path.getType(), path.getMetadata());
    }

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

С помощью этого класса мы не будем создавать наши запросы.

В качестве побочного примечания - если вы используете Eclipse, введение этого плагина вызовет в pom следующее предупреждение:

Вам нужно запустить сборку с JDK или иметь tools.jar в пути к классам. Если это происходит во время сборки eclipse, убедитесь, что вы также запускаете eclipse под JDK (com.mysema.maven: apt-maven-plugin: 1.1.3: process: default: generate-sources

Maveninstall отлично работает и создается классQUser, но плагин выделен в файле pom.

Быстрое исправление - вручную указать JDK вeclipse.ini:

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

5.3. The New Repositoryс

Теперь нам нужно включить поддержку QueryDSL в наших репозиториях - это делается простоextending the QueryDslPredicateExecutor interface:

public interface UserRepository extends
  MongoRepository, QuerydslPredicateExecutor

5.4. Eqс

При включенной поддержкеlet’s now implement the same queries, как показано ранее.

Начнем с простого равенства:

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

5.5. StartingWith иEndingWith

Точно так же давайте реализуем предыдущие запросы и найдем пользователей с именами, начинающимися сA:

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

И заканчиваяc:

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

Результат такой же, как в 2.2, 3.2 или 4.2.

5.6. Betweenс

Следующий запрос вернет пользователей в возрасте от 20 до 50 лет - аналогично предыдущим разделам:

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

6. Заключение

В этой статье мы рассмотрели множество способов запроса с помощью Spring Data MongoDB.

Интересно сделать шаг назад и увидеть, сколько у нас есть эффективных способов запроса MongoDB - от ограниченного контроля до полного контроля с помощью необработанных запросов.

Реализация всех этих примеров и фрагментов кодаcan be found inthe GitHub project - это проект на основе Eclipse, поэтому его должно быть легко импортировать и запускать как есть.