Guide d'Elasticsearch en Java

1. Vue d'ensemble

Dans cet article, nous allons nous plonger dans certains concepts clés liés aux moteurs de recherche en texte intégral, avec un accent particulier sur Elasticsearch.

Comme il s'agit d'un article orienté Java, nous n'allons pas donner un tutoriel détaillé étape par étape sur la configuration d'Elasticsearch et montrer comment cela fonctionne sous le capot. Au lieu de cela, nous allons cibler le client Java et comment utiliser les principales fonctionnalités telles que l' indexation , la suppression , l' obtention et la recherche .

2. Configuration

Par souci de simplicité, nous utiliserons une image docker pour notre instance Elasticsearch, bien que toute instance Elasticsearch écoutant sur le port 9200 fera l'affaire .

Nous commençons par lancer notre instance Elasticsearch:

docker run -d --name es762 -p 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2

Par défaut, Elasticsearch écoute sur le port 9200 les requêtes HTTP à venir. Nous pouvons vérifier qu'il est lancé avec succès en ouvrant l' URL // localhost: 9200 / dans votre navigateur préféré:

{ "name" : "M4ojISw", "cluster_name" : "docker-cluster", "cluster_uuid" : "CNnjvDZzRqeVP-B04D3CmA", "version" : { "number" : "7.6.2", "build_flavor" : "default", "build_type" : "docker", "build_hash" : "2f4c224", "build_date" : "2020-03-18T23:22:18.622755Z", "build_snapshot" : false, "lucene_version" : "8.4.0", "minimum_wire_compatibility_version" : "6.8.0", "minimum_index_compatibility_version" : "6.8.0-beta1" }, "tagline" : "You Know, for Search" }

3. Configuration Maven

Maintenant que notre cluster Elasticsearch de base est opérationnel, passons directement au client Java. Tout d'abord, nous devons déclarer la dépendance Maven suivante dans notre fichier pom.xml :

 org.elasticsearch elasticsearch 7.6.2 

Vous pouvez toujours vérifier les dernières versions hébergées par Maven Central avec le lien fourni auparavant.

4. API Java

Avant de passer directement à l'utilisation des principales fonctionnalités de l'API Java, nous devons lancer RestHighLevelClient :

ClientConfiguration clientConfiguration = ClientConfiguration.builder().connectedTo("localhost:9200").build(); RestHighLevelClient client = RestClients.create(clientConfiguration).rest();

4.1. Indexation de documents

La fonction index () permet de stocker un document JSON arbitraire et de le rendre interrogeable:

@Test public void givenJsonString_whenJavaObject_thenIndexDocument() { String jsonObject = "{\"age\":10,\"dateOfBirth\":1471466076564," +"\"fullName\":\"John Doe\"}"; IndexRequest request = new IndexRequest("people"); request.source(jsonObject, XContentType.JSON); IndexResponse response = client.index(request, RequestOptions.DEFAULT); String index = response.getIndex(); long version = response.getVersion(); assertEquals(Result.CREATED, response.getResult()); assertEquals(1, version); assertEquals("people", index); }

Notez qu'il est possible d'utiliser n'importe quelle bibliothèque JSON Java pour créer et traiter vos documents. Si vous n'êtes familiarisé avec aucun de ces éléments, vous pouvez utiliser les helpers Elasticsearch pour générer vos propres documents JSON :

XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .field("fullName", "Test") .field("dateOfBirth", new Date()) .field("age", "10") .endObject(); IndexRequest indexRequest = new IndexRequest("people"); indexRequest.source(builder); IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT); assertEquals(Result.CREATED, response.getResult());

4.2. Interroger des documents indexés

Maintenant que nous avons un document JSON indexé et indexé, nous pouvons continuer et rechercher à l'aide de la méthode search () :

SearchRequest searchRequest = new SearchRequest(); SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); SearchHit[] searchHits = response.getHits().getHits(); List results = Arrays.stream(searchHits) .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class)) .collect(Collectors.toList());

Les résultats renvoyés par la méthode search () sont appelés Hits , chaque Hit fait référence à un document JSON correspondant à une demande de recherche.

Dans ce cas, la liste des résultats contient toutes les données stockées dans le cluster. Notez que dans cet exemple, nous utilisons la bibliothèque FastJson afin de convertir des chaînes JSON en objets Java.

