sql >> Databáze >  >> NoSQL >> MongoDB

Průvodce dotazy v Spring Data MongoDB

1. Přehled

Tento tutoriál se zaměří na vytváření různých typů dotazů v Spring Data MongoDB .

Podíváme se na dotazování dokumentů pomocí Dotaz a Kritéria třídy, automaticky generované metody dotazů, dotazy JSON a QueryDSL.

Pokud jde o nastavení Maven, podívejte se na náš úvodní článek.

2. Dotaz na dokumenty

Jedním z nejběžnějších způsobů dotazování MongoDB pomocí Spring Data je použití Dotazu a Kritéria třídy, které velmi přesně odrážejí nativní operátory.

2.1. Je

Toto je prostě kritérium využívající rovnost. Pojďme se podívat, jak to funguje.

V následujícím příkladu budeme hledat uživatele s názvem Eric .

Podívejme se na naši databázi:

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

Nyní se podívejme na kód dotazu:

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

Podle očekávání tato logika vrací:

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

2.2. Regulační výraz

Flexibilnějším a výkonnějším typem dotazu je regulární výraz. Tím se vytvoří kritérium pomocí MongoDB $regex který vrátí všechny záznamy vhodné pro regulární výraz pro toto pole.

Funguje podobně jako startingWith a endingWith operace.

V tomto příkladu budeme hledat všechny uživatele, jejichž jména začínají na A .

Zde je stav databáze:

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

Nyní vytvoříme dotaz:

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

Toto se spustí a vrátí 2 záznamy:

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

Zde je další rychlý příklad, tentokrát hledání všech uživatelů, jejichž jména končí na c :

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

Takže výsledek bude:

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

2.3. Lt a gt

Tyto operátory vytvářejí kritérium pomocí $lt (méně než) a $gt (větší než) operátory.

Vezměme si rychlý příklad, kde hledáme všechny uživatele ve věku od 20 do 50 let.

Databáze je:

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

Kód dotazu:

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

A výsledky pro všechny uživatele ve věku nad 20 a méně než 50 let:

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

2.4. Řadit

Řadit se používá k určení pořadí řazení pro výsledky.

Níže uvedený příklad vrátí všechny uživatele seřazené podle věku ve vzestupném pořadí.

Nejprve jsou zde existující 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
    }
]

Po provedení třídění :

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

A tady je výsledek dotazu pěkně seřazený podle věku :

[
    {
        "_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. Stránkovatelné

Podívejme se na rychlý příklad pomocí stránkování.

Zde je stav databáze:

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

Nyní je zde logika dotazu, která jednoduše požaduje stránku o velikosti 2:

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

A výsledek, 2 dokumenty, jak se očekávalo:

[
    {
        "_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. Metody generovaných dotazů

Nyní prozkoumáme běžnější typ dotazu, který Spring Data obvykle poskytuje, automaticky generované dotazy mimo názvy metod.

Jediná věc, kterou musíme udělat, abychom využili tyto druhy dotazů, je deklarovat metodu v rozhraní úložiště:

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

3.1. FindByX

Začneme jednoduše, prozkoumáním typu dotazu findBy. V tomto případě použijeme find by name:

List<User> findByName(String name);

Stejně jako v předchozí sekci 2.1 bude mít dotaz stejné výsledky a najde všechny uživatele s daným jménem:

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

3.2. Počínaje a endingWith

V sekci 2.2 jsme prozkoumali regulární výraz založený dotaz. Začátky a konce s jsou samozřejmě méně výkonné, ale přesto docela užitečné, zvláště pokud je nemusíme skutečně implementovat.

Zde je rychlý příklad toho, jak by operace vypadaly:

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

Příklad skutečného použití by byl samozřejmě velmi jednoduchý:

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

A výsledky jsou úplně stejné.

3.3. Mezi

Podobně jako v části 2.3 to vrátí všechny uživatele s věkem mezi ageGT a ageLT:

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

Volání metody povede k nalezení přesně stejných dokumentů:

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

3.4. To se mi líbí a OrderBy

Pojďme se tentokrát podívat na pokročilejší příklad, který kombinuje dva typy modifikátorů pro vygenerovaný dotaz.

Budeme hledat všechny uživatele, kteří mají jména obsahující písmeno A, a také seřadíme výsledky vzestupně podle věku:

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

Pro databázi, kterou jsme použili v sekci 2.4, bude výsledek:

[
    {
        "_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. Metody dotazů JSON

Pokud nemůžeme reprezentovat dotaz pomocí názvu metody nebo kritérií, můžeme udělat něco na nižší úrovni, použijte @Query anotace .

Pomocí této anotace můžeme zadat nezpracovaný dotaz jako řetězec dotazu Mongo JSON.

4.1. FindBy

Začněme jednoduše a podívejme se, jak bychom znázornili nález podle typ metody první:

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

Tato metoda by měla vrátit uživatele podle jména. Zástupný symbol ?0 odkazuje na první parametr metody.

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

4.2. $regex

Můžeme se také podívat na dotaz řízený regulárním výrazem což samozřejmě vede ke stejnému výsledku jako v částech 2.2 a 3.2:

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

Použití je také úplně stejné:

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

4.3. $lt a $gt

Nyní implementujme lt a gt dotaz:

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

Nyní, když má metoda 2 parametry, odkazujeme na každý z nich podle indexu v nezpracovaném dotazu ?0 a ?1:

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

5. QueryDSL dotazy

Repozitář Mongo má dobrou podporu pro projekt QueryDSL, takže i zde můžeme využít toto pěkné, typově bezpečné API.

5.1. Závislosti Maven

Nejprve se ujistíme, že máme v pom definované správné Mavenovy závislosti:

<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. Otázka -třídy

QueryDSL používal pro vytváření dotazů třídy Q, ale protože je nechceme vytvářet ručně, musíme je vygenerovat nějak.

K tomu použijeme 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>

Podívejme se na Uživatel třídy se zaměřením konkrétně na @QueryEntity anotace:

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

Po spuštění procesu cíl životního cyklu Maven (nebo jakýkoli jiný cíl po tomto), plugin apt vygeneruje nové třídy pod target/generated-sources/java/{struktura vašeho balíčku} :

/**
 * 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);
    }
}

Je to kvůli této třídě, že nepotřebujeme vytvářet naše dotazy.

Jako vedlejší poznámku, pokud používáme Eclipse, zavedení tohoto pluginu vygeneruje v pom následující varování:

Maveninstalace funguje dobře a QUser třída se vygeneruje, ale v pom je zvýrazněn plugin.

Rychlá oprava je ruční nasměrování na JDK v eclipse.ini :

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

5.3. Nové úložiště

Nyní musíme skutečně povolit podporu QueryDSL v našich úložištích, což se provádí jednoduše rozšířením QueryDslPredicateExecutor rozhraní :

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

5.4. Eq

S povolenou podporou teď implementujme stejné dotazy jako ty, které jsme ilustrovali dříve.

Začneme jednoduchou rovností:

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

5.5. Počínaje a EndingWith

Podobně implementujme předchozí dotazy a najdeme uživatele se jmény začínajícími na A :

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

Stejně jako končí na c :

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

Výsledek je stejný jako v částech 2.2, 3.2 a 4.2.

5.6. Mezi

Další dotaz vrátí uživatele ve věku mezi 20 a 50 lety, podobně jako v předchozích částech:

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

  1. Celery beat + redis s heslem vyvolá výjimku No Auth

  2. Operátor dotazu MongoDB $nin

  3. Ovladač MongoDb c# najde položku v poli podle hodnoty pole

  4. Jak implementovat server push v rámci Flask?