# Elasticsearch QueryString 查詢語法筆記 最近開始想把 LOG 儲存在 Elasticsearch 裡,但這樣做需要有個方便查詢的 UI 介面,對我而言 Kibana 功能過於複雜,Postman 查詢又比較麻煩。後來發現了 Elasticvue 這個工具,選擇它的原因是它支援多種瀏覽器擴充套件及桌面應用程式版本。 [Elasticvue](https://github.com/cars10/elasticvue) 的查詢介面主要基於 query_string 語法,這是 Elasticsearch 中基於 Lucene 查詢語法的擴展版本。對熟悉 SQL 的使用者來說,`query_string` 比其他 DSL 語法更容易上手,因此我整理了一些常用的語法方便查閱。 可從 [Elasticvue 官網](https://elasticvue.com/) 查看各個瀏覽器擴充套件,或是桌面應用程式的安裝檔案或連結。 測試版本:Elasticsearch 9.1.4 ## 基本語法 ### 基本 API 結構 ```json { "query": { "query_string": { "query": "your query string here", // 必要參數:查詢字串(使用 "*" 可搜尋所有文件) "default_field": "content", // 選用參數:預設搜尋欄位,未指定,預設為 "*",搜尋全部欄位 "default_operator": "OR" // 選用參數:預設運算子,預設值為 OR // 其他可選參數 } }, "size": 10, // 選用參數:回傳結果數量,預設為 10 "from": 0, // 選用參數:起始位置,預設為 0 "sort": [] // 選用參數:結果排序 } ``` 未指定欄位時,QueryString 會在所有可搜尋欄位中進行搜尋。 ## 查詢語法 ### 1. 基本搜尋 簡單關鍵字搜尋: ``` apple ``` 多個詞彙(預設使用 OR 連接): ``` apple banana ``` #### 不同欄位類型的搜尋行為 Elasticsearch 對不同欄位型別使用不同分析策略([官方文件](https://www.elastic.co/docs/reference/text-analysis/analyzer-reference)): * **text 欄位**:使用 standard 分析器,會分詞並轉小寫。 * **keyword 欄位**:使用 keyword 分析器,保持完整字串不變。 * **數值/日期/布林欄位**:不使用分析器,索引原始值。 * **query_string**:會依目標欄位型別選擇對應策略處理查詢字串。 | 欄位類型 | 查詢字串 | 行為說明 | |---------|---------|---------| | text | `apple banana` | 等同 `apple OR banana`,找到任一詞彙即符合 | | keyword | `"apple" "banana"` | 必須用雙引號明確搜尋多個完整字串,或著使用 `apple OR banana` | | keyword | `apple banana` | 查不到,因為會被視為完整字串比對 | | 數值/日期/布林 | `"123" "456"` | 多值搜尋必須用雙引號明確指定值,或著使用 `123 OR 456` | | 數值/日期/布林 | `123 456` | 查不到,因為 query_string 不會自動拆分多個完整值 | **JSON 多詞彙查詢範例(使用雙引號跳脫):** ```json { "query": { "query_string": { "query": "\"apple\" \"banana\"" } } } ``` 精確片語搜尋(使用雙引號): ``` "red apple" ``` ### 2. 布林運算子 AND 運算子(兩個詞都必須存在): ``` apple AND banana ``` OR 運算子(至少有一個詞必須存在): ``` apple OR banana ``` NOT 運算子(排除包含某詞的文件): ``` apple NOT banana ``` 加號(必須包含此詞): ``` +apple banana ``` 減號(必須排除此詞): ``` apple -banana ``` :::warning 英文運算子必需全部大寫。 ::: ### 3. 欄位指定查詢 針對特定欄位搜尋: ``` title:apple ``` 針對多個欄位的查詢: ``` title:apple AND content:banana ``` 多值查詢(OR 條件): ``` user_id:(1234 OR 5678) ``` 欄位存在性查詢: ``` _exists_:email // 查詢 email 欄位存在的文件,可以理解成不為 null NOT _exists_:phone // 查詢 phone 欄位不存在的文件,可以理解成為 null ``` 欄位指定優先順序:查詢字串中明確指定欄位 > fields 參數 > default_field 參數。 ### 4. 範圍查詢與比較運算子 **範圍查詢:** ``` price:[10 TO 20] // 閉區間,包含 10 和 20 price:{10 TO 20} // 開區間,不包含 10 和 20 price:[10 TO *] // 大於等於 10 price:[* TO 20] // 小於等於 20 ``` **比較運算子:** ``` price:>10 // 大於 10 price:>=10 // 大於等於 10 price:<20 // 小於 20 price:<=20 // 小於等於 20 ``` 這兩種寫法在功能上大致相同,可依情境選擇使用。不過實測發現,範圍查詢語法在各種參數組合下的相容性較佳,以下為測試範例: **比較運算子使用限制:** 比較運算子(`>`、`>=`、`<`、`<=`)在搭配 `fields` 陣列參數時會產生錯誤,但使用 `default_field` 或直接在查詢字串中指定欄位時可正常運作。 ✅ **可正常使用的寫法:** ```json // 方式 1:未指定欄位 { "query": { "query_string": { "query": ">=10" } } } // 方式 2:在查詢字串中指定欄位 { "query": { "query_string": { "query": "price:>=10" } } } // 方式 3:使用 default_field 參數 { "query": { "query_string": { "query": ">=10", "default_field": "price" } } } ``` ❌ **會產生錯誤的寫法:** ```json // 使用 fields 陣列會導致查詢失敗 { "query": { "query_string": { "query": ">=10", "fields": ["price"] } } } ``` ✅ **解決方案:改用範圍查詢語法** 範圍查詢語法(`[x TO y]`)與 `fields` 參數相容,可正常使用: ```json { "query": { "query_string": { "query": "[10 TO *]", "fields": ["price"] } } } ``` ### 5. 萬用字元搜尋 萬用字元搜尋: ``` te?t // 問號代表一個字元 test* // 星號代表零個或多個字元 ``` ### 6. 日期時間查詢 #### 基本日期範圍查詢 使用範圍語法進行日期查詢: ``` timestamp:[2023-01-01 TO 2023-01-31] ``` 也可以使用相對時間進行查詢: ``` timestamp:>now-1d // 查詢過去 24 小時的資料 ``` **常用相對時間表達式:** * `now-1h`:一小時前 * `now-1d`:一天前 * `now-1w`:一週前 * `now/d`:今天開始(00:00:00) * `now/w`:本週開始 * `now/M`:本月開始 #### 日期時間格式 Elasticsearch 的 `date` 型別預設支援到毫秒精度,若需要奈秒精度可改用 `date_nanos` 型別。 **支援的格式:** * **標準格式**:`yyyy-MM-ddTHH:mm:ss.SSSZ`(例如:`2023-01-15T08:30:00.000Z`) * **簡化格式**:`yyyy-MM-dd`(例如:`2023-01-15`) * 預設時區為 UTC **格式行為差異:** 不同精度的日期格式在查詢時會有不同的行為,以下是關鍵差異: ```json // 查詢到「年」時,會精確比對該年的第一秒 { "query": { "query_string": { "query": "timestamp:2023" // 等同於 ="2023-01-01T00:00:00Z" } } } // 查詢到「月」時,會精確比對該月的第一秒 { "query": { "query_string": { "query": "timestamp:2023-02" // 等同於 ="2023-02-01T00:00:00Z" } } } // 當精度達到「日」,行為開始改變 // 這會查詢該日期的整個區間範圍 { "query": { "query_string": { "query": "timestamp:2023-03-01" // 查詢 >="2023-03-01T00:00:00Z" <"2023-03-02T00:00:00Z" } } } // 精度到「小時」時,查詢該小時的整個區間 { "query": { "query_string": { "query": "timestamp:2023-03-01T08" // 查詢 >="2023-03-01T08:00:00Z" <"2023-03-01T09:00:00Z" } } } // 精度到「分鐘」時,需要用雙引號(因包含冒號) { "query": { "query_string": { "query": "timestamp:\"2023-03-01T08:00\"" // 查詢 >="2023-03-01T08:00:00Z" <"2023-03-01T08:01:00Z" } } } ``` #### 使用注意事項 **1. 標準格式需要使用雙引號** 因為標準格式包含冒號 `:`,在 QueryString 中會被視為特殊字元,必須用雙引號包起來: ❌ **錯誤寫法(會產生解析錯誤):** ```json { "query": { "query_string": { "query": "timestamp:2023-01-15T08:30:00Z" } } } ``` ✅ **正確寫法:** ```json // 方法 1:使用雙引號包起來 { "query": { "query_string": { "query": "timestamp:\"2023-01-15T08:30:00Z\"" } } } // 方法 2:使用簡化格式(不含冒號) { "query": { "query_string": { "query": "timestamp:2023-01-15" } } } ``` **2. 日期查詢不支援比較運算子** 當使用比較運算子查詢日期時,查詢會失效或產生錯誤: ```json // 這個查詢實際上會在「全部欄位」中搜尋符合該日期的資料 // 而不是在 timestamp 欄位中進行比較 { "query": { "query_string": { "query": "timestamp:>=\"2023-01-15T08:30:00Z\"" } } } // 若改用 default_field 或 fields,會直接產生解析錯誤 { "query": { "query_string": { "query": ">=\"2023-01-15T08:30:00Z\"", "fields": ["timestamp"] // ❌ 會報錯 } } } { "query": { "query_string": { "query": ">=\"2023-01-15T08:30:00Z\"", "default_field": "timestamp" // ❌ 會報錯 } } } ``` ✅ **正確做法:使用範圍查詢語法** ```json { "query": { "query_string": { "query": "timestamp:[2023-01-15T08:30:00Z TO *]" } } } ``` **3. 自訂格式的影響** 當 `date` 型別的欄位有指定 [format](https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-date-format#strict-date-time) 時(非預設的 `strict_date_optional_time||epoch_millis`),查詢格式必須完全符合指定格式: ```json // 假設欄位定義如下 "timestamp": { "type": "date", "format": "yyyy-MM-dd'T'HH:mm:ss'Z'" } ``` 使用錯誤格式會導致不同結果: ```json // 在 query 中直接指定欄位:查詢無結果(靜默失敗) { "query": { "query_string": { "query": "timestamp:[2023-01-15T08:30 TO *]" // 格式不符 } } } // 使用 default_field 或 fields:產生錯誤訊息 { "query": { "query_string": { "query": "[2023-01-15T08:30 TO *]", "default_field": "timestamp" // 會報錯:failed to parse date field } } } ``` 錯誤訊息範例: ``` failed to parse date field [2023-01-15T08:30] with format [yyyy-MM-dd'T'HH:mm:ss'Z'] ``` ## 其他參數 ### 1. 欄位相關參數 **default_field vs fields**: * `default_field`: 指定預設搜尋的單一欄位。 * `fields`: 指定多個搜尋欄位及其權重。 ```json // 使用 default_field { "query_string": { "query": "apple", "default_field": "content" } } // 使用 fields { "query_string": { "query": "apple", "fields": ["title^2", "content", "tags"] } } ``` ### 2. analyzer 指定 `query_string` 如何處理和分析查詢字串,Elasticsearch 提供多種內建分析器,詳細列表可參考[官方文件](https://www.elastic.co/docs/reference/text-analysis/analyzer-reference)。 ```json { "query_string": { "query": "The Quick Brown Fox", "analyzer": "standard" } } ``` #### 重要概念:索引時 vs 查詢時分析器 Elasticsearch 有兩個階段會使用分析器: **1. 索引時分析器(Index-time Analyzer)** * 處理存入索引的資料。 * 例子:text 欄位存入 `"Wing Chou"` → 分詞為 `["wing", "chou"]`。 **2. 查詢時分析器(Query-time Analyzer)** * 處理查詢字串。 * `query_string` 的 `analyzer` 參數控制此階段。 #### 實例說明 假設 text 欄位已存入 `"Wing Chou"`,索引時分詞為 `["wing", "chou"]`: ```json // ✅ 使用 standard 分析器 // 查詢字串被分析為 ["wing", "chou"],然後預設運算子為 OR,因為不論是 wing 還是 chou 都可以比對到資料 { "query_string": { "query": "Wing Chou", "analyzer": "standard" } } // ❌ 使用 keyword 分析器,錯誤查詢條件 // 查詢字串保持完整 "Wing Chou",無法符合分詞結果 { "query_string": { "query": "Wing Chou", "analyzer": "keyword" } } // 查詢字串保持 "Wing"(大寫),無法符合資料中的 "wing"(小寫) { "query_string": { "query": "Wing", "analyzer": "keyword" } } // ✅ 使用 keyword 分析器,正確查詢條件 { "query_string": { "query": "wing", "analyzer": "keyword" } } { "query_string": { "query": "wing OR chou", "analyzer": "keyword" } } ``` ### 3. analyze_wildcard 控制是否對萬用字元表達式進行分析處理。[官方文件](https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-query-string-query#query-string-top-level-params)說明如下: > (選用參數,布林值)若設為 `true`,查詢時會嘗試對萬用字元詞彙進行分析處理。預設為 `false`。 > > 需要注意的是,即使設為 `true`,也只有**結尾是 `*` 的查詢**才會進行完整分析。**開頭或中間包含 `*` 的查詢**只會進行正規化處理。 ```json { "query_string": { "query": "Te*", "analyze_wildcard": true, "analyzer": "standard" } } ``` #### 參數值說明 * `analyze_wildcard: false`(預設):萬用字元表達式不經過分析器處理 * `analyze_wildcard: true`:萬用字元表達式會經過分析器處理,但處理方式取決於萬用字元位置: * **結尾萬用字元**(如 `Te*`):進行完整分析 * **開頭或中間萬用字元**(如 `*Te*`、`T*e`):只進行正規化 #### 實測觀察與疑問 在實際測試中發現,這個參數的行為與官方說明存在一些不一致或難以理解的地方: **1. Text 欄位的行為** 不管 `analyze_wildcard` 設定為 `true` 或 `false`,查詢都是大小寫不敏感: ```json // 以下兩個查詢結果相同,都能匹配到資料 { "query_string": { "query": "name:Te*", "analyze_wildcard": true } } { "query_string": { "query": "name:Te*", "analyze_wildcard": false } } ``` **2. Keyword 欄位不指定 analyzer** 不管 `analyze_wildcard` 設定為 `true` 或 `false`,查詢都是大小寫敏感: ```json // 以下兩個查詢結果相同,都只能精確匹配大小寫 { "query_string": { "query": "email:Te*", "analyze_wildcard": true } } { "query_string": { "query": "email:Te*", "analyze_wildcard": false } } ``` **3. Keyword 欄位指定 analyzer 時的矛盾行為** 當對 keyword 欄位指定 `analyzer: "standard"` 時,出現了與官方說明不一致的現象(也可能是我對「完整分析」與「正規化」的理解有誤): ```json // 開頭或中間有萬用字元:會轉小寫(符合「正規化」說明) // 實際查詢:*te* { "query_string": { "query": "email:(*Te*)", "analyze_wildcard": true, "analyzer": "standard" } } // 結尾萬用字元:不會轉小寫(與「完整分析」說明矛盾) // 實際查詢:Te* { "query_string": { "query": "email:(Te*)", "analyze_wildcard": true, "analyzer": "standard" } } ``` 根據官方說明,結尾萬用字元應該進行「完整分析」,理論上 `Te*` 應該被 standard analyzer 轉換成 `te*`,但實測結果卻是保持 `Te*` 不變。 #### 萬用字元位置的處理差異 | 萬用字元模式 | 官方說明的處理方式 | Keyword + standard analyzer 實測 | |-------------|------------------|----------------------------------| | `Te*` | 完整分析 | 不轉小寫(`Te*`) | | `*Te*` | 只正規化 | 會轉小寫(`*te*`) | | `*Te` | 只正規化 | 會轉小寫(`*te`) | | `T*e` | 只正規化 | 會轉小寫(`t*e`) | ### 4. auto_generate_synonyms_phrase_query 控制同義詞處理方式: * `true`(預設):自動為同義詞生成片語查詢,保持詞序和相鄰性。 * `false`:只生成普通同義詞查詢,不考慮詞序。 例如,若設定 "ny" 與 "new york" 為同義詞: ```json { "query_string": { "query": "ny restaurants", "auto_generate_synonyms_phrase_query": true } } ``` * true:會比對 "ny restaurants" 或 "new york restaurants"("new york" 作為整體片語)。 * false:會比對 "ny restaurants" 或 "new restaurants" 或 "york restaurants"。 ### 5. 權重控制:boost 與欄位加權 Elasticsearch 提供兩種層級的權重控制機制: #### 5.1 欄位加權(Field Boost) 使用 `^` 語法調整特定欄位的權重。 ```json { "query_string": { "query": "apple iphone", "fields": ["title^3", "description^2", "content"] } } ``` * **功能**:控制同一個查詢中不同欄位的相對重要性。 * **作用方式**: * 每個欄位各自獨立計算基礎分數(基於 BM25 演算法)。 * title 欄位的分數會乘以 3。 * description 欄位的分數會乘以 2。 * content 欄位的分數乘以 1(預設值)。 * 最後將所有加權後的欄位分數相加,得到最終分數。 * **重要說明**:`title^3` 並非表示 title 的最終得分是 content 的 3 倍,而是表示 title 欄位的基礎分數會被放大 3 倍後,再與其他欄位組合。最終分數還會受到詞頻、逆文件頻率、文件長度等因素影響。 * **適用場景**:多欄位搜尋時,某些欄位(如標題)比其他欄位(如內容)更能代表文件主題。 **計算範例**(基礎分數為假設值): 假設搜尋 "apple",各欄位的基礎分數如下: | 欄位 | 基礎分數 | 加權後分數 | |------|---------|-----------| | title | 2.0 | 2.0 × 3 = 6.0 | | description | 1.5 | 1.5 × 2 = 3.0 | | content | 2.5 | 2.5 × 1 = 2.5 | | **最終分數** | | **11.5** | > **注意**:上述基礎分數為假設性範例,用於說明計算邏輯。實際分數會依照索引狀態、文件內容、詞頻等因素而有所不同,需使用 `_explain` API 查看真實評分過程。 --- #### 5.2 查詢層級 boost(Query Boost) 調整整個查詢子句的權重。 ```json { "query_string": { "query": "apple iphone", "boost": 2.0 } } ``` * **功能**:在複合查詢(如 bool query)中調整整個查詢子句的重要性。 * **重要限制**:如果單獨使用一個查詢,boost 參數對排序沒有影響(所有文件的分數都乘以相同倍數,相對排序不會改變)。 * **主要用途**: * 在 `bool` query 的 `should` 子句中,調整不同查詢條件的相對重要性。 * 讓某些匹配條件比其他條件對最終排序有更大影響。 * 體現業務邏輯中不同搜尋維度的重要性差異。 * **適用場景**:結合多種查詢方式(文字搜尋、精確匹配、範圍查詢等),需要調整它們對最終排序的影響權重。 --- #### 5.3 兩者結合使用 **範例** ```json { "query": { "bool": { "should": [ { "query_string": { "query": "apple", "fields": ["title^2"], "boost": 2.0 } }, { "query_string": { "query": "iphone", "fields": ["description"], "boost": 4.0 } } ] } } } ``` **計算說明**(基礎分數為假設值): 假設有以下三個文件: | 文件 | title 包含 "apple" | description 包含 "iphone" | 分數計算 | |------|-------------------|-------------------------|---------| | 文件 A | ✅ (基礎分 3.0) | ✅ (基礎分 2.0) | (3.0 × 2 × 2.0) + (2.0 × 1 × 4.0) = **20.0** | | 文件 B | ✅ (基礎分 2.5) | ❌ | (2.5 × 2 × 2.0) + 0 = **10.0** | | 文件 C | ❌ | ✅ (基礎分 2.5) | 0 + (2.5 × 1 × 4.0) = **10.0** | **排序結果**:文件 A (20.0) > 文件 B (10.0) = 文件 C (10.0) **分析**: * 文件 A 同時匹配兩個條件,獲得最高分。 * 文件 B 只匹配 title 中的 "apple":欄位基。礎分 2.5 × 欄位 boost 2 × 查詢 boost 2.0 = 10.0。 * 文件 C 只匹配 description 中的 "iphone":欄位基礎分 2.5 × 欄位 boost 1 × 查詢 boost 4.0 = 10.0。 即使文件 C 的查詢 boost (4.0) 比文件 B 的 (2.0) 高,但因為文件 B 有欄位 boost (^2),實際結果仍需依照基礎分數而定。 > **注意**:上述基礎分數為假設性範例,實際分數需使用 `_explain` API 查看。 --- #### 5.4 關鍵差異 | 特性 | 欄位加權 (`^`) | 查詢 boost (`boost`) | |------|---------------|---------------------| | **作用範圍** | 單一查詢內的不同欄位 | 複合查詢中的不同查詢子句 | | **控制對象** | 欄位間的相對權重 | 查詢子句間的相對權重 | | **使用位置** | `fields` 參數中 | 查詢子句的頂層參數 | | **單獨使用** | 有效(影響欄位分數組合) | 無效(不改變排序) | | **典型場景** | 標題比內容重要 | 使用者搜尋比篩選條件重要 | --- ### 6. 模糊搜尋(fuzziness 與 phrase_slop) 波浪號 `~` 在 QueryString 中有兩種不同用途,取決於它的位置: #### 6.1 模糊搜尋(單詞後的波浪號) ``` apple~ // 單詞後接波浪號,表示模糊搜尋 apple~2 // 指定模糊度為 2 ``` * 對應參數:`fuzziness`。 * 功能:處理單個詞的拼寫錯誤,基於編輯距離。 * 識別方式:用於**單詞**後面。 **模糊度值的限制:** * 有效值:`0`、`1`、`2` 或 `"AUTO"`。 * 超過 2 會報錯:`Valid edit distances are [0, 1, 2]`。 **fuzziness 參數生效條件:** `fuzziness` 參數只在查詢字串中有單詞使用 `~`(但未指定數字)時才會生效: ```json // ✅ 單詞有 ~,無數字,未設定 fuzziness:預設模糊度為 1 { "query_string": { "query": "apple~" // apple 使用模糊度 1(預設值) } } // ✅ 單詞有 ~,無數字:使用 fuzziness 參數 { "query_string": { "query": "apple~", "fuzziness": 2 // 生效,apple 使用模糊度 2 } } // ❌ 單詞無 ~:不生效 { "query_string": { "query": "apple banana", "fuzziness": 1 // 不生效,兩個詞都是精確匹配 } } // ❌ 單詞有 ~N(明確數字):以查詢字串中的數字為準 { "query_string": { "query": "apple~2", "fuzziness": 1 // 不生效,以查詢字串中的 2 為準 } } // ⚠️ 混用情況:只有帶 ~ 的詞會套用模糊搜尋 { "query_string": { "query": "apple~2 banana", "fuzziness": 1 // 不生效,apple 用 2,banana 精確匹配 } } { "query_string": { "query": "apple~ banana", "fuzziness": 1 // 只對 apple 生效(模糊度 1),banana 精確匹配 } } ``` **優先順序規則:** 1. 查詢字串中的 `~N`(明確數字):直接使用該數字 2. 查詢字串中的 `~`(無數字)+ `fuzziness` 參數:使用 `fuzziness` 參數值 3. 查詢字串中的 `~`(無數字)+ 未設定 `fuzziness`:預設模糊度為 1 4. 查詢字串中沒有 `~`:該詞為精確匹配,`fuzziness` 參數不生效 ⚠️ **重要注意事項**: **`fuzziness` 參數必須搭配查詢字串中的 `~` 才會生效**。如果詞彙後面沒有 `~`,該詞會被視為精確匹配,無論 `fuzziness` 參數設定為何。 **fuzziness 設定差異**: * `"fuzziness": "AUTO"`:根據詞長自動調整。預設行為等同於 `AUTO:3,6`,規則如下: * 詞長 0-2 字元:fuzziness = 0(必須精確匹配) * 詞長 3-5 字元:fuzziness = 1 * 詞長 6+ 字元:fuzziness = 2 * `"fuzziness": "AUTO:[low],[high]"`:自訂閾值的 AUTO 模式。例如 `AUTO:4,7` 代表: * 詞長 0-3 字元:fuzziness = 0 * 詞長 4-6 字元:fuzziness = 1 * 詞長 7+ 字元:fuzziness = 2 * `"fuzziness": 1` 或 `"fuzziness": 2`:固定允許的最大編輯距離 詳細說明請參考[官方文件](https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#fuzziness)。 **範例說明**: * `apple~1` 可比對出 "aple"、"appla" 等拼寫錯誤的詞 --- #### 6.2 近似搜尋(片語後的波浪號) ``` "apple banana"~5 // 片語後接波浪號,表示近似搜尋 ``` * 對應參數:`phrase_slop` * 功能:處理片語中詞的順序和距離 * 識別方式:用於**引號包圍的片語**後面 **Slop 值的計算邏輯:** Slop 代表詞彙需要「移動」的最小步數,可以處理: 1. 詞彙間有其他詞穿插 2. 詞彙順序不對(對調) **計算方式說明:** 假設索引中有 `"quick brown fox jumps"` ``` // 範例 1:詞彙間有穿插 查詢:"quick fox" 索引:"quick brown fox" → "fox" 需要往左移動 1 步跳過 "brown" → slop = 1 // 範例 2:相鄰詞序對調 查詢:"brown quick" 索引:"quick brown" → 兩個詞互換位置 → slop = 2(官方文件說明:對調成本為 2) // 範例 3:不相鄰詞序對調 + 穿插 查詢:"fox quick" 索引:"quick brown fox" → "fox" 需要移動到 "quick" 前面,跨過 2 個位置 → slop = 3 ``` **實際範例:** ```json // slop = 0:必須完全符合 { "query_string": { "query": "\"quick brown\"" } } // ✅ 匹配:"quick brown fox" // slop = 1:允許中間有 1 個詞 { "query_string": { "query": "\"quick fox\"~1" } } // slop = 2:允許相鄰詞對調 // ✅ 匹配:"quick brown fox" // ❌ 不匹配:"quick fox" { "query_string": { "query": "\"brown quick\"~2" } } // slop = 3:允許更複雜的移動 // ✅ 匹配:"quick brown fox" { "query_string": { "query": "\"fox quick\"~3" } } ``` **在 API 中設定:** ```json { "query_string": { "query": "apple~2 \"quick fox\"~3", "fuzziness": "AUTO", // 全域設定,會被查詢中的具體值覆蓋 "phrase_slop": 2 // 全域設定,會被查詢中的具體值覆蓋 } } ``` **優先順序**: 查詢字串中使用 `~` 設定的值會覆蓋 API 參數中的全域設定。 ## 分頁與排序 ### 分頁參數 ```json { "query": { "query_string": { "query": "apple" } }, "from": 0, // 起始位置,預設為 0 "size": 10 // 每頁數量,預設為 10 } ``` * `from`:指定從第幾筆結果開始回傳(0 代表第一筆) * `size`:指定要回傳幾筆結果 **注意事項:** * 深度分頁(`from` + `size` 過大)會影響效能。 * Elasticsearch 預設限制 `from` + `size` 不能超過 10000。 * 如需處理大量資料,建議使用 Search After 或 Scroll API。 --- ### 排序參數 `sort` 參數接受陣列,可以指定多個排序條件,排序優先順序由陣列順序決定。 #### 排序語法 **方式 1:簡單欄位名稱(預設升序)** ```json { "sort": ["price"] // 按 price 升序排序 } ``` **方式 2:欄位 + 排序方向物件** ```json { "sort": [ { "price": "asc" } // 按 price 升序 ] } ``` **方式 3:完整設定物件** ```json { "sort": [ { "price": { "order": "desc", // 排序方向:asc(升序)或 desc(降序) "missing": "_last" // 缺少該欄位的文件排在最後 } } ] } ``` #### 多欄位排序 ```json { "query": { "query_string": { "query": "apple" } }, "sort": [ { "price": "asc" }, // 第一優先:按價格升序 { "created_at": "desc" }, // 第二優先:價格相同時按建立時間降序 "_score" // 第三優先:其他條件相同時按相關性分數排序 ] } ``` #### 特殊排序值 * `"_score"`:按查詢相關性分數排序(預設為降序)。 * `"_doc"`:按文件的內部順序排序(最快,但順序不固定)。 #### 排序方向 * `"asc"`:升序(Ascending)- 從小到大。 * `"desc"`:降序(Descending)- 從大到小。 #### 處理缺失值 使用 `missing` 參數指定缺少排序欄位的文件應該排在哪裡: ```json { "sort": [ { "price": { "order": "asc", "missing": "_last" // 選項:_first(排最前)、_last(排最後)、或指定預設值 } } ] } ``` #### 完整範例 ```json { "query": { "query_string": { "query": "laptop" } }, "from": 0, "size": 20, "sort": [ { "price": { "order": "asc", "missing": "_last" } }, { "rating": "desc" }, "_score" ] } ``` **排序邏輯說明:** 1. 優先按價格升序排列(沒有價格的排最後)。 2. 價格相同時,按評分降序排列。 3. 價格和評分都相同時,按相關性分數排列。 ### 特殊字元轉義 QueryString 中有許多特殊字元具有特定含義,若要作為普通字元使用,需要使用反斜線 `\` 轉義: ``` \+ \- \= \&\& \|\| \> \< \! \( \) \{ \} \[ \] \^ \" \~ \* \? \: \\ \/ ``` 例如: * 搜尋包含加號的文件:`title:\+1`。 * 搜尋包含括號的文件:`content:\(sample\)`。 * 搜尋包含引號的文件:`description:\"quoted text\"`。 ## 異動歷程 * 2025-04-13 初版文件建立。 * 2025-10-03 * 修正語句用詞,統一使用台灣慣用語。 * 修正日期查詢範圍語法說明(日期欄位不支援比較運算子,應使用範圍查詢語法)。 * 修正權重計算遺漏欄位基礎分數的部分。 * 補充技術細節。 ###### tags: `ELK` `Elasticsearch`