Go工程师体系课 011【学习笔记】

Table of Contents

查询的倒排索引

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 使用知识图谱。

主题测试文章,只做测试使用。发布者:Walker,转转请注明出处:https://joyjs.cn/archives/4784

(0)
Walker的头像Walker
上一篇 2025年11月25日 11:00
下一篇 2025年11月25日 09:00

相关推荐

  • TS珠峰 003【学习笔记】

    装饰器 // 装饰器 // 只能在类中使用(类本身,类成员使用) // 装饰器,类的装饰器,属性装饰器,访问装饰器 参数装饰器 // 1. 类型装饰器 给类进行扩展,也可以返回一个子类 // 先要在tsconfig.json中开启experimentalDecorators const classDecorator1 = <T extends new …

    个人 2025年3月27日
    1.2K00
  • Nuxt3_扫盲 入门与原理介绍【学习笔记】

    Nuxt 3 入门与原理介绍 💡 什么是 Nuxt 3? Nuxt 3 是基于 Vue 3 和 Vite 打造的全栈前端框架,支持: 服务端渲染(SSR) 静态站点生成(SSG) 单页应用(SPA) 构建全栈应用(支持 API) Nuxt 3 是 Vue 的“加强版”,帮你简化项目结构和开发流程。 🔧 核心原理 功能 Nuxt 如何处理 ✅ 页面路由 自动根…

    个人 2025年4月6日
    1.8K00
  • 深入理解ES6 010【学习笔记】

    改进的数组功能 new Array()的怪异行为,当构造函数传入一个数值型的值,那么数组的length属性会被设为该值;如果传入多个值,此时无论这些值是不是数值型的,都会变为数组的元素。这个特性另人困惑,你不可能总是注意传入数据的类型,所以存在一定的风险。 Array.of() 无论传多少个参数,不存在单一数值的特例(一个参数且数值型),总是返回包含所有参数…

    个人 2025年3月8日
    1.0K00
  • Node深入浅出(圣思园教育) 003【学习笔记】

    WebSocket 与 SSE 总览 WebSocket 基础 定位:WebSocket 是一条在 HTTP 握手后升级的全双工连接,允许客户端与服务器在同一 TCP 通道上双向推送数据,省去了反复轮询。 握手流程: 客户端通过 Upgrade: websocket 头发起 HTTP 请求; 服务器响应 101 Switching Protocols,双方协…

    个人 2025年11月24日
    6200
  • Go工程师体系课 protobuf_guide【学习笔记】

    Protocol Buffers 入门指南 1. 简介 Protocol Buffers(简称 protobuf)是 Google 开发的一种语言无关、平台无关、可扩展的结构化数据序列化机制。与 JSON、XML 等序列化方式相比,protobuf 更小、更快、更简单。 项目主页:https://github.com/protocolbuffers/prot…

    个人 2025年11月25日
    1.1K00

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信
欢迎🌹 Coding never stops, keep learning! 💡💻 光临🌹