iT邦幫忙

2024 iThome 鐵人賽

DAY 0
0
自我挑戰組

重新開始 elasticsearch 系列 第 12

2024 鐵人賽 Day13: Significant-Text Aggregation ( ?

  • 分享至 

  • xImage
  •  

因為 elasticsearch 要打很多字,寫文苦手如我決定縮寫它,所以會用 ES 代稱。

auto-complete 除了自動補完之外,另外一個說法是『猜你想搜尋什麼』也就是從各種資訊中猜測使用者想要搜尋的內容;我們可以從幾個方向去『猜』使用者的想法,第一個是機率論,紀錄比較多的關鍵字,就代表越多人討論,也就有可能越多人想問;另外一個方向會是從使用者的『歷史搜尋』中猜測,曾經搜尋過的,可能會想再搜尋一次,或是與其相關的主題。

要實作出使用者的搜尋紀錄,需要有一個 db 或檔案紀錄使用者搜尋歷程,這部分目前並沒有在這系列的計畫中。因此『猜』的方向會是從現有紀錄中,找到最相關的資訊為主。

之前實作時的想法大概分成三個部分:

  1. 補完沒有打完的單字(詞):在使用者輸入 cov 的時候,可能會顯示 covid、cover 等等的單詞,所以要先從未完成的 token 補完。
  2. 找出接續的關鍵字:在確認第一個 token 之後,找出跟這個 token 相關的其他 token 作為建議搜尋,例如 covid → covid 19 、covid symptom 等。
  3. 將上面兩個流程結合,在使用者輸入的過程中不斷的產生搜尋與 suggestion。

本篇先來重現一下第一個部分:補完沒有打完的單字(詞),假設是 cov。

想法是這樣的:

(1) 找出以這個沒有打完的單字為前綴(prefix)的 token,

(2) 根據某種規則排序這些 token 誰會在最前面。

上述的 (1) 蠻好解的,只要透過 prefix-query 篩選出包含以 cov 為前綴的 token 的文件就可以了:

GET /covid19_tweets/_search
{
  "query": {
    "prefix": {
      "tweet": {
        "value": "cov"
      }
    }
  }
}

上述的 (2) 就有點困難,尤其是『根據某種規則排序』這件事;當時的解決方式是透過 significant_text 這個 aggregation 來完成。

先簡單說明一下 aggregation 是什麼,前面描述了很多搜尋的方法,但當搜尋出來的結果,想要進行彙整時,就會透過 aggregation 來處理;例如某個 query 搜尋出了 200 篇文章,這 200 篇文章我想要依據日期進行計算出每一天有幾篇文章,就可以透過 aggregation 來進行。

ES 提供很多 aggregation function 可以使用,significant_text 就是其中一個,他所執行的內容就是針對搜尋出來的文章,依據某種方式找出『重要的 token』,aggregation query 內容如下:

GET /covid19_tweets/_search
{
  "query": {
    "prefix": {
      "tweet": {
        "value": "cov"
      }
    }
  },
  "aggs": {
    "find_the_whole_token": {
      "significant_text": {
        "field": "tweet",
        "include": "cov.+",
        "size": 2,
        "min_doc_count": 100
      }
    }
  },
  "fields": ["aggregation"],
   "_source": false 
}

翻譯一下 aggs 內的語法:find_the_whole_token 是一個的 aggregation 名稱,這個 aggregation 要使用 significant_text 這個 ES 提供的 aggregation function,將其做用在 tweet 欄位,透過 significant_text 找出來的 token 要:

a.  符合  `cov.+` 這個 pattern(regex) 

b. 至少在 100 筆資料(documents)中出現

並且請最多回傳兩個這樣的 token 給我;ES 給了以下的產出:

{
  "took": 371,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 10000,
      "relation": "gte"
    },
    "max_score": 1,
    "hits": [
      ...(略)...
    ]
  },
  "aggregations": {
    "find_the_whole_token": {
      "doc_count": 50000,
      "bg_count": 354600,
      "buckets": [
        {
          "key": "covid",
          "doc_count": 32250,
          "score": 1.64217,
          "bg_count": 64500
        },
        {
          "key": "covid19",
          "doc_count": 16263,
          "score": 0.82811196,
          "bg_count": 32526
        }
      ]
    }
  }
}

這樣的搜尋找出的兩個 token 是:covidcovid19 ,看起來結果蠻符合預期的,但其實有一些不太理想的地方:

  1. significant_text 效率沒有很好,這個搜尋花了一些時間,在一個搜尋的狀況下還沒有什麼影響,但當要進行 search as you type 時,使用者每打一個字就要等待一小段時間,會顯得相當緩慢,當使用者人數變多時也會對 ES 服務有很大的負擔。
  2. 這個方法有點取巧,透過 include 這個篩選機制來達到目的,並不是當初 significant_text 設計的使用方式。

接下來幾篇會先照著之前的實作設計走完,把遇到覺得不太理想的地方都記錄下來,最後再來看看有沒有可以優化的地方。


上一篇
2024 鐵人賽 Day12: Ingest Tweeter data
下一篇
2024 鐵人賽 Day14: Significant-Text Aggregation again( ?
系列文
重新開始 elasticsearch 29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言