Go工程師體系課 011

11次閱讀

查詢的倒排索引

1. 什麼是倒排索引?

倒排索引(Inverted Index)是一種數據結構,用於快速查找包含特定詞彙的文檔。它是搜索引擎的核心技術之一。

1.1 基本概念

  • 正排索引 :文檔 ID → 文檔內容(詞列表)
  • 倒排索引 :詞 → 包含該詞的文檔 ID 列表

1.2 爲什麼叫 ” 倒排 ”?

倒排索引將傳統的 ” 文檔包含哪些詞 ” 的關係倒轉爲 ” 詞出現在哪些文檔中 ”,因此稱爲 ” 倒排 ”。

2. 倒排索引的結構

2.1 基本結構

 詞項 → 文檔頻率 → 文檔列表

2.2 詳細結構

 詞項 → {
    文檔頻率: N,
    文檔列表: [{ 文檔 ID: 1, 詞頻: 2, 位置: [0, 5]},
        {文檔 ID: 3, 詞頻: 1, 位置: [2]}
    ]
}

3. 倒排索引的工作原理

3.1 構建過程

  1. 文檔預處理 :分詞、去停用詞、詞幹提取
  2. 詞項統計 :統計每個詞在文檔中的出現頻率和位置
  3. 索引構建 :建立詞項到文檔的映射關係

3.2 查詢過程

  1. 查詢解析 :將查詢字符串分詞
  2. 索引查找 :在倒排索引中查找每個詞項
  3. 結果合併 :合併多個詞項的文檔列表
  4. 排序返回 :按相關性排序返回結果

4. Go 語言實現倒排索引

4.1 數據結構定義

package main

import (
    "fmt"
    "sort"
    "strings"
)

// 文檔信息
type Document struct {
    ID   int
    Text string
}

// 詞項在文檔中的位置信息
type Posting struct {
    DocID     int
    Frequency int
    Positions []int}

// 倒排索引項
type InvertedIndexItem struct {
    Term      string
    DocFreq   int
    Postings  []Posting}

// 倒排索引
type InvertedIndex struct {Index map[string]*InvertedIndexItem
}

// 創建新的倒排索引
func NewInvertedIndex() *InvertedIndex {
    return &InvertedIndex{Index: make(map[string]*InvertedIndexItem),
    }
}

4.2 索引構建

// 添加文檔到索引
func (idx *InvertedIndex) AddDocument(docID int, text string) {
    // 簡單的分詞(實際應用中需要更復雜的分詞算法)words := strings.Fields(strings.ToLower(text))

    for pos, word := range words {if idx.Index[word] == nil {idx.Index[word] = &InvertedIndexItem{
                Term:     word,
                DocFreq:  0,
                Postings: make([]Posting, 0),
            }
        }

        // 查找是否已存在該文檔的 posting
        var posting *Posting
        for i := range idx.Index[word].Postings {if idx.Index[word].Postings[i].DocID == docID {posting = &idx.Index[word].Postings[i]
                break
            }
        }

        if posting == nil {
            // 創建新的 posting
            newPosting := Posting{
                DocID:     docID,
                Frequency: 1,
                Positions: []int{pos},
            }
            idx.Index[word].Postings = append(idx.Index[word].Postings, newPosting)
            idx.Index[word].DocFreq++
        } else {
            // 更新現有 posting
            posting.Frequency++
            posting.Positions = append(posting.Positions, pos)
        }
    }
}

4.3 查詢實現

// 單詞查詢
func (idx *InvertedIndex) Search(term string) []int {term = strings.ToLower(term)
    if item, exists := idx.Index[term]; exists {docIDs := make([]int, len(item.Postings))
        for i, posting := range item.Postings {docIDs[i] = posting.DocID
        }
        return docIDs
    }
    return []int{}
}

// 多詞查詢(AND 操作)func (idx *InvertedIndex) SearchAnd(terms []string) []int {if len(terms) == 0 {return []int{}}

    // 獲取第一個詞的結果
    result := idx.Search(terms[0])

    // 與其他詞的結果求交集
    for i := 1; i < len(terms); i++ {otherResult := idx.Search(terms[i])
        result = intersect(result, otherResult)
    }

    return result
}

