小袁的秘密基地
  • 🙌🏻Hi there!
  • 🧑🏻‍💻学习碎片
    • Golang
    • Java
    • Python
    • C++
    • Rust
    • 计算机网络
    • 操作系统与 Linux
    • 数据存储
    • 消息队列
    • 分布式系统
    • 云原生与 DevOps
    • 网络安全
    • 数据结构与算法
    • 业务场景
  • 🧑🏻‍🏫系统性学习
    • Go 底层设计
    • Go 高手技法
    • K8s 入门实战
    • 分布式系统典型实例
    • 数据密集型应用系统设计
    • 常见设计模式总结
    • 程序数据流静态分析指北
    • MySQL 实战技巧
    • ElasticSearch 101
  • 📝Leetcode
    • 二分查找
    • 动态规划
    • 哈希表
    • 双指针
    • 数学
    • 数据结构
    • DFS
    • BFS
    • 位运算
    • 模拟
    • 剑指 Offer
    • Go CodeTop 题解
  • 🫥CQUPT
    • 算法设计与分析
    • 计算机组织与结构
    • 计算机图形学
    • 大数据导论
由 GitBook 提供支持
在本页
  • 初识 ElasticSearch
  • 快速开始
  • Kibana 快速开始
  • Logstash 快速开始
  • Cerebro 快速开始
  • ElasticSearch 入门
  • 与关系型数据库类比
  • 健康状况
  • CRUD
  • 倒排索引
  • Analysis & Analyzer
  • Search API
  • Mapping
  • 聚合(Aggregation)
  • 深入搜索
  • Term 搜索与全文搜索
  • 结构化搜索
  • 搜索相关性
  • 多字段多字符串查询
  • 多字段单字符串查询
  • 自然语言与查询
  • Search Template
  • Function Score Query
  • Suggester
  • Demo
  • 全文搜索
  • 参考
  1. 系统性学习

ElasticSearch 101

上一页MySQL 实战技巧下一页二分查找

最后更新于3天前

Work In Progress

初识 ElasticSearch

使用场景

  1. 搜索引擎:生产环境中使用最多,例如京东、淘宝、美团等使用 ES 实现高性能商品搜索,支持多条件筛选、排序和相关性排名等。

  2. 日志分析与监控:ELK 作为日志分析的行业标准,是 ES 经典的使用场景。

  3. 数据分析:大数据分析,通过 DSL 快速得到结果。

与类似组件对比

对比维度

Elasticsearch

竞品

优劣总结

RDBMS

1、高性能组合查询 2、适合半结构化数据

1、完善的事务支持 2、强一致性

选 ES:复杂查询/分析场景 选 RDBMS:强事务需求

Solr

1、更成熟的分布式架构 2、近实时能力更强

ES 是 Solr 是上位替代

新项目优先选择 ES,Solr 被淘汰。

ClickHouse

1、全文检索优势 2、近实时响应

1、超大规模聚合更快 2、支持二次聚合

ES处理搜索/日志 ClickHouse处理深度分析

存储场景决策树

快速开始

⚠️ 注意:自行下载 Docker 以及 docker-compose 工具

Kibana 快速开始

通过 localhost:5601 进入:

使用 Dev Tools:

Logstash 快速开始

运行 docker-compose up 命令后可以到日志中查看是否导入数据成功。

Cerebro 快速开始

通过 localhost:9000 进入:

可以看到刚导入的 movies 数据。

ElasticSearch 入门

概念名称

描述

关键点 / 特点

文档

Elasticsearch 中的最小数据单元,以 JSON 格式存储数据。

1、类比于关系型数据库中的一行数据。 2、包含实际数据字段(如 title, content 等)。

文档元数据

描述文档自身属性的系统字段,用于唯一标识和管理文档。

包含以下核心元数据: - _id:文档唯一标识符(可自定义或自动生成)。 - _index:文档所属的索引。 - _version:文档版本号(支持乐观锁)。

索引

一类结构相似文档的集合,类似于关系型数据库中的「表」。

通过 Mapping 定义字段类型和属性(如文本、数值、日期等)。支持倒排索引,优化全文搜索性能。

节点

Elasticsearch 集群中的一个运行实例,本质是一个 Java 进程。

节点类型包括: - 主节点:管理集群状态。 - 数据节点:存储数据。 - 协调节点:处理请求路由。

分片

索引的物理子集,用于分布式存储和计算。分片分为主分片(Primary Shard)和副本分片(Replica Shard)。

主分片:数据存储和写入的基本单元,数量在索引创建时固定。 副本分片:主分片的冗余拷贝,提供高可用和查询负载均衡。

与关系型数据库类比

RDBMS
ElasticSearch

Table

Index

Row

Document

Column

Field

Schema

Mapping

SQL

DSL

健康状况

1、在 Kibana 中查询,使用命令 GET _cluster/health

2、在 Cerebro 中查询

测试节点下线

此时状态为 Yellow。

CRUD

操作

HTTP 方法

响应码

Source 处理

幂等性

备注

Create

PUT 或 POST

201(成功) 409(冲突)

必须提供完整文档 Source

是(需指定 ID)

仅当文档不存在时创建。需指定 ID(PUT)或自动生成(POST)。

Get

GET

200(存在) 404(不存在)

可指定 _source 过滤返回字段

是

仅用于查询文档,不修改数据。

Index

PUT 或 POST

201(新建) 200(更新)

必须提供完整文档 Source

否

若文档存在则替换(全量更新)。可指定 ID(PUT)或自动生成(POST)。

Update

POST(带 _update)

200(成功) 404(不存在)

提供部分字段或脚本(支持 doc 或 script)

否

部分更新。支持 upsert(不存在时插入)。需启用 _source 字段。默认返回更新后的 Source。

Create

Demo1

# Req
# create document 自动生成 _id
POST users/_doc
{
	"user" : "LanLance"
}

# Resp
{
  "_index" : "users",
  "_type" : "_doc",
  "_id" : "64IRqpYBLb6gKsOx04Rm",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

Demo2

# Req
# create document 指定 ID
PUT users/_doc/1?op_type=create
{
    "user" : "LanLance_2"
}

# Resp
{
  "_index" : "users",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "failed" : 0
  },
  "_seq_no" : 1,
  "_primary_term" : 1
}

Demo3

# Req
# create document 指定 ID;如果已经存在就报错
PUT users/_create/1
{
    "user" : "LanLance"
}

