Query di Elasticsearch con dati di primavera

1. Introduzione

In un articolo precedente, abbiamo dimostrato come configurare e utilizzare Spring Data Elasticsearch per un progetto. In questo articolo, esamineremo diversi tipi di query offerti da Elasticsearch e parleremo anche degli analizzatori di campo e del loro impatto sui risultati di ricerca.

2. Analizzatori

Tutti i campi stringa memorizzati vengono, per impostazione predefinita, elaborati da un analizzatore. Un analizzatore è costituito da un tokenizer e diversi filtri token ed è solitamente preceduto da uno o più filtri di caratteri.

L'analizzatore predefinito divide la stringa in separatori di parole comuni (come spazi o punteggiatura) e mette ogni token in minuscolo. Ignora anche le parole inglesi comuni.

Elasticsearch può anche essere configurato per considerare un campo come analizzato e non analizzato allo stesso tempo.

Ad esempio, in una classe Article , supponiamo di memorizzare il campo del titolo come un campo analizzato standard. Lo stesso campo con il suffisso verbatim verrà memorizzato come campo non analizzato:

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

Qui, applichiamo l' annotazione @MultiField per dire a Spring Data che vorremmo che questo campo fosse indicizzato in diversi modi. Il campo principale utilizzerà il titolo del nome e verrà analizzato secondo le regole sopra descritte.

Ma forniamo anche una seconda annotazione, @InnerField , che descrive un'indicizzazione aggiuntiva del campo del titolo . Utilizziamo FieldType.keyword per indicare che non si desidera utilizzare un analizzatore quando si esegue l'indicizzazione aggiuntiva del campo e che questo valore deve essere memorizzato utilizzando un campo nidificato con il suffisso verbatim .

2.1. Campi analizzati

Diamo un'occhiata a un esempio. Supponiamo che un articolo con il titolo "Spring Data Elasticsearch" venga aggiunto al nostro indice. L'analizzatore predefinito suddividerà la stringa in corrispondenza dei caratteri dello spazio e produrrà token minuscoli: " spring ", " data" e " elasticsearch ".

Ora possiamo usare qualsiasi combinazione di questi termini per abbinare un documento:

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

2.2. Campi non analizzati

Un campo non analizzato non è tokenizzato, quindi può essere abbinato solo nel suo insieme quando si utilizzano query di corrispondenza o termine:

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

Utilizzando una query di corrispondenza, possiamo cercare solo per il titolo completo, che fa anche distinzione tra maiuscole e minuscole.

3. Query di corrispondenza

Una query di corrispondenza accetta testo, numeri e date.

Esistono tre tipi di query di "corrispondenza":

  • booleano
  • frase e
  • phrase_prefix

In questa sezione, esploreremo la query di corrispondenza booleana .

3.1. Corrispondenza con operatori booleani

booleano è il tipo predefinito di una query di corrispondenza; puoi specificare quale operatore booleano utilizzare ( o è il valore predefinito):

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(matchQuery("title","Search engines").operator(Operator.AND)) .build(); SearchHits articles = elasticsearchTemplate() .search(searchQuery, Article.class, IndexCoordinates.of("blog"));

Questa query restituirà un articolo con il titolo "Motori di ricerca" specificando due termini dal titolo con operatore e . Ma cosa succederà se cerchiamo con l' operatore predefinito ( o ) quando solo uno dei termini corrisponde?

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(matchQuery("title", "Engines Solutions")) .build(); SearchHits articles = elasticsearchTemplate() .search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(1, articles.getTotalHits()); assertEquals("Search engines", articles.getSearchHit(0).getContent().getTitle());

L' articolo " Motori di ricerca " è ancora abbinato, ma avrà un punteggio inferiore perché non tutti i termini corrispondono.

La somma dei punteggi di ogni termine corrispondente si somma al punteggio totale di ogni documento risultante.

Potrebbero verificarsi situazioni in cui un documento contenente un termine raro inserito nella query avrà un rango più elevato rispetto a un documento che contiene diversi termini comuni.

3.2. Sfocatura

Quando l'utente fa un errore di battitura in una parola, è ancora possibile abbinarla a una ricerca specificando un parametro di sfocatura , che consente una corrispondenza inesatta.

Per i campi stringa, sfocatura indica la distanza di modifica: il numero di modifiche di un carattere che devono essere apportate a una stringa per renderla uguale a un'altra stringa.

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

Il parametro prefix_length viene utilizzato per migliorare le prestazioni. In questo caso, richiediamo che i primi tre caratteri corrispondano esattamente, il che riduce il numero di combinazioni possibili.

5. Ricerca per frase

La ricerca di fase è più rigorosa, sebbene sia possibile controllarla con il parametro slop . Questo parametro indica alla query della frase quanto distanti possono essere i termini pur considerando il documento una corrispondenza.

In altre parole, rappresenta il numero di volte che è necessario spostare un termine per far corrispondere la query e il documento:

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

Qui la query corrisponderà al documento con il titolo " Spring Data Elasticsearch " perché abbiamo impostato lo slop su uno.

6. Query di corrispondenza multipla

Quando vuoi cercare in più campi, puoi usare QueryBuilders # multiMatchQuery () dove specifichi tutti i campi da abbinare:

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

Qui cerchiamo una corrispondenza nei campi del titolo e dei tag .

Si noti che qui utilizziamo la strategia di punteggio "campi migliori". Ci vorrà il punteggio massimo tra i campi come punteggio del documento.

7. Aggregazioni

In our Article class we have also defined a tags field, which is non-analyzed. We could easily create a tag cloud by using an aggregation.

Remember that, because the field is non-analyzed, the tags will not be tokenized:

TermsAggregationBuilder aggregation = AggregationBuilders.terms("top_tags") .field("tags") .order(Terms.Order.count(false)); SearchSourceBuilder builder = new SearchSourceBuilder().aggregation(aggregation); SearchRequest searchRequest = new SearchRequest().indices("blog").types("article").source(builder); SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); 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. Summary

In this article, we discussed the difference between analyzed and non-analyzed fields, and how this distinction affects search.

We also learned about several types of queries provided by Elasticsearch, such as the match query, phrase match query, full-text search query, and boolean query.

Elasticsearch provides many other types of queries, such as geo queries, script queries and compound queries. You can read about them in the Elasticsearch documentation and explore the Spring Data Elasticsearch API in order to use these queries in your code.

Puoi trovare un progetto contenente gli esempi utilizzati in questo articolo nel repository GitHub.