// 多詞查詢(OR 操作)func (idx *InvertedIndex) SearchOr(terms []string) []int {if len(terms) == 0 {return []int{}}

    resultSet := make(map[int]bool)

    for _, term := range terms {docIDs := idx.Search(term)
        for _, docID := range docIDs {resultSet[docID] = true
        }
    }

    result := make([]int, 0, len(resultSet))
    for docID := range resultSet {result = append(result, docID)
    }

    sort.Ints(result)
    return result
}

// 求兩個切片的交集
func intersect(a, b []int) []int {set := make(map[int]bool)
    for _, x := range a {set[x] = true
    }

    result := make([]int, 0)
    for _, x := range b {if set[x] {result = append(result, x)
        }
    }

    return result
}

4.4 完整示例

func main() {
    // 創建倒排索引
    index := NewInvertedIndex()

    // 添加文檔
    documents := []Document{{ID: 1, Text: "Go is a programming language"},
        {ID: 2, Text: "Go is fast and efficient"},
        {ID: 3, Text: "Programming in Go is fun"},
        {ID: 4, Text: "Go language is simple"},
    }

    // 構建索引
    for _, doc := range documents {index.AddDocument(doc.ID, doc.Text)
    }

    // 查詢示例
    fmt.Println(" 搜索 'go':", index.Search("go"))
    fmt.Println(" 搜索 'programming':", index.Search("programming"))
    fmt.Println(" 搜索 'go' AND 'language':", index.SearchAnd([]string{"go", "language"}))
    fmt.Println(" 搜索 'go' OR 'fast':", index.SearchOr([]string{"go", "fast"}))

    // 打印索引結構
    fmt.Println("\n 倒排索引結構:")
    for term, item := range index.Index {fmt.Printf(" 詞項: %s, 文檔頻率: %d\n", term, item.DocFreq)
        for _, posting := range item.Postings {
            fmt.Printf("  文檔 ID: %d, 詞頻: %d, 位置: %v\n",
                posting.DocID, posting.Frequency, posting.Positions)
        }
    }
}

5. 倒排索引的優化

5.1 壓縮技術

  • 變長編碼 :使用變長編碼壓縮文檔 ID
  • 差分編碼 :存儲文檔 ID 的差值而不是絕對值
  • 位圖壓縮 :使用位圖表示文檔集合

5.2 查詢優化

  • 跳躍表 :在長列表中快速定位
  • 緩存機制 :緩存熱門查詢結果
  • 並行查詢 :多線程處理查詢

6. 實際應用場景

6.1 搜索引擎

  • Google、百度等搜索引擎的核心技術
  • 網頁內容索引和檢索

6.2 數據庫系統

  • 全文搜索功能
  • 文本字段的快速查詢

6.3 代碼搜索

  • GitHub 代碼搜索
  • IDE 中的代碼導航

6.4 日誌分析

  • 日誌文件的快速檢索
  • 錯誤日誌的定位

7. 性能分析

7.1 時間複雜度

  • 構建索引 :O(N×M),N 爲文檔數,M 爲平均詞數
  • 單詞查詢 :O(1) 平均情況
  • 多詞查詢 :O(k×log(n)),k 爲結果數,n 爲文檔數

7.2 空間複雜度

  • 存儲空間 :O(V×D),V 爲詞彙量,D 爲平均文檔頻率

7.3 優缺點

優點

  • 查詢速度快
  • 支持複雜查詢
  • 易於實現

缺點

  • 構建索引耗時
  • 存儲空間較大
  • 更新索引複雜

8. 總結

倒排索引是信息檢索領域的核心技術,通過將 ” 文檔 - 詞 ” 的關係倒轉爲 ” 詞 - 文檔 ” 的關係,實現了高效的文本搜索。在 Go 語言中,我們可以使用 map 和切片等基本數據結構來實現倒排索引,爲應用程序提供強大的搜索功能。

multi_match 使用說明

multi_match 是 ES 中在多個字段上同時進行搜索的查詢類型,本質上是對 match 查詢在多字段上的擴展。適合標題、描述、標籤等多個文本字段聯合檢索,常配合字段權重、不同查詢類型與分詞器使用。

1. 基本用法

