該文章同步發佈於:我的部落格
也歡迎關注我的 Facebook 以及 Instagram 接收軟體相關的資訊!
在前面幾篇的文章中,我們已經把 Elasticsearch ( 以下簡稱 ES ) 的許多概念都大致上地帶過,這邊快速地複習一下現在我們學到了什麼:
seq_no
搭配 primary_term
來達成樂觀鎖?text
的資料類型text
的資料類型所做到的在大致上理解這些概念後,我們就可以進入到搜尋的環節,但在開始學習使用搜尋的 API 之前,我們需要建立一份有意義的 Index,這時候就可以利用我們學到的 bulk
API 來導入這份 2000's 電影的資料。
這邊是下載的 Gist連結,請自行下載成檔案後,使用 bulk
API 來導入這
份文件到 ES 之中。
簡單範例:
$ curl -H "Content-Type: application/x-ndjson" -XPOST "localhost:9202/_bulk" --data-binary "@bulk_movies.json"
你可能會遇到下面的錯誤訊息:
curl: (7) Failed to connect to localhost port 9202 after 4 ms: Couldn't connect to server
這很簡單,在開一個 node 然後 --publish 9202:9200
即可。
$ docker container run --name robert-es-node-3 --net elastic -it --publish 9202:9200 -m 1GB -e "xpack.security.enabled=false" -e "node.name=node-3" -e "discovery.seed_hosts=robert-es" -d docker.elastic.co/elasticsearch/elasticsearch:8.9.1
你也可以參考 Bulk Import 這篇文章 的方式,直接複製貼上然後導入,我已經整理好資料,讓它符合 NDJSON 的格式,所以應該不會有問題。
其他有關於 ES 的問題應該都可以往前面的文章找到答案,或是留言告訴我!
ES 最主要有兩種方式可以搜尋,分別是 URI Searches
以及 Query DSL
。
簡單介紹一下這兩個差在哪裡:
可以直接透過 HTTP URI 的查詢參數執行搜尋,如下圖所示:
GET /movies/_search?q=title:days
從 URI 上可以很直覺地看到是針對 title 包含 days 的資料進行搜尋。
好處:
壞處:
什麼時候可以使用?簡單的、不涉及複雜邏輯和 aggregation 的查詢。
順便一提,後面的查詢參數其實是 Lucene 的語法,所以可能還要熟悉一下 Lucene 會讓你使用起來更順手
這個是我自己比較推薦的方式,先用一個之前可能就見過的方式來查詢:
GET /movies/_search
{
"query": {
"match_all": {}
}
}
接著 ES 會回應我們所有這個 Index 內的 documents ( 預設是顯示 10 筆 )
好處:
壞處:
什麼時候可以使用?複雜的、需要精確控制的查詢和 aggregation 的操作。
DSL 是 Domain Specific Language 的縮寫,意指語法只使用在特定的場合,像是這個 Query DSL 就只用在 ES 的查詢之中
很顯然 Query DSL 的壞處就是麻煩,寫起來複雜,但這樣的痛苦在 Kibana 的 Dev tools 中得到緩解,因為 auto-completion 的關係,撰寫的時候不需要記一些特別的語法,可以縮短學習曲線。
如果圖片不容易閱讀的話,這是 response 大致上的樣貌:
{
"took": 4,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2430,
"relation": "eq"
},
"max_score": 1,
"hits": [
{
"_index": "movies",
"_id": "1",
"_score": 1,
"_ignored": [
"extract.keyword"
],
"_source": {
"title": "102 Dalmatians",
"year": 2000,
"cast": [
"Glenn Close",
"Gérard Depardieu",
"Alice Evans"
],
"genres": [
"Comedy",
"Family",
"Crime"
],
"href": "102_Dalmatians",
"extract": """102 Dalmatians is a 2000 American crime comedy film directed by Kevin Lima and produced by Edward S. Feldman and Walt Disney Pictures. The sequel to the 1996 film 101 Dalmatians, a live-action remake of the 1961 Disney animated film of the same name, it stars Glenn Close reprising her role as Cruella de Vil as she attempts to steal puppies for her "grandest" fur coat yet. Glenn Close and Tim McInnerny were the only two actors from the 1996 film to return for the sequel. The film received generally negative reviews from critics and was unable to achieve the box office success of its predecessor, although the film was nominated for the Academy Award for Best Costume Design.""",
"thumbnail": "https://upload.wikimedia.org/wikipedia/en/f/fe/102_dalmatians_poster.jpg",
"thumbnail_width": 220,
"thumbnail_height": 327
}
},
...
]
}
}
首先是最上面的 took
,代表的是這次查詢耗費的時間,單位是 millisecond,所以這次查詢耗費了 0.005 秒。
第二個是 time_out
的 flag,很直覺地告訴我們這次的查詢有沒有超時。
第三個看到的是 _shards
這邊包含了 primary shard 以及 replica shard 兩種不同的 shards。
比較有趣的是 skipped
這個 key,它代表了 ES 判斷如果你要查詢的資料不在某一個 shard 之中,直接跳過某些 shard 來提高查詢的效率,常常適用於 range
的資料類型查詢。
需要注意的是這個 shard 可能包含了還沒有被分配到的 shard,如果這數字比你預期的還高,那大概是這個原因
接著進入到 hits
之中,從字面上就可以知道這代表的是符合查詢參數的結果:
我們先從 total
來說,value
代表了符合查詢的筆數,這個很好理解;但下面的 relation
代表的意思就是這次 value
結果是否精確,如果是 "eq"
代表說這次返回的數字是準確的,而另外一個可能返回的結果是 "gte"
代表 greater than or equal to,表示這個 value
是符合查詢結果的最低標。
至於為什麼會有這樣的情況,之後有機會會解釋,但大概可以查詢的關鍵字是 Deep Paging 或是 track_total_hits
。
再來是 max_score
代表的則是這些符合查詢的資料中,最符合的那份資料的得分,可以看到資料中也有會一個 _score
的欄位,有關於 scoring 的細節,之後會在仔細的解釋,這比較複雜,總之絕對不是 0 - 100 分的概念。
最後就是 hits
裡面的 hits
,我知道這有點搞笑,但沒辦法,ES 要這樣取名就是這樣取,在這個陣列之中,就會包含符合查詢條件的資料,並且以 JSON 物件顯示。
看到 JSON 物件內,第一個 _index
是 index 的名字,你可能會問,我們不就是在一個 index 內搜尋了嗎?為什麼需要這個欄位呢?這是因為 ES 不止可以在一個 index 之中搜尋,所以這個欄位可以幫助我們辨別資料來自哪一個 index 之中。
而 _id
和 _score
就跳過不談,要介紹的另外一個是 ignored
的陣列,裡面會列出在建立資料時被忽略的欄位,以我提供的例子來說:
extract.keyword
代表 extract
欄位的 keyword
資料類型超過字數限制,ES 預設是 256,所以超過的就沒有被建立到 inverted index 之中。
這就是 Dynamic Mapping 做的事情了,有時候我們會需要針對同一個欄位做 text
以及 keyword
兩種資料類型,為的就是不同的目的,text
保證了全文的搜尋,而 keyword
則可以做到資料的 aggregation 以及排序。
最後則是 _source
欄位,也是之前介紹過的,我們當初建立資料時的內容,都會原封不動的在這個 key 裡面。
上述就是基本的 Search API 使用方式,以及在 response 之中欄位代表的意思,接著我們會來討論不同層級的搜尋方式,看看 ES 為什麼被稱作地表最強搜尋引擎。