1. 前言

Elasticsearch (后面简称es) 是一个分布式搜索和分析引擎,是目前全文搜索引擎的首选,它可以快速地搜索、存储和分析庞大的数据。

在公司业务迅猛的发展下,一些业务上的数据量也爆炸式增长,随之而来的是海量数据的查询带来的挑战,需要数量在亿级别的规模下能以秒级甚至毫秒级的速度返回,在搜索引擎中, es是绝大多公司的首选,以下分享的是es7。

2. es与关系型数据库结构对比

Es Mysql 说明
Index Table 索引(Index),是es文档的集合,类似数据库的表(Table)
Document Row 文档(Document), 是es文档中的数据,类似数据库中的行(row)
Field Column 字段(Field), 是es文档中的字段,类似数据库中的列(Column)
Mapping Schema 映射(Mapping), 是es索引中文档的约束,如字段类型的约束,类似数据库中的表结构(Schema )
Query DSL SQL 查询表达式(Query DSL),是es提供的JSON请求语句,实现CRUD

此处分享的是es7,没有type结构

3. es版本中type演变

版本 type 概念的演变情况
5.X 一个 index 下可以创建多个 type
6.X 一个 index 下只能存在一个 type
7.X 直接去除了 type 的概念,就是说 index 不再会有 type
  • 为何要去除 type 的概念?

因为 es 早期设计是直接查考了关系型数据库的设计模式,存在了 type(数据表)的概念。

因为es搜索引擎是基于 Lucene 的,这种 “基因”决定了 type 是多余的。 Lucene 的全文检索功能之所以快,是因为 倒序索引 的存在。

而这种 倒序索引 的生成是基于 index 的,而并非 type。多个type 反而会减慢搜索的速度。

为了保持 Elasticsearch “一切为了搜索” 的宗旨,适当的做些改变(去除 type)也是无可厚非的,也是值得的。

4. Query DSL

查询表达式(Query DSL)是一种非常灵活又富有表现力的 查询语言。 es 使用它可以以简单的 JSON 接口来展现 Lucene 功能的绝大部分。它可以使你的查询语句更灵活、更精确、易读和易调试。

基本Rest命令说明

method url 地址描述
POST localhost:9200/索引名称/类型名称 创建文档(随机文档id)
PUT localhost:9200/索引名称/类型名称/文档id 创建/更新文档(指定文档id)
GET localhost:9200/索引名称/类型名称文档id 通过文档id查询文档
GET localhost::9200/索引名称/类型名称/_search 查询所有数据
DELETE localhost::9200/索引名称/类型名称/文档id 删除文档
DELETE localhost::9200/索引名称 删除索引
PUT localhost::9200/索引名称 创建索引

es 7.X版本中去除了type的概念,上面类型名称查询可用_doc替代

// 创建或更新(指定文档id)
PUT /student/_doc/1
{
  "name":"小明",
  "age":18
}

// 创建文档(随机文档id)
POST /student/_doc
{
  "name":"小红",
  "age":18
}

// 根据id查询文档
GET /student/_doc/1


// 查询全部文档
GET /student/_search
{

}

// 分页查询文档
GET /student/_search
{
  "from": 0, 
  "size" : 20
}

// 删除文档记录
DELETE /student/_doc/1


// 删除索引
DELETE /student

// 创建索引
PUT /test

5. Mapping

索引字段属性设置

// 设置属性
PUT /recipes
{
  "mappings": {
    "properties": {
      "name":   { "type": "text"  },  
      "rating":    { "type": "integer" },  
      "type":  { "type": "keyword"  }   
    }
  }
}

// 增加字段
PUT /recipes/_mapping
{
  "properties": {
    "num": {
      "type": "integer"
    }
  }
}

// 查看索引的字段
GET /recipes/_mapping

6. 设置默认值

上面索引recipes没有创建时间字段,如何让在插入的时候自动生成一个时间呢

可以借助索引设计层面在 setting 中关联 default_pipeline 实现的。

create_time 借助 pipeline 管道预处理 set processor 实现即可。

PUT _ingest/pipeline/create_time_pipeline
{
  "description": "Adds create_time timestamp to documents",
  "processors": [
    {
      "set": {
        "field": "_source.create_time",
        "value": "{{_ingest.timestamp}}"
      }
    }
  ]
}

DELETE recipes

PUT /recipes
{
 "settings": {
  "index.default_pipeline": "create_time_pipeline"
 }
}


// 增加字段
PUT /recipes/_mapping
{
  "properties": {
    "num": {"type": "integer"},
    "rating": { "type": "integer" },  
    "type": { "type": "keyword"  }   
  }
}

POST recipes/_doc/
{
  "name":"广式鲫鱼汤","rating":5,"type":"粤菜", "num":20
}

GET recipes/_search

此时在添加数据就可以看到出现create_time字段

7. 全文搜索

添加测试数据

先清空刚刚的测试册数

POST recipes/_delete_by_query
{
  "query": {
    "match_all": {}
  }
}

添加数据

POST recipes/_doc/
{
  "name":"清蒸鱼头","rating":1,"type":"湘菜","num":5
}

