一、初识
- 1、概念:
elasticsearch
是一款强大的开源搜索引擎,可以从海量数据中快速找到需要的内容。- 使用:
elasticsearch(存储,计算,搜索数据)
可以结合kibana(数据可视化)
和Logstash、Beats(数据抓取)
一起使用
- 2、原理:
- 正向索引(传统数据库):
select * from tb_goods where title like '%手机%' # tb_goods中逐行搜索 title中包含手机的数据。找到存入结果集;找不到丢弃
- 倒排索引(elasticsearch):
# 搜索有包含手机的文档 # 原理:根据正向索引生成倒排索引[词条,文档id];1、对用户输入进行分词 2、根据分词检索词条列表 3、根据词条列表对应的文档id 查询数据 4、将符合条件的输入存入结果集
id | title | price |
---|---|---|
华为电脑 | $1600 | 9999 |
华为手机 | $12 | 999 |
导管 | $1 | 9 |
词条 | 文档id |
---|---|
电脑 | 1 |
手机 | 2 |
导管 | 3 |
华为 | 1、2 |
-
3、名词解释:
- 索引:Index 类似数据库的表
- 文档:Document 类似数据库的一行数据
- 字段:Field 类似数据库的字段
- 映射:Mapping 类似数据库字段的约束
- DSL:json格式的请求语句,类似SQL语句
-
4、简单使用
(1)停用某些分词:修改ik分词器目录中的config–IkAnalyzer.cfg.xml文件,在IK Analyzer 扩展配置中,ext_dict配置自己的扩展字典;ext_stopwords配置自己扩展停用词字典
(2)mapping属性- type:字段数据类型
- 字符串 text(可分词的文本)、keyword(精确值,不分词)
- 数值 long、integer、short、byte、double、float
- 布尔 boolean
- 日期 date
- 对象 object
- index:是否创建索引,默认为true
- analyzer:指定分词器
- properties:该字段的子字段
(3)操作索引库
- type:字段数据类型
# 创建索引库
PUT /索引库名称
{
"mappings": {
"properties": {
"字段名1":{
"type": "text",
"analyzer":"ik_smart"
},
"字段名2":{
"type": "keyword",
"index":"false"
},
"字段名3":{
"properties": {
"子字段": {
"type": "keyword"
}
}
},
# 省略
}
}
}
# 查看索引库
GET /索引库名
# 删除索引库
DELETE /索引库名
# 修改索引库
PUT /索引库名/_mapping{
"properties":{
"新字段名":{
"type": "integer"
}
}
}
(4)操作文档
# 新增文档 (文档id不能省)
POST /索引库名/_doc/文档id{
"字段1": "值1",
"字段2": "值2",
"字段3": {
"子属性1":"值3",
"子属性2":"值4"
},
}
# 查看文档
GET /索引库名/_doc/文档id
# 删除文档
DELETE /索引库名/_doc/文档id
# 修改文档 1、全量修改 2、增量修改
PUT /索引库名/_doc/文档id{
"字段1": "值1",
"字段2": "值2",
# 省略
}
POST /索引库名/_update/文档id{
"doc": {
"字段名": "新的值",
}
}
(5)地理坐标类型
- geo_point:由纬度和经度确定一个点"12.1234567, 120.1234567"
- geo_shape:由多个geo_point组成复杂几何图形(12.4567894 23.1234567, 2.7654321 4.7654321)
(6)多参数搜索
- 字段拷贝:使用copy_to属性将当前字段拷贝到指定字段
"all":{
"type": "text",
"analyzer": "ik_max_word"
},
"brand":{
"type": "keyword",
"copy_to": "all"
}
二、查询语句
1、查询分类
- 查询所有:查询所有数据,一般测试使用 match_all
GET /IndexName/_search
{
"query":{
"match_all":{
"查询条件": "条件值"
}
}
}
- 全文检索:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。
- match_query:根据一个字段去查询
- multi_match_query:根据多个字段去查询,查询字段越多,性能越差
GET /indexName/_search
{
"query":{
"match":{
"FIELD":"TEXT"
}
}
}
GET /indexName/_search
{
"query":{
"multi_match":{
"query":"TEXT",
"fields":["FIELD1","FIELD2"]
}
}
}
- 精确查询:利用精确词条值查找数据,一般查找keyword。
- ids、range、term
# 根据term查询
GET /indexName/_search
{
"query":{
"term":{
"FIELD":{
"value": "VALUE"
}
}
}
}
- 地理查询:根据经纬度查询。
- geo_distance:查询到指定中心点小于某个距离值的所有文档
- geo_bounding_box:查询geo_point值落在某个矩形范围的所有文档
GET /indexName/_search
{
"query":{
"geo_distance":{
"distance":"15km",
"FIELD":"44.5,151.2"
}
}
}
GET /indexName/_search
{
"query":{
"geo_bounding_box":{
"FIELD":{
"top_left":{
"lat"15.2:,
"lon"98.4,
},
"bottom_right":{
"lat"30.5:,
"lon"40.3,
}
}
}
}
}
-
复合查询:将上述各种查询条件组合起来,合并查询条件。
相关性算分 :
TF(词条频率) = 词条出现次数/文档中词条总数 //一个文档的词条频率越高,相关性越高TF-IDF算法
IDF(逆文档频率) = Log(文档总数/包含词条的文档总数)//词条在文档中出现的频率越少说着这个词条的权重越高
s c o r e = ∑ i = 1 n ( T F ∗ I D F ) score =\sum_{i=1}^{n} (TF*IDF) score=i=1∑n(TF∗IDF)
缺点:会随着词频增加而越来越大BM25算法
S c o r e ( Q , d ) = ∑ i = 1 n l o g ( 1 + N − n + 0.5 n + 0.5 ) ∗ f i f i + k 1 ∗ ( 1 − b + b ∗ d l a v g d l ) Score(Q,d) =\sum_{i=1}^{n} log(1+{\frac{N-n+0.5}{n+0.5}}) *{\frac{f~i}{f~i+k~1*(1-b+b*\frac{dl}{avgdl})}} Score(Q,d)=i=1∑nlog(1+n+0.5N−n+0.5)∗f i+k 1∗(1−b+b∗avgdldl)f i
会随着词频增加,逐渐趋于平缓- function_score:算分查询函数,控制文档相关性算分,进而控制文档排序
- BooleanQuery:一个或多个查询子句的组合。子查询的组合方式:必须匹配;选择匹配;必须不匹配,不参与算分;必须匹配,不参与算分【算分会影响性能】
# function_score
GET /hotel/_search
{
"query":{
"function_score":{
"query": {"match": {"all": "外滩"}}, # 原始查询条件,根据打分规则打分
"functions":[
{
"filter": {"term": {"id": "1"}}, # 过滤需要被重新打分的文档
"weight":10 # 算分函数(结果为function score):1、weight 给一个常量值,作为函数结果function score;2、field_value_factor 用文档中某个字段值作为函数结果;3、random_score:随机生成一个值,作为函数结果;4、script_score:自定义计算公式
}
],
"boost_mode": "multiply" # 加权模式,定义function score与query score的运算方式。1、multiply 两者相乘(默认);2、replace 使用function score 代替query score 3、其他:sum、avg、max、min
}
}
}
# BooleanQuery
GET /hotel/_search
{
"query":{
"bool":{
"must":[
{"term": {"city": "上海"}}
],
"should":[
{"term": {"brand": "皇冠假日"}},
{"term": {"brand": "华美达"}}
],
"must_not":[
{"range": {"price": {"lte":500}}}
],
"filter":[
{"range": {"score": {"gte":45}}}
]
}
}
}
2、搜索结果处理
- es支持对搜索结果排序,支持的排序字段:keyword、数值、地理坐标、日期
GET /indexName/_search
{
"query":{
"match_all": {}
},
"sort":[
{
"FIELD": "desc" # 排序字段:排序方式
}
]
}
# 地理坐标排序
GET /indexName/_search
{
"query":{
"match_all": {}
},
"sort":[
{
"_geo_distance": {
"FIELD": "纬度,经度", # 字段:中心点经纬度
"order": "asc"
"unit": "km"
}
}
]
}
- es 通过修改from、size参数来控制要返回的分页结果【搜索页数过深,结果集越大,对内存和CPU的消耗越高】
GET /indexName/_search
{
"query":{
"match_all": {}
},
"from": 990, # 分页开始的位置,默认为0
"size": 10, # 期望获取的分页总数
"sort":[
{"price": "asc"}
]
}
- es 高亮显示:在搜索结果中把搜索关键字突出显示
# 原理:将搜索结果中的关键字用标签标记出来;在页面中给标签添加css样式
GET /hotel/_search
{
"query": {
"match": { # 不能用match_all
"FIELD": "TEXT"
}
},
"highlight": {
"fields":{
"FIELD": { # 指定要高亮的字段
"pre_tags": "<em>", # 标记高亮字段的前置标签
"post_tags": "</em>" # 标记高亮字段的后置标签
}
}
}
}
三 、数据处理
1、数据聚合
- 聚合概念:实现对文档数据的统计、分析、运算,常见有三个分类:
- 桶(Bucket)聚合::用来对文档进行分组(term、date 等等),默认是对所有文档进行聚合
- 度量(Metric)聚合:计算一些值(Avg、Max、Min、Stats)
- 管道(pipeline)聚合:以其他聚合的结果为基础再做聚合
- 注意事项:参与聚合的字段一定不能是分词的字段
# 限定范围的聚合和嵌套聚合
GET /hotel/_search
{
# 查询价格低于200的酒店
"query": {
"range": {
"price": {
"lte" :200
}
}
},
"size":0, # 结果中不包含文档,只包含聚合结果
"aggs": {
"brandAgg": { # 自定义名称
"terms": { # 聚合类型
"field": "brand", # 聚合字段
"size": 20 # 聚合结果的数量
"order": {
"_count": "asc" # 指定显示顺序 asc/desc
}
}
},
# 根据品牌聚合后的结果对每个品牌求最小、最大、平均、求和
"aggs": {
"scoreAgg": { # 自定义名称
"stats": { # 聚合类型
"field": "score" # 聚合字段
}
}
}
}
}
2、自动补全
- 分词器的组成分为三部分:
- character filters : 在tokenizer之前对文本进行处理,例如删除、替换字符
- tokenizer:将文本按照一定的规则切割成词条(term)
- tokenizer filter:将tokenizer输出的词条做进一步处理。大小写转换、同义词处理、拼音处理
PUT /test
{
"settings": {
"analysis": {
"analyzer": { # 自定义分词器
"my_analyzer": { # 分词器名称
"tokenizer": "ik_max_word",
"filter": "py" # 由于py不是规定的过滤器类型,所以会向下找定义 py过滤器的地方
}
},
"filter":{ # 自定义tokenizer filter
"py": { # 过滤器名称
"type": "pinyin", # 过滤器类型
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_original": true,
"limit_first_letter_length": 16,
"remove_duplicated_term": true,
"none_chinese_pinyin_tokenize": false
}
}
}
}
}
- 拼音分词器适合在创建倒排索引时使用,不能在搜索时使用
PUT /test
{
"settings": {
"analysis": {
"analyzer": { # 自定义分词器
"my_analyzer": { # 分词器名称
"tokenizer": "ik_max_word",
"filter": "py" # 由于py不是规定的过滤器类型,所以会向下找定义 py过滤器的地方
}
},
"filter":{ # 自定义tokenizer filter
"py": { # 过滤器名称
"type": "pinyin", # 过滤器类型
.....}
}
}
},
"mappings": {
"properties":{
"name": {
"type": "text",
"analyzer": "my_analyzer", # 创建索引时使用的分词器
"search_analyzer": "ik_smart" # 搜索时使用的分词器
}
}
}
}
四 、结束
内容来源:https://www.bilibili.com/video/BV1Gh411j7d6/?spm_id_from=333.337.search-card.all.click