ElasticSearch学习

ES

从海量数据中快速找到所需内容

ElasticSearch结合kibana,Logstash,Beats,也就是ELK技术栈,被广泛用于日志数据分析(将日志数据可视化的展示出来),实时监控(项目的运行情况)等领域
在这里插入图片描述

lucene:Java语言搜索引擎,也就是个jar包,es是基于它做的二次开发

  • 易扩展
  • 高性能:(基于倒排索引)

概念

**文档:**es是面向文档存储的,可以是数据库中的一条商品数据,一个订单信息

文档数据会被序列化成json格式存在es中

**索引:**相同类型的文档的集合,类似于数据库的表

**映射:**索引中文档的约束,类似于数据库的字段类型,可以看成数据库的表结构

**字段:**json文档中的字段,类似于数据库中的列

**DSL:**es提供的json风格的请求语句,操作es,实现crud

Mysql擅长事务类型操作,确保数据安全和统一性

es擅长海量数据搜索,分析

倒排索引

  • 文档:每条数据就是一个文档
  • 词条:文档按照语义分成的词语

词条是唯一的,如果数据中包含这个词条,就将它的文档id,记录进去
在这里插入图片描述

先找到词条再找到文档,而传统的mysql搜索先找到文档再找词条,所以叫倒排索引

分词器

创建倒排索引时对文档分词

用户搜索时,对输入的内容分词

IK分词器模式:ik_smart

IK分词器拓展词条:ik_max_word

IK分词器停用词条:修改config目录下的IKAnalyzer.cfg.xml

索引库操作

mapping属性

字段的属性

  • type:字段数据类型
    • 字符串:text(可分词文本),keyword(精确值,如品牌,国家,ip)
    • 数值:和java中差不多
    • 布尔:boolean
    • 日期:date
    • 对象:object
    • 没有数组类型,但是允许一个字段多个值
    • 支持两种地理坐标数据类型:
    • geo_point:由纬度和经度确定的一个点,如“32.231321,64.561551”
    • geo_shape:由多个geo_point组成的复杂几何图形
  • index:是否创建索引,默认true
  • analyzer:使用哪种分词器
  • properties:子字段(对象嵌套时使用)

创建索引库

es中通过RestFul请求操作索引库和文档,请求内容用dsl语句表示

示例:

PUT /myes 
{
  "mappings": {
    "properties": {
      "info": {
        "type":"text",
        "analyzer": "ik_smart"
      },
      "emali": {
        "type": "keyword",
        "index": false
      },
      "name": {
        "type": "object",
        "properties": {
          "firstName": {
            "type":"keyword"
          },
          "lastName":{
            "type":"keyword"
          }
          
        }
      }
    }
  }
}

查看删除索引库

get /索引库名
delete /name

es是禁止修改索引库的,因为会基于mapping创建索引,一旦修改,会导致原本索引完全失效但是可以添加新字段

PUT /索引库名/_mapping
{
  "properties":{
    "新字段": {
      "type":""
        ……
    }
  }
}

文档操作

添加

POST /索引库名/_doc/文档id (如果不填就会自动生成)
{
  "age":12,
  "tall":13,
  "email":"46461651@qq.com",
  "info":"白片白嫖学java,华为云享智能",
  "name":{
    "firstName":"赵",
    "lastName":"云"
  }
   "字段名":}
查看删除
GET /索引库名/_doc/文档id
GET /索引库名/_search
DELETE /索引库名/_doc/文档id
修改
  1. 全量修改,删除旧文档,添加新文档

    PUT /索引库名/_doc/文档id(如果这个id根本不存在,就直接新增)
    {
      "age":12,
      "tall":13,
      "email":"46461651@qq.com",
      "info":"白片白嫖学java,华为云享智能",
      "name":{
        "firstName":"赵",
        "lastName":"云"
      }
       "字段名":}
    
  2. 增量修改,修改指定字段值

    POST /索引库名/_update/文档id
    {
      "doc": {
        "要修改的字段":修改的值
        "age":30,
        "tall":99,
        "email":"zhaoyun@qq.com"
      }
    }
    

RestClient操作索引库

操作es的客户端,本质就是组装dsl语句,通过http请求发送给es