# Resp
{
  "error" : {
    "root_cause" : [
      {
        "type" : "version_conflict_engine_exception",
        "reason" : "[1]: version conflict, document already exists (current version [1])",
        "index_uuid" : "J6rYdyr6TY-H9asFHjyTwQ",
        "shard" : "0",
        "index" : "users"
      }
    ],
    "type" : "version_conflict_engine_exception",
    "reason" : "[1]: version conflict, document already exists (current version [1])",
    "index_uuid" : "J6rYdyr6TY-H9asFHjyTwQ",
    "shard" : "0",
    "index" : "users"
  },
  "status" : 409
}

Get

Demo1

# Req
# Get 指定 ID
GET users/_doc/1

# Resp
{
  "_index" : "users",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 1,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "user" : "LanLance_2"
  }
}

Index & Update

Demo1

# Req
# Update 指定 ID (先删除,在写入)
PUT users/_doc/1
{
	"user" : "LanLance"
}

# Resp
{
  "_index" : "users",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 2,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1
}

Demo2

# Req
# Update 在原文档上增加字段
POST users/_update/1/
{
    "doc":{
        "message" : "trying out Elasticsearch"
    }
}

# Resp
{
  "_index" : "users",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 3,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "failed" : 0
  },
  "_seq_no" : 3,
  "_primary_term" : 1
}

Delete

Demo1

# Req
# Delete 指定 ID
DELETE users/_doc/1

# Resp
{
  "_index" : "users",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 4,
  "result" : "deleted",
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "failed" : 0
  },
  "_seq_no" : 4,
  "_primary_term" : 1
}

Bulk、MGet、MSearch

API

HTTP 方法

操作类型

主要用途

响应码

幂等性

性能优化点

Bulk

POST

批量增删改(Create/Index/Update/Delete)

批量写入或更新数据

200(整体成功,可能部分失败)

否

单次请求处理大量操作

mget

GET/POST

批量读取文档

批量获取多个文档内容

200(包含每个文档状态)

是

减少网络请求次数

msearch

POST

批量搜索请求

批量执行多个搜索查询

200(包含每个查询结果)

是

合并多个查询请求,减少延迟

功能与场景对比

特性

Bulk API

mget

msearch

核心操作

增删改(CRUD)

读取文档

执行搜索查询

数据量优化

单次请求处理数千操作

单次请求获取数百文档

单次请求合并多个复杂查询

错误处理

响应中标记每个操作的 error 和 status

响应中标记每个文档的 found 状态

每个查询独立返回状态码和结果

适用场景

数据迁移、日志流写入

批量加载关联数据、初始化页面

仪表盘批量拉取数据、跨索引聚合分析

原子性

非原子(部分成功需重试)

非原子

非原子

性能与限制对比

维度

Bulk API

mget

msearch

网络开销

单次请求处理大量操作(高吞吐)

减少多次 GET 请求(中吞吐)

合并多个查询(中高吞吐)

内存消耗

高(需缓存批量数据)

中(文档数量和大小决定)

高(复杂查询可能占用内存)

超时风险

大数据量可能触发请求超时

大文档列表可能超时

复杂查询或大数据集可能超时

分片影响

写入压力分散到多个分片

读取压力分散到多个分片

搜索压力分散到多个分片

限制规避

分批提交(每批 5-15MB)

分批查询(每批 100-1000 文档)

控制单个查询复杂度

使用

# bulk
## 执行第1次
POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test2", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }

## 执行第2次
POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test2", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }

# mget
GET /_mget
{
    "docs" : [
        {
            "_index" : "test",
            "_id" : "1"
        },
        {
            "_index" : "test",
            "_id" : "2"
        }
    ]
}

## URI 中指定 index
GET /test/_mget
{
    "docs" : [
        {

            "_id" : "1"
        },
        {

            "_id" : "2"
        }
    ]
}

GET /_mget
{
    "docs" : [
        {
            "_index" : "test",
            "_id" : "1",
            "_source" : false
        },
        {
            "_index" : "test",
            "_id" : "2",
            "_source" : ["field3", "field4"]
        },
        {
            "_index" : "test",
            "_id" : "3",
            "_source" : {
                "include": ["user"],
                "exclude": ["user.location"]
            }
        }
    ]
}

清除数据

DELETE users
DELETE test
DELETE test2

倒排索引

倒排索引是搜索引擎和全文检索系统的核心数据结构,核心思想是通过建立「单词到文档」的映射关系从而实现 Keyword 快速定位包含该词的所有文档。例如当用户搜索「ElasticSearch」时,系统可直接通过倒排索引找到包含这一词汇的所有文档集合。

倒排索引的实现依赖于两个关键结构:单词词典(Term Dictionary)和倒排列表(Posting List)。

Analysis & Analyzer

Analysis 是通过 Analyzer 来实现的。

分词器

由三部分组成:

  • Character Filters:针对原始文本处理,例如去除 html。

  • Tokenizer:按照规则切分为单词。

  • Token Filter:将切分的的单词进行加工,例如小写、删除 stopwords、增加同义词等。

Character Filters => Tokenizer => Token Filters

ElasticSearch 内置分词器

分词器
使用场景
分词逻辑

Standard

通用文本处理,支持大多数语言(默认选择)。

按 Unicode 标准分词,移除标点符号,转小写,支持多语言基础处理。

Simple

快速简单分词,忽略标点符号和数字。

在非字母字符处分割文本,删除非字母字符,转小写(如 Hello-World → ["hello", "world"])。

Whitespace

按空格严格分割,保留原始格式(如代码、特定标识)。

仅按空格分割,保留大小写和标点(如 Quick-Brown → ["Quick-Brown"])。

Stop

需过滤常见停用词(如英文中的“the”、“is”)的文本。

按非字母字符分割出连续字母词条,转小写后移除停用词(如 The fox → ["fox"])。

Keyword

需精确匹配的字段(如 ID、状态码)。

将整个输入作为单一词条,不进行任何处理(如 Hello World → ["Hello World"])。

Pattern

需自定义分隔规则(如按特定符号分割)的文本。

通过正则表达式(默认 \W+)分割文本,转小写(可自定义正则)。

Language(如 english)

针对特定语言优化(如英文词干提取、停用词过滤)。

按语言规则分词,处理停用词、转小写、词干提取等(如 running → ["run"])。

analyzer API

通过 analyzer API 能够快速得到分词结果进行测试,以下提供了一些例子可以去到 Kibana 的 Dev Tools 进行使用。

