iT邦幫忙

2024 iThome 鐵人賽

DAY 0
0
自我挑戰組

重新開始 elasticsearch 系列 第 15

2024 鐵人賽 Day16: Optimization I

  • 分享至 

  • xImage
  •  

從對之前作法的 Review 中,大致可以總結以下幾個可以改善的地方:

  1. 效率:因為 query 中大量的使用 significant-text aggregation,單一次搜尋的時間過長。
  2. 建議字過於單一:因為並沒有考量全部的搜尋詞加上 significant-text aggregation 算法的關係,就算是搜尋不一樣的內容,文本中常見的單詞就會一直出現。
  3. tokenizer setting 過於符合特定條件,且 token 沒有 normalize。

第 1 點在官方文件中有提到 significant-text aggregation 應該要搭配 sampler 一起用,也就是並非對全部的資料進行計算,而是只計算經過抽樣的資料,這樣可以解決搜尋效率的問題。

第 2 點其實我們只要把所有的搜尋字加入 suggestion 部分的 query, 讓 aggregation 的範圍限制在有符合搜尋字的資料中,讓 suggestion 和搜尋字的關聯更強烈,在應該可以解決某個程度上建議字比較單一的問題:

我們試試看在原本的方法做以下的修改:

  1. 於 significant-text aggregation 加上sampler
  2. 將所有搜尋字都納入 suggestion 的 query

修改後的 query 和 python 查詢 script:

import elasticsearch
import itertools
import re

def get_token_compelete(kw_phrase, index_name):
    tail = re.findall("\S+", kw_phrase)[-1] # 取得最後沒有打完的那個字
    query_to_compelete = {
              "query": {
                "prefix": {
                  "tweet": {
                    "value": tail
                  }
                }
              },
               "aggregations": {
                    "my_sample": {
                        "sampler": {
                            "shard_size": 1000
                        },
                        "aggregations": {
                            "find_the_whole_token": {
                                "significant_text": { 
                                    "field": "tweet",
                                    "include": f"{tail}.+",
                                    "min_doc_count": 10,
                                "size": 2
                                }
                            }
                        }
                    }
               },
              "fields": ["aggregation"],
               "_source": False 
            }
    r = es_cli.search(index=index_name, body=query_to_compelete)
    buckets = r["aggregations"]["my_sample"]["find_the_whole_token"]["buckets"]
    tokens = [bucket["key"] for bucket in buckets]
    return tokens

def get_suggestion(search_phrase, index_name):
    suggestion_query = {
        "query": {
          "match": {
            "tweet": search_phrase
          }
        },
        "aggregations": {
            "my_sample": {
                "sampler": {
                    "shard_size": 1000
                    },
                "aggregations": {
                    "tags": {
                        "significant_text": { 
                            "field": "tweet",
                            "exclude": search_phrase.split(" "),
                            "min_doc_count": 10,
                            "size": 3
                        }
                    }
                }
            }
        }
    }
    r = es_cli.search(index=index_name, body=suggestion_query)
    buckets = r["aggregations"]["my_sample"]["tags"]["buckets"]
    suggestions = [bucket["key"] for bucket in buckets]
    return suggestions

def get_auto_complete(search_phrase):
    pre_search_phrase = " ".join(re.findall("\S+", search_phrase)[0:-1])
    comelete_tokens = get_token_compelete(search_phrase, index_name)
    guess = []
    for token in comelete_tokens:
        compelete_search_prahse = " ".join([pre_search_phrase, token])
        suggestions = get_suggestion(compelete_search_prahse, index_name)
        if not suggestions:
            guess.append(token)
        for suggestion in suggestions:
            guess.append(" ".join([compelete_search_prahse, suggestion]).strip())
    return guess

es_cli = elasticsearch.Elasticsearch("http://localhost:9200")
index_name = 'covid19_tweets'
search_phrase = "syn"
print(get_auto_complete(search_phrase))

稍微說明一下 sampler 的 shard_size 參數,代表的是每一個 shard 要取出幾分篩選後分數最高的資
料;因爲我使用的環境是單一節點,也只有一個 shard,所以就是我要取分數最高的前多少筆的意思。

加了 sampler 之後,搜尋的速度有明顯的提升,然 suggestion 的部分使用跟前一版的測試相同案例,結果如下:

輸入: cov

輸出:['covid19 stayathome', 'covid19 thelockdown', 'covid19 coronavirus', 'covid 19']

輸入: covid

輸出:['covid19 stayathome', 'covid19 thelockdown', 'covid19 coronavirus', 'covid_19 coronavirus', 'covid_19 coronaupdate', 'covid_19 evirahealth']

輸入: covid sym

輸出:['covid symptoms mild', 'covid symptoms 19', 'covid symptoms showing', 'covid symptom 19']

似乎只有最後一個有顯著的差異,看起來是有比較好一點(就一點點 XD)

關於主題的部分今天先到這邊,下一篇會繼續看有什麼優化可以進行。


這是鐵人賽的第 17 篇文章,也就是我連續寫了第 17 天的文章,第一天雖然有發文,但因為技術問題(用手機發文不知道為什麼沒有發成鐵人賽的文),再加上中間有一天確實是過了 12 點才發(純粹是忘記了),已經沒有領取完賽證明的資格了。

大概在第 10 天時已經沒有存稿了,所以每天都是當天寫的,最近幾天的內容更是邊寫邊研究作法,研究不太順利的時候真是壓力山大 Q Q,往回看發現好像理解錯了的時候也壓力山大 Q Q,覺得有在定期寫文章的大大們都太厲害了。

總之,如果內容有什麼錯誤,不論是筆誤、覺得我理解錯誤、覺得我根本胡言亂語,都希望有看到這邊的朋友盡量跟我說,希望可以有更正的機會;然,如果你是跟這個主題不熟的朋友,請抱持著質疑的心看這些文章,有什麼覺得不太合理的、不明白的,也歡迎發問,感激不盡。


上一篇
2024 鐵人賽 Day15: The combination of two
下一篇
2024 鐵人賽 Day17: Optimization II
系列文
重新開始 elasticsearch 29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言