Elasticsearch-zoekopdrachten met Spring Data

1. Inleiding

In een vorig artikel hebben we gedemonstreerd hoe Spring Data Elasticsearch voor een project kan worden geconfigureerd en gebruikt. In dit artikel zullen we verschillende typen zoekopdrachten van Elasticsearch onderzoeken, en we zullen ook praten over field analyzers en hun impact op zoekresultaten.

2. Analysatoren

Alle opgeslagen stringvelden worden standaard verwerkt door een analyzer. Een analysator bestaat uit één tokenizer en meerdere tokenfilters, en wordt meestal voorafgegaan door een of meer karakterfilters.

De standaardanalysator splitst de tekenreeks op door gemeenschappelijke woordscheidingstekens (zoals spaties of interpunctie) en zet elk token in kleine letters. Het negeert ook gewone Engelse woorden.

Elasticsearch kan ook worden geconfigureerd om een ​​veld als geanalyseerd en niet-geanalyseerd te beschouwen.

Bijvoorbeeld in een Artikel klasse, stel dat we het titelveld opslaan als een standaard geanalyseerd veld. Hetzelfde veld met het achtervoegsel letterlijk wordt opgeslagen als een niet-geanalyseerd veld:

@MultiField (mainField = @Field (type = Tekst, fielddata = true), otherFields = {@InnerField (achtervoegsel = "verbatim", type = Trefwoord)}) private String-titel;

Hier passen we de @MultiField annotatie om Spring Data te vertellen dat we willen dat dit veld op verschillende manieren wordt geïndexeerd. Het hoofdveld zal de naam gebruiken titel en worden geanalyseerd volgens de hierboven beschreven regels.

Maar we bieden ook een tweede annotatie, @BuienRadarNL, die een aanvullende indexering van de titel veld. We gebruiken FieldType.keyword om aan te geven dat we geen analysator willen gebruiken bij het uitvoeren van de aanvullende indexering van het veld, en dat deze waarde moet worden opgeslagen met behulp van een genest veld met het achtervoegsel letterlijk.

2.1. Geanalyseerde velden

Laten we naar een voorbeeld kijken. Stel dat er een artikel met de titel "Spring Data Elasticsearch" aan onze index wordt toegevoegd. De standaardanalysator verdeelt de tekenreeks op de spatie-tekens en produceert tokens in kleine letters: "voorjaar“, “gegevens", en "Elasticsearch“.

Nu kunnen we elke combinatie van deze termen gebruiken om een ​​document te matchen:

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

2.2. Niet-geanalyseerde velden

Een niet-geanalyseerd veld heeft geen tokenized, dus kan alleen in zijn geheel worden gematcht bij gebruik van match- of termquery's:

NativeSearchQuery searchQuery = nieuwe NativeSearchQueryBuilder () .withQuery (matchQuery ("title.verbatim", "Tweede artikel over Elasticsearch")) .build ();

Met een matchquery kunnen we alleen zoeken op de volledige titel, die ook hoofdlettergevoelig is.

3. Match zoekopdracht

EEN match zoekopdracht accepteert tekst, cijfers en datums.

Er zijn drie soorten "match" -zoekopdrachten:

  • boolean
  • uitdrukking en
  • frase_prefix

In deze sectie zullen we de boolean match zoekopdracht.

3.1. Overeenkomen met Booleaanse operatoren

boolean is het standaardtype van een matchquery; je kunt specificeren welke booleaanse operator moet worden gebruikt (of is de standaard):

NativeSearchQuery searchQuery = nieuwe NativeSearchQueryBuilder () .withQuery (matchQuery ("titel", "Zoekmachines"). Operator (Operator.AND)) .build (); SearchHits-artikelen = elasticsearchTemplate () .search (searchQuery, Article.class, IndexCoordinates.of ("blog"));

Deze zoekopdracht zou een artikel opleveren met de titel "Zoekmachines" door twee termen uit de titel op te geven met en operator. Maar wat gebeurt er als we zoeken met de standaard (of) operator als slechts een van de termen overeenkomt?

NativeSearchQuery searchQuery = nieuwe NativeSearchQueryBuilder () .withQuery (matchQuery ("title", "Engines Solutions")) .build (); SearchHits-artikelen = elasticsearchTemplate () .search (searchQuery, Article.class, IndexCoordinates.of ("blog")); assertEquals (1, artikelen.getTotalHits ()); assertEquals ("Zoekmachines", artikelen.getSearchHit (0) .getContent (). getTitle ());