#standard
GET _analyze
{
  "analyzer": "standard",
  "text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}

#simple
GET _analyze
{
  "analyzer": "simple",
  "text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}

#stop
GET _analyze
{
  "analyzer": "stop",
  "text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}

#whitespace
GET _analyze
{
  "analyzer": "whitespace",
  "text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}

#keyword
GET _analyze
{
  "analyzer": "keyword",
  "text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}

#pattern
GET _analyze
{
  "analyzer": "pattern",
  "text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}

#english
GET _analyze
{
  "analyzer": "english",
  "text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}

示例:standard 分词器的分词结果

{
  "tokens" : [
    {
      "token" : "2",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "<NUM>",
      "position" : 0
    },
    {
      "token" : "running",
      "start_offset" : 2,
      "end_offset" : 9,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "quick",
      "start_offset" : 10,
      "end_offset" : 15,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "brown",
      "start_offset" : 16,
      "end_offset" : 21,
      "type" : "<ALPHANUM>",
      "position" : 3
    },
    {
      "token" : "foxes",
      "start_offset" : 22,
      "end_offset" : 27,
      "type" : "<ALPHANUM>",
      "position" : 4
    },
    {
      "token" : "leap",
      "start_offset" : 28,
      "end_offset" : 32,
      "type" : "<ALPHANUM>",
      "position" : 5
    },
    {
      "token" : "over",
      "start_offset" : 33,
      "end_offset" : 37,
      "type" : "<ALPHANUM>",
      "position" : 6
    },
    {
      "token" : "lazy",
      "start_offset" : 38,
      "end_offset" : 42,
      "type" : "<ALPHANUM>",
      "position" : 7
    },
    {
      "token" : "dogs",
      "start_offset" : 43,
      "end_offset" : 47,
      "type" : "<ALPHANUM>",
      "position" : 8
    },
    {
      "token" : "in",
      "start_offset" : 48,
      "end_offset" : 50,
      "type" : "<ALPHANUM>",
      "position" : 9
    },
    {
      "token" : "the",
      "start_offset" : 51,
      "end_offset" : 54,
      "type" : "<ALPHANUM>",
      "position" : 10
    },
    {
      "token" : "summer",
      "start_offset" : 55,
      "end_offset" : 61,
      "type" : "<ALPHANUM>",
      "position" : 11
    },
    {
      "token" : "evening",
      "start_offset" : 62,
      "end_offset" : 69,
      "type" : "<ALPHANUM>",
      "position" : 12
    }
  ]
}

Search API

  • URI Search

  • Request Body Search

示例

# URI Search
GET kibana_sample_data_ecommerce/_search?q=customer_first_name:Eddie
GET kibana*/_search?q=customer_first_name:Eddie
GET /_all/_search?q=customer_first_name:Eddie

# Request Body Search
POST kibana_sample_data_ecommerce/_search
{
	"profile": true,
	"query": {
		"match_all": {}
	}
}

指定查询的索引

语法
范围

/_search

所有索引

/index1/_search

index1

/index1,index2/_search

index1 和 index2

/index*/_search

以 index 开头的索引

URI Search

示例

GET /movies/_search?q=2012&df=title&sort=year:desc&from=0&size=10&timeout=1s
{
	"profile":"true"
}

参数:

  • q 指定查询语句,使用 Query String Syntax。

  • df 指定默认字段,不指定时会对所有字段进行查询。

  • sort 代表排序。

  • from 和 size 用于分页。

  • profile 可以查看查询是如何被执行的。

泛查询

GET /movies/_search?q=2012
{
	"profile":"true"
}

未指定 df 参数时 ES 会搜索所有字段,可能触发跨字段匹配,性能消耗较大。分析结果如下:

{  
    "type": "DisjunctionMaxQuery",  
    "description": "(title.keyword:2012 | id.keyword:2012 | year:[2012 TO 2012] | genre:2012 | @version:2012 | @version.keyword:2012 | id:2012 | genre.keyword:2012 | title:2012)"  
}

可以看到在所有字段进行了匹配。

显式字段查询

GET /movies/_search?q=title:2012
{
	"profile":"true"
}

通过 title:2012 显式指定字段,精准限定搜索范围,比泛查询更高效。分析结果如下:

{  
    "type": "TermQuery",  
    "description": "title:2012"  
}

短语分割问题

GET /movies/_search?q=title:Beautiful Mind
{
	"profile":"true"
}

实际执行 title:Beautiful OR Mind,空格被识别为 OR 逻辑,返回包含任意词的文档。

精确短语匹配

GET /movies/_search?q=title:"Beautiful Mind"
{
	"profile":"true"
}

使用双引号包裹词组,强制进行短语搜索,要求词语按顺序完整出现。

分组查询

GET /movies/_search?q=title:(Beautiful Mind)
{
	"profile":"true"
}

括号实现逻辑分组,等效于 title:Beautiful OR title:Mind,优先执行组内操作。

布尔运算符

GET /movies/_search?q=title:(Beautiful AND Mind)

显式布尔查询,要求同时包含两个词(AND 逻辑)。

范围查询

GET /movies/_search?q=title:beautiful AND year:[2002 TO 2018%7D

[2002 TO 2018} 表示闭区间包含 2002,开区间不包含 2018(%7D 为 URL 编码的 } 符号)。

通配符搜索

GET /movies/_search?q=title:b*
{
	"profile":"true"
}

b* 匹配以 b 开头的任意长度字符,支持 ? 匹配单个字符,注意通配符在前端影响性能。

模糊匹配

GET /movies/_search?q=title:beautiful~1
{
	"profile":"true"
}

GET /movies/_search?q=title:"Lord Rings"~2
{
	"profile":"true"
}
  • ~1 允许 1 个字符的编辑距离(拼写纠错)。

  • "Lord Rings"~2 表示短语中允许间隔 2 个单词。

Request Body Search & Query DSL

通常生成环境都使用这种方法,更加强大、功能更丰富。

示例

POST movies/_search
{
  "from":0,
  "size":10,
  "query": {
    "match": {
      "title": {
        "query": "last christmas",
        "operator": "and"
      }
    }
  }
}

基本 Match 查询

POST movies/_search
{
  "query": {
    "match": {
      "title": "last christmas"
    }
  }
}
  • 默认使用 OR 逻辑匹配分词结果。

  • 自动对搜索词进行分词处理。

精确 AND 匹配

POST movies/_search
{
  "query": {
    "match": {
      "title": {
        "query": "last christmas",
        "operator": "and"
      }
    }
  }
}

通过 operator:"and" 强制要求所有分词必须同时存在。

短语搜索

POST movies/_search
{
  "query": {
    "match_phrase": {
      "title": {
        "query": "one love"
      }
    }
  }
}
  • 要求词语按顺序完整出现。

  • 等效于 URI Search 中的引号。

模糊短语匹配

POST movies/_search
{
  "query": {
    "match_phrase": {
      "title": {
        "query": "one love",
        "slop": 1
      }
    }
  }
}

slop 参数允许词语间隔位置数(此处允许间隔 1 个词)。

Query String 搜索

GET /movies/_search
{
  "query": {
    "query_string": {
      "default_field": "title",
      "query": "Beafiful AND Mind"
    }
  }
}
  • 支持 AND/OR/NOT 布尔逻辑。

多字段搜索

GET /movies/_search
{
  "query": {
    "query_string": {
      "fields": ["title","year"],
      "query": "2012"
    }
  }
}
  • fields 数组定义多个搜索字段。

  • 自动进行跨字段联合查询。

Simple Query 搜索

GET /movies/_search
{
  "query": {
    "simple_query_string": {
      "query": "Beautiful +mind",
      "fields": ["title"]
    }
  }
}
  • + 代替 AND 操作。

  • 自动忽略无效语法。

  • 适合直接暴露给前端搜索框使用。

跨索引查询

POST /movies,404_idx/_search?ignore_unavailable=true
{
  "query": {
    "match_all": {}
  }
}
  • 逗号分隔多个索引名称。

  • ignore_unavailable=true 忽略不存在索引。

源过滤

POST kibana_sample_data_ecommerce/_search
{
  "_source":["order_date"],
  "query": {
    "match_all": {}
  }
}

_source 过滤返回字段。

脚本字段

GET kibana_sample_data_ecommerce/_search
{
  "script_fields": {
    "new_field": {
      "script": {
        "lang": "painless",
        "source": "doc['order_date'].value+'hello'"
      }
    }
  }
}

动态计算返回字段。

Mapping

Mapping 类似数据库中的 schema 定义,用于定义字段名称、类型、相关配置。

字段的数据类型

字段类型

使用场景

底层实现

实例数据

text

全文搜索(如文章内容、长文本)。

倒排索引(分词后存储),支持模糊匹配和相关性评分。

"LanTech 指南 "

keyword

精确匹配(如 ID、状态码)、聚合和排序。

未分词的原始字符串,基于精确值匹配。

"user_123"

数值类型

范围查询(如价格、年龄)、数学运算和聚合。

Lucene 的数值索引优化(如 integer、long)。

42 / 3.14

date

时间序列数据(如日志时间、事件时间戳)。

存储为长整型时间戳(毫秒级),支持时区转换。

"2023-10-05T12:30:00Z"

boolean

真/假状态(如开关、是否有效)。

布尔索引结构,仅存储 true 或 false。

true

binary

存储二进制数据(如图片、文件)。

Base64 编码存储,不支持直接查询。

"U29tZSBoZWxsbyB3b3JsZA=="

range

区间查询(如价格区间、年龄区间)。

Lucene 的数值/日期范围索引,支持 >=、<= 等操作。

{"gte": 10, "lte": 20}

object

嵌套 JSON 对象(如用户信息中的地址字段)。

内部文档结构,支持嵌套查询。

{"city": "Beijing", "zip": "100000"}

nested

复杂嵌套关系(如订单与多个商品的关联)。

独立索引的子文档,需通过 nested 查询访问。

[{"name": "book", "price": 15}]

geo_point

地理位置数据(如经纬度)。

存储为坐标对,支持地理距离计算和范围查询。

{"lat": 40.7128, "lon": -74.0060}

数组类型

存储多个相同类型值(如标签列表、商品分类)。

多值字段(无需显式声明),底层以扁平化多值形式存储。

["red", "blue", "green"]

ip

IP 地址存储与查询(如访问日志中的 IP)。

存储为 32 位或 128 位整数,支持 CIDR 范围查询。

"192.168.1.1"

Dynamic Mapping

在写入文档时如果索引不存在会自动创建索引,该机制使得我们不用手动定义 Mappings,但是通常不用这个,因为容易推算错误,并且 ES 禁止对有数据写入的字段修改定义。

示例

# 插入测试数据
PUT mapping_test/_doc/1
{
    "uid" : "123",
    "isVip" : false,
    "isAdmin": "true",
    "age":19,
    "heigh":180
}

# 查看字段类型
GET mapping_test/_mapping

# Resp
{
  "mapping_test" : {
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "long"
        },
        "heigh" : {
          "type" : "long"
        },
        "isAdmin" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "isVip" : {
          "type" : "boolean"
        },
        "uid" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

如上结果所示,Dynamic Mapping 机制会自动推断类型,同时 text 类型会新增一个 keyword 类型支持精确查找。

控制 Dynamic Mappings

dynamic 字段不同情况下的表现。

true
false
strict

文档可索引

YES

YES

NO

字段可索引

YES

NO

NO

Mapping 被更新

YES

NO

NO

Index

index 用于控制字段是否被索引。

示例

"mobile" : {
  "type" : "text",
  "index": false
}

Index Options

Index Options

记录内容

作用

默认类型

docs

仅文档编号(doc id)

仅记录文档 ID,节省空间,适用于只需文档匹配的场景

非 text 类型字段默认

freqs

文档编号(doc id)+ 词频(term frequency)

记录文档 ID 和词频,支持基于频率的查询优化

positions

文档编号 + 词频 + 位置(position)

记录位置信息,支持短语查询(Phrase Query)和邻近查询(Proximity Query)

text 类型字段默认

offsets

文档编号 + 词频 + 位置 + 偏移量(offset)

记录字符偏移量,支持高亮显示等精细文本处理

index_options 用于控制是否存储文档 ID、词频、位置和偏移量等,从而影响搜索效率和功能支持(如短语查询、高亮等)。同时记录的内容越多,占用存储空间越大。

null_value

  • 需要对 Null 值实现搜索。

  • 只有 Keyword 类型支持设定 null_value。

示例

"mobile" : {
  "type" : "keyword",
  "null_value": "NULL"
}

自定义 Analyzer

Character Filter

示例

POST _analyze
{
  "tokenizer":"keyword",
  "char_filter":["html_strip"],
  "text": "<b>hello world</b>"
}

能够将 html 的标签去除。

POST _analyze
{
  "tokenizer": "standard",
  "char_filter": [
      {
        "type" : "mapping",
        "mappings" : [ "- => _"]
      }
    ],
  "text": "123-456, I-test! test-990 650-555-1234"
}

能够将 text 中的 - 替换为 _。

Tokenizer

示例

POST _analyze
{
  "tokenizer":"path_hierarchy",
  "text":"/user/ymruan/a/b/c/d/e"
}

能够按照目录层级进行切分。

Token Filter

示例

GET _analyze
{
  "tokenizer": "whitespace",
  "filter": ["lowercase","stop","snowball"],
  "text": ["The girls in China are playing this game!"]
}
  • lowercase - 仅小写

  • stop - 停用词过滤

  • snowball - 词干提取

Index Template

用于自动设定 Mappings 和 Settings,并按照一定的规则自动匹配到新创建的索引中。

  • 仅在索引被新创建时才回起作用。

  • 可以设置多个模版,设置会 merge 在一起。

  • 可以控制 order 的数值控制 merge 的过程。先应用 order 低的,后续高的会覆盖之前的设定。

示例

PUT /_template/template_test
{
    "index_patterns" : ["test*"],
    "order" : 1,
    "settings" : {
    	"number_of_shards": 1,
        "number_of_replicas" : 2
    },
    "mappings" : {
    	"numeric_detection": true
    }
}

表示当一个新索引以 test 开头时,会自动将索引的分片数设置为 2,同时会自动探测数字类型。

Dynamic Template

动态设定字段类型。例如:

  • 将所有字符串类型设定为 keyword。

  • is 开头的字段都设置为 boolean。

示例

PUT my_index
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_boolean": {
          "match_mapping_type": "string",
          "match": "is*",
          "mapping": {
            "type": "boolean"
          }
        }
      },
      {
        "strings_as_keywords": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "keyword"
          }
        }
      }
    ]
  }
}

表示当字符串类型为 is 开头时设置为 boolean 类型,其余设置为 keyword 类型。

聚合(Aggregation)

  • ElasticSearch 除了提供搜索以外,还提供了针对 ES 进行同喜分析的功能。

  • 聚合是一个分析总结全套的数据,而不是寻找单个文档。

  • 性能高且实时性高(不用 T+1)。

本节示例需要在 Kibana 中添加官方提供的 Sample flight data 样例数据。

Bucket

示例

GET kibana_sample_data_flights/_search
{
	"size": 0,
	"aggs":{
		"flight_dest":{
			"terms":{
				"field":"DestCountry"
			}
		}
	}
}

# Resp
"aggregations" : {
"flight_dest" : {
  "doc_count_error_upper_bound" : 0,
  "sum_other_doc_count" : 3187,
  "buckets" : [
	{
	  "key" : "IT",
	  "doc_count" : 2371
	},
	{
	  "key" : "US",
	  "doc_count" : 1987
	},
	// ...
	]
  }
}

将国家分成了桶。

Metric

  • 基于数据集计算结果。

  • 大多数是数学计算,仅输出一个值。

示例

GET kibana_sample_data_flights/_search
{
	"size": 0,
	"aggs":{
		"flight_dest":{
			"terms":{
				"field":"DestCountry"
			},
			"aggs":{
				"avg_price":{
					"avg":{
						"field":"AvgTicketPrice"
					}
				},
				"max_price":{
					"max":{
						"field":"AvgTicketPrice"
					}
				},
				"min_price":{
					"min":{
						"field":"AvgTicketPrice"
					}
				}
			}
		}
	}
}

# Resp
// ...
{
  "key" : "IT",
  "doc_count" : 2371,
  "max_price" : {
	"value" : 1195.3363037109375
  },
  "min_price" : {
	"value" : 100.57646942138672
  },
  "avg_price" : {
	"value" : 586.9627099618385
  }
},
// ...

进行了平均值、最大值、最小值的计算。

嵌套

示例

GET kibana_sample_data_flights/_search
{
	"size": 0,
	"aggs":{
		"flight_dest":{
			"terms":{
				"field":"DestCountry"
			},
			"aggs":{
				"stats_price":{
					"stats":{
						"field":"AvgTicketPrice"
					}
				},
				"wather":{
				  "terms": {
				    "field": "DestWeather",
				    "size": 5
				  }
				}
			}
		}
	}
}

# Resp
// ...
{
  "key" : "IT",
  "doc_count" : 2371,
  "wather" : {
	"doc_count_error_upper_bound" : 0,
	"sum_other_doc_count" : 506,
	"buckets" : [
	  {
		"key" : "Clear",
		"doc_count" : 428
	  },
	  {
		"key" : "Sunny",
		"doc_count" : 424
	  },
	  {
		"key" : "Rain",
		"doc_count" : 417
	  },
	  {
		"key" : "Cloudy",
		"doc_count" : 414
	  },
	  {
		"key" : "Heavy Fog",
		"doc_count" : 182
	  }
	]
  },
  "stats_price" : {
	"count" : 2371,
	"min" : 100.57646942138672,
	"max" : 1195.3363037109375,
	"avg" : 586.9627099618385,
	"sum" : 1391688.585319519
  }
},
// ...

在使用国家分桶后再进行票价统计和 5 组最常见天气的分布。

深入搜索

Term 搜索与全文搜索

对比维度

Term 搜索

全文搜索

查询类型

精确匹配

模糊匹配

分析过程

不分词,直接匹配索引中的词项(Term)

分词处理,匹配词条(Token)

适用字段类型

keyword、数字、日期等精确值字段

text 类型字段

使用场景

过滤(Filter)、聚合(Aggregation)

自由文本搜索(如搜索框输入)

性能特点

高效,适合大数据集和实时过滤

相对耗时,依赖分词和相关性计算

查询语法

{"term": {"field": "value"}}

{"match": {"field": "user_input"}}

倒排索引使用

直接定位词项的文档列表

通过词条组合计算相关性得分

Term 搜索

示例

1、插入数据

POST /products/_bulk
{ "index": { "_id": 1 }}
{ "productID" : "XHDK-A-1293-#fJ3","desc":"iPhone" }
{ "index": { "_id": 2 }}
{ "productID" : "KDKE-B-9947-#kL5","desc":"iPad" }
{ "index": { "_id": 3 }}
{ "productID" : "JODL-X-1937-#pV7","desc":"MBP" }

2、Term 查询

POST /products/_search
{
  "query": {
    "term": {
      "desc": {
        // "value": "iPhone"
        // "value": "iphone"
      }
    }
  }
}

使用 iPhone 不能搜索出结果,而小写的 iphone 可以。因为是精确查询,而原始数据在分词后的结果为 iphone。

POST /products/_search
{
  "query": {
    "term": {
      "desc.keyword": {
        // "value": "iPhone"
        // "value": "iphone"
      }
    }
  }
}

此时使用 keyword 类型能成功获取数据。

3、Constant Score 转为 Filter

POST /products/_search
{
  "explain": true,
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "productID.keyword": "XHDK-A-1293-#fJ3"
        }
      }
    }
  }
}
  • 将 Query 转为 Filter,忽略 TF-IDF 计算,避免相关性算分的开销。

  • Filter 可以有效利用缓存。