Nous pouvons améliorer la requête en ajoutant des paramètres supplémentaires afin de personnaliser la requête à l'aide des méthodes QueryBuilders :

SearchSourceBuilder builder = new SearchSourceBuilder() .postFilter(QueryBuilders.rangeQuery("age").from(5).to(15)); SearchRequest searchRequest = new SearchRequest(); searchRequest.searchType(SearchType.DFS_QUERY_THEN_FETCH); searchRequest.source(builder); SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

4.3. Récupération et suppression de documents

Les méthodes get () et delete () permettent d'obtenir ou de supprimer un document JSON du cluster en utilisant son id:

GetRequest getRequest = new GetRequest("people"); getRequest.id(id); GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT); // process fields DeleteRequest deleteRequest = new DeleteRequest("people"); deleteRequest.id(id); DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);

La syntaxe est assez simple, il vous suffit de spécifier l'index à côté de l'id de l'objet.

5. Exemples de QueryBuilders

La classe QueryBuilders fournit une variété de méthodes statiques utilisées comme correspondants dynamiques pour rechercher des entrées spécifiques dans le cluster. Lors de l'utilisation de la méthode search () pour rechercher des documents JSON spécifiques dans le cluster, nous pouvons utiliser des générateurs de requêtes pour personnaliser les résultats de la recherche.

Voici une liste des utilisations les plus courantes de l' API QueryBuilders .

La méthode matchAllQuery () retourne un objet QueryBuilder qui correspond à tous les documents du cluster:

QueryBuilder matchAllQuery = QueryBuilders.matchAllQuery();

Le rangeQuery () correspond aux documents où la valeur d'un champ est dans une certaine plage:

QueryBuilder matchDocumentsWithinRange = QueryBuilders .rangeQuery("price").from(15).to(100)

Fournir un nom de champ - par exemple fullName , et la valeur correspondante - par exemple John Doe , la méthode matchQuery () correspond à tous les documents avec la valeur exacte de ces champs:

QueryBuilder matchSpecificFieldQuery= QueryBuilders .matchQuery("fullName", "John Doe");

Nous pouvons également utiliser la méthode multiMatchQuery () pour créer une version multi-champs de la requête de correspondance:

QueryBuilder matchSpecificFieldQuery= QueryBuilders.matchQuery( "Text I am looking for", "field_1", "field_2^3", "*_field_wildcard");

Nous pouvons utiliser le symbole caret (^) pour amplifier des champs spécifiques .

Dans notre exemple, field_2 a une valeur de boost définie sur trois, ce qui le rend plus important que les autres champs. Notez qu'il est possible d'utiliser des caractères génériques et des requêtes regex, mais en termes de performances, méfiez-vous de la consommation de mémoire et du délai de réponse lorsque vous traitez avec des caractères génériques, car quelque chose comme * _apples peut avoir un impact énorme sur les performances.

Le coefficient d'importance est utilisé pour classer l'ensemble de résultats des hits renvoyés après l'exécution de la méthode s earch () .

Si vous êtes plus familier avec la syntaxe des requêtes Lucene, vous pouvez utiliser la méthode simpleQueryStringQuery () pour personnaliser les requêtes de recherche:

QueryBuilder simpleStringQuery = QueryBuilders .simpleQueryStringQuery("+John -Doe OR Janette");

Comme vous pouvez probablement le deviner, nous pouvons utiliser la syntaxe de Lucene's Query Parser pour créer des requêtes simples mais puissantes . Voici quelques opérateurs de base qui peuvent être utilisés avec les opérateurs AND / OR / NOT pour créer des requêtes de recherche:

  • L'opérateur requis ( + ): nécessite qu'un morceau de texte spécifique existe quelque part dans les champs d'un document.
  • L'opérateur d'interdiction ( - ): exclut tous les documents contenant un mot-clé déclaré après le symbole ( - ).

6. Conclusion

Dans cet article rapide, nous avons vu comment utiliser l'API Java d'ElasticSearch pour exécuter certaines des fonctionnalités courantes liées aux moteurs de recherche en texte intégral.

Vous pouvez consulter l'exemple fourni dans cet article dans le projet GitHub.