De "Zoekmachines”Artikel is nog steeds gematcht, maar het zal een lagere score hebben omdat niet alle termen overeenkwamen.

De som van de scores van elke overeenkomende term wordt opgeteld bij de totale score van elk resulterend document.

Er kunnen situaties zijn waarin een document dat een zeldzame term bevat die in de zoekopdracht is ingevoerd, een hogere rang heeft dan een document dat meerdere veelgebruikte termen bevat.

3.2. Wazigheid

Wanneer de gebruiker een typefout in een woord maakt, is het nog steeds mogelijk om het te matchen met een zoekopdracht door een wazigheid parameter, die een onnauwkeurige overeenkomst mogelijk maakt.

Voor stringvelden, wazigheid betekent de bewerkingsafstand: het aantal wijzigingen van één teken dat moet worden aangebracht in een string om deze hetzelfde te maken als een andere string.

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

De prefix_length parameter wordt gebruikt om de prestaties te verbeteren. In dit geval vereisen we dat de eerste drie tekens exact overeenkomen, waardoor het aantal mogelijke combinaties afneemt.

5. Zoeken op zinnen

Het zoeken naar fasen is strikter, hoewel u dit kunt regelen met de slop parameter. Deze parameter vertelt de woordgroepquery hoe ver de termen van elkaar verwijderd mogen zijn, terwijl het document nog steeds als een overeenkomst wordt beschouwd.

Met andere woorden, het vertegenwoordigt het aantal keren dat u een term moet verplaatsen om de zoekopdracht en het document overeen te laten komen:

NativeSearchQuery searchQuery = nieuwe NativeSearchQueryBuilder () .withQuery (matchPhraseQuery ("titel", "spring elasticsearch"). Slop (1)) .build ();

Hier komt de zoekopdracht overeen met het document met de titel 'Spring Data Elasticsearch”Omdat we de slop op één hebben gezet.

6. Multi Match Query

Als u in meerdere velden wilt zoeken, kunt u gebruik maken van QueryBuilders # multiMatchQuery () waar u alle overeenkomende velden specificeert:

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

Hier zoeken we het titel en tags velden voor een wedstrijd.

Merk op dat we hier de scorestrategie "beste velden" gebruiken. Het neemt de maximale score van de velden als een documentscore.

7. Aggregaties

In onze Artikel klasse hebben we ook een tags veld, dat niet is geanalyseerd. We zouden eenvoudig een tagwolk kunnen maken door een aggregatie te gebruiken.

Onthoud dat, omdat het veld niet is geanalyseerd, de tags niet worden getokeniseerd:

TermsAggregationBuilder aggregation = AggregationBuilders.terms ("top_tags") .field ("tags") .order (Terms.Order.count (false)); SearchSourceBuilder builder = nieuwe SearchSourceBuilder (). Aggregatie (aggregatie); SearchRequest searchRequest = nieuwe SearchRequest (). Indices ("blog"). Types ("artikel"). Source (builder); SearchResponse response = client.search (searchRequest, RequestOptions.DEFAULT); Kaartresultaten = response.getAggregations (). AsMap (); StringTerms topTags = (StringTerms) results.get ("top_tags"); Lijstsleutels = topTags.getBuckets () .stream () .map (b -> b.getKeyAsString ()) .collect (toList ()); assertEquals (asList ("elasticsearch", "spring data", "zoekmachines", "tutorial"), keys);

8. Samenvatting

In dit artikel hebben we het verschil tussen geanalyseerde en niet-geanalyseerde velden besproken en hoe dit onderscheid het zoeken beïnvloedt.

We hebben ook kennis gemaakt met verschillende soorten zoekopdrachten die worden aangeboden door Elasticsearch, zoals de zoekvraag voor overeenkomsten, de zoekwoordgroep, de volledige tekstzoekopdracht en de booleaanse zoekopdracht.

Elasticsearch biedt vele andere soorten zoekopdrachten, zoals geoquery's, scriptquery's en samengestelde queries. U kunt erover lezen in de Elasticsearch-documentatie en de Spring Data Elasticsearch API verkennen om deze queries in uw code te gebruiken.

U kunt een project met de voorbeelden die in dit artikel worden gebruikt, vinden in de GitHub-repository.