全文搜索

Match / Match Phrase / Query String

  • 索引和搜索时都会进行分词,查询字符串先传递到一个合适的分词器,然后生成一个供查询的列表。

  • 查询会对每个词项逐个查询再将结果进行合并,并为每个文档生成一个算分。

场景
推荐查询类型

普通全文搜索

match

精确短语匹配

match_phrase

高级搜索(用户输入带逻辑)

query_string

用户输入框 + 安全性优先

match / multi_match,避免 query_string

结构化搜索

结构化数据是指具有固定格式和明确字段的数据,每个字段都有特定的类型(如字符串、数字、日期等),并且数据是可预测、易于解析的。结构化搜索即对结构化数据进行搜索。

示例

1、插入数据

DELETE products
POST /products/_bulk
{ "index": { "_id": 1 }}
{ "price" : 10,"avaliable":true,"date":"2018-01-01", "productID" : "XHDK-A-1293-#fJ3" }
{ "index": { "_id": 2 }}
{ "price" : 20,"avaliable":true,"date":"2019-01-01", "productID" : "KDKE-B-9947-#kL5" }
{ "index": { "_id": 3 }}
{ "price" : 30,"avaliable":true, "productID" : "JODL-X-1937-#pV7" }
{ "index": { "_id": 4 }}
{ "price" : 30,"avaliable":false, "productID" : "QQPX-R-3956-#aD8" }