POST /index/_search
{
  "query": {
    "multi_match": {
      "query": "iPhone 15",
      "fields": ["title", "description", "tags"]
    }
  }
}

2. 字段權重(boost)

POST /index/_search
{
  "query": {
    "multi_match": {
      "query": "iPhone 15",
      "fields": ["title^3", "description^1.5", "tags"]
    }
  }
}

說明:title^3 表示爲 title 字段的匹配結果乘以 3 的權重,從而在排序時提升該字段命中結果的分數。

3. type 選項與適用場景

  • best_fields(默認):在所有字段中挑選最匹配的字段分數作爲主分數,可配合 tie_breaker
POST /index/_search
{
  "query": {
    "multi_match": {
      "query": "apple phone",
      "fields": ["title", "description", "tags"],
      "type": "best_fields",
      "tie_breaker": 0.2
    }
  }
}
  • most_fields:多個字段得分疊加,適合同一語義分佈在多個字段的情況(如同一文本拆分存儲在不同字段)
POST /index/_search
{
  "query": {
    "multi_match": {
      "query": "iphone",
      "fields": ["title", "title.ngram", "description"],
      "type": "most_fields"
    }
  }
}
  • cross_fields:將多個字段當作一個大字段進行匹配,適合將詞語分佈在不同字段的場景(如 first_name + last_name)
POST /index/_search
{
  "query": {
    "multi_match": {
      "query": "tim cook",
      "fields": ["first_name", "last_name"],
      "type": "cross_fields",
      "operator": "and"
    }
  }
}
  • phrase:短語匹配,要求詞序與距離嚴格,適合精確短語搜索
POST /index/_search
{
  "query": {
    "multi_match": {
      "query": "iphone 15 pro",
      "fields": ["title", "description"],
      "type": "phrase"
    }
  }
}
  • phrase_prefix:短語前綴匹配,適合輸入法聯想 / 搜索建議
POST /index/_search
{
  "query": {
    "multi_match": {
      "query": "iph 15",
      "fields": ["title", "description"],
      "type": "phrase_prefix",
      "max_expansions": 50
    }
  }
}

4. 操作符與最小匹配

POST /index/_search
{
  "query": {
    "multi_match": {
      "query": "apple flagship phone",
      "fields": ["title", "description"],
      "operator": "and",
      "minimum_should_match": "75%"
    }
  }
}

說明:

  • operator: and 要求查詢詞全部匹配;or(默認)爲匹配任意一個
  • minimum_should_match 控制最少匹配詞的比例或數量,如 23<75%75%

5. 模糊匹配(fuzziness)與糾錯

POST /index/_search
{
  "query": {
    "multi_match": {
      "query": "iphine",
      "fields": ["title", "description"],
      "fuzziness": "AUTO",
      "prefix_length": 1
    }
  }
}

說明:fuzziness: AUTO 對常見拼寫錯誤具備容錯能力;prefix_length 指定前綴必須精確匹配的長度。

6. 分詞器與字段選擇

POST /index/_search
{
  "query": {
    "multi_match": {
      "query": " 蘋果 手機 ",
      "fields": ["title", "title.keyword^5", "description"],
      "analyzer": "ik_smart"
    }
  }
}

建議:

  • 多用於 text 字段進行全文檢索;精確匹配與聚合 / 排序使用 keyword 字段(可配合 boost)
  • 中文檢索可使用 ik_smartik_max_word 等分詞器(需安裝插件)

7. 組合示例(綜合字段、權重、過濾與排序)

POST /products/_search
{"_source": ["id", "title", "price", "brand"],
  "from": 0,
  "size": 20,
  "sort": [{"_score": "desc"},
    {"price": "asc"}
  ],
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "iphone 15 pro",
            "fields": ["title^4", "subtitle^2", "description", "tags"],
            "type": "best_fields",
            "tie_breaker": 0.3,
            "minimum_should_match": "66%"
          }
        }
      ],
      "filter": [{"term": {"brand": "apple"}},
        {"range": {"price": {"gte": 3000, "lte": 10000}}}
      ]
    }
  },
  "highlight": {
    "fields": {"title": {},
      "description": {}}
  }
}

