Eine einfache Tagging-Implementierung mit Elasticsearch

Ausdauer oben

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> KURS PRÜFEN Dieser Artikel ist Teil einer Reihe: • Eine einfache Tagging-Implementierung mit Elasticsearch (aktueller Artikel) • Eine einfache Tagging-Implementierung mit JPA

• Eine erweiterte Tagging-Implementierung mit JPA

• Eine einfache Tagging-Implementierung mit MongoDB

1. Übersicht

Tagging ist ein gängiges Entwurfsmuster, mit dem wir Elemente in unserem Datenmodell kategorisieren und filtern können.

In diesem Artikel implementieren wir das Tagging mit Spring und Elasticsearch. Wir werden sowohl Spring Data als auch die Elasticsearch-API verwenden.

Zunächst werden wir uns nicht mit den Grundlagen des Abrufs von Elasticsearch und Spring Data befassen - Sie können diese hier untersuchen.

2. Tags hinzufügen

Die einfachste Implementierung von Tagging ist ein Array von Zeichenfolgen. Wir können dies implementieren, indem wir unserem Datenmodell ein neues Feld wie folgt hinzufügen:

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

Beachten Sie die Verwendung des Feldtyps Schlüsselwort . Wir möchten nur genaue Übereinstimmungen unserer Tags, um ein Ergebnis zu filtern. Dies ermöglicht es uns, ähnliche, aber separate Tags wie elasticsearchIsAwesome und elasticsearchIsTerrible zu verwenden .

Analysierte Felder würden teilweise Treffer zurückgeben, was in diesem Fall ein falsches Verhalten ist.

3. Abfragen erstellen

Mithilfe von Tags können wir unsere Abfragen auf interessante Weise bearbeiten. Wir können sie wie jedes andere Feld durchsuchen oder sie verwenden, um unsere Ergebnisse nach match_all- Abfragen zu filtern . Wir können sie auch mit anderen Abfragen verwenden, um unsere Ergebnisse zu verbessern.

3.1. Tags suchen

Das neue Tag- Feld, das wir in unserem Modell erstellt haben, ist genau wie jedes andere Feld in unserem Index. Wir können nach jeder Entität suchen, die ein bestimmtes Tag wie dieses hat:

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

In diesem Beispiel wird ein Spring Data Repository zum Erstellen unserer Abfrage verwendet. Wir können jedoch genauso schnell eine Restvorlage verwenden, um den Elasticsearch-Cluster manuell abzufragen.

Ebenso können wir die Elasticsearch-API verwenden:

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

Angenommen, wir verwenden die folgenden Dokumente in unserem Index:

[ { "id": 1, "title": "Spring Data Elasticsearch", "authors": [ { "name": "John Doe" }, { "name": "John Smith" } ], "tags": [ "elasticsearch", "spring data" ] }, { "id": 2, "title": "Search engines", "authors": [ { "name": "John Doe" } ], "tags": [ "search engines", "tutorial" ] }, { "id": 3, "title": "Second Article About Elasticsearch", "authors": [ { "name": "John Smith" } ], "tags": [ "elasticsearch", "spring data" ] }, { "id": 4, "title": "Elasticsearch Tutorial", "authors": [ { "name": "John Doe" } ], "tags": [ "elasticsearch" ] }, ]

Jetzt können wir diese Abfrage verwenden:

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

3.2. Alle Dokumente filtern

Ein gängiges Entwurfsmuster besteht darin, eine gefilterte Listenansicht in der Benutzeroberfläche zu erstellen, in der alle Entitäten angezeigt werden. Der Benutzer kann jedoch auch nach verschiedenen Kriterien filtern.

Angenommen, wir möchten alle Artikel zurückgeben, die nach dem vom Benutzer ausgewählten Tag gefiltert sind:

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

Wieder verwenden wir Spring Data, um unsere deklarierte Abfrage zu erstellen.

Folglich ist die von uns verwendete Abfrage in zwei Teile geteilt. Die Bewertungsabfrage ist der erste Begriff, in diesem Fall match_all . Die Filterabfrage ist die nächste und teilt Elasticsearch mit, welche Ergebnisse verworfen werden sollen.

So verwenden wir diese Abfrage:

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

Es ist wichtig zu wissen, dass diese Abfrage zwar die gleichen Ergebnisse wie in unserem obigen Beispiel liefert, jedoch eine bessere Leistung erbringt.

3.3. Abfragen filtern

Manchmal gibt eine Suche zu viele Ergebnisse zurück, um verwendet werden zu können. In diesem Fall ist es hilfreich, einen Filtermechanismus bereitzustellen, mit dem dieselbe Suche erneut ausgeführt werden kann, nur wenn die Ergebnisse eingegrenzt sind.

Hier ist ein Beispiel, in dem wir die Artikel, die ein Autor geschrieben hat, auf diejenigen mit einem bestimmten Tag eingrenzen:

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

Auch hier erledigt Spring Data die ganze Arbeit für uns.

Schauen wir uns auch an, wie diese Abfrage selbst erstellt wird:

QueryBuilder builder = boolQuery().must( nestedQuery("authors", boolQuery().must(termQuery("authors.name", "doe")), ScoreMode.None)) .filter(termQuery("tags", "elasticsearch"));

Wir können natürlich dieselbe Technik verwenden, um nach jedem anderen Feld im Dokument zu filtern. Tags eignen sich jedoch besonders gut für diesen Anwendungsfall.

So verwenden Sie die obige Abfrage:

SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder) .build(); List articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); // articles contains [ 1, 4 ] assertThat(articleByTags, containsInAnyOrder( hasProperty("id", is(1)), hasProperty("id", is(4))) );

4. Kontext filtern

Wenn wir eine Abfrage erstellen, müssen wir zwischen dem Abfragekontext und dem Filterkontext unterscheiden. Jede Abfrage in Elasticsearch hat einen Abfragekontext, daher sollten wir daran gewöhnt sein, sie zu sehen.

Not every query type supports the Filter Context. Therefore if we want to filter on tags, we need to know which query types we can use.

The bool query has two ways to access the Filter Context. The first parameter, filter, is the one we use above. We can also use a must_not parameter to activate the context.

The next query type we can filter is constant_score. This is useful when uu want to replace the Query Context with the results of the Filter and assign each result the same score.

The final query type that we can filter based on tags is the filter aggregation. This allows us to create aggregation groups based on the results of our filter. In other words, we can group all articles by tag in our aggregation result.

5. Advanced Tagging

So far, we have only talked about tagging using the most basic implementation. The next logical step is to create tags that are themselves key-value pairs. This would allow us to get even fancier with our queries and filters.

For example, we could change our tag field into this:

@Field(type = Nested) private List tags;

Then we'd just change our filters to use nestedQuery types.

Sobald wir verstanden haben, wie Schlüssel-Wert-Paare verwendet werden , ist es ein kleiner Schritt, komplexe Objekte als unser Tag zu verwenden. Nicht viele Implementierungen benötigen ein vollständiges Objekt als Tag, aber es ist gut zu wissen, dass wir diese Option haben, falls wir sie benötigen.

6. Fazit

In diesem Artikel haben wir die Grundlagen der Implementierung von Tagging mit Elasticsearch behandelt.

Beispiele finden Sie wie immer auf GitHub.

Weiter » Eine einfache Tagging-Implementierung mit JPA-Persistenz unten

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs