Een eenvoudige tagging-implementatie met Elasticsearch

Persistentie top

Ik heb zojuist het nieuwe aangekondigd Leer de lente natuurlijk, gericht op de basisprincipes van Spring 5 en Spring Boot 2:

>> BEKIJK DE CURSUS Dit artikel maakt deel uit van een reeks: • Een eenvoudige tagging-implementatie met Elasticsearch (huidig ​​artikel) • Een eenvoudige tagging-implementatie met JPA

• Een geavanceerde tagging-implementatie met JPA

• Een eenvoudige tagging-implementatie met MongoDB

1. Overzicht

Tagging is een veelgebruikt ontwerppatroon waarmee we items in ons datamodel kunnen categoriseren en filteren.

In dit artikel gaan we tagging implementeren met Spring en Elasticsearch. We gebruiken zowel Spring Data als de Elasticsearch API.

Allereerst gaan we niet ingaan op de basisprincipes van het verkrijgen van Elasticsearch- en Spring-gegevens - u kunt deze hier verkennen.

2. Tags toevoegen

De eenvoudigste implementatie van tagging is een reeks strings. We kunnen dit implementeren door als volgt een nieuw veld aan ons datamodel toe te voegen:

@Document (indexName = "blog", type = "article") openbare klasse Artikel {// ... @Field (type = trefwoord) privé String [] tags; // ...}

Let op het gebruik van de Trefwoord veld soort. We willen alleen exacte overeenkomsten van onze tags om een ​​resultaat te filteren. Hierdoor kunnen we vergelijkbare maar aparte tags gebruiken, zoals ElasticsearchIsAwesome en ElasticsearchIsVreselijk.

Geanalyseerde velden zouden gedeeltelijke treffers opleveren, wat in dit geval een verkeerd gedrag is.

3. Query's maken

Met tags kunnen we onze vragen op interessante manieren manipuleren. We kunnen ze doorzoeken zoals elk ander veld, of we kunnen ze gebruiken om onze resultaten op te filteren match_all vragen. We kunnen ze ook gebruiken met andere vragen om onze resultaten aan te scherpen.

3.1. Tags zoeken

De nieuwe label veld dat we op ons model hebben gemaakt, is net als elk ander veld in onze index. We kunnen zoeken naar elke entiteit die een specifieke tag heeft, zoals deze:

@Query ("{\" bool \ ": {\" must \ ": [{\" match \ ": {\" tags \ ": \"? 0 \ "}}]}}") Pagina findByTagUsingDeclaredQuery (tekenreeks tag, pageable pageable);

In dit voorbeeld wordt een Spring Data Repository gebruikt om onze query samen te stellen, maar we kunnen net zo snel een Rest Template gebruiken om het Elasticsearch-cluster handmatig te doorzoeken.

Evenzo kunnen we de Elasticsearch API gebruiken:

boolQuery (). must (termQuery ("tags", "elasticsearch"));

Stel dat we de volgende documenten in onze index gebruiken:

[{"id": 1, "title": "Spring Data Elasticsearch", "auteurs": [{"name": "John Doe"}, {"name": "John Smith"}], "tags": ["elasticsearch", "spring data"]}, {"id": 2, "title": "Zoekmachines", "auteurs": [{"name": "John Doe"}], "tags": [ "zoekmachines", "tutorial"]}, {"id": 3, "title": "Tweede artikel over Elasticsearch", "auteurs": [{"name": "John Smith"}], "tags": ["elasticsearch", "spring data"]}, {"id": 4, "title": "Elasticsearch Tutorial", "auteurs": [{"name": "John Doe"}], "tags": [ "elasticsearch"]},]

Nu kunnen we deze vraag gebruiken:

Page articleByTags = articleService.findByTagUsingDeclaredQuery ("elasticsearch", PageRequest.of (0, 10)); // articleByTags bevat 3 artikelen [1, 3, 4] assertThat (articleByTags, containsInAnyOrder (hasProperty ("id", is (1)), hasProperty ("id", is (3)), hasProperty ("id", is (4))));

3.2. Alle documenten filteren

Een veelgebruikt ontwerppatroon is om een Gefilterde lijstweergave in de gebruikersinterface die alle entiteiten toont, maar de gebruiker ook toestaat om te filteren op basis van verschillende criteria.

Laten we zeggen dat we alle artikelen willen retourneren die zijn gefilterd op de tag die de gebruiker selecteert:

@Query ("{\" bool \ ": {\" must \ ":" + "{\" match_all \ ": {}}, \" filter \ ": {\" term \ ": {\" tags \ ": \"? 0 \ "}}}}") Pagina findByFilteredTagQuery (String-tag, pageable pageable);

Nogmaals, we gebruiken Spring Data om onze gedeclareerde query samen te stellen.

Daarom is de zoekopdracht die we gebruiken in twee delen opgesplitst. De scorevraag is de eerste term, in dit geval match_all. De filterquery is de volgende en vertelt Elasticsearch welke resultaten moeten worden verwijderd.

Hier is hoe we deze vraag gebruiken:

Page articleByTags = articleService.findByFilteredTagQuery ("elasticsearch", PageRequest.of (0, 10)); // articleByTags bevat 3 artikelen [1, 3, 4] assertThat (articleByTags, containsInAnyOrder (hasProperty ("id", is (1)), hasProperty ("id", is (3)), hasProperty ("id", is (4))));

Het is belangrijk om te beseffen dat hoewel dit dezelfde resultaten oplevert als in ons voorbeeld hierboven, deze query beter zal presteren.

3.3. Query's filteren

Soms levert een zoekopdracht te veel resultaten op om bruikbaar te zijn. In dat geval is het leuk om een ​​filtermechanisme bloot te leggen dat dezelfde zoekopdracht opnieuw kan uitvoeren, alleen met de resultaten beperkt.

Hier is een voorbeeld waarin we de artikelen die een auteur heeft geschreven, beperken tot alleen de artikelen met een specifieke tag:

@Query ("{\" bool \ ": {\" must \ ":" + "{\" match \ ": {\" auteurs.naam \ ": \"? 0 \ "}}," + "\ "filter \": {\ "term \": {\ "tags \": \ "? 1 \"}}} ") Pagina findByAuthorsNameAndFilteredTagQuery (String naam, String tag, Pageable pageable);

Nogmaals, Spring Data doet al het werk voor ons.

Laten we ook kijken hoe we deze query zelf kunnen construeren:

QueryBuilder builder = boolQuery (). Must (nestedQuery ("auteurs", boolQuery (). Moet (termQuery ("auteurs.name", "doe")), ScoreMode.None)) .filter (termQuery ("tags", " elasticsearch "));

We kunnen natuurlijk dezelfde techniek gebruiken om op elk ander veld in het document te filteren. Maar tags lenen zich bijzonder goed voor deze use case.

Hier is hoe u de bovenstaande query gebruikt:

SearchQuery searchQuery = nieuwe NativeSearchQueryBuilder (). WithQuery (builder) .build (); Lijstartikelen = elasticsearchTemplate.queryForList (searchQuery, Article.class); // artikelen bevat [1, 4] assertThat (articleByTags, containsInAnyOrder (hasProperty ("id", is (1)), hasProperty ("id", is (4))));

4. Filtercontext

Wanneer we een query maken, moeten we onderscheid maken tussen de querycontext en de filtercontext. Elke zoekopdracht in Elasticsearch heeft een zoekopdrachtcontext, dus we zouden eraan gewend moeten zijn om ze te zien.

Niet elk type query ondersteunt de filtercontext. Als we daarom op tags willen filteren, moeten we weten welke typen zoekopdrachten we kunnen gebruiken.

De bool query heeft twee manieren om toegang te krijgen tot de filtercontext. De eerste parameter, filter, is degene die we hierboven gebruiken. We kunnen ook een moet niet parameter om de context te activeren.

Het volgende type zoekopdracht dat we kunnen filteren is constante_score. Dit is handig als uu de querycontext wilt vervangen door de resultaten van het filter en aan elk resultaat dezelfde score wilt toekennen.

Het laatste type zoekopdracht dat we kunnen filteren op basis van tags is het filteraggregatie. Hierdoor kunnen we aggregatiegroepen maken op basis van de resultaten van ons filter. Met andere woorden, we kunnen alle artikelen op tag groeperen in ons aggregatieresultaat.

5. Geavanceerde tagging

Tot nu toe hebben we het alleen gehad over tagging met de meest elementaire implementatie. De volgende logische stap is om tags te maken die zichzelf zijn sleutel / waarde-paren. Dit zou ons in staat stellen om nog exclusiever te worden met onze vragen en filters.

We zouden bijvoorbeeld ons tagveld hierin kunnen veranderen:

@Field (type = geneste) privélijsttags;

Dan veranderden we gewoon onze filters om te gebruiken nestedQuery types.

Zodra we begrijpen hoe te gebruiken sleutel / waarde-paren het is een kleine stap om complexe objecten als onze tag te gebruiken. Niet veel implementaties hebben een volledig object als tag nodig, maar het is goed om te weten dat we deze optie hebben als we die nodig hebben.

6. Conclusie

In dit artikel hebben we de basisprincipes besproken van het implementeren van tagging met Elasticsearch.

Zoals altijd zijn voorbeelden te vinden op GitHub.

De volgende » Een eenvoudige tagging-implementatie met JPA Persistence-onderkant

Ik heb zojuist het nieuwe aangekondigd Leer de lente natuurlijk, gericht op de basisprincipes van Spring 5 en Spring Boot 2:

>> BEKIJK DE CURSUS