8. 常見問題與建議

  • 相關性不理想:
  • 爲核心字段設置更高權重(如 title^N
  • 選擇合適的 type:跨字段詞分佈用 cross_fields,綜合得分用 most_fields
  • 使用同義詞、拼寫糾錯(fuzziness)與領域詞典
  • 性能問題:
  • 控制返回字段(_source 過濾)與 size
  • 將過濾條件放入 filter,命中緩存且不參與評分
  • 避免在巨量字段上使用 wildcard/phrase_prefix 進行前綴擴展
  • 精確 vs 全文:
  • 精確匹配與聚合使用 keyword;全文檢索使用 text + 分詞器
  • 可爲同一業務字段建 multi-fieldstext + keyword

term 查詢詳解

term 查詢是 ES 中用於精確匹配的查詢類型,不會對查詢詞進行分詞處理,直接與索引中的詞項進行精確匹配。適用於 keyword 類型字段、數值字段、日期字段等。(沒有進行分詞,小寫化處理)

1. 基本用法

POST /products/_search
{
  "query": {
    "term": {"status": "active"}
  }
}

2. 多字段 term 查詢

POST /products/_search
{
  "query": {
    "bool": {
      "must": [{"term": {"status": "active"}},
        {"term": {"category": "electronics"}},
        {"term": {"brand": "apple"}}
      ]
    }
  }
}

3. 數值字段精確匹配

POST /products/_search
{
  "query": {
    "term": {"price": 5999}
  }
}

4. 日期字段精確匹配

POST /products/_search
{
  "query": {
    "term": {"created_date": "2025-01-18"}
  }
}

5. 數組字段匹配

POST /products/_search
{
  "query": {
    "term": {"tags": "phone"}
  }
}

6. 使用 boost 提升權重

POST /products/_search
{
  "query": {
    "term": {
      "status": {
        "value": "active",
        "boost": 2.0
      }
    }
  }
}

7. terms 查詢(多值匹配)

POST /products/_search
{
  "query": {
    "terms": {"status": ["active", "pending", "review"]
    }
  }
}

8. 與 filter 結合使用

POST /products/_search
{
  "query": {
    "bool": {
      "must": [{"match": {"title": "iPhone"}}
      ],
      "filter": [{"term": {"status": "active"}},
        {"term": {"category": "electronics"}}
      ]
    }
  }
}

term vs match 查詢對比

1. 核心區別

特性 term 查詢 match 查詢
分詞處理 不進行分詞,精確匹配 對查詢詞進行分詞處理
匹配方式 精確匹配索引中的詞項 模糊匹配,支持相關性評分
適用字段 keyword、數值、日期等 text 類型字段
性能 更快(不計算相關性) 較慢(需要計算評分)
緩存 結果可被緩存 結果通常不被緩存

2. 實際示例對比

2.1 相同查詢詞的不同結果

# 數據準備
POST /test/_doc/1
{
  "title": "iPhone 15 Pro Max",
  "title.keyword": "iPhone 15 Pro Max",
  "status": "active"
}

# term 查詢 - 精確匹配
POST /test/_search
{
  "query": {
    "term": {"title.keyword": "iPhone 15 Pro Max"}
  }
}
# 結果:匹配成功

# term 查詢 - 對 text 字段使用 term(通常不匹配)POST /test/_search
{
  "query": {
    "term": {"title": "iPhone 15 Pro Max"}
  }
}
# 結果:不匹配(因爲 title 被分詞爲 ["iphone", "15", "pro", "max"])# match 查詢 - 對 text 字段使用 match
POST /test/_search
{
  "query": {
    "match": {"title": "iPhone 15 Pro Max"}
  }
}
# 結果:匹配成功,有相關性評分

2.2 部分匹配對比

# term 查詢 - 部分詞不匹配
POST /test/_search
{
  "query": {
    "term": {"title.keyword": "iPhone 15"}
  }
}
# 結果:不匹配(需要完全一致)# match 查詢 - 部分詞匹配
POST /test/_search
{
  "query": {
    "match": {"title": "iPhone 15"}
  }
}
# 結果:匹配成功,相關性評分較低

3. 使用場景對比

3.1 term 查詢適用場景

# 1. 狀態過濾
POST /products/_search
{
  "query": {
    "bool": {
      "filter": [{"term": {"status": "active"}}
      ]
    }
  }
}

# 2. 分類篩選
POST /products/_search
{
  "query": {
    "bool": {
      "filter": [{"term": {"category": "electronics"}}
      ]
    }
  }
}

# 3. 標籤匹配
POST /products/_search
{
  "query": {
    "bool": {
      "filter": [{"term": {"tags": "premium"}}
      ]
    }
  }
}

# 4. 聚合統計
POST /products/_search
{
  "size": 0,
  "aggs": {
    "status_count": {
      "terms": {"field": "status"}
    }
  }
}

3.2 match 查詢適用場景

# 1. 全文搜索
POST /products/_search
{
  "query": {
    "match": {"title": "iPhone 15 Pro"}
  }
}

# 2. 描述搜索
POST /products/_search
{
  "query": {
    "match": {"description": " 最新款手機 "}
  }
}

# 3. 多字段搜索
POST /products/_search
{
  "query": {
    "multi_match": {
      "query": " 蘋果手機 ",
      "fields": ["title", "description", "tags"]
    }
  }
}

4. 性能對比

4.1 查詢性能

# term 查詢 - 高性能
POST /products/_search
{
  "query": {
    "bool": {
      "filter": [{"term": {"status": "active"}},
        {"term": {"category": "electronics"}}
      ]
    }
  }
}
# 特點:不計算相關性,結果可緩存

# match 查詢 - 相對較慢
POST /products/_search
{
  "query": {
    "bool": {
      "must": [{"match": {"title": "iPhone"}},
        {"match": {"description": " 手機 "}}
      ]
    }
  }
}
# 特點:需要計算相關性評分,結果通常不緩存

4.2 混合使用優化

# 最佳實踐:term 用於過濾,match 用於搜索
POST /products/_search
{
  "query": {
    "bool": {
      "must": [{"match": {"title": "iPhone 15"}}
      ],
      "filter": [{"term": {"status": "active"}},
        {"term": {"category": "electronics"}},
        {"range": {"price": {"gte": 1000, "lte": 10000}}}
      ]
    }
  }
}

5. 常見錯誤與解決方案

5.1 對 text 字段使用 term 查詢

# 錯誤用法
POST /products/_search
{
  "query": {
    "term": {"title": "iPhone"  # title 是 text 字段,會被分詞}
  }
}

# 正確用法
POST /products/_search
{
  "query": {
    "term": {"title.keyword": "iPhone"  # 使用 keyword 字段}
  }
}

# 或者使用 match
POST /products/_search
{
  "query": {
    "match": {"title": "iPhone"}
  }
}

5.2 大小寫敏感問題

# term 查詢大小寫敏感
POST /products/_search
{
  "query": {
    "term": {"status": "Active"  # 如果索引中是 "active",則不匹配}
  }
}

# 解決方案:使用 match 或確保大小寫一致
POST /products/_search
{
  "query": {
    "match": {"status": "Active"  # match 會進行分詞和標準化}
  }
}

6. 最佳實踐建議

6.1 字段映射設計

# 創建支持兩種查詢的映射
PUT /products
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "ik_smart",
        "fields": {
          "keyword": {"type": "keyword"}
        }
      },
      "status": {"type": "keyword"},
      "price": {"type": "double"}
    }
  }
}

6.2 查詢組合策略

# 推薦:精確過濾 + 模糊搜索
POST /products/_search
{
  "query": {
    "bool": {
      "must": [{"match": {"title": " 用戶搜索詞 "}}
      ],
      "filter": [{"term": {"status": "active"}},
        {"term": {"category": "electronics"}},
        {"range": {"price": {"gte": 1000}}}
      ]
    }
  },
  "sort": [{"_score": "desc"},
    {"price": "asc"}
  ]
}

7. 總結

  • term 查詢 :適用於精確匹配、過濾、聚合,性能更好,結果可緩存
  • match 查詢 :適用於全文搜索、模糊匹配,支持相關性評分
  • 最佳實踐 :term 用於過濾條件,match 用於搜索內容,兩者結合使用
  • 字段設計 :爲需要精確匹配的字段創建 keyword 子字段
  • 性能優化 :將精確匹配條件放在 filter 中,避免不必要的評分計算

ES Mapping 概念與使用

1. 什麼是 Mapping

Mapping 是索引的“結構定義”,類似關係型數據庫的表結構 schema,用於聲明每個字段的類型與索引方式,決定:

  • 字段的數據類型與存儲形式(text、keyword、numeric、date、boolean、geo、nested 等)
  • 是否參與倒排索引與如何分詞(indexanalyzer
  • 是否可用於聚合 / 排序(doc_values
  • 多字段定義(multi-fields):同一業務字段以多種方式建索引
  • 動態字段處理策略(dynamic

自 ES 7 起,一個索引僅有一個 type(內部 _doc),建模直接面向“索引 + 映射”。

2. 常用字段類型與場景

  • text:分詞,用於全文檢索;不適合聚合 / 排序
  • keyword:不分詞,適合精確匹配、聚合、排序;默認有 doc_values
  • 數值與日期:integer/long/double/date 等,適合範圍過濾、聚合和排序
  • 結構化:object(同文檔扁平對象)、nested(數組中每個對象獨立建模,支持獨立子查詢)
  • 地理:geo_point/geo_shape

典型 multi-fields(既要全文又要精確):

"title": {
  "type": "text",
  "analyzer": "ik_smart",
  "fields": {"keyword": { "type": "keyword", "ignore_above": 256}
  }
}

3. 創建索引並顯式設置映射

PUT /products
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1
  },
  "mappings": {
    "dynamic": "true",
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "standard",
        "fields": {"keyword": { "type": "keyword", "ignore_above": 256}
        }
      },
      "price": {"type": "double"},
      "status": {"type": "keyword"},
      "createdAt": {"type": "date"},
      "tags": {"type": "keyword"},
      "attrs": {"type": "object"},
      "specs": {"type": "nested"}
    }
  }
}

