Um guia para consultas no Spring Data MongoDB

Um guia para consultas no Spring Data MongoDB

1. Visão geral

Este artigo se concentrará na construção de diferentestypes of queries in Spring Data MongoDB.

Estaremos olhando documentos de consulta com classesQueryeCriteria, métodos de consulta gerados automaticamente, consultas JSON e QueryDSL.

Para a configuração do Maven, dê uma olhada em nossointroductory article.

2. Consulta de Documentos

Uma das maneiras mais comuns de consultar o MongoDB com Spring Data é usando as classesQueryeCriteria - que refletem muito de perto os operadores nativos.

2.1. Is

Este é simplesmente um critério de igualdade - vamos ver como funciona.

No exemplo a seguir, estamos procurando usuários chamadosEric.

Vejamos nosso banco de dados:

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

Agora vamos examinar o código de consulta:

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

Essa lógica retorna, conforme o esperado:

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

2.2. Regex

Um tipo de consulta mais flexível e poderoso é o regex. Este c` ++ `reanima um critério usando um MongoDB$regex que retorna todos os registros adequados para este regexp para este campo.

Funciona de maneira semelhante às operações destartingWith, endingWith - vejamos um exemplo.

Agora estamos procurando por todos os usuários que têm nomes começando comA.

Este é o estado do banco de dados:

[
    {
        "_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
    }
]

Agora vamos criar a consulta:

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

Isso executa e retorna 2 registros:

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

Aqui está outro exemplo rápido, desta vez procurando por todos os usuários que têm nomes terminados emc:

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

Então o resultado será:

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

2.3. Lt egt

Esses operadores criam um critério usando o operador$lt (menor que) e$gt (maior que).

Vamos dar uma olhada em um exemplo - estamos procurando todos os usuários com idade entre 20 e 50 anos.

O banco de dados é:

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

Este código de consulta:

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

E o resultado - todos os usuários com idade superior a 20 e inferior a 50:

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

2.4. Sort

Sort é usado para especificar uma ordem de classificação para os resultados.

O exemplo abaixo retorna todos os usuários classificados por idade em ordem crescente.

Primeiro - aqui estão os dados existentes:

[
    {
        "_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
    }
]

Depois de executarsort:

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

E aqui está o resultado da consulta - bem classificado porage:

