elasticsearch学习

概述

功能

  • 分布式搜索引擎
  • 大数据近实时分析引擎

学习

  • 开发: 产品基本功能,底层原理,数据建模
  • 运维: 容量规划; 性能优化; 问题诊断; 滚动升级
  • 方案: 搜索与如何解决搜索; 大数据分析实践,理论知识实际场景

考试: elastic 官网

简介

1
2
3
restful 接口, 供各种语言使用
功能: 搜索,聚合

elastic 生态

  • logstash 开源服务端数据处理管道, 从不同源采集数据,转换数据,发送到不同存储库
1
2
3
4
5
6
7
8
9
实时解析和转换数据
从IP地址破译地理位置
将PII(个人身份信息)数据匿名,排除敏感字段
可扩展
200多个插件 (日志、数据库。。。)
可靠安全性
持久化队列保证至少送达一次
数据传输加密
监控
  • kibana : 可视化分析利器
1
2
数据可视化公司
基于 logstash 的工具, 2013被es收购
  • BEATS : 轻量数据采集器
1
2
3
4
有多种不同的beat
filebeat: 日志,文件
packetbeat: 网络数据抓包
。。。

场景

安装上手

安装

文件目录结构

  • bin : 脚本执行文件 包括: 启动,安装插件,运行统计数据等
  • config : 集群配置文件
  • JDK : java运行环境
  • data : 数据文件
  • lib : java类库
  • logs : 日志文件
  • modules : 包含所有ES模块
  • plugins : 所有已安装插件

启动

  • 物理机
1
2
3
4
5
6
7
/bin/elasticsearch 启动
/bin/elasticsearch-plugin 插件管理 install 安装

分布式
./elasticsearch -E node.name=node1 -E cluster.name=geektime -E path.data=node1_data -d
./elasticsearch -E node.name=node2 -E cluster.name=geektime -E path.data=node2_data -d
./elasticsearch -E node.name=node3 -E cluster.name=geektime -E path.data=node3_data -d
  • docker
1

入门

索引,文档,RESTAPI

文档

1
2
3
4
5
6
ES是面向文档的, 文档是可搜索数据的最小单位
文档会被 序列化为JSON格式,保存在ES中 (JSON对象由字段组成,每个字段有类型)
每个文档都有一个 唯一ID, 可以自己指定/或ES自动生成

类似数据的一条记录
JSON文档格式灵活,不需要预先定义格 (字段类型可以指定,也可以ES自动推算)(支持 数组/嵌套)

文档的元数据

1
2
3
4
5
6
7
8
ES用于标注文档的相关信息

_index 文档所属索引名称
_type 文档所属类型名称
_id 文档唯一ID
_source 文档原始JSON数据
_version 文档版本信息
_score 相关性打分

索引

1
2
3
4
5
文档的容器, 一类文档的集合
体现逻辑空间的概念: 索引有自己的Mapping定义,定义包含文档的字段名和字段类型

索引的Mapping : 定义文档字段的类型
索引的Settings : 定义不同的数据分布

Shard 则是体现 物理空间的概念: 索引中的数据分散在 shard 上

1
2
3
索引的不同语意: 
名称: 文档的集合, 可创建多个索引
动词: 保存一个文档到ES的过程 (在ES中是创建一个 倒排索引) (有B树索引 , 倒排索引)
  • 类型 type

    1
    2
    7.0 版本之前, 每个索引可以建立多个type, 每个type下就是同类文档
    7.0开始废弃不用,每个索引只能建立一个type (_doc)

REST API

1
集成各种语言

节点,集群,分片,副本

1
2
3
4
5
6
7
8
分布式系统的可用性和扩展性

高可用
服务可用: 允许有节点停止服务
数据可用: 部分节点丢失,不会丢失数据

可扩展:
请求量提升 / 数据不断增长 (将数据分布到所有节点上)

ES的分布式架构