4. 查看 / 更新映射

  • 查看映射
GET /products/_mapping
  • 新增字段(只能新增,不能改變已存在字段類型)
PUT /products/_mapping
{
  "properties": {"brand": { "type": "keyword"}
  }
}

5. 修改字段類型的正確做法(重建索引)

  1. 創建新索引並定義正確映射 products_v2
  2. 遷移數據
POST /_reindex
{"source": { "index": "products"},
  "dest":   {"index": "products_v2"}
}
  1. 用別名切換流量
POST /_aliases
{
  "actions": [{ "remove": { "index": "products", "alias": "products_read"}},
    {"add":    { "index": "products_v2", "alias": "products_read"}}
  ]
}

6. 動態映射策略

"mappings": {
  "dynamic": "strict",
  "properties": {/* 顯式列出字段,未知字段將被拒絕 */}
}

建議在覈心索引使用 strict,避免髒數據自動推斷成錯誤類型(例如把數值當 text)。

7. 性能與實踐要點

  • 只爲需要搜索 / 過濾的字段開啓 index;純展示字段可 index: false
  • 需要聚合 / 排序的字段保持 doc_values: truetext 無 doc_values)
  • 中文場景安裝 IK 分詞器,並在 text 字段指定 analyzer
  • 嵌套數組使用 nested,避免 object 造成交叉匹配
  • 使用 multi-fields 同時支持全文與精確匹配