如果用户输入一行数据,希望这一行数据能够根据多个字段搜索,但是明显根据一个字段搜性能更好

为了解决这个问题,es提供了字段拷贝的功能

使用copy_to属性将当前字段拷贝到指定字段,es会根据这个字段创建全新索引

  1. 引入依赖

    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>elasticsearch-rest-high-level-client</artifactId>
    </dependency>
    
  2. 统一es版本和服务端相同

    <properties>
        <java.version>1.8</java.version>
        <elasticsearch.version>7.12.1</elasticsearch.version>
    </properties>
    
  3. 初始化客户端

    RestHighLevelClient restHighLevelClient = new RestHighLevelClient(RestClient.builder(
            HttpHost.create("http://81.70.144.36:9200")
    ));
    

RestClient操作文档

从数据库中查询酒店数据,导入到索引库

DSL查询语法

在这里插入图片描述

基本语法

GET /索引库名/_search
{
  "query": {
    "查询类型": {
        “field”:"text",
        …………
    }
  }
}

精确查询

一般查找keyword的字段,不会对搜索条件分词,常见的有

  • term:根据词条精确值查询
  • range:根据值的范围查询可以是数值,日期的范围

地理查询

根据经纬度查询

  • geo_bounding_box:查询geo_point值落在某个矩形范围的所有文档

  • geo_distance:查询指定中心点小于某个距离值的所有文档

    GET /索引库名/_search
    {
      "query": {
        "geo_distance":{
          "distance":"5km",
          "表示经纬度的字段":"31.21,121.5"
        }
      }
    }
    

复合查询

将简单查询组合起来,实现更复杂的搜索逻辑

FunctionScoreQuery

在match查询中,文档的相关性算分影响文档排名。

算分是消耗性能的操作

在这里插入图片描述

function score query:算分函数查询,可以修改文档的相关性算分,得到新的算分排序

在这里插入图片描述

BooleanQuery

将多个查询组合在一起形成新的查询

  • must:与
  • should:或
  • must_not:必须不匹配,不参与算分,非
  • filter:必须匹配,不参与算分

例如:

GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {
          "name": "如家"
        }}
      ],
      "must_not": [
        {"range": {
          "FIELD": {
            "gte": 400
          }
        }}
      ],
      "filter": [
        {"geo_distance": {
          "distance":"10km",
          "location":"31.21,121.5"
        }}
      ]
    }
  }
}

搜索结果处理

排序

默认根据算分来排序,可以排序的字段有keyword类型,数值类型,地理坐标类型,日期类型

如果我们自定义排序方式,es就会放弃打分

GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {
          "name": "如家"
        }}
      ],
        "filter": [
        {"geo_distance": {
          "distance":"10km",
          "location":"31.21,121.5"
        }}
      ]
    }
  },
  "sort": [
    {
      "price": "asc",
      "score": "desc",
      "_geo_distance": {
        "location": {
          "lat": 40,
          "lon": -70
        },
        "order": "asc",
        "unit": "km"
      }
    }
  ]
}
分页

es默认只返回top10的数据,想要获取更多需要修改分页参数

GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "from": 990, //分页开始的位置
  "size": 10 //期望获得文档总数
}

es分页的底层原理是如上面这个例子,先排序获取前1000条文档,在截取990~1000条文档

对于es集群,他只能将所有数据分片的前1000条文档拿出来,再做一个排序,再截取990~1000的文档,当分片多了的时候,对内存消耗是很大的,所以为了避免搜索页数过深,或者结果集(from+size)过大,es设定结果集查询上限是10000

搜索页数深的解决方案是after search 和 scroll

  • from+size:
    • 优点:支持随机翻页
    • 缺点:深度分页问题,默认from+size上限10000
  • after search:
    • 无查询上限
    • 只能向后逐页查询,不支持随机翻页
    • 手机向下滚动翻页
  • scroll:
    • 无查询上限
    • 通过快照,内存消耗大,搜索结果不是实时的
高亮
  • 将搜索结果中的关键字用标签标记出来
  • 在页面中给标签添加css样式

默认情况下,es搜索字段必须和高亮字段一致

如果不一致(如搜索字段是个all,all中包含了好几个字段,但是要显示高亮的只有一个字段),需要添加require_field_match=false

