当前位置:   article > 正文

Elasticsearch(全文搜索引擎)_elasticsearch 搜索引擎底层语言

elasticsearch 搜索引擎底层语言

Elasticsearch

一. 简介

​ Elasticsearch是一个搜索引擎,它底层是基于Lucene来实现的。

Lucene是一个Java库,它存在着一定的问题,如果项目的开发语言不是Java,那么是无法使用 Lucene;Lucene底层设计非常的复杂,需要深入的了解搜索相关的知识,才能用好它。

​ Elaticsearch它是完全基于Restful风格的API来使用,那么就不在局限于任何一门语言,都可以直接调用接口即可实现搜索服务。

二. 倒排索引和前向索引(正排索引)

**分词:**分词就是将一句话或者一段话,按照某个语义拆分为一个个的字或者词的过程。例如:

中华人民共和国

中华  华人 人民  共和国
  • 1
2.1 前向索引

前向索引在数据库领域用的很多,例如 MySQL的全文检索。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6L89unqV-1678946462776)(images/前向索引.jpg)]

2.2 倒排索引

搜索引擎中都是使用这种方式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iR9NMoeR-1678946462778)(images/倒排索引.jpg)]

2.3 总结

​ 前向索引,每个文档都有一个文档的编号,每个文档的编号对应着该文档的分词信息,用户在搜索匹配的时候,根据文档编号找到该文档对应的分词,去进行分词的匹配,如果有匹配的分词,那么该文档就在最终的结果集中,所以它查询的效率会很低,但是其分词构建索引过程较快,因为每个文档的分词都是独立的。

​ 倒排索引,文档在写入搜索引擎的时候,对文档进行分词,将字典中已经存在的分词直接与文档进行映射,没有的分词,先添加到字典中,然后再映射。用户在搜索匹配的时候,直接到分词字典中去匹配,匹配到之后,将映射到该分词的文档提取出来,这种方式查询效率高,可以作相关性算分。

三. ES与Kibana的安装

3.1 ES的安装

Elasticsearch是整个 elastic 产品栈的核心,数据的存储、分词、搜索都是通过 Elasticsearch来完成。

通过 docker安装,直接执行如下命令即可:

docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.8.1
  • 1

验证ES是否安装成功,在浏览器输入:http://ip:9200

3.2 Kibana的安装

Kibana是ES的可视化操作界面,安装方式如下所示:

 docker run -d -p 5601:5601 -e ELASTICSEARCH_HOSTS=http://192.168.24.129:9200 kibana:7.8.1
  • 1

验证Kibana是否安装成功,在浏览器输入: http://ip:5601

3.3 logstash导入数据

要进入到logstash的bin目录下,打开dos命令行,导入数据使用如下的命令:

logstash.bat -f D:/logstash/logstash-7.8.1/config/logstash.conf
  • 1

logstash的内容如下所示:

input {
  file {
    path => "D:/logstash/logstash-7.8.1/movies.csv"
    start_position => "beginning"
    sincedb_path => "D:/logstash/logstash-7.8.1/db_path.log"
  }
}
filter {
  csv {
    separator => ","
    columns => ["id","content","genre"]
  }

  mutate {
    split => { "genre" => "|" }
    remove_field => ["path", "host","@timestamp","message"]
  }

  mutate {
    split => ["content", "("]
    add_field => { "title" => "%{[content][0]}"}
    add_field => { "year" => "%{[content][1]}"}
  }

  mutate {
    convert => {
      "year" => "integer"
    }
    strip => ["title"]
    remove_field => ["path", "host","@timestamp","message","content"]
  }

}
output {
   elasticsearch {
     hosts => "http://192.168.24.129:9200"
     index => "movies"
     document_id => "%{id}"
   }
  stdout {}
}
  • 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

四. ES的基本概念

我们类比 RDBMDS 来熟悉ES的一些基本概念:

RDBMSElasticsearch
数据库索引
type(在ES7之后,一个索引中只有一个type,叫做 _doc)
记录文档(一个JSON对象)
字段属性
schemamapping

索引在ES中三层含义:

  1. 索引,数据集;
  2. 索引,将数据写入到ES的过程叫做索引。
  3. 倒排索引。

五. ES的基本增删改

查看所有的索引

// 查看所有的索引
GET _cat/indices
  • 1
  • 2

添加数据

// 简单的添加数据, 如果没有指定id, 
// 那么ES会自动的生成一个id
POST user/_doc
{
  "firstname": "Elon",
  "lastname": "Musk"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

指定id添加数据

// 指定id添加数据, 如果存在id相同的数据,
// 会直接覆盖原本的数据
POST user/_doc/1
{
  "id": 1,
  "firstname": "Pony",
  "lastname": "Ma"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

添加数据,如果存在就报错

// 添加数据,如果没有对应的id的数据就添加,
// 如果存在这种数据,就报错
POST user/_create/2
{
  "id": 2,
  "firstname": "Will",
  "lastname": "Smith"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

更新文档的结构

// 更新数据
POST user/_update/1
{
  "doc": {
    "firstname": "Kobe",
    "lastname": "Bryant"
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

删除数据

// 删除数据
DELETE user/_doc/2
  • 1
  • 2

根据id查询数据

// 根据id查询
GET user/_doc/1
  • 1
  • 2

查看部分数据

// 查看部分数据
GET user/_search
  • 1
  • 2

查看数据总量

GET movies/_count
  • 1

六. Request Body查询

查询电影名字中包含有 seconds 的所有的电影

GET movies/_search
{
  "query": {
    "match": {
      "title": "seconds"
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

查询电影的名字中包含有 beautiful 或者 mind所有的电影

GET movies/_search
{
  "query": {
    "match": {
      "title": "beautiful mind"
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

查询电影的名字中包含有 beautiful 和 mind所有的电影

GET movies/_search
{
  "query": {
    "match": {
      "title": {
        "query": "mind beautiful",
        "operator": "and"
      }
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

查询电影的名字中包含有 "beautiful mind"这个短语的所有的电影

GET movies/_search
{
  "query": {
    "match_phrase": {
      "title": "beautiful mind"
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

如果某个字段的数据是 keyword 类型,就是做等值比较, 最好使用 term查询(term是标准的比较)

// 查看某个属性的数据类型,就是查看其mapping信息
GET kibana_sample_data_flights/_mapping

GET kibana_sample_data_flights/_search
{
  "query": {
    "term": {
      "Carrier": {
        "value": "ES-Air"
      }
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

查询电影在 [2000, 2002] 年上映的所有的电影

GET movies/_search
{
  "query": {
    "range": {
      "year": {
        "gte": 2000,
        "lte": 2002
      }
    }
  },
  "from": 0,
  "size": 3
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

查询电影的名字中包含有 beautiful 或者 mind,而且上映时间在 [2000, 2003] 所有的电影,多条件查询的结构如下所示:

{
    "query": {
        "bool": {
            "must": [

            ]
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
GET movies/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "beautiful mind"
          }
        },
        {
          "range": {
            "year": {
              "gte": 2000,
              "lte": 2003
            }
          }
        }
      ]
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

查询电影的名字和类型中包含有 Romance 所有的电影

GET movies/_search
{
  "query": {
    "multi_match": {
      "query": "Romance",
      "fields": ["title", "genre"]
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

查询电影名字中包含有 seconds 所有的电影,但是只查询id和title着两个属性的内容

GET movies/_search
{
  "_source": ["id", "title"],
  "query": {
    "match": {
      "title": "seconds"
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

查询电影名字中包含有 seconds 所有的电影,属性都不看

GET movies/_search
{
  "_source": false,
  "query": {
    "match": {
      "title": "seconds"
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

七. 前缀匹配

​ 前缀匹配是一个网站中使用频率非常高的一个功能,而且在添加和删除字符的时候都会向服务器端发送请求,对服务器和ES都是一个挑战,所以ES针对这种前缀匹配,专门设计了一个数据类型 completion。如果某个属性的数据类型是 completion,只能进行前缀匹配,并且ES会将查询的数据放到缓存中,如果接着有其他用户进行相同的前缀匹配的时候,会直接从内存中查找。

​ ES中一旦某个索引中有数据了,我们是不能调整它的 mapping 信息,所以在ES中要向指定某个属性的数据类型的时候,需要先设定mapping.

7.1 设计mapping的思路
  1. 预先构思好索引中要存放什么数据,说白了就是有哪些属性
  1. 利用ES本身的能力(写入数据的时候,会自动创建mapping),写入一个样例数据,让ES帮我们创建好mapping.
  1. 查询mapping,按照我们的需求做出适当的调整
// 查看索引的mapping信息
GET movies/_mapping
  • 1
  • 2
  1. 将之前的索引删掉,将修改之后的mapping添加到ES中。
// 删除索引
DELETE movies

// 添加mapping信息
PUT movies
{
  "mappings" : {
    "properties" : {
      "@version" : {
        "type" : "text",
        "fields" : {
          "keyword" : {
            "type" : "keyword",
            "ignore_above" : 256
          }
        }
      },
      "genre" : {
        "type" : "text",
        "fields" : {
          "keyword" : {
            "type" : "keyword",
            "ignore_above" : 256
          }
        }
      },
      "id" : {
        "type" : "text",
        "fields" : {
          "keyword" : {
            "type" : "keyword",
            "ignore_above" : 256
          }
        }
      },
      "title" : {
        "type" : "completion"
      },
      "year" : {
        "type" : "long"
      }
    }
  }
}
  • 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
  1. 导入正规数据
  1. 验证前缀匹配
GET movies/_search
{
  "_source": false, 
  "suggest": {
    "title_suggest": {
      "prefix": "beau",
      "completion": {
        "field": "title",
        "size": 10
      }
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

八. Analysis分词

​ Analysis是分词,它内部是通过分词器(analyzer)来完成,数据在写入到ES的过程会对文本内容进行分词,用户在搜索的时候,也会对用户搜索的内容再进行分词

​ Analyzer在整个分词的过程中,经过三个流程:

  1. character filter,将内容的信息进行过滤
  2. tokenizer,将全文本进行分词处理
  3. token filter,去掉分词之后的停用词,然后将其他内容转小写

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-suLcQp15-1678946462779)(images/Analyzer.jpg)]

8.1 ES内置的分词器

ES内置的分词器只能对英文进行分词处理,ES内置的分词器有:

  1. whitespace
  2. stop
  3. standard
  4. simple

验证某个分词器的分词效果:

GET _analyze
{
  "analyzer": "stop",
  "text": " Use event filter to 89 drop-events earlier than the specified timestamp in timestamp startup mode"
}
  • 1
  • 2
  • 3
  • 4
  • 5
8.2 IK和pinyin分词器的使用
8.2.1 IK的安装

IK是目前社区用的最多一款中文分词器,它内部包含了两个分词器,ik_smartik_max_word, 下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases

安装的方式:解压到 $ES_HOME/plugins 目录下即可。需要注意的是,要使用一个文件夹将其包裹起来,因为ik分词器解压之后没有文件夹包裹的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nSx5XLwL-1678946462779)(images/ik.jpg)]

ik分词的验证:

GET _analyze
{
  "analyzer": "ik_smart",
  "text": "回顾过往的315,让我们重新审视那些被曝光过的企业,看看消费权益的威力。"
}

GET _analyze
{
  "analyzer": "ik_max_word",
  "text": "回顾过往的315,让我们重新审视那些被曝光过的企业,看看消费权益的威力。"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
8.2.2 pinyin的安装

pinyin就是将中文转换为拼音的方式,下载地址:https://github.com/medcl/elasticsearch-analysis-pinyin/releases?page=7

安装的方式:解压到 $ES_HOME/plugins 目录下即可。pinyin分词器是有文件夹包裹的。

GET _analyze
{
  "analyzer": "pinyin",
  "text": "刘德华"
}
  • 1
  • 2
  • 3
  • 4
  • 5

九. 中文、pinyin前缀搜索

  1. 因为ik、pinyin他们并没有提供的完善的拼音+汉字的前缀搜索,需要在pinyin分词器的基础上定制
  1. 自定义分词器
PUT users
{
  "settings": {
    "analysis": {
      "analyzer": {
        // 自定义一个分词器,名字叫做 name_analyzer
        "name_analyzer": {
          // character_filter, 用的是ES官方提供的character filter
          // html_strip 是官方提供的一个,就是用来去掉 html标签
          "char_filter": "html_strip",
          // tokenizer, keyword 表示不分词
          "tokenizer": "keyword",
          // token_fitler
          "filter": "my_filter"
        }
      },
      "filter": {
        "my_filter": {
          // type: "pinyin" 就表示我们是在pinyin分词器的基础上改
          "type": "pinyin",
          "keep_first_letter": true, 
          "keep_full_pinyin": false,
          "keep_joined_full_pinyin": true,
          "keep_original": true
        }
      }
    }
  }
}
  • 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
  1. 定义mapping
PUT users/_mapping
{
  "properties": {
    "id": {
      "type": "long"
    },
    "name": {
      "type": "completion",
      // 数据写入的时候使用的分词器
      "analyzer": "name_analyzer",
      // 用户在前缀提示的时候,使用的分词器
      "search_analyzer": "keyword"
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  1. 插入数据
POST users/_doc/1
{
  "id": 1,
  "name": "刘德华"
}

POST users/_doc/2
{
  "id": 2,
  "name": "柳宗元"
}

POST users/_doc/3
{
  "id": 3,
  "name": "柳岩"
}
POST users/_doc/4
{
  "id": 4,
  "name": "李思思"
}

POST users/_doc/5
{
  "id": 5,
  "name": "李小路"
}

POST users/_doc/6
{
  "id": 6,
  "name": "李小小"
}
  • 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

插入数据之后,在倒排索引的字典中会有如下的分词结果:

刘德华  
ldh     
liudehua 
柳宗元
liuzongyuan
lzy
柳岩
liuyan
ly
李思思
lisisi
lss
李小路
lxl
lixiaolu
李小小
lxx
lixiaoxiao
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  1. 执行搜索
GET users/_search
{
  "_source": false, 
  "suggest": {
    "name_suggest": {
      "prefix": "liu",
      "completion": {
        "field": "name",
        "size": 10
      }
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

“刘德华”
}

POST users/_doc/2
{
“id”: 2,
“name”: “柳宗元”
}

POST users/_doc/3
{
“id”: 3,
“name”: “柳岩”
}
POST users/_doc/4
{
“id”: 4,
“name”: “李思思”
}

POST users/_doc/5
{
“id”: 5,
“name”: “李小路”
}

POST users/_doc/6
{
“id”: 6,
“name”: “李小小”
}


> 插入数据之后,在倒排索引的字典中会有如下的分词结果:

  • 1
  • 2
  • 3

刘德华
ldh
liudehua
柳宗元
liuzongyuan
lzy
柳岩
liuyan
ly
李思思
lisisi
lss
李小路
lxl
lixiaolu
李小小
lxx
lixiaoxiao


> 5. 执行搜索

```js
GET users/_search
{
  "_source": false, 
  "suggest": {
    "name_suggest": {
      "prefix": "liu",
      "completion": {
        "field": "name",
        "size": 10
      }
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/543400
推荐阅读
相关标签
  

闽ICP备14008679号