一句話:Mapping 決定“字段如何被存、被索引、被查”,建索引前先明確查詢與聚合需求,再設計映射,才能拿到正確且高性能的檢索效果。

ES Analyzer(分詞器)使用與說明

1. 什麼是 Analyzer

Analyzer 是寫入 / 搜索時對文本字段進行“標準化 → 分詞 → 過濾”的組件,通常由三部分組成:

  • char_filter:字符級預處理(如去掉 HTML 標籤)
  • tokenizer:將文本切分爲 token(詞元),如 standardwhitespaceik_smart
  • filter:對 token 再加工(小寫化、去停用詞、同義詞、詞幹提取等)

寫入階段使用字段的 analyzer,搜索階段默認使用同一個 analyzer,可通過 search_analyzer 單獨指定。

2. 內置常用 Analyzer

  • standard(默認):通用英文分詞,小寫化
  • simple:按非字母切分,小寫化
  • whitespace:僅按空白切分,不改變大小寫
  • stop:在 simple 基礎上去停用詞
  • keyword:不分詞,整體作爲一個 token(多用於 normalizer 對 keyword 字段)
  • pattern:基於正則表達式分割

中文常用:需要安裝插件的 ik_smartik_max_word

3. 使用 _analyze 測試分詞效果

POST /_analyze
{
  "analyzer": "standard",
  "text": "iPhone 15 Pro Max"
}