GET /hotel/_search
{
  "query": {
    "term": {
      "city": {
        "value": "北京"
      }
    }
  },
  "highlight": {
    "fields": {
      "city": {
        "pre_tags": "<em>",
        "post_tags": "</em>" //这两个不写,默认标签就是em
      }
    }
  }
}

RestClient查询文档

org.elasticsearch.index.query.QueryBuilders中提供了各种各样的查询

request.source()中提供了查询和对搜索结果的处理,可以理解成dsl查询语句的最外层的大json

以match_all查询为例

SearchRequest request = new SearchRequest("hotel");
request.source().query(QueryBuilders.matchAllQuery());
SearchResponse re = client.search(request, RequestOptions.DEFAULT);
//解析结果
SearchHits hits = re.getHits();
long value = hits.getTotalHits().value;
System.out.println("总条数:"+value);
SearchHit[] res = hits.getHits();//得到结果数组
for (SearchHit hit : res) {
    String json = hit.getSourceAsString();
    System.out.println(json);
}

数据聚合

聚合可以实现对文档数据的统计,分析,运算,可视化

聚合的字段类型一定是不分词的

常见的有三类

  • 桶(bucket)聚合:用来对文档分组
    • TermAggregation:按照文档字段分组
    • Date Histogram:按照日期阶梯分组,例如一周为一组,一年为一组
  • 度量(Metrics)聚合:
    • Avg
    • Max
    • Min
    • Stats:同时求max,min,avg,sum等
  • 管道聚合:其他聚合的结果为基础做聚合

DSL实现Bucket聚合

# 聚合
GET /索引库名/_search
{
  "size": 0, //显示文档数量,为0表示不想看文档,只看聚合结果
  "aggs": {
    "brands": { //桶聚合名称,任意起
      "terms": { //根据字段聚合的字段类型
        "field": "brand", //根据哪个字段聚合
        "size": 20 //要显示桶聚合的数量
      }
    }
  }
}

聚合结果排序

在这里插入图片描述

默认,Bucket聚合会统计Bucket内文档数量,记为_count,并且按照 _count降序排序

可以修改排序方式

GET /hotel/_search
{
  "size": 0,
  "aggs": {
    "brands": {
      "terms": {
        "field": "brand",
        "size": 20,
        "order": { //加一个order属性即可
          "_count": "asc"
        }
      }
    }
  }
}
限定聚合范围

默认情况下,是对索引库中所有文档做聚合,当库中数据特别多时,对内存消耗特别大,我们可以限定聚合文档范围,只要添加query条件即可。

GET /hotel/_search
{
  "size": 0,
  "query": {
    "range": {
      "price": {
        "gte": 100,
        "lte": 200  //只对价格在100~200的文档做聚合
      }
    }
  }, 
  "aggs": {
    "brands": {
      "terms": {
        "field": "brand",
        "size": 20
      }
    }
  }
}

DSL实现Metrics聚合

GET /hotel/_search
{
  "size": 0,
  "aggs": {
    "brandAgg": {
      "terms": {
        "field": "brand",
        "size": 20,
        "order": {
          "score_stats.avg": "desc" //按照度量聚合的avg排序
        }
      },
      "aggs": {    //这里是对每个桶中的score做统计,所以要放到桶里面
        "score_stats": { //聚合名称
          "stats": { //聚合类型
            "field": "score" //根据哪个字段聚合
          }
        }
      }
    }
  }
}

RestClient实现聚合

Snipaste_2024-04-24_17-13-01

搜索页面中的品牌,城市等信息不应该在页面写死,应该通过聚合索引库中数据得来

而且,在真正实现聚合时,往往需要添加一些过滤条件来限制聚合的范围,例如,索引库中文档所属city有上海,杭州,北京,但是此时用户搜索条件为价格区间为100~300,假设城市为北京的根本没有符合这个价格条件的,搜索时就没必要将北京显示在过滤条件中

自动补全

拼音分词器

安装方式和IK分词器一样

  1. 解压
  2. 上传到es的plugin目录
  3. 重启es

自定义分词器

拼音分词器会将词一个字一个字的转成拼音,而且没有中文,所以我们还要修改拼音分词器的配置