1
2
3
4
5
6
7
不同的集群 通过不同名字区分  (默认名字 elasticsearch)
通过配置文件修改,或命令行 -E cluster.name=geektime 设定
一个集群可以有一个或多个节点

好处:
存储的水平扩容
提高系统的可用性,部分节点停止服务,整个集群服务不受影响

节点

1
2
3
4
5
节点: 一个 ES 的实例
本质就是一个JAVA进程
一台机器可运行多个ES进程,但生产环境一般建议一台机器一个实例
每个节点设置名字: 通过配置文件,或启动命令行 -E node.name=node1 指定
每个节点启动后,会分配一个UID,保存在 data 目录下
  • master-eligible nodes

    1
    2
    3
    4
    可以参加 选主流程,成为master 节点

    每个节点启动后: 默认就是一个 master eligible 节点
    可设置 node.master: false 禁止
  • master node

    1
    2
    当第一个节点启动时, 会将自己选举成 master 节点
    每个节点都保存了集群的状态, 只有 master 节点才能修改集群的状态信息 (任何节点都能修改会导致数据不一致)

    集群状态: 维护集群中的必要信息

    所有节点的信息

    所有索引 和 其相关的 Mapping 与 Setting 信息

    分片的路由信息

  • Data Node

    • 保存数据的节点,叫做Data Node, 负责保存分片数据 (在数据扩展 起重要作用)
  • Coordinating Node

    • 负责接受client的请求,将请求分发到合适节点,最终把结果汇聚到一起。
    • 每个节点默认起到 coordinating node 职责
  • Hot & Warm Node

    • 冷热节点, Hot节点配置高, warm配置低(可存储比较旧的数据)
    • 不同硬件配置的 Data Node, 用来实现 Hot & Warm 架构,降低集群部署的成本
  • Machine Learning Node

    • 负责跑 机器学习的 Job, 用来做异常检测
  • Tribe Node

    • 连接到不同的 ES集群,支持将这些集群当成一个单独的集群处理
    • 淘汰, 5.3开始使用 cross cluster search(跨集群搜索)

节点类型的配置: 生成环境中,应该设置单一角色的节点

节点类型 配置参数 默认值
master eligible node.master true
data Node.data true
ingest node.ingest True
coordinating only 每个节点默认都是coordinating节点。设置其他类型则为false
machine learning node.ml true (需 enable x-pack )

分片

  • 主分片 ( primary shard )

    1
    2
    3
    解决数据水平扩展的问题。  主分片可以将数据分布到集群内的所有节点之上
    一个分片 是一个运行的 lucene 的实例
    主分片数 在索引创建时指定, 后续不允许修改, 除非 Reindex
  • 副本 ( replica shard )

    1
    2
    3
    用以解决数据高可用的问题。 副本分片是主分片的拷贝
    副本分片数 可以动态调整
    增加副本数,还可在一定程度上提高服务的可用性 ( 读取的吞吐 )

分片的设置: 生成环境分片的设定,提前做好容量规划

1
2
3
4
5
6
分片数设置过小: 
后续无法增加及节点实现水平扩展
单个分片数据量太大,导致数据重新分配耗时
分片数设置过大, 7.0 开始, 默认主分片设置成1, 解决了 over-sharding 的问题
影响搜索结果的 相关性打分,影响 统计结果的准确性
单个节点上过多的分片,会导致资源浪费, 同时也会影响性能
1
2
3
4
5
6
查看 集群健康状况
GET _cluster/health