POST /_analyze
{
  "analyzer": "ik_smart",
  "text": " 蘋果手機保護殼 "
}

4. 字段上設置 analyzersearch_analyzer

PUT /docs
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "ik_smart",
        "search_analyzer": "ik_max_word"
      }
    }
  }
}

說明:

  • 寫入時使用 ik_smart,查詢時使用更細粒度的 ik_max_word 提升召回。

5. 查詢時臨時指定 analyzer(不改映射)

POST /docs/_search
{
  "query": {
    "match": {
      "title": {
        "query": " 蘋果手機 ",
        "analyzer": "ik_max_word"
      }
    }
  }
}

6. 自定義 Analyzer(含同義詞 / 停用詞)

PUT /articles
{
  "settings": {
    "analysis": {
      "filter": {
        "my_synonyms": {
          "type": "synonym",
          "synonyms": ["iphone, 蘋果手機 ", "notebook, 筆記本 "]
        }
      },
      "analyzer": {
        "my_zh_analyzer": {
          "type": "custom",
          "char_filter": ["html_strip"],
          "tokenizer": "ik_smart",
          "filter": ["lowercase", "my_synonyms"]
        }
      }
    }
  },
  "mappings": {
    "properties": {"content": { "type": "text", "analyzer": "my_zh_analyzer"}
    }
  }
}

7. Normalizer(針對 keyword 的標準化)

keyword 字段不分詞,無法使用 analyzer;若需大小寫歸一、去標點,可使用 normalizer:

PUT /users
{
  "settings": {
    "analysis": {
      "normalizer": {
        "lowercase_normalizer": {
          "type": "custom",
          "filter": ["lowercase"]
        }
      }
    }
  },
  "mappings": {
    "properties": {"email": { "type": "keyword", "normalizer": "lowercase_normalizer"}
    }
  }
}

8. IK 分詞器安裝與字段示例(簡要)

  1. 安裝(根據版本):bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/... 重啓 ES
  2. 使用:
PUT /goods
{
  "mappings": {
    "properties": {"title": { "type": "text", "analyzer": "ik_smart", "search_analyzer": "ik_max_word"}
    }
  }
}

9. 變更 analyzer 的注意事項

  • 已存在字段的 analyzer 基本不可直接修改;需走“重建索引(reindex)”流程
  • 不同 analyzer 會影響倒排索引結構,變更後注意重新驗證查詢語義與相關性

10. 性能與實踐

  • 選擇儘可能簡單的寫入分詞器(如 ik_smart),查詢端更細粒度(ik_max_word)提升召回
  • _analyze 驗證分詞是否符合預期;頻繁的過濾條件應使用 keyword + normalizer
  • 控制字段數量與分詞粒度,避免索引爆炸;同義詞表外置管理便於更新

ES 術語與專有名詞速查(Glossary)

以下概念按主題歸類,便於快速理解與查閱。

索引與文檔建模

  • Index(索引):文檔集合的邏輯容器,類似數據庫的庫。內部由多個分片組成
  • Document(文檔):一條記錄,以 JSON 存儲,通過 _id 唯一標識
  • Field(字段):文檔屬性,決定了可用的查詢與聚合方式
  • Mapping(映射):字段類型與索引策略的定義,等同表結構 schema
  • Type:6.x 及以下存在的邏輯“表”概念。7.x 起固定 _doc,8.x 對外隱藏
  • Text:會分詞的字段類型,用於全文檢索,不適合聚合 / 排序
  • Keyword:不分詞,適合精確匹配、聚合 / 排序,通常有 doc_values
  • Multi-fields:同一字段以多種方式建索引,如 titletitle.keyword
  • Object:對象字段,屬性扁平合併到同一文檔
  • Nested:嵌套對象,每個數組元素獨立索引,避免交叉匹配,可獨立子查詢
  • Dynamic mapping:未知字段出現時的策略(true/false/strict)