es中的分词器组成包含三部分

  • character filters:在tokenizer之前对文本进行处理,例如删除字符,替换字符
  • tokenizer:将文本按照一定规则切割成词条
  • tokenizer filter:将tokenizer输出的词条做进一步处理,例如大小写转化,同义词处理,拼音处理等

可以在创建分词库时,通过setting来配置自定义的analyzer(分词器)

PUT /test
{
  "settings": {
    "analysis": {
      "analyzer": {  //自定义分词器
        "my_ana":{  //自定义分词器的名称
          "tokenizer": "ik_max_word",
          "filter":"py"
        }
      },
      "filter": {  //自定义filter
        "py":{ //filter名称
          "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
        }
      }
    }
  }
}

在这里插入图片描述

但是拼音分词器只适合在创建索引库时使用,为了避免搜索时搜到同音字,搜索一般用ik分词器,所以我们还需要指定搜索分词器

"mappings": {
    "properties": {
      "name":{
        "type": "text",
        "analyzer": "my_ana",
        "search_analyzer": "ik_smart" //指定搜索分词器
      }
    }
  }

DSL自动补全查询

es中提供了Completion Suggester查询来实现自动补全功能,这个查询会匹配用户输入内容开头的词条并返回,为了提高补全查询效率,对于文档中字段的类型有一些约束

  • 参与补全查询的字段类型必须是completion类型
  • 字段值得是多词条的数组
// 自动补全查询
GET /索引库/_search
{
  "suggest": {
    "title_suggest": {
      "text": "s", // 关键字
      "completion": {
        "field": "title", // 补全字段
        "skip_duplicates": true, // 跳过重复的
        "size": 10 // 获取前10条结果
      }
    }
  }
}

数据同步

解决es和mysql数据一致性问题

微服务应用中,es搜索和修改数据库数据肯定在不同的服务模块中

  • 方案一:同步调用
    es搜索服务将接口暴露,在管理服务模块数据库新增数据完成后,调用es搜索服务更新es
    • 缺点:耦合,如果一个环节出现问题,整个接口都会报错
  • 方案二:异步通知,结合MQ
    管理服务新增完数据后,向mq发送消息,es搜索服务监听到消息,更新es
    • 依赖mq的可靠性
  • 方案三:监听binlog
    mysql中的binlog默认关闭,一旦开启,每当mysql数据变化,都会记录在binlog中,在用一个中间件监听mysql的binlog,数据发生变更就通知es搜索服务更新es
    • 实现复杂,且开启mysqlbinlog对mysql压力增大
  • 29
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
你好!关于学习Elasticsearch,我可以给你一些指导。Elasticsearch是一个开源的分布式搜索和分析引擎,主要用于快速、实时地存储、搜索和分析大量数据。下面是一些学习Elasticsearch的步骤: 1. 了解基本概念:开始学习Elasticsearch之前,你需要了解一些基本的概念,比如索引(index)、类型(type)、文档(document)、字段(field)等。这将帮助你更好地理解Elasticsearch的工作原理。 2. 安装和配置:根据你的操作系统,你可以从Elasticsearch官方网站下载并安装合适的版本。安装完成后,你需要进行适当的配置,如设置集群名称、分配内存等。 3. 学习REST API:Elasticsearch提供了丰富的REST API,用于与其进行交互。了解如何使用这些API来索引、搜索和删除数据是学习Elasticsearch的重要一步。 4. 索引和搜索数据:学习如何创建索引、添加文档以及执行搜索操作是使用Elasticsearch的关键。掌握查询语法、过滤器、聚合操作等功能可以帮助你更有效地使用Elasticsearch。 5. 数据建模和分析:学习如何设计合适的数据模型和映射,以及如何使用Elasticsearch进行数据分析和可视化是提高你的技能的重要一步。 6. 扩展和优化:学习如何在生产环境中扩展和优化Elasticsearch集群是非常重要的。了解如何分片、复制、调优性能等将帮助你更好地管理和维护你的数据。 7. 学习资源:除了官方文档,还有很多优秀的学习资源可供参考,如书籍、教程和在线课程等。利用这些资源可以更系统地学习和掌握Elasticsearch。 希望这些步骤能对你学习Elasticsearch有所帮助!如果有任何问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值