Requêtes Elasticsearch avec des données de printemps

Requêtes Elasticsearch avec des données de printemps

1. introduction

Dans unprevious article, nous avons montré comment configurer et utiliser Spring Data Elasticsearch pour un projet. Dans cet article, nous examinerons plusieurs types de requêtes proposés par Elasticsearch et nous parlerons également des analyseurs de champ et de leur impact sur les résultats de recherche.

2. Analyseurs

Tous les champs de chaîne stockés sont, par défaut, traités par un analyseur. Un analyseur se compose d’un tokenizer et de plusieurs filtres de jetons. Il est généralement précédé d’un ou de plusieurs filtres de caractères.

L'analyseur par défaut divise la chaîne en séparateurs de mots courants (tels que des espaces ou des signes de ponctuation) et place chaque jeton en minuscule. Il ignore également les mots anglais courants.

Elasticsearch peut également être configuré pour considérer un champ comme analysé et non analysé en même temps.

Par exemple, dans une classeArticle, supposons que nous stockions le champ de titre en tant que champ analysé standard. Le même champ avec le suffixeverbatim sera stocké en tant que champ non analysé:

@MultiField(
  mainField = @Field(type = Text, fielddata = true),
  otherFields = {
      @InnerField(suffix = "verbatim", type = Keyword)
  }
)
private String title;

Ici, nous appliquons l'annotation@MultiField pour indiquer à Spring Data que nous souhaitons que ce champ soit indexé de plusieurs manières. Le champ principal utilisera le nomtitle et sera analysé selon les règles décrites ci-dessus.

Mais nous fournissons également une deuxième annotation,@InnerField, qui décrit une indexation supplémentaire du champtitle. Nous utilisonsFieldType.keyword pour indiquer que nous ne voulons pas utiliser d'analyseur lors de l'indexation supplémentaire du champ, et que cette valeur doit être stockée à l'aide d'un champ imbriqué avec le suffixeverbatim.

2.1. Champs analysés

Regardons un exemple. Supposons qu'un article intitulé «Spring Data Elasticsearch» soit ajouté à notre index. L'analyseur par défaut divisera la chaîne au niveau des caractères d'espacement et produira des jetons minuscules: «spring», «data“, et«elasticsearch ».

Nous pouvons maintenant utiliser n'importe quelle combinaison de ces termes pour faire correspondre un document:

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title", "elasticsearch data"))
  .build();

2.2. Champs non analysés

Un champ non analysé n'est pas tokenisé et ne peut donc être associé dans son ensemble que lorsque vous utilisez des requêtes de correspondance ou de terme:

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title.verbatim", "Second Article About Elasticsearch"))
  .build();

En utilisant une requête de correspondance, nous ne pouvons rechercher que par le titre complet, ce qui est également sensible à la casse.

3. Requête de correspondance

Unmatch query accepte le texte, les nombres et les dates.

Il existe trois types de requêtes de «correspondance»:

  • booléen

  • phrase et

  • phrase_prefix

Dans cette section, nous explorerons la requête de correspondanceboolean.

3.1. Correspondance avec les opérateurs booléens

boolean est le type par défaut d'une requête de correspondance; vous pouvez spécifier l'opérateur booléen à utiliser (or est la valeur par défaut):

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title","Search engines").operator(AND))
  .build();
List
articles = getElasticsearchTemplate() .queryForList(searchQuery, Article.class);

Cette requête renverrait un article avec le titre «Moteurs de recherche» en spécifiant deux termes du titre avec l'opérateurand. Mais que se passera-t-il si nous recherchons avec l'opérateur par défaut (or) lorsqu'un seul des termes correspond?

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title", "Engines Solutions"))
  .build();
List
articles = getElasticsearchTemplate() .queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); assertEquals("Search engines", articles.get(0).getTitle());

L'article «Search engines» est toujours associé, mais il aura un score inférieur car tous les termes ne correspondent pas.

La somme des notes de chaque terme correspondant correspond à la note totale de chaque document obtenu.

Il peut y avoir des situations dans lesquelles un document contenant un terme rare entré dans la requête aura un rang plus élevé qu'un document contenant plusieurs termes courants.

3.2. Flou

Lorsque l'utilisateur fait une faute de frappe dans un mot, il est toujours possible de le faire correspondre avec une recherche en spécifiant un paramètrefuzziness, ce qui permet une correspondance inexacte.

Pour les champs de chaînefuzziness signifie la distance d'édition: le nombre de modifications d'un caractère qui doivent être apportées à une chaîne pour la rendre identique à une autre chaîne.

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title", "spring date elasticsearch")
  .operator(AND)
  .fuzziness(Fuzziness.ONE)
  .prefixLength(3))
  .build();

Le paramètreprefix_length est utilisé pour améliorer les performances. Dans ce cas, nous exigeons que les trois premiers caractères correspondent exactement, ce qui réduit le nombre de combinaisons possibles.

La recherche de phase est plus stricte, bien que vous puissiez la contrôler avec le paramètreslop. Ce paramètre indique à la requête d'expression à quelle distance les termes sont autorisés tout en considérant le document comme une correspondance.

En d'autres termes, il représente le nombre de fois que vous avez besoin de déplacer un terme pour faire correspondre la requête et le document:

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchPhraseQuery("title", "spring elasticsearch").slop(1))
  .build();

Ici, la requête correspondra au document avec le titre «Spring Data Elasticsearch» car nous avons défini la pente à un.

6. Requête de correspondance multiple

Lorsque vous souhaitez rechercher dans plusieurs champs, vous pouvez utiliserQueryBuilders#multiMatchQuery() où vous spécifiez tous les champs à faire correspondre:

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(multiMatchQuery("tutorial")
    .field("title")
    .field("tags")
    .type(MultiMatchQueryBuilder.Type.BEST_FIELDS))
  .build();

Ici, nous recherchons une correspondance dans les champstitle ettags.

Notez que nous utilisons ici la stratégie de notation «meilleurs champs». Le score maximum parmi les champs sera considéré comme un score de document.

7. Agrégations

Dans notre classeArticle, nous avons également défini un champtags, qui n'est pas analysé. Nous pourrions facilement créer un nuage de tags en utilisant une agrégation.

N'oubliez pas que, le champ n'étant pas analysé, les balises ne seront pas marquées:

TermsAggregationBuilder aggregation = AggregationBuilders.terms("top_tags")
  .field("tags")
  .order(Terms.Order.count(false));
SearchResponse response = client.prepareSearch("blog")
  .setTypes("article")
  .addAggregation(aggregation)
  .execute().actionGet();

Map results = response.getAggregations().asMap();
StringTerms topTags = (StringTerms) results.get("top_tags");

List keys = topTags.getBuckets()
  .stream()
  .map(b -> b.getKeyAsString())
  .collect(toList());
assertEquals(asList("elasticsearch", "spring data", "search engines", "tutorial"), keys);

8. Sommaire

Dans cet article, nous avons discuté de la différence entre les champs analysés et non analysés et de la manière dont cette distinction affecte la recherche.

Nous avons également découvert plusieurs types de requêtes fournies par Elasticsearch, telles que la requête de correspondance, la requête de correspondance de phrase, la requête de recherche en texte intégral et la requête booléenne.

Elasticsearch propose de nombreux autres types de requêtes, telles que des requêtes géographiques, des requêtes de script et des requêtes composées. Vous pouvez les lire dans lesElasticsearch documentation et explorer l'API Spring Data Elasticsearch afin d'utiliser ces requêtes dans votre code.

Vous pouvez trouver un projet contenant les exemples utilisés dans cet article dansthe GitHub repository.