分詞與標準化

  • Analyzer:分詞器,含 char_filtertokenizerfilter 三階段
  • Tokenizer:切分爲 token 的組件,如 standardwhitespaceik_smart
  • Token(詞元 / 項):倒排索引中的基本單位
  • Char filter:字符級預處理,如 html_strip
  • Token filter:對 token 再加工,如 lowercasesynonymstop
  • Normalizer:面向 keyword 的標準化(小寫化、去重音等),不分詞

倒排索引與評分

  • Inverted index(倒排索引):term → 文檔列表(postings)的索引結構
  • Term:索引中的詞項(已標準化 / 分詞後的 token)
  • Posting:文檔出現信息,包含 docID、頻次、位置等
  • Relevance score:相關性評分,用於排序
  • BM25:默認相關性模型(取代 TF-IDF)
  • Query vs Filter:Query 參與評分,Filter 只做布爾過濾且可緩存
  • Bool query:must/should/must_not/filter 組合查詢

存儲與段(Segment)

  • Segment:不可變數據段,寫入追加產生;合併(merge)減少段數
  • Refresh:將內存中的增量刷新爲新段,默認週期 1s,刷新後可見
  • Flush:將 translog 持久化並創建新的提交點(commit)
  • Translog:寫入日誌,用於崩潰恢復
  • Doc values:列式存儲,支撐聚合 / 排序 / 腳本,text 無 doc values
  • _source:原始 JSON 文檔,默認存儲,用於重取與 reindex
  • Stored fields:單獨存儲的字段(不常用),與 _source 區分
  • Norms:字段級長度歸一化等評分因子,可關閉以省空間

集羣與分片

  • Cluster:由多個節點組成的 ES 集羣
  • Node:集羣中的實例,常見角色:masterdataingestcoordinating
  • Shard(主分片):索引的物理分片單位,創建時確定數量
  • Replica(副本):主分片的拷貝,提升高可用與查詢吞吐
  • Routing:根據路由值決定文檔落在哪個主分片,默認基於 _id hash
  • Alias:別名,可指向一個或多個索引,便於無縫切換

寫入與批處理

  • Bulk API:批量寫入 / 更新 / 刪除
  • Update by query:按條件批量更新
  • Delete by query:按條件批量刪除
  • Reindex:從源索引複製到目標索引(常用於變更映射)
  • Ingest pipeline:寫入前處理管道(grok、rename、set、script 等)
  • Painless:ES 內置腳本語言,用於腳本更新、腳本排序等

搜索與分頁

  • Match:全文查詢,會分詞
  • Term/Terms:精確匹配,不分詞
  • Range:範圍查詢(數值 / 日期)
  • Multi-match:多字段全文查詢
  • Nested query:對 nested 字段的子查詢
  • Aggregation:聚合分析(terms、stats、date_histogram、range 等)
  • Highlight:高亮顯示命中片段
  • Suggesters:搜索建議(term/phrase/completion)
  • From/size:基礎分頁,深分頁代價高
  • Search after:遊標式分頁,替代深分頁
  • Scroll:大批量導出快照式遊標,非實時查詢
  • PIT(Point in time):時間點一致性快照,用於穩定分頁

生命週期與索引管理

  • ILM(Index Lifecycle Management):熱 / 溫 / 冷 / 刪 生命週期策略
  • Rollover:基於大小 / 文檔數 / 時間切換新索引
  • Snapshot/Restore:快照與恢復(倉庫可對接 S3、HDFS 等)

運維與性能

  • Cluster health:集羣健康(green/yellow/red)
  • Refresh interval:刷新週期,寫多場景可調大以提速寫入
  • Replicas:副本數影響查詢吞吐與寫入成本
  • Force merge:對只讀索引合併段,減少文件數提高查詢性能
  • Slow logs:慢查詢與慢索引日誌,用於排障與優化

以上術語覆蓋 ES 日常建模、寫入、檢索、聚合與運維優化的高頻概念,結合前文 Mapping、Analyzer、term/match 與 multi_match 等章節,可形成完整的 ES 使用知識圖譜。

正文完
 0