En simpel implementering af tagging med Elasticsearch

Udholdenhedstop

Jeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:

>> KONTROLLER KURSEN Denne artikel er en del af en serie: • En simpel taggingimplementering med elastiksøgning (nuværende artikel) • En simpel taggingimplementering med JPA

• En avanceret tagging-implementering med JPA

• En simpel implementering af tagging med MongoDB

1. Oversigt

Mærkning er et almindeligt designmønster, der giver os mulighed for at kategorisere og filtrere emner i vores datamodel.

I denne artikel implementerer vi tagging ved hjælp af Spring og Elasticsearch. Vi bruger både Spring Data og Elasticsearch API.

Først og fremmest vil vi ikke dække det grundlæggende ved at få Elasticsearch og Spring Data - du kan udforske disse her.

2. Tilføjelse af tags

Den enkleste implementering af tagging er en række strenge. Vi kan implementere dette ved at tilføje et nyt felt til vores datamodel som denne:

@Document (indexName = "blog", type = "article") public class Article {// ... @Field (type = Keyword) private String [] tags; // ...}

Bemærk brugen af Nøgleord felttype. Vi ønsker kun nøjagtige matches af vores tags for at filtrere et resultat. Dette giver os mulighed for at bruge lignende, men separate tags som elasticsearchIsAwesome og elasticsearchIsTerrible.

Analyserede felter vil returnere delvise hits, hvilket i denne sag er forkert.

3. Opbygningsforespørgsler

Mærker giver os mulighed for at manipulere vores forespørgsler på interessante måder. Vi kan søge på tværs af dem som ethvert andet felt, eller vi kan bruge dem til at filtrere vores resultater på match_all forespørgsler. Vi kan også bruge dem med andre forespørgsler for at stramme vores resultater.

3.1. Søger efter tags

Den nye tag felt, vi oprettede på vores model, er ligesom alle andre felter i vores indeks. Vi kan søge efter enhver enhed, der har et specifikt tag som dette:

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

Dette eksempel bruger et Spring Data Repository til at konstruere vores forespørgsel, men vi kan lige så hurtigt bruge en hvileskabelon til at forespørge klyngen Elasticsearch manuelt.

På samme måde kan vi bruge Elasticsearch API:

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

Antag, at vi bruger følgende dokumenter i vores indeks:

[{"id": 1, "title": "Spring Data Elasticsearch", "author": [{"name": "John Doe"}, {"name": "John Smith"}], "tags": ["elasticsearch", "spring data"]}, {"id": 2, "title": "Søgemaskiner", "author": [{"name": "John Doe"}], "tags": [ "søgemaskiner", "tutorial"]}, {"id": 3, "title": "Anden artikel om elastiksøgning", "author": [{"name": "John Smith"}], "tags": ["elasticsearch", "spring data"]}, {"id": 4, "title": "Elasticsearch Tutorial", "author": [{"name": "John Doe"}], "tags": [ "elasticsearch"]},]

Nu kan vi bruge denne forespørgsel:

Side articleByTags = articleService.findByTagUsingDeclaredQuery ("elasticsearch", PageRequest.of (0, 10)); // articleByTags vil indeholde 3 artikler [1, 3, 4] assertThat (articleByTags, containInAnyOrder (hasProperty ("id", is (1)), hasProperty ("id", is (3)), hasProperty ("id", er (4))));

3.2. Filtrering af alle dokumenter

Et almindeligt designmønster er at skabe et Filtreret listevisning i brugergrænsefladen, der viser alle enheder, men også tillader brugeren at filtrere baseret på forskellige kriterier.

Lad os sige, at vi vil returnere alle artikler, der er filtreret efter det tag, brugeren vælger:

@Query ("{\" bool \ ": {\" skal \ ":" + "{\" match_all \ ": {}}, \" filter \ ": {\" term \ ": {\" tags \ ": \"? 0 \ "}}}}") Side findByFilteredTagQuery (String tag, Pageable pageable);

Endnu en gang bruger vi Spring Data til at konstruere vores deklarerede forespørgsel.

Derfor er forespørgslen, vi bruger, opdelt i to stykker. Scoreforespørgslen er den første periode, i dette tilfælde match_all. Filterforespørgslen er næste og fortæller Elasticsearch, hvilke resultater der skal kasseres.

Sådan bruger vi denne forespørgsel:

Side articleByTags = articleService.findByFilteredTagQuery ("elasticsearch", PageRequest.of (0, 10)); // articleByTags vil indeholde 3 artikler [1, 3, 4] assertThat (articleByTags, indeholderInAnyOrder (hasProperty ("id", er (1)), hasProperty ("id", er (3)), hasProperty ("id", er (4))));

Det er vigtigt at indse, at selv om dette giver de samme resultater som vores eksempel ovenfor, vil denne forespørgsel klare sig bedre.

3.3. Filtreringsforespørgsler

Nogle gange returnerer en søgning for mange resultater til at være anvendelige. I så fald er det rart at udsætte en filtreringsmekanisme, der kan køre den samme søgning igen, bare med resultaterne indsnævret.

Her er et eksempel, hvor vi indsnævrer de artikler, en forfatter har skrevet, til kun dem med et specifikt tag:

@Query ("{\" bool \ ": {\" skal \ ":" + "{\" matche \ ": {\" forfattere.navn \ ": \"? 0 \ "}}," + "\ "filter \": {\ "term \": {\ "tags \": \ "? 1 \"}}}} ") Side findByAuthorsNameAndFilteredTagQuery (Stringnavn, String-tag, Sider, der kan sides);

Igen gør Spring Data alt arbejdet for os.

Lad os også se på, hvordan vi selv konstruerer denne forespørgsel:

QueryBuilder-builder = boolQuery (). Skal (nestetQuery ("forfattere", boolQuery (). Skal (termQuery ("forfatter.navn", "doe")), ScoreMode.None)) .filter (termQuery ("tags", " elasticsearch "));

Vi kan selvfølgelig bruge den samme teknik til at filtrere på ethvert andet felt i dokumentet. Men tags egner sig særligt godt til denne brugssag.

Sådan bruges ovenstående forespørgsel:

SearchQuery searchQuery = ny NativeSearchQueryBuilder (). MedQuery (builder) .build (); Liste artikler = elasticsearchTemplate.queryForList (searchQuery, Article.class); // artikler indeholder [1, 4] assertThat (articleByTags, indeholderInAnyOrder (hasProperty ("id", er (1)), hasProperty ("id", er (4))));

4. Filterkontekst

Når vi bygger en forespørgsel, er vi nødt til at skelne mellem forespørgselskonteksten og filterkonteksten. Hver forespørgsel i Elasticsearch har en Query Context, så vi skal være vant til at se dem.

Ikke alle forespørgselstyper understøtter filterkonteksten. Derfor, hvis vi vil filtrere på tags, skal vi vide, hvilke forespørgselstyper vi kan bruge.

Det bool forespørgsel har to måder at få adgang til filterkonteksten. Den første parameter, filter, er den, vi bruger ovenfor. Vi kan også bruge en må ikke parameter for at aktivere konteksten.

Den næste forespørgselstype, vi kan filtrere, er konstant_score. Dette er nyttigt, når uu ønsker at erstatte Query Context med resultaterne af filteret og tildele hvert resultat den samme score.

Den sidste forespørgselstype, som vi kan filtrere baseret på tags, er filteraggregering. Dette giver os mulighed for at oprette aggregeringsgrupper baseret på resultaterne af vores filter. Med andre ord kan vi gruppere alle artikler efter tag i vores aggregeringsresultat.

5. Avanceret tagging

Indtil videre har vi kun talt om tagging ved hjælp af den mest basale implementering. Det næste logiske trin er at oprette tags, der er sig selv nøgleværdipar. Dette vil give os mulighed for at blive endnu mere avancerede med vores forespørgsler og filtre.

For eksempel kunne vi ændre vores tagfelt til dette:

@Field (type = Nested) private List tags;

Så ville vi bare ændre vores filtre, så de skulle bruge nestedQuery typer.

Når vi først har forstået, hvordan man bruger nøgleværdipar det er et lille skridt til at bruge komplekse objekter som vores tag. Ikke mange implementeringer har brug for et komplet objekt som et tag, men det er godt at vide, at vi har denne mulighed, hvis vi skulle have brug for det.

6. Konklusion

I denne artikel har vi dækket det grundlæggende ved implementering af tagging ved hjælp af Elasticsearch.

Som altid kan eksempler findes på GitHub.

Næste » En simpel implementering af tagging med JPA Persistence-bund

Jeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:

>> KONTROLLER KURSEN