← 返回
后端开发 2026.03.07

Go工程師體系課 011

后端开发

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 使用知識圖譜。