2、Bool

POST products/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "avaliable": true
        }
      }
    }
  }
}

3、数字

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

4、Range

POST products/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "range" : {
                    "price" : {
                        "gte" : 20,
                        "lte"  : 30
                    }
                }
            }
        }
    }
}

搜索相关性

  • 搜索的相关性算分,描述了一个文档和查询语句匹配的程度。ES 会对每个匹配查询条件的结果进行算分 _score。

  • 打分的本质是排序,需要把最符合用户需求的文档排在前面。ES5 之前,默认的相关性算分采用 TF-IDF,现在采用 BM 25。

词频 TF

  • TF 即是词在一篇文档中出现的频率。

  • 度量一条查询和结果文档相关性的简单方法:将搜索中的每一个词 TF 相加。

  • Stop Word 不应考虑,类似 「the」、「的」。

逆文档频率 IDF

  • DF:检索词在所有文档中出现的评率。

  • Inverse Document Frequency:$log(全部文档数/检索词出现过的文档总数)$

  • TF-IDF 的本质就是将 TF 求和变成了加权求和:$TF(X)*IDF(X)$

BM 25

与 TF-IDF 相比,当一个词的 TF 无限增加时,BM 25 算分会趋于一个稳定值。

Boosting

Boosting 是控制相关度的一种手段。

  • 当 boost > 1 时打分的相关度相对性提升。

  • 当 0 < boost < 1 时打分的权重相对性降低。

  • 当 boost < 0 时,贡献负分。