[
    {
        "_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

Vejamos um exemplo rápido de paginação.

Este é o estado do banco de dados:

[
    {
        "_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
    }
]

Agora, a lógica da consulta, simplesmente pedindo uma página de tamanho 2:

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

E o resultado - os 2 documentos, como esperado:

[
    {
        "_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. Métodos de consulta gerados

Vamos agora explorar o tipo mais comum de consulta que Spring Data geralmente fornece - consultas geradas automaticamente a partir de nomes de métodos.

A única coisa que precisamos fazer para aproveitar esses tipos de consultas é declarar o método na interface do repositório:

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

3.1. FindByX

Começaremos de forma simples, explorando o tipo de consulta findBy - neste caso, localizar por nome: __

List findByName(String name);

O mesmo que na seção anterior - 2.1 - a consulta terá os mesmos resultados, localizando todos os usuários com o nome fornecido:

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

3.2. StartingWith e terminando com.

Em 2.2, exploramos uma consulta baseada emregex. Começa e termina com são, obviamente, menos poderosos, mas, no entanto, bastante úteis - especialmente se não tivermos que implementá-los de fato.

Aqui está um exemplo rápido de como as operações seriam: __

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

O exemplo de realmente usar isso seria, é claro, muito simples:

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

E os resultados são exatamente os mesmos.

3.3. Between

Semelhante ao 2.3, isso retornará todos os usuários com idade entreageGTeageLT:

List findByAgeBetween(int ageGT, int ageLT);

A chamada do método resultará na localização exata dos mesmos documentos:

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

3.4. Like eOrderBy

Vamos dar uma olhada em um exemplo mais avançado desta vez - combinando dois tipos de modificadores para a consulta gerada.

Procuraremos todos os usuários com nomes contendo a letraA e também ordenaremos os resultados por idade, em ordem crescente:

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

Para o banco de dados que usamos no 2.4 - o resultado será:

[
    {
        "_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. Métodos de consulta JSON

Se não podemos representar uma consulta com a ajuda de um nome de método, ou critérios, podemos fazer algo mais baixo nível -use the @Query annotation.

Com esta anotação, podemos especificar uma consulta bruta - como uma sequência de consulta JSON do Mongo.

4.1. FindBy

Vamos começar de forma simples e ver como representaríamosa find by type of method primeiro:

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

Este método deve retornar usuários pelo nome - o espaço reservado?0 faz referência ao primeiro parâmetro do método.

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

4.2 $regex

Vejamos tambéma regex driven query - que obviamente produz o mesmo resultado que em 2.2 e 3.2:

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

O uso também é exatamente o mesmo:

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

4.3. $lt e$gt

Vamos agora implementar a consulta lt egt:

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

Agora como, agora que o método tem 2 parâmetros, estamos referenciando cada um deles por índice na consulta bruta:?0e?1.

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

5. Consultas QueryDSL

MongoRepository tem um bom suporte para o projetoQueryDSL - portanto, podemos aproveitar essa API agradável e segura para tipos aqui também.

5.1. As dependências do Maven

Primeiro, vamos ter certeza de que temos as dependências corretas do Maven definidas no pom:


    com.mysema.querydsl
    querydsl-mongodb
    3.6.6


    com.mysema.querydsl
    querydsl-apt
    3.6.6

5.2. Q-classes

QueryDSL usou Q-classes para criar consultas. Mas, como não queremos realmente criá-los manualmente,we need to generate them de alguma forma.

Vamos usar o plugin apt-maven para fazer isso:


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

Vamos dar uma olhada na classeUser - focando especificamente na anotação@QueryEntity:

@QueryEntity
@Document
public class User {

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

    // standard getters and setters
}

Depois de executar a metaprocess do ciclo de vida do Maven (ou qualquer outra meta após essa) - o plugin aptwill generate the new classes emtarget/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);
    }
}

É com a ajuda desta classe que não criaremos nossas consultas.

Como uma observação lateral - se você estiver usando o Eclipse, a introdução deste plug-in irá gerar o seguinte aviso no pom:

Você precisa executar o build com JDK ou ter tools.jar no classpath. Se isso ocorrer durante a construção do eclipse, certifique-se de executar o eclipse no JDK também (com.mysema.maven: apt-maven-plugin: 1.1.3: process: default: generate-sources

Maveninstall funciona bem e a classeQUser é gerada, mas um plugin é destacado no pom.

Uma solução rápida é apontar manualmente para o JDK emeclipse.ini:

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

5.3. The New Repository

Agora precisamos habilitar o suporte QueryDSL em nossos repositórios - o que é feito simplesmenteextending the QueryDslPredicateExecutor interface:

public interface UserRepository extends
  MongoRepository, QuerydslPredicateExecutor

5.4. Eq

Com o suporte ativado,let’s now implement the same queries como os que ilustramos antes.

Começaremos com igualdade simples:

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

5.5. StartingWith eEndingWith

Da mesma forma, vamos implementar as consultas anteriores - e encontrar usuários com nomes que começam comA:

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

E terminando comc:

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

O resultado é o mesmo que em 2.2, 3.2 ou 4.2.

5.6. Between

A próxima consulta retornará usuários com idade entre 20 e 50 - semelhante às seções anteriores:

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

6. Conclusão

Neste artigo, exploramos as várias maneiras pelas quais podemos consultar usando o Spring Data MongoDB.

É interessante dar um passo para trás e ver quantas maneiras poderosas temos para consultar o MongoDB - variando de controle limitado até o controle total com consultas brutas.

A implementação de todos esses exemplos e fragmentos de códigocan be found inthe GitHub project - este é um projeto baseado em Eclipse, portanto, deve ser fácil de importar e executar como está.