POST recipes/_doc/
{
  "name":"剁椒鱼头(微辣)","rating":2,"type":"湘菜","num":10
}

POST recipes/_doc/
{
  "name":"剁椒鱼头(辣)","rating":3,"type":"湘菜","num":20
}

POST recipes/_doc/
{
  "name":"剁椒鱼头(超级辣)","rating":3,"type":"湘菜", "num":5
}


POST recipes/_doc/
{
  "name":"红烧鲫鱼","rating":3,"type":"湘菜", "num":5
}


POST recipes/_doc/
{
  "name":"鲫鱼汤(微辣)","rating":4,"type":"湘菜", "num":5
}


POST recipes/_doc/
{
  "name":"广式鲫鱼汤","rating":5,"type":"粤菜", "num":20
}

POST recipes/_doc/
{
  "name":"鱼香肉丝","rating":2,"type":"川菜", "num":20
}

POST recipes/_doc/
{
  "name":"奶油鲍鱼汤","rating":2,"type":"西菜", "num":1
}
  • match
GET recipes/_search
{
  "query" : { "match" : { "name" : "奶" }}
}

es 默认从位置0开始一次返回10条结果,可以通过size字段改变返回数量,from字段改变指定位置。

GET recipes/_search
{
  "from": 1, 
  "size" : 2,
  "query" : { "match" : { "name" : "鱼" }}
}

8. 逻辑运算

  • 如果有多个搜索关键字,es会认为它们是or关系。
GET recipes/_search
{
  "query" : { "match" : { "name" : "奶 广" }}
}
  • 执行多个关键词的and搜索,必须使用布尔查询
// 相当于and
GET recipes/_search
{
  "query" : {
    "bool": {
      "must": [
        {"match" : { "name" : "鱼" }},
        {"match" : { "name" : "奶" }}
      ]
    }
  }
}

GET recipes/_search
{
  "query" : {
    "bool": {
      "must": [
        {"match" : { "name" : "鱼" }},
        {"term": {"num": 20}}
      ]
    }
  }
}

9. 数据折叠

根据上面的索引recipes数据,如果要做一个菜单推荐,搜索关键字带“鱼”的菜,数量最多的排在前面,每页显示3条

GET recipes/_search
{
  "query": {
    "match": {
      "name": "鱼"
    }
  },
  "size": 3,
  "from": 0,
  "sort": { "num": { "order": "desc" }}
}

前面返回的都是同一个菜系的,想要每页不重样的话,可使用collapse

GET recipes/_doc/_search
{
  "query": {
    "match": {
      "name": "鱼"
    }
  },
  "collapse": {
    "field": "type"
  },
  "size": 3,
  "from": 0
}

上面的查询结构每种菜系只饭hi一条数据,如果我想每个菜系多返回几条,可以多点选择

这时可加上参数 inner_hits 来控制返回的条数,这里设置每个菜系最多返回2条,按 rating 也排个序,新的查询构造如下:

GET recipes/_search
{
  "query": {
    "match": {
      "name": "鱼"
    }
  },
  "collapse": {
    "field": "type",
    "inner_hits": {
      "name": "top_rated",
      "size": 2,
      "sort": [
        {
          "rating": "desc"
        }
      ]
    }
  },
  "sort": [
    {
      "rating": {
        "order": "desc"
      }
    }
  ],
  "size": 3,
  "from": 0
}

注意:折叠(collapse)只影响搜索结果,不影响聚合,搜索结果的 total 是所有的命中纪录数,去重的结果数未知(无法计算)

10. Aggregation

10.1. Terms Aggregation

Terms Aggregation 多值聚合,根据库中的文档动态构建桶(桶聚合)

GET recipes/_search
{
  "query": {
    "match": {
      "name": "鱼"
    }
  },
  "collapse": {
    "field": "type"
  },
  "aggs": {
    "group_by_key": {
      "terms": {
        "field": "type"
      }
    }
  },
  "size": 5,
  "from": 0,
  "sort": { "num": { "order": "desc" }}
}

默认情况,对于某字段的聚合,只会返回数量最高的10项,通过调整size参数

size越大越准确,但是超过100个桶后,会很消耗es内存,如果是多个字段嵌套聚合。有可能撑爆内存,引发OOM。

10.2. Cardinality Aggregation

Cardinality Aggregation 基数聚合,可以计算不同值的近似计数

例如统计上述recipes折叠后的总数:

GET recipes/_search
{
  "query": {
    "match": {
      "name": "鱼"
    }
  },
  "collapse": {
    "field": "type"
  },
  "aggs":{
    "total":{
      "cardinality":{
      "field":"type",
      "precision_threshold": 100
      }
    }
  },
  "size": 3,
  "from": 0,
  "sort": { "num": { "order": "desc" }}
}

precision_threshold 控制 Cardinality 算法的精确度和内存消耗,它接受 0–40000 之间的数字,更大的值还是会被当作 40000 来处理,默认值为3000。 如定义一个唯一的计数值,低于改值,准确率几乎100%。 高于改值,es就会开始节省内存而牺牲准确度。

Copyright © yzx该文章修订时间: 2022-12-08 14:57:56

results matching ""

    No results matching ""