多字段多字符串查询

bool 查询

一个 bool 查询是一个或者多个查询子句的组合。

子句
描述

must

必须匹配,贡献算分。

should

选择性匹配,贡献算分。

must_not

Filter Context 查询字句,必须不能匹配。

filter

Filter Context 必须匹配,不贡献算分。

示例

1、bool 查询

POST /products/_search
{
  "query": {
    "bool" : {
      "must" : {
        "term" : { "price" : "30" }
      },
      "filter": {
        "term" : { "avaliable" : "true" }
      },
      "must_not" : {
        "range" : {
          "price" : { "lte" : 10 }
        }
      },
      "should" : [
        { "term" : { "productID.keyword" : "JODL-X-1937-#pV7" } },
        { "term" : { "productID.keyword" : "XHDK-A-1293-#fJ3" } }
      ],
      "minimum_should_match" :1
    }
  }
}
  • 子查询可以任意顺序出现。

  • 可以嵌套多个查询。

  • 如果 bool 查询中没有 must 条件,那么 should 中必须至少满足一条查询。

2、boost 控制查询分数

POST /news/_bulk  
{ "index": { "_id": 1 }}  
{ "content":"Apple Mac" }  
{ "index": { "_id": 2 }}  
{ "content":"Apple iPad" }  
{ "index": { "_id": 3 }}  
{ "content":"Apple employee like Apple Pie and Apple Juice" }

POST news/_search
{
  "query": {
    "boosting": {
      "positive": {
        "match": {
          "content": "apple"
        }
      },
      "negative": {
        "match": {
          "content": "pie"
        }
      },
      "negative_boost": 0.5
    }
  }
}

此时会将苹果产品放前边,而 id 为 3 的显示在最后。

多字段单字符串查询

Disjunction Max Query

将任何与任一查询匹配的文档作为结果返回。采用字段上最匹配的评分最终评分返回。

示例

1、插入数据

PUT /blogs/_doc/1
{
    "title": "Quick brown rabbits",
    "body":  "Brown rabbits are commonly seen."
}

PUT /blogs/_doc/2
{
    "title": "Keeping pets healthy",
    "body":  "My quick brown fox eats rabbits on a regular basis."
}

2、bool 测试

POST /blogs/_search
{
    "query": {
        "bool": {
            "should": [
                { "match": { "title": "Brown fox" }},
                { "match": { "body":  "Brown fox" }}
            ]
        }
    }
}

结果可以看到 1 号文档在前,是因为 bool 会对两个进行加和平均,不符合直觉。

2、dis_max 测试

POST blogs/_search
{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "Brown fox" }},
                { "match": { "body":  "Brown fox" }}
            ]
        }
    }
}

此时 2 号文档在前,符合直觉。

POST blogs/_search
{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "Quick pets" }},
                { "match": { "body":  "Quick pets" }}
            ]
        }
    }
}

结果还是 1 号文档在前,同时分数相同。因为最高分数的 quick 都只出现一次。但 2 号文档中有 pet ,直觉上应该 2 号文档更高,但 dis_max 只取最大的一条。

POST blogs/_search
{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "Quick pets" }},
                { "match": { "body":  "Quick pets" }}
            ],
            "tie_breaker": 0.2
        }
    }
}