green - 主分片 和 副本 都正常
yellow - 主分片正常, 有副本分片未正常分配
red - 有主分片未能分配 (例: 当服务器磁盘容量超过 85% 时, 去创建了一个新的索引

文档的基本 CRUD

操作 命令 (type约定用_dec) 说明
Index PUT my_index/_doc/1 如果ID不存在,创建信息的。 已经存在则删除旧的创建新的,版本号增加
Create PUT my_index/_create/1 如果id已经存在,会失败
Read GET my_index/_doc/1
Update POST my_index/_update/1 文档必须已经存在, 更新只会对相应字段做增量修改
Delete DELETE my_index/_doc/1

创建说明

1
2
3
4
支持 自动生成文档ID 和 指定文档ID 两种方式

通过调用 POST /my_index/_doc (系统自动创建ID)
通过 PUT /my_index/_create/1 创建, 指定 _create , 如果ID文档已存在,操作失败

注意: create是创建新的, index 是 删除旧的创建新的, update是在旧的上面做增量修改

批量操作

1
批量操作, 减少 网络连接产生的开销,提高性能
  • Bulk API

    1
    2
    3
    4
    5
    6
    7
    8
    9
    POST _bulk
    支持一次API调用中, 对不同索引进行不同操作

    支持四种操作:
    Index, Creat, Update, Delete

    可以在 URI 中指定Index, 也可以再请求 payload 中进行
    操作中 单条操作失败, 并不会影响其他操作
    返回结果包括 每一条操作执行的结果
  • mget 批量读取

    1
    GET /_mget
  • msearch 批量查询

    1
    POST /_msearch

常见错误返回

1
2
3
4
5
无法连接:   网络故障或集群挂了
连接无法关闭: 网络故障或节点出错
429 : 集群过于繁忙
4xx : 请求体格式错误
500 : 集群内部错误

注意: 批量操作一次请求不宜过多

倒排索引

1
数据引擎中的 重要数据结构

书本对比理解

1
2
3
4
书本开始: 有目录,书本内容的索引    - 正排索引
根据目录中的 章节名称对应页码 查询具体内容

有一些书本最后: 有一些重要内容对应页码的索引页 - 倒排索引

搜索引擎中

1
2
正排索引: 文档ID 到 文档内容和单词 的关联
倒排索引: 单词 到 文档ID 的关系

一个例子:

image-20220330115421025

1
2
左边正排索引, 三篇 es书 的文档
右边倒排索引, 所有词条的索引,出现次数, 在哪个文档中哪个位置

倒排索引的核心组成

  • 单词词典( term dictionary ), 记录所有文档的单词, 记录单词 到 倒排列表 的关联关系
    • 单词词典很大, 通过 B+树 或 哈希拉链法实现, 满足高性能的插入和查询
  • 倒排列表 ( posting list ) , 记录了 单词对应的文档 的结合, 由倒排索引项组成
    • 倒排索引项(posting)
      • 文档ID
      • 词频 TF - 该单词在文档中出现的次数, 用于相关性评分
      • 位置(position) - 单词在文档中分词的位置。 用于 语句搜索
      • 偏移(offset)-记录单词的开始结束位置, 实现高亮显示

上面例子在 ES中的实现

image-20220330121209363

  • ES中 JSON文档每个字段都有自己的 倒排索引
  • 可以指定 对某些字段不做索引
    • 优点: 节省存储空间
    • 缺点: 字段无法被搜索

通过 analyzer 进行分词

1
2
3
4
5
6
analysis 文本分析: 把全文本转换为 一系列单词 (term / token)的过程, 也叫 分词

analysis 通过 analyzer 实现
可使用ES内置的分析器 / 或按需定制化分析器

除了 数据写入时 转换词条, 匹配Query语句时也需要用相同的分析器对查询语句进行分享
  • 分词器是专门 处理分词的组件, analyzer 由三部分组成 (按顺序执行)
    • character filters ( 针对原始文本处理, 例如去除html )
    • Tokenizer ( 按照规则切分为 单词 )
    • Token Filter ( 将切分的单词进行加工, 小写,删除 stopwords,增加同义词 )

ES内置分词器

  • standard analyzer - 默认分词器,按词切分,小写处理
  • simple - 按 非字母切分 (符号被过滤), 小写处理
  • stop - 小写处理, 停止词过滤(the,a,is)
  • whitespace - 按空格切分,不转小写
  • keyword - 不分词,直接将输入当做输出
  • patter - 正则表达式, 默认 \W+ (非字符分割)
  • language - 提供 30多种常见语言的分词器
  • customer - 自定义分词器

使用 _analyzer API

1
2
GET /_analyze        // 直接指定 analyzer 进行测试 对文本如何分词
{"analyzer":"standard", "text":""}
1
2
POST my_index/_analyze  // 指定索引字段, 查看 该字段如何进行分词
{"field":"", "text":""}
1
2
POST /_analyze	 // 自定义分词测试, 定义一个tokenizer,定义fileter, 组合后 是如何进行分词的
{"tokenizer":"standard","filter":[""], text:""}

中文分词 的难点

1
2
3
中文句子,切分成一个个词 (而不是字)
英文,单词有自然的空格作为分割
中文,在不同上下文有不同理解
  • ICU Analyzer

    1
    2
    安装plugin:  elasticsearch-plugin install analysis-icu
    提供unicode支持,更好支持亚洲语言
  • IK

    1
    支持自定义词库, 支持热更新分词字典
  • THULAC

1
清华大学自然语言处理 和 社会人文计算实验室的一套中文分词器

Search API

  • URI search
    • 在URL中使用查询参数
  • Request Body search
    • 使用 ES 提供的, 基于JSON格式的更加完备的 Query Domain Specific Language (DSL)
1
2
3
4
5
指定查询的索引
/_search 集群上所有的索引
/index1/_search 索引index1
/index1,index2/_search 索引 index1,2
/index*/_search 以index开头的索引

搜索的相关性

1
2
3
4
5
6
搜索是: 用户 和 搜索引擎 的对话
用户关心的是: 搜索结果的相关系
是否可以找到所有相关的内容
有多少不相关的内容被返回了
文档的打分是否合理
结合业务需求,平衡结果排名
  • Web 搜索
    • page rank 算法 (不仅仅是内容,更重要的是内容的可信度)
  • 电商搜索
    • 扮演 销售的角色
    • 提高用户购物体验,提升网站销售业绩,去库存

衡量相关性

学科 information Retrieval

1
2
3
precision (查准率) - 尽可能返回较少的无关文档
recall (查全率) - 尽可能返回较多的相关文档
ranking - 是否能够按照相关度进行排序

URI search 详解

1
2
3
4
5
通过 URI query 实现搜索
q指定查询语句: 使用
df 默认字段, 不指定会对所有字段进行查询
sort 排序 / from 和 size 用于分页
profile 查看 查询是如何被执行的
  • 指定字段 , 泛查询(会查询所有字段)

    • q=title:2021 / q=2021
  • Term, Phrase ( 查询 beautiful mind )

    • Term : 等效 OR
    • Phrase:等效 AND, 还要求 前后顺序一致
  • 分组 与 引号

    • title:(beautiful AND mind)
    • title=”beautiful mind”
  • 布尔操作

    • AND / OR / NOT 或者 && / || / ! (必须大写)
  • 分组

    • + 表示 must
    • - 表示 must_not
    • title:(+matrix -reloaded)
  • 范围查询

    • 区间表示: []闭区间, {}开区间
    • year:{2019 TO 2018}
  • 算数符号

    • year:>2010
  • 通配符查询 ( 效率低,占用内存大,不建议使用)

    • ? 代表一个字符, * 代表 0 或 多个字符
  • 正则表达

    • title:[bt]oy
  • 模糊匹配 与 近似查询

Request Body 与 Query DSL 介绍

  • 将查询语句通过 HTTP Request Body 发送给 ES
  • Query DSL
1
2
3
4
5
6
7
8
```

## Query string 与 Simple Query string

## Dynamic Mapping 和 常见字段类型

### Mapping

类似于 数据库中的 schema 的定义, 作用:
定义索引中 字段的名称
定义字段的数据类型
字段,倒排索引的相关配置 ( Analyzed or Not Analyzed, Analyzer )

Mapping 会把 JSON文档 映射成 Lucene 所需要的扁平格式

一个Mapping属于一个索引的Type ( 7.0 开始一个索引一个type )
每个文档都属于 一个索引的type
一个type 有一个 Mapping的定义
7.0开始, 不需要在mapping定义中指定type信息

1
2
3

- 什么是 Dynamic Mapping

写入文档时: 如果索引不存在,自动创建索引
ES自动根据文档信息推算出字段的类型

有时候会推算不对,如地理信息 (类型不对,会导致一些功能出错)

1
2
3
4
5

- 能否更改mapping的字段类型

- 新增字段


dynamic 设为true, 一旦有新增字段的文档写入, mapping也同时被更新
dynamic 设为 false, mapping不会更新,新增字段的数据无法被索引,但信息会出现在 _source 中
dynamic 设置为 strict, 文档写入失败
1
2
3

- 已有字段, 不在支持修改字段定义


lucene 实现的倒排索引, 一旦生成,不允许修改
改变字段类型, 必须 Reindex API, 重建索引
1
2
3
4
5
6
7

- 原因
- 修改字段的数据类型, 导致已被索引的数据无法被搜索
- 增加新的字段不会有影响

## 显示Mapping的设置与常见参数介绍


定义一个Mapping

PUT movies
{
“mappings”:{
// 定义mapping
}
}

建议:
参考API手册, 纯手写
步骤:
创建一个临时的index, 写入一些样本数据
通过访问 mapping API 获得临时文件的动态 Mapping 定义
进行修改后,使用该配置创建真正的索引
删除临时索引

1

控制当前字段是否被索引: 默认为true, 如果要设置为不可被搜索,字段添加设置 “index”:false

null_value, 需要对null值实现搜索: 只有keyword类型支持设定 “Null_value”:”NULL”

_all 在版本7中被 copy_to 所替代: copy_to 将字段的数值拷贝到目标字段(如 firstName,lastName 两个字段拷贝到 fullName,实现全名称搜索), copy_to 目标字段不出现在_source 中

数组类型: ES不提供专门的数组类型。 但任何字段,都可以包含多个相同类型的数值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

- 控制 倒排索引 记录内容的配置
- docs - 记录 doc id
- freqs - 记录 doc id 和 term frequencies
- positions - 记录 doc id / term frequencies / term position
- offsets - 记录 doc id / term frequencies / term position / character offects
- Text 类型默认记录 postions, 其他默认为 docs
- 记录内容越多,占用存储空间越大

## 多字段特性 及 mapping中配置自定义 Analyzer

- 多字段特性
- 厂商名字实现精确匹配
- 增加一个 keyword 字段
- 使用不同的 analyzer
- 不同语言
- pinyin 字段的搜索 ( 中文实现拼音搜索)
- 支持为 搜索和索引 指定不同的analyzer

Excat values : 包括 数字 / 日期 / 具体一个字符串 (ES中的 keyword)(在索引时,不分词)
Full Text : 全文本,非结构化的文本树 (ES中的 text)

1
2
3

### 自定义分词

当ES自带的分词器 无法满足,可以自定义分词器。 通过自组合不同组件实现:
Character Filter :
在Tokenizer之前,对文本进行第一步处理。
可配置多个,会影响Tokenizer的 position 和 offset 信息
1. HTML strip - 去除 html 标签
2. Mapping - 字符串替换
3. Pattern replace - 正则匹配替换

Tokenizer : 
    将原始文本安装一定规则,切分为词 ( term or token )
    ES内置的: whitspace / standard / uax_url_email / pattern / keyword / path hierarchy
    可使用Java开发插件,实现自己的Tokenizer

Token Filter : 
    将Tokenizer 输出的单词( term ), 进行增加,修改,删除
    ES自带的 Token Filters:  lowercase(小写) / stop(停止词) / synonym(添加近义词)
1

创建索引时, 在 settings 中 自定义 analysis

1
2
3
4
5
6
7
8

## Index Template 和 Dynamic Template

- Index template - 帮助设置 Mappings 和 Settings, 并按照一定规则,自动匹配到新创建的索引之上
- 模板 仅在一个索引被新创建时,才会产生作用。 修改模板不会影响已创建的索引
- 可设置多个 索引 模板, 这些设置会被 "merge" 在一起
- 可指定 "order" 的数值, 控制 "merging"的过程

当一个索引 被新创建时
应用ES默认的 settings 和 mappngs
应用 order 数值低的 Index Template 中的设定
应用 order 数值高的 Index Template 中的设定,之前设定被覆盖
应用 用户指定的 Settings 和 Mappings,并覆盖之前模板中的设定

1
2
3
4
5
6

- Dynamic Template : 根据ES识别的数据类型,结合字段名称,动态设定 字段类型
- 所有 字符串类型设定成 keyword, 或关闭 keyword
- is 开头的字段都设置成 boolean
- long_ 开头的都设置成 long 类型

Dynamic Template 定义在 某个索引的 Mapping 中
Template有一个 名称
匹配规则是 一个数组
为匹配到字段设置 Mapping

1
2
3
4
5

## ES 聚合分析介绍

### 聚合 ( aggregation )

ES 除搜索外,提供 针对ES数据进行统计分析的功能
实时性高
Hadoop (T+1)
通过聚合,得到数据的概览,分析和总结全套的数据
高性能, 只需要一条语句,就可以从ES中得到分析结果 (无需客户端自己去实现分析逻辑)

1

kibana 可视化报表 - ES的聚合分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

- 聚合的分类
- Bucket Aggregation - 满足特定条件文档
- Term & Range ( 时间 / 年龄区间 / 地理位置 )
- Metric Aggregation - 一些数学运算, 可以对文档字段进行统计分析
- 基于数据集 计算结果, 支持字段上的计算,也支持脚本产生的结果上计算
- 数字计算,输出一个值: min / max / sum / avg / cardinality
- 输出多个值: stats / percentiles / percentile_ranks
- Pipeline Aggregation - 对其他聚合结果进行二次聚合
- Matrix Aggregation - 支持对多个字段的操作并提供一个结果矩阵

# 深入搜索

## 基于词项和基于全文的搜索

### 基于Term的查询

- Term : 表达语意的最小单位。 搜索 和 利用统计语言模型进行自然语言处理 都需要处理Term
- 特点:
- Term Level Query: Term / Range / Exists / Prefix / wildcard
- Term查询,对输入不做分词。 在倒排索引中查找准确的词项,并为每个包含该词项的文档进行 相关度算分
- 可以通过复合查询 Constant Score 将查询转换成一个 Filtering, 跳过算分,并可以有效利用缓存, 提高性能

### 基于全文的查询

- Match Query / Match Phrase Query / Query String Query
- 特点:
- 索引 和 搜索 时都会进行分词, 查询字符串先传递到一个合适的分词器,生成一个供查询的词项列表
- 查询过程,先 对输入的查询进行分词, 然后每个词项逐个进行底层的查询打分,最终将结果汇总。 为每个文档生成一个算分。

## 结构化搜索

- 对 结构化数据 的搜索
- 日期,布尔,数字 都是结构化的。 有精确的格式,可以对这些格式进行逻辑操作。 范围,大小等
- 文本也可以结构化
- 可以做 精确匹配 或者 部分匹配 ( term查询 / prefix前缀查询 等)
- 结构化结果只有 "是" 或 "否" 两个值。 根据场景需要决定是否需要打分

## 搜索的相关性算分

### 相关性

搜索的相关性算分: 描述 文档 和 查询语句 匹配的程度。 ES会对每个匹配查询条件的结果进行算分 _score
打分的本质是 排序, 把最符合用户需求的文档排在前面。 ES5之前的默认算分采用 TF-IDF, 现在采用 BM25

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

### TF-IDF

- 词频 TF ( Term Frequency ) : 检索词在一篇文档中出现的频率
- 检索词出现的次数 除以 文档的总字数
- 度量一条查询和结果文档相关性的简单方法: 将搜索中每个词的TF进行相加
- Stop Word (停止词) , 如"的", 在文档中出现很多次,但对贡献相关度几乎没有用处,不应考虑它们的TF

- 逆文档频率IDF ( Inverse Document Frequency ) :

- DF: 检索词在所有文档中出现的频率
- 而IDF, 简单说就是公式: log(全部文档数 / 检索词出现过的文档总数)

- TF-IDF 本质上就是将 TF 求和 变成了 加权求和

- 例子: 搜索 区块链的应用 ( 分词为: 区块链 的 应用 三个词)

  TF(区块链)*IDF(区块链) + TF(的)*IDF(的) + TF(应用)*IDF(应用)
  
1
2
3

### BM25

和经典的TF-IDF, 当TF无限增加时, BM25算分会趋于一个数值

1
2
3

### 定制 Similarity

PUT /my_index
{
“settings”:{
“similarity”:{
“custom_similarity”:{
“type”:”BM25”,
“b”:0,
“k1”:2
}
}
}
}
K 默认值是 1.2, 数值越小, 饱和度越高
b 默认值是 0.75(取值范围0-1), 0代表禁止 Normalization

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

### Boosting Relevance

- Boosting 控制相关度的一种手段 ( 索引,字段 或查询子条件)
- 含义:
- boost > 1 时, 打分相关度 相对性的提升
- 0 < boost < 1 , 打分权重 降低
- boost < 0 , 贡献负分

## Query & Filtering 与 多字符串多字段查询

- 高级搜索的功能: 支持多选 文本 输入, 针对多个字段进行搜索
- 搜索引擎 一般也提供 基于时间,价格等条件的过滤
- 在ES中, 有 Query 和 Filter 两种不同的 Context
- Query Context:相关性算分
- Filter Context: 不算分,利用缓存,更好性能

### bool 查询

- 一个 或者 多个 查询子句的组合 ( 4种子句)
- 相关性 除了全文本检索, 也使用 yes|no 的子句, 匹配子句越多,评分越高
- 子查询顺序任意, 可嵌套多个查询
- must : 必须匹配,贡献算分
- should : 选择性匹配, 贡献算分
- must_not :必须不匹配, 是 filter context, 不贡献算分
- filter : 过滤条件,必须匹配, 是 filter context, 不贡献算分

- 结构化查询 : 包含而不是相等 的问题 (多值字段 )
- 增加一个 count 字段进行计数

- 查询语句的结构, 对相关度算分的影响
- 同一层级的竞争字段,具有相同的权重
- 通过 嵌套bool查询,可以改变 对算分的影响

## 单字符串多字段查询

- should 算分过程
- 查询 should 语句中的两个查询
- 加和 两个查询的评分
- 乘以 匹配语句的总数
- 除以 所有语句的总数

### Dis Max Query

两字段竞争,不应 将分数简单叠加,应该找到单个最佳匹配的字段的评分

1
2
3
4
5
6
7

- dis max query
- 将任何 与 任一查询匹配的文档作为结果返回。 采用字段上最匹配的评分作为 最终评分返回
- 通过 tie_breaker 参数调整,将其他匹配语句的评分与tie_breaker 相乘,最终结果为 将最佳匹配评分与tie算法相加 (tie_breaker 介于 0-1, 0:使用最佳匹配,1:所有语句同等重要)

### Multi Match

三种场景:
最佳字段(Best Fields): 当字段之间相互竞争又相互关联。 评分来自 最匹配字段
多数字段(Most Fields):
处理英文内容时,常见手段:
在主字段(采用户English Analyzer),抽取词干,加入同义词,以匹配更多的文档
相同的文本,加入子字段(采用Standard Analyzer), 以提供更加精确的匹配
其他字段作为匹配文档提高相关度的信号。匹配字段越多则越好
混合字段(cross Field)
对某些实体, 例如人名,地址等, 需要在多个字段中确定信息,单个字段只能作为整体的一部分。 希望在任何这些字段中找到尽可能多的词

1

POST my_index/_search
{
“query”: {
“multi_match”: {
“type”: “best_fields”,
“query”: “Quick pets”,
“fields”: [“title”,”body”],
“tie_breaker”: 0.2,
“minimum_should_match”: “20%”
}
}
}

1

跨字段搜索
无法使用 Operator
可以用 copy_to 解决,但是需要额外的存储空间

cross_fields 类型的 multi_match
支持 Operator
与 copy_to 相比, 优势: 在搜索时为单个字段提升权重

1
2
3
4
5

## 多语言 及 中文分词与检索

### 多语言

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

挑战:
词干提取
不正确的文档频率 - 英文为主的文字,德文算分高(稀有)
需要判断用户搜索时使用的语言,语言识别( compact language detector) - 根据语言查询不同索引

技巧:
归一化词元 / 单词词根抽取 / 同义词 / 拼写错误

1
2
3
4
5
6
7

### 中文分词

- 常用中文分词器: hanLP / IK / pinyin

- 演变

  1. 查字典, (有的词就标示,复合词就找最长的。 不认识就分割成单字)
  2. 最小词数的分词理论 ( 一句话分词数量最少的词串, 二义性分割不行)
  3. 统计语言模型 ( 解决二义性问题, 将中文分词错误率降低。 概率问题: 动态规划+利用维特比算法 )
  4. 基于统计的机器学习算法 ( HMM,CRF,SVM,深度学习等算法, 基本思路是对汉字进行标注训练。。。)
    1
    2
    3
    4
    5

    ## Search Template 和 Index Alias 查询

    ### search Template - 解耦程序 & 搜索DSL

    ES的查询语句 对 相关性算分 / 查询性能 都至关重要

在开发初期, 可以明确查询参数,但往往还不能最终定义查询的DSL的具体结构
通过 search Template 定义一个 Contract

各司其职,解耦
开发人员 / 搜索工程师 / 性能工程师

1

创建一个 search Template

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

1

搜索工程师 维护 search Template
开发工程师 使用 Template 查询, 不用感知具体的查询语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

### Index Alias

- 别名: 实现零停机运维

## 综合查询: function score query 优化算分

- 算分与排序
- ES默认会以 文档的相关度算法进行排序
- 可以通过指定 一个或多个字段进行排序
- 使用相关度算分(score)排序,不能满足某些特定条件
- 无法针对相关度,对排序实现更多控制

### Function Score Query

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

默认计算分值的函数:
Weight: 为每个文档设置一个简单而不被规范化的权重

Field Value Factor: 使用该数值修改 _score, 例如将 "热度","点赞数"作为算分的参考因素。
    新的算分=老的算分*热度 (问题,热度数值差异很大 0和10000,结果分数差异过大)
    使用 modifier 平滑曲线(使用各种 公式计算):如log1p, 则 新的算分 = 老的算分 * log(1+热度)
    
Random Score: 为每一个用户使用一个不同的 随机算分结果 
    (网站广告提高展现率, 让每个用户看到不同的随机排名,但是也希望同一个用户访问时,结果的相对顺序保持一致)
    
衰减函数: 以某个字段的值为标准,距离某个值越近,得分越高

Script Score: 自定义脚本完全控制所需逻辑
1
2
3
4
5
6
7
8
9
10
11

## Suggester

```text
用户在输入过程 自动补全或者纠错的功能
在ES中 suggester API 实现
原理: 将输入的文本分解为token,然后在索引的字典里查找相似的term返回

ES中设计了 4中类别的 Suggester
Term & Phrase Suggester
Complete & Context Suggester