知識庫 / Spring RSS 訂閱

Elasticsearch 簡單標記實現

NoSQL,Spring
HongKong
6
02:07 PM · Dec 06 ,2025
<div>
 <a class="article-series-header" href="javascript:void(0);">該文章是系列的一部分</a>
 <div>
  <div>
   <div>
    • 使用 Elasticsearch 的簡單標記實現 (當前文章)
   </div>
   • 使用 JPA 的簡單標記實現
   <br>
   • 使用 JPA 的高級標記實現
   <br>
   • 使用 MongoDB 的簡單標記實現
   <br>
  </div>
  <!-- end of article series inner -->
 </div>
 <!-- .article-series-links -->
</div>
<!-- end of article series section -->

1. 概述

標籤是一種常見的設計模式,允許我們在數據模型中對項目進行分類和過濾。

在本文中,我們將使用 Spring 和 Elasticsearch 實現標籤功能。我們將使用 Spring Data 和 Elasticsearch API。

首先,我們不會涵蓋 Elasticsearch 和 Spring Data 的基本知識 – 您可以在這裏探索這些內容。

2. 添加標籤

最簡單的標籤實現方式是字符串數組。我們可以通過向我們的數據模型添加一個新字段來實現,例如如下所示:

@Document(indexName = "blog", type = "article")
public class Article {

    // ...

    @Field(type = Keyword)
    private String[] tags;

    // ...
}

請注意使用 關鍵詞 字段類型。我們只想精確匹配我們的標籤,以便過濾結果。這允許我們使用相似但獨立的標籤,例如 elasticsearchIsAwesomeelasticsearchIsTerrible

分析後的字段會返回部分匹配,這在當前情況下是錯誤的。

3. 構建查詢

標籤允許我們以有趣的方式操縱我們的查詢。我們可以像任何其他字段一樣搜索它們,也可以使用它們來過濾 查詢的結果。 此外,我們還可以使用它們與其他查詢結合,以縮小搜索範圍。

3.1. 搜索標籤

我們創建的 <em >標籤</em> 字段與索引中的其他字段一樣。 我們可以通過以下方式搜索具有特定標籤的任何實體:

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

此示例使用 Spring Data Repository 構建我們的查詢,但我們也可以同樣快速地使用 RestTemplate 手動查詢 Elasticsearch 集羣。

同樣,我們可以使用 Elasticsearch API:

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

假設我們使用以下文檔在索引中:

[
    {
        "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" ]
    },
]

現在我們可以使用這個查詢:

Page<Article> 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. 全文檔過濾

一個常見的設計模式是在 UI 中創建一個 篩選列表視圖,它顯示所有實體,並允許用户根據不同的標準進行過濾。

假設我們想要根據用户選擇的標籤返回所有文章:

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

我們再次使用 Spring Data 構建我們的聲明式查詢。

因此,我們使用的查詢被分解成兩部分。評分查詢是第一部分,在本例中是 match_all。過濾查詢是第二部分,告訴 Elasticsearch 丟棄哪些結果。

以下是如何使用此查詢:

Page<Article> 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)))
);

重要的是要意識到,雖然此查詢返回與我們示例中的相同結果,但該查詢將執行得更好。

3.3. 過濾查詢

有時,搜索結果過多,無法直接使用。在這種情況下,提供一個過濾機制,可以重新運行相同的搜索,但只保留篩選後的結果,會很有幫助。

以下是一個示例,我們篩選出作者撰寫的文章,只保留具有特定標籤的文章:

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

再次強調,Spring Data 正在為我們完成所有工作。

我們還將探討如何自己構建這個查詢:

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

我們當然可以使用這種技術來過濾文檔中的任何其他字段。但標籤尤其適合這種用法。

以下是如何使用上述查詢的方法:

SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder)
  .build();
List<Article> articles = 
  elasticsearchTemplate.queryForList(searchQuery, Article.class);

// articles contains [ 1, 4 ]
assertThat(articleByTags, containsInAnyOrder(
 hasProperty("id", is(1)),
 hasProperty("id", is(4)))
);

4. 過濾上下文

在構建查詢時,我們需要區分查詢上下文和過濾上下文。Elasticsearch 中的每個查詢都有查詢上下文,因此我們應該已經熟悉它們。

並非所有查詢類型都支持過濾上下文。因此,如果我們想按標籤進行過濾,我們需要知道可以使用哪些查詢類型。

bool 查詢有兩訪問過濾上下文的方法。第一個參數,filter,是我們上面使用的那個。我們還可以使用 must_not 參數來激活上下文。

下一個我們可以根據標籤進行過濾的查詢類型是 constant_score。這在您想用過濾上下文的結果替換查詢上下文併為每個結果分配相同得分時非常有用。

最後一個可以根據標籤進行過濾的查詢類型是 filter aggregation。這允許我們根據過濾結果創建聚合組。換句話説,我們可以將所有文章按標籤分組到聚合結果中。

5. 高級標記

我們之前只討論了使用最基本實現的標記方法。接下來自然是創建本身是 鍵值對 的標記。這將使我們能夠使用更復雜的查詢和過濾器。

例如,我們可以將標記字段更改為如下所示:

@Field(type = Nested)
private List<Tag> tags;

然後,我們只需更改我們的過濾器,使其使用 嵌套查詢 類型。

一旦我們理解如何使用 鍵值對,那麼使用複雜對象作為我們的標籤就只是小步驟。 並非許多實現都需要一個完整的對象作為標籤,但我們知道我們擁有這個選項,如果需要的話。

6. 結論

在本文中,我們介紹了使用 Elasticsearch 實現標記(tagging)的基礎知識。

下一篇
JPA 中一個簡單的標記實現
user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.