查询的倒排索引
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 构建过程
- 文档预处理:分词、去停用词、词干提取
- 词项统计:统计每个词在文档中的出现频率和位置
- 索引构建:建立词项到文档的映射关系
3.2 查询过程
- 查询解析:将查询字符串分词
- 索引查找:在倒排索引中查找每个词项
- 结果合并:合并多个词项的文档列表
- 排序返回:按相关性排序返回结果
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控制最少匹配词的比例或数量,如2、3<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_smart、ik_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-fields(text+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 等)
- 是否参与倒排索引与如何分词(
index、analyzer) - 是否可用于聚合/排序(
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. 修改字段类型的正确做法(重建索引)
- 创建新索引并定义正确映射
products_v2 - 迁移数据
POST /_reindex
{
"source": { "index": "products" },
"dest": { "index": "products_v2" }
}
- 用别名切换流量
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: true(text无 doc_values) - 中文场景安装 IK 分词器,并在
text字段指定analyzer - 嵌套数组使用
nested,避免object造成交叉匹配 - 使用 multi-fields 同时支持全文与精确匹配
一句话:Mapping 决定“字段如何被存、被索引、被查”,建索引前先明确查询与聚合需求,再设计映射,才能拿到正确且高性能的检索效果。
ES Analyzer(分词器)使用与说明
1. 什么是 Analyzer
Analyzer 是写入/搜索时对文本字段进行“标准化 → 分词 → 过滤”的组件,通常由三部分组成:
char_filter:字符级预处理(如去掉 HTML 标签)tokenizer:将文本切分为 token(词元),如standard、whitespace、ik_smartfilter:对 token 再加工(小写化、去停用词、同义词、词干提取等)
写入阶段使用字段的 analyzer,搜索阶段默认使用同一个 analyzer,可通过 search_analyzer 单独指定。
2. 内置常用 Analyzer
standard(默认):通用英文分词,小写化simple:按非字母切分,小写化whitespace:仅按空白切分,不改变大小写stop:在simple基础上去停用词keyword:不分词,整体作为一个 token(多用于 normalizer 对 keyword 字段)pattern:基于正则表达式分割
中文常用:需要安装插件的 ik_smart、ik_max_word。
3. 使用 _analyze 测试分词效果
POST /_analyze
{
"analyzer": "standard",
"text": "iPhone 15 Pro Max"
}
POST /_analyze
{
"analyzer": "ik_smart",
"text": "苹果手机保护壳"
}
4. 字段上设置 analyzer 与 search_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 分词器安装与字段示例(简要)
- 安装(根据版本):
bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/...重启 ES - 使用:
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:同一字段以多种方式建索引,如
title与title.keyword - Object:对象字段,属性扁平合并到同一文档
- Nested:嵌套对象,每个数组元素独立索引,避免交叉匹配,可独立子查询
- Dynamic mapping:未知字段出现时的策略(true/false/strict)
分词与标准化
- Analyzer:分词器,含
char_filter→tokenizer→filter三阶段 - Tokenizer:切分为 token 的组件,如
standard、whitespace、ik_smart - Token(词元/项):倒排索引中的基本单位
- Char filter:字符级预处理,如
html_strip - Token filter:对 token 再加工,如
lowercase、synonym、stop - 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:集群中的实例,常见角色:
master、data、ingest、coordinating - Shard(主分片):索引的物理分片单位,创建时确定数量
- Replica(副本):主分片的拷贝,提升高可用与查询吞吐
- Routing:根据路由值决定文档落在哪个主分片,默认基于
_idhash - 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