ElasticSearch

94 阅读6分钟

Elasticsearch简介

高性能分布式搜索引擎

基于lucene基础架构,使用Java语言开发的一个支持多集群,高亮,快速搜索的数据库

优势:

  • 支持分布式,可水平扩展
  • 提供Restful接口,可被任何语言调用

Elasticsearch倒排索引

基础概念

索引(index):相同类型的文档的集合--类似表

文档(document):每条数据就是一个文档--类似行

词条(term):文档按照语义分成的词语

字段(Field):JSON文档中的字段--类似列

映射(mapping):索引中文档的字段约束--类似表结构约束

DSL:JSON风格的请求语句,定义搜索条件--类似SQL

正向索引(MySQL)

基于文档id创建索引。根据id查询快,但是查询词条时必须先找到文档,而后判断是否包含词条

倒排索引

对文档内容分词,对词条创建索引,并记录词条所在文档的id。查询时先根据词条查询到文档id,而后根据文档id查询文档

IK分词器--中文分词器

允许我们配置拓展词典来增加自定义的词库,有ik_smart(粗粒度的切分模式)和ik_max_word(细粒度切分模式)。

作用:

  • 创建倒排索引时,对文档分词
  • 用户搜索时,对输入的内容分词

索引库与文档操作

索引库

  • 创建索引库:PUT /索引库名
  • 查询索引库:GET /索引库名
  • 删除索引库:DELETE /索引库名
  • 添加字段:PUT /索引库名/_mapping

mapping属性包括:

  • type:字段数据类型,常见的简单类型有:
    • 字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)
    • 数值:long、integer、short、byte、double、float、
    • 布尔:boolean
    • 日期:date
    • 对象:object
  • index:是否创建索引,默认为true
  • analyzer:使用哪种分词器
  • properties:该字段的子字段

JavaRestClient客户端

  1. 创建XxxIndexRequest。XXX是Create、Get、Delete
  2. 准备请求参数( Create时需要)
  3. 发送请求。调用RestHighLevelClient#indices().xxx()方法,xxx是create、get(exists)、delete

文档

  • 创建文档:POST /索引库名/_doc/文档id { json文档 }
  • 查询文档:GET /索引库名/_doc/文档id
  • 删除文档:DELETE /索引库名/_doc/文档id
  • 修改文档:
  • 全量修改:PUT /索引库名/_doc/文档id { json文档 }
  • 增量修改:POST /索引库名/_update/文档id { "doc": {字段}}

JavaRestClient客户端

引入的依赖如下:

xml
 代码解读
复制代码
`<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>6.4.3</version>
</dependency>

<dependency>
  <groupId>org.elasticsearch</groupId>
  <artifactId>elasticsearch-cli</artifactId>
  <version>6.4.3</version>
  <scope>compile</scope>
</dependency>`
  1. 初始化RestHighLevelClient
  2. 创建XxxRequest。XXX是Index、Get、Update、Delete
  3. 准备参数(Index和Update时需要)
  4. 发送请求。调用RestHighLevelClient#.xxx()方法,xxx是index、get、update、delete
  5. 解析结果(Get时需要)

DSL查询

分为两大类:

  • 叶子查询(Leaf query clauses):一般是在特定的字段里查询特定值,属于简单查询,很少单独使用。
  • 复合查询(Compound query clauses):以逻辑方式组合多个叶子查询或者更改叶子查询的行为方式。

在查询以后,还可以对查询的结果做处理,包括:

  • 排序:按照1个或多个字段值做排序
  • 分页:根据from和size做分页,类似MySQL
  • 高亮:对搜索结果中的关键字添加特殊样式,使其更加醒目
  • 聚合:对搜索结果做数据统计以形成报表

叶子查询

全文检索(full text)查询:利用分词器对用户输入内容分词,然后去词条列表中匹配。例如:

// match_query:根据一个字段查询

GET /indexName/_search
{
  "query": {
    "match": {
      "FIELD""TEXT"
    }
  }
}

// multi_match_query:根据多个字段查询,参与查询字段越多,查询性能越差

GET /indexName/_search
{
  "query": {
    "multi_match": {
      "query""TEXT",
      "fields": ["FIELD1"" FIELD12"]
    }
  }
}

精确查询:词条级别的查询,不对用户输入内容分词,直接精确匹配,一般是查找keyword、数值、日期、布尔等类型。例如:

// term:根据词条精确匹配,一般搜索keyword类型、数值类型、布尔类型、日期类型字段

GET /indexName/_search
{
  "query": {
    "term": {
      "FIELD": {
        "value""VALUE"
      }
    }
  }
}

// range:根据数值范围查询,可以是数值、日期的范围

GET /indexName/_search
{
  "query": {
    "range": {
      "FIELD": {
        "gte": 10,
        "lte": 20
      }
    }
  }
}

复合查询

第一类:基于逻辑运算组合叶子查询,实现组合条件,例如: bool布尔查询是一个或多个查询子句的组合。子查询的组合方式有:

  • must:必须匹配每个子查询,类似“与”
  • should:选择性匹配子查询,类似“或”
  • must_not:必须不匹配,不参与算分,类似“非”
  • filter:必须匹配,不参与算分,用于筛选的

第二类:基于某种算法修改查询时的文档相关性算分,从而改变文档排名。(不常用,电商中的广告之类)

排序

Elasticsearch支持对搜索结果排序,默认是根据相关度算分(_score)来排序,也可以指定字段排序。可以排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等

GET /indexName/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "FIELD": "desc"  // 排序字段和排序方式ASCDESC
    }
  ]
}

分页

Elasticsearch 默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数了。通过修改from、size参数来控制要返回的分页结果:

GET /items/_search
{
  "query": {
    "match_all": {}
  },
  "from": 0, // 分页开始的位置,默认为0
  "size": 10, // 期望获取的文档总数
  "sort": [
    {"price": "asc"}
  ]
}

针对深度分页,ES提供了两种解决方案,官方文档: search after:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。官方推荐使用的方式。 scroll:原理将排序数据形成快照,保存在内存。官方已经不推荐使用。

高亮显示

就是在搜索结果中把搜索关键字突出显示。

GET /items/_search
{
  "query": {
    "match": {
      "name": "华为"
    }
  },
  "from": 0, // 分页开始的位置
  "size": 20, // 期望获取的文档总数
  "sort": [ 
    { "price": "asc" }, // 普通排序
  ],
  "highlight": {
    "fields": { // 高亮字段
      "name": {
        "pre_tags": "<em>",  // 高亮字段的前置标签
        "post_tags": "</em>" // 高亮字段的后置标签
      }
    }
  }
}

DSL聚合

统计所有商品中共有哪些商品分类,其实就是以分类(category)字段对数据分组。category值一样的放在同一组,属于Bucket聚合中的Term聚合。

默认情况下,Bucket聚合是对索引库的所有文档做聚合,我们可以限定要聚合的文档范围,只要添加query条件即可。

除了对数据分组(Bucket)以外,我们还可以对每个Bucket内的数据进一步做数据计算和统计。 例如:我想知道手机有哪些品牌,每个品牌的价格最小值、最大值、平均值。

GET /items/_search
{
  "query": {"match_all": {}},  // 可以省略
  "size": 0,  // 设置size为0,结果中不包含文档,只包含聚合结果
  "aggs": { // 定义聚合
    "cateAgg": { //给聚合起个名字
      "terms": { // 聚合的类型,按照品牌值聚合,所以选择term
        "field": "category", // 参与聚合的字段
        "size": 20 // 希望获取的聚合结果数量
      }
    }
  }
}