加入 tie_breaker 后会把最匹配一条之外的分数与其值相乘,这样 2 号文档就能更高,符合直觉。

MultiMatch

multi_match 是 Elasticsearch 中一种用于在多个字段中执行全文搜索的查询方式。它扩展了 match 查询,允许你在多个字段上同时进行匹配。

类型
描述
使用场景

best_fields

匹配最佳字段(默认)

标题或正文关键词搜索

most_fields

多字段尽量都匹配

多语言字段匹配

cross_fields

字段合并为整体匹配

姓名拆分(first_name + last_name)

phrase

短语匹配

查找完整短语

phrase_prefix

短语前缀匹配

自动补全

bool_prefix

前缀词项布尔匹配

高效前缀搜索

示例

1、插入数据

PUT /titles
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "english",
        "fields": {"std": {"type": "text","analyzer": "standard"}}
      }
    }
  }
}

POST titles/_bulk
{ "index": { "_id": 1 }}
{ "title": "My dog barks" }
{ "index": { "_id": 2 }}
{ "title": "I see a lot of barking dogs on the road " }

2、使用 most_fields

GET /titles/_search
{
   "query": {
        "multi_match": {
            "query":  "barking dogs",
            "type":   "most_fields",
            "fields": [ "title", "title.std" ]
        }
    }
}

结果显示 2 号文档在前,符合直觉。若不使用 MultiMatch 则会 1 号文档在前,因为 English 分词器会把 barking dogs 分为 bark 和 dog,1 号文档分数更高。

自然语言与查询

当处理人类自然语言时,有时尽管搜索和原文不完全匹配,但是希望搜到一些内容。

可以采取的措施:

  • 归一化词元:例如消除变音符号(西语,拼音)。

  • 抽取词根:消除单复数等。

  • 包含同义词。

  • 拼写错误处理。

混合多语言的挑战

不同的索引使用不同的语言;同一个索引中,不同的字段使用不同的语言;一个文档的一个字段内混合不同的语言。

  • 词干提取:以色列文档,包含了希伯来语,阿拉伯语,俄语和英文。

  • 不正确的文档频率:英文为主的文章中,德文算分高(稀有)。

  • 需要判断用户搜索时使用的语言。

中文分词(IK)

由于生产环境中最常见的中文分词器为 IK,因此本篇也以 IK 为主。

安装

示例

POST _analyze
{
  "analyzer": "ik_smart",
  "text": ["剑桥分析公司多位高管对卧底记者说,他们确保了唐纳德·特朗普在总统大选中获胜"]
}

Search Template

用于解耦程序和搜索 DSL。

示例

POST _scripts/tmdb
{
  "script": {
    "lang": "mustache",
    "source": {
      "_source": [
        "title",
        "overview"
      ],
      "size": 20,
      "query": {
        "multi_match": {
          "query": "{{q}}",
          "fields": [
            "title",
            "overview"
          ]
        }
      }
    }
  }
}

POST tmdb/_search/template
{
    "id":"tmdb",
    "params": {
        "q": "basketball with cartoon aliens"
}
}```

上游可以不感知模版的变化,避免耦合。

### Index Alias

实现零停机运维。比如在进行索引重建、版本升级、滚动更新等操作时,无需中断服务。

**示例**

1、插入数据

```json
PUT movies-2019/_doc/1
{
  "name":"the matrix",
  "rating":5
}

PUT movies-2019/_doc/2
{
  "name":"Speed",
  "rating":3
}

2、设置别名

POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "movies-2019",
        "alias": "movies-latest"
      }
    }
  ]
}

POST movies-latest/_search
{
  "query": {
    "match_all": {}
  }
}

Function Score Query

可以在查询结束后对每一个匹配的文档进行一系列的重新算分,根据新生成的分数进行排序。

  • Weight:为每一个文档设置一个简单而不被规范化的权重。

  • Field Value Factor:使用该数值来修改 \_score,例如将「热度」和「点赞数」作为算分的参考因素。

  • Random Score: 为每一个用户使用一个不同的,随机算分结果。

  • 衰减函数:以某个字段的值为标准,距离某个值越近,得分越高。

  • Script Score:自定义脚本完全控制所需逻辑。

示例

1、插入数据

DELETE blogs
PUT /blogs/_doc/1
{
  "title":   "About popularity",
  "content": "In this post we will talk about...",
  "votes":   0
}

PUT /blogs/_doc/2
{
  "title":   "About popularity",
  "content": "In this post we will talk about...",
  "votes":   100
}

PUT /blogs/_doc/3
{
  "title":   "About popularity",
  "content": "In this post we will talk about...",
  "votes":   1000000
}

2、使用多个参数测试

POST /blogs/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": {
          "query":    "popularity",
          "fields": [ "title", "content" ]
        }
      },
      "field_value_factor": {
        "field": "votes",
        "modifier": "log1p" ,
        "factor": 0.1
      }
    }
  }
}
  • 初始逻辑:$新的算分 = 老的算分 * 投票数$

  • 使用 modifier 平滑参数后:$新的算分 = 老的算分 * log(1 + 投票数)$

  • Factor 参数:$新的算分 = 老的算分 * log(1 + factor * 投票数)$

3、一致性随机函数

POST /blogs/_search
{
  "query": {
    "function_score": {
      "random_score": {
        "seed": 911119
      }
    }
  }
}
  • 使用场景:网站的广告需要提高展现率。

  • 具体需求:让每个用户能看到不同的随机排名,但是也希望同一个用户访问时,结果的相对顺序保持一致。

4、Boost Mode 和 Max Boost

POST /blogs/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": {
          "query":    "popularity",
          "fields": [ "title", "content" ]
        }
      },
      "field_value_factor": {
        "field": "votes",
        "modifier": "log1p" ,
        "factor": 0.1
      },
      "boost_mode": "sum",
      "max_boost": 3
    }
  }
}

Max Boost 可以将算分控制在一个最大值。Boost Mode 用于进行算分的数学运算。

Suggester

实现搜索引擎中纠错的功能。原理是将文本分解为 Token 然后在索引的字典中查找相似的 Term 并返回。

四种 Suggester 的不同之处:

Suggester 类型
功能与特点
适用场景

Term Suggester

对输入文本的每个词条进行纠错或建议,基于索引中的词典查找相似 Term。

单个词条级别的纠错和建议(如拼写错误修正)。

Phrase Suggester

在 Term Suggester 基础上,考虑多个词条之间的关系(如是否共同出现、相邻程度等)。

多个词组成的短语级别的纠错和建议(如句子片段的修正)。

Completion Suggester

提供前缀匹配的自动补全功能,支持快速搜索建议(如用户输入时的提示)。

快速自动补全(如搜索框输入提示)。

Context Suggester

基于 Completion Suggester,增加了上下文信息的支持(如地理位置、类别等过滤条件)。

需要结合上下文信息的自动补全(如特定分类下的搜索提示)。

示例

1、插入数据

DELETE articles

POST articles/_bulk
{ "index" : { } }
{ "body": "lucene is very cool"}
{ "index" : { } }
{ "body": "Elasticsearch builds on top of lucene"}
{ "index" : { } }
{ "body": "Elasticsearch rocks"}
{ "index" : { } }
{ "body": "elastic is the company behind ELK stack"}
{ "index" : { } }
{ "body": "Elk stack rocks"}
{ "index" : {} }
{  "body": "elasticsearch is rock solid"}

2、Term Suggester

几种 Suggestion Mode

  • Missing:如果索引中已经存在,就不提供建议。

  • Popular:推荐出现频率更加高的词。

  • Always:无论是否存在,都提供建议。

POST /articles/_search
{
  "size": 1,
  "query": {
    "match": {
      "body": "lucen rock"
    }
  },
  "suggest": {
    "term-suggestion": {
      "text": "lucen rock",
      "term": {
        "suggest_mode": "missing",
        "field": "body"
      }
    }
  }
}

3、Phrase Suggester

在 Term Suggester 的基础上增加了一些额外的逻辑。

  • Max Errors:最多可以拼错的 Terms 数。

  • Confidence:控制返回建议的置信度阈值。只有当建议短语的原始得分加上长度归一化后 ≥confidence 时才会被返回,默认为 1。

POST /articles/_search
{
  "suggest": {
    "my-suggestion": {
      "text": "lucne and elasticsear rock hello world ",
      "phrase": {
        "field": "body",
        "max_errors":2,
        "confidence":2,
        "direct_generator":[{
          "field":"body",
          "suggest_mode":"always"
        }],
        "highlight": {
          "pre_tag": "<em>",
          "post_tag": "</em>"
        }
      }
    }
  }
}

4、Completion Suggester

提供了自动补全功能。对性能要求比较严苛,采用了非倒排索引的数据结构,将 Analyze 数据编码成 FST 和索引一起存放。FST 会整个加载进内存,速度很快。同时 FST 仅支持前缀查找。

DELETE articles
PUT articles
{
  "mappings": {
    "properties": {
      "title_completion":{
        "type": "completion"
      }
    }
  }
}

POST articles/_bulk
{ "index" : { } }
{ "title_completion": "lucene is very cool"}
{ "index" : { } }
{ "title_completion": "Elasticsearch builds on top of lucene"}
{ "index" : { } }
{ "title_completion": "Elasticsearch rocks"}
{ "index" : { } }
{ "title_completion": "elastic is the company behind ELK stack"}
{ "index" : { } }
{ "title_completion": "Elk stack rocks"}
{ "index" : {} }
POST articles/_search?pretty
{
  "size": 0,
  "suggest": {
    "article-suggester": {
      "prefix": "elk ",
      "completion": {
        "field": "title_completion"
      }
    }
  }
}

5、Context Suggester

是 Completion Suggester 的拓展,能够在搜索中加入更多的上下文信息。

可以定义两种类型的 Context:

  • Category 一任意的字符串。

  • Geo—地理位置信息。

实现 Context Suggester 的具体步骤:

  • 定制一个 Mapping。

  • 索引数据,并且为每个文档加入 Context 信息。

  • 结合 Context 进行 Suggestion 查询。

DELETE comments
PUT comments
PUT comments/_mapping
{
  "properties": {
    "comment_autocomplete":{
      "type": "completion",
      "contexts":[{
        "type":"category",
        "name":"comment_category"
      }]
    }
  }
}

POST comments/_doc
{
  "comment":"I love the star war movies",
  "comment_autocomplete":{
    "input":["star wars"],
    "contexts":{
      "comment_category":"movies"
    }
  }
}

POST comments/_doc
{
  "comment":"Where can I find a Starbucks",
  "comment_autocomplete":{
    "input":["starbucks"],
    "contexts":{
      "comment_category":"coffee"
    }
  }
}
POST comments/_search
{
  "suggest": {
    "MY_SUGGESTION": {
      "prefix": "sta",
      "completion":{
        "field":"comment_autocomplete",
        "contexts":{
          "comment_category":"coffee"
        }
      }
    }
  }
}

Demo

全文搜索

tmdb-search 是电影搜索项目,主要用于索引和搜索 TMDB(The Movie Database)的电影数据。

项目组成部分

  1. 数据处理脚本:

  • ingest_tmdb_from_file.py: 将 TMDB 数据导入 Elasticsearch

  • ingest_tmdb_to_appserarch.py: 将数据导入 AppSearch(可选功能)

  • query_tmdb.py: 搜索接口实现

  1. 映射配置:

  • mapping/english_analyzer.json: 默认英文分析器配置

  • mapping/english_english_3_shards.json: 3 分片的配置版本

  1. 查询示例:

  • 多个针对 "Space Jam" 电影的查询示例,展示不同的查询策略

快速开始

  1. 启动服务

确保已安装所需 Python 包和 Python3 环境:

pip install -r requirements.txt

ElasticSearch 的安装详见上方快速开始章节内容。

  1. 导入数据:

python ingest_tmdb_from_file.py
  • 运行后会提示选择 mapping 配置。

  • 选择 0 使用默认配置,或选择其他预定义配置。

  1. 搜索电影:

# 普通搜索
python query_tmdb.py

# 带高亮显示的搜索
python query_tmdb.py highlight
  • 运行后会提示选择查询文件

  • 结果会显示相关度分数和标题

  • 使用 highlight 参数可以显示匹配高亮

整体流程

参考

  1. https://github.com/onebirdrocks/geektime-ELK/

  2. https://www.elastic.co/elasticsearch

仓库地址:

克隆仓库后进入到 目录运行 docker-compose up。

更多 DSL 语法请参考 。

仓库地址:

克隆仓库后进入到 目录,将 docker.elastic.co/elasticsearch/elasticsearch:7.8.0 改为 zingimmick/elasticsearch-ik:7.8.0。运行 docker-compose up。

仓库地址:

🧑🏻‍🏫
L2ncE/es101
docker-compose 文件
官方文档
L2ncE/es101
docker-compose 文件
L2ncE/es101