ElasticSearch简介
Elaticsearch,简称为es,可以看成一个数据库,可以存储数据、搜索数据
- es是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据
- 本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据,1PB=1024TB
- es也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能
- 但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单
将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。这部分从非结构化数据中提取出的然后重新组织的信息,我们称之索引。
数据库中的数据存储是有规律的,有行有列而且数据格式、数据长度都是固定的,所以 数据库搜索很容易
先建立索引,再对索引进行搜索的过程就叫全文检索。
- 索引创建:将现实世界中所有的结构化和非结构化数据提取信息,创建索引的过程。
- 搜索索引:就是得到用户的查询请求,搜索创建的索引,然后返回结果的过程。
虽然创建索引的过程也是非常耗时的,但是索引一旦创建就可以多次使用,全文检索主要处理的是查询,所以耗时间创建索引是值得的
索引的目的是为了搜索,最终要实现只搜索被索引的语汇单元从而找到Document(文档)。
正常:先找文档,再在文件内容中匹配搜索关键字
倒排索引:根据内容(词语)锁定文档。
倒排索引结构也叫反向索引结构,包括索引和文档两部分,索引即词汇表,它的规模较小,而文档集合较大
ElasticSearch和MySql分工不同,MySQL负责存储数据,ElasticSearch负责搜索数据。
学习的是ElasticSearch的Java客户端的使用,安装较为简便的Window版本
1.1解压elasticsearch-7.8.0-windows-x86_64.zip到 英文且没有空格的目录
1.2安装IK分词器插件:在plugin目录下创建ik文件夹,将elasticsearch-analysis-ik-7.8.0.zip内容解压到ik目录下
1.3启动es服务,双击bin目录下的elasticsearch.bat
1.4 通过浏览器访问ElasticSearch服务器:http://localhost:9200
2.1Postman做ES客户端(废弃不用)
2.2Kibana客户端(Windows版)
2.2.1、解压kibana-7.8.0-windows-x86_64.zip
2.2.2、进入config目录修改kibana.yml第2、28行,配置自身端口和连接的ES服务器地址。
2.2.3、进入kibana的bin目录,双击kibana.bat启动
2.2.4、访问:http://localhost:5601
2.3Elasticsearch head客户端
将ElasticSearch-head-Chrome-0.1.5-Crx4Chrome.crx用压缩工具解压,打开Chrome扩展程序
IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包
1)采用了特有的“正向迭代最细粒度切分算法“,具有60万字/秒的高速处理能力。
2)采用了多子处理器分析模式,支持:英文字母(IP地址、Email、URL)、数字(日期,常用中文数量词,罗马数字,科学计数法),中文词汇(姓名、地名处理)等分词处理。
3)对中英联合支持不是很好,在这方面的处理比较麻烦.需再做一次查询,同时是支持个人词条的优化的词典存储,更小的内存占用。
4)支持用户词典扩展定义。
在没有使用IK分词器时,默认是standard方式分词,这种方式分词就是一个字就是一个词。
IK分词器的俩种模式
ik_max_word
会将“乒乓球明年总冠军”拆分为:乒乓球、乒乓、球、明年、总冠军、冠军【可将下面代码运行在
http://localhost:5601/app/kibana#/dev_tools/console】
- #方式一ik_max_word
- GET /_analyze
- {
- "analyzer": "ik_max_word",
- "text": "乒乓球明年总冠军"
- }
ik_smart(粗粒度分词)
会将“乒乓球明年总冠军”拆分为乒乓球、明年、总冠军
可运行在这个里测试; http://localhost:5601/app/kibana#/dev_tools/console
- #方式二ik_smart
- GET /_analyze
- {
- "analyzer": "ik_smart",
- "text": "乒乓球明年总冠军"
- }
Elasticsearch是面向文档型数据库,一条数据在这里就是一个文档,用JSON作为文档序列化的格式
索引 index
文档存储的地方,类似于MySQL中的数据库。建立"索引"之后,可以直接往 索引 中写入"文档"
字段 Field
字段是ES中JSON数据的键,相当于Mysql数据表的字段
映射 mapping
映射是对文档中每个字段的类型进行定义
mappings 映射相当于表结构
每个文档都有映射,但是在大多数使用场景中,我们并不需要显示的创建映射,因为ES中实现了动态映射
string类型由text和keyword类型替代
- text 类型,需要分词则设置text类型,比如产品描述
- keyword类型 ,不需要分词则设置keyword类型,比如email地址
文档 document
在MySQL中插入一行数据 和 ES中插入一个JSON文档是一个意思
{ "name":"", "age":18, "gender":1 }一个文档不只有数据。它还包含了元数据(metadata)——关于文档的信息。三个必须的元数据节点是
节点
说明
_index
文档存储的地方
_type
文档代表的对象的类
_id
文档的唯一标识
- # 创建索引
- PUT person
- # 查询索引
- GET person
- # 删除索引
- DELETE person
-
- # 查询映射
- GET person/_mapping
-
- # 添加映射(创建索引库再添加映射)
- PUT person/_mapping
- {
- "properties":{
- "name":{
- "type":"keyword"
- },
- "age":{
- "type":"integer"
- }
- }
- }
- # ------------------------------------------------
- # 创建索引并添加映射
- PUT person
- {
- "mappings": {
- "properties": {
- "name":{
- "type": "keyword"
- },
- "age":{
- "type":"integer"
- }
- }
- }
- }
-
- # 索引库中添加字段
- PUT person/_mapping
- {
- "properties":{
- "address":{
- "analyzer": "ik_max_word",
- "type":"text"
-
- }
- }
- }
- # 创建索引并添加映射
- PUT person
- {
- "mappings": {
- "properties": {
- "name":{
- "type": "keyword"
- },
- "age":{
- "type":"integer"
- }
- }
- }
- }
-
- # 索引库中添加字段
- PUT person/_mapping
- {
- "properties":{
- "address":{
- "type":"text"
- }
- }
- }
-
- #------------文档操作----------------------
- # 查询索引
- GET person
-
- # 添加文档,指定id
- PUT person/_doc/1
- {
- "name":"太一",
- "age":20000,
- "address":"太阳星"
- }
-
- # 查询文档
- GET person/_doc/1
-
- # 添加文档,不指定id,自动生成id
- POST person/_doc/
- {
- "name":"帝俊",
- "age":20001,
- "address":"太阳星"
- }
-
- # 查询所有文档
- GET person/_search
-
-
- # 查询文档
- GET person/_doc/Rh8kOoIB_EsGPygii4_Y
-
-
- # 删除文档
- DELETE person/_doc/1
-
- # 修改文档 根据id修改 ,相当于把原来的删除,再把这个新添加进去。
- #即如果修改的时候只有一个属性,那么修改后也就只有一个属性
- PUT person/_doc/1
- {
- "name":"东皇太一",
- "age":20000,
- "address":"太阳星"
- }
3.1全文查询-match查询
先执行GET person/_search,查询person索引库下所有文档,可得结果
- "hits" : [
- {
- "_index" : "person",
- "_type" : "_doc",
- "_id" : "Rh8kOoIB_EsGPygii4_Y",
- "_score" : 1.0,
- "_source" : {
- "name" : "帝俊",
- "age" : 20001,
- "address" : "太阳星"
- }
- },
- {
- "_index" : "person",
- "_type" : "_doc",
- "_id" : "1",
- "_score" : 1.0,
- "_source" : {
- "name" : "羲和",
- "age" : 20000,
- "address" : "太阴星"
- }
- }
- ]
match查询
会分析查询条件,先将查询条件进行分词,然后查询,求并集
# match 先会对查询的字符串进行分词,在查询,求并集 GET person/_search { "query": { "match": { "address": "太阳星" } } }查询的结果还是很意外的,太阳星和太阴星都查到了,因为会先分词查询,再取并集
3.2 term查询-条件部分词
词条查询不会分析查询条件(即查询条件不分词,归结为精确查找),只有当词条和查询字符串完全匹配时才匹配搜索
- GET person/_search
- {
- "query": {
- "term": {
- "address": {
- "value": "太阳星"
- }
- }
- }
- }
遗憾的是,上面并未查找出来,出乎意料了吧
根据address字段,建立倒排索引时,需要对其分词,产生多个词条,而词条集合中没有"太阳星"的词条,故而查询不到数据,
如果你这么查,就不一样了,结果会把帝俊和太一查出来,也是意料之中
GET person/_search { "query": { "term": { "address": { "value": "太阳" } } } }
3.3 DSL查询
索引库renzu中内容如下
3.3.1 根据年龄查询
- #根据年龄查询
- GET renzu/_search
- {
- "query":{
- "match":{
- "age":20
- }
- }
- }
3.3.2 查询年龄大于20岁的女性
- #bool查询表示多条件,must和filter都表示且的关系
- GET renzu/_search
- {
- "query":{
- "bool":{
- "filter":{
- "range":{
- "age":{
- "gt":20
- }
- }
- },
- "must":{
- "match":{
- "sex":"女"
- }
- }
- }
- }
- }
3.3.3 一个字段匹配俩次找不同的值
- GET renzu/_search
- {
- "query":{
- "match":{
- "name": "张三 李四"
- }
- }
- }
3.4 高亮显示
- # 搜索条件高亮显示
- GET renzu/_search
- {
- "query":{
- "match":{
- "name": "张三 李四"
- }
- },
- "highlight": {
- "fields": {
- "name": {}
- }
- }
- }
结果是多一点标签一样的东西
- "highlight" : {
- "name" : [
- "<em>张em><em>三em>"
- ]
- }
3.5聚合
aggs:翻译为groupby,默认使用count
- #term 可以换为avg,max
- GET renzu/_search
- {
- "aggs": {
- "all_ages": {
- "terms": {
- "field": "age"
- }
- }
- }
- }
"all_ages"名称是任意的。对应如下
3.6指定响应字段
- #指定响应的字段
- GET renzu/_doc/1001?_source=id,name
等价于
- #指定响应的字段
- GET renzu/_search
- {
- "query": {
- "match": {
- "id": "1001"
- }
- },
- "_source": ["id","name"]
- }
3.7判断文档是否存在
如果我们只需要判断文档是否存在,而不是查询文档内容,那么可以这样:
HEAD renzu/_doc/1001
存在返回:200 - OK
不存在返回:404 – Not Found
3.8批量查询
有些情况下可以通过批量操作以减少网络请求。如:批量查询、批量插入数据。
- #批量查询 等价于in()
- POST renzu/_doc/_mget
- {
- "ids" : [ "1001", "1003" ]
- }
-
- #1006 这条数据不存在,则found的值是false
- POST renzu/_doc/_mget
- {
- "ids" : [ "1001", "1006" ]
- }
3.9_bulk操作
在Elasticsearch中,支持批量的插入、修改、删除操作,都是通过_bulk的api完成的。
批量插入数据:
# 批量插入数据 POST _bulk {"create":{"_index":"renzu","_id":2001}} {"id":2001,"name":"name1","age": 21,"sex": "男"} {"create":{"_index":"renzu","_id":2002}} {"id":2002,"name":"name2","age": 22,"sex": "男"} {"create":{"_index":"renzu","_id":2003}} {"id":2003,"name":"name3","age": 23,"sex": "女"}
批量删除:
由于delete没有请求体,所以,action的下一行直接就是下一个action。
#批量删除 POST _bulk {"delete":{"_index":"renzu","_id":2001}} {"delete":{"_index":"renzu","_id":2002}} {"delete":{"_index":"renzu","_id":2003}}
3.10 分页
Elasticsearch接受 from 和 size 参数:
size: 结果数,默认10
from: 跳过开始的结果数,默认0
- #跳过2个,显示2个
- GET renzu/_search?size=2&from=2
也可以这么写
- #跳过2个,显示2个
- POST renzu/_search
- {
- "query" : {
- "match_all": {}
- },
- "from": 2,
- "size": 2
- }
3.11 terms查询
terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去做匹配。老师说了,都不会分词。。(我也没验证过对不对)
- #批量查询 等价于in()
- POST renzu/_doc/_mget
- {
- "ids" : [ "1001", "1003" ]
- }
-
-
- POST renzu/_search
- {
- "query" : {
- "terms" : {
- "id" : [1001,1003]
- }
- }
- }
3.12 range查询
range 过滤允许我们按照指定范围查找一批数据
范围操作符包含:
gt 大于 gte 大于等于 lt 小于 lte 小于等于
- #按照指定范围查找一批数据
- POST renzu/_search
- {
- "query": {
- "range": {
- "age": {
- "gte": 20,
- "lte": 30
- }
- }
- }
- }
3.13 exits查询
exists 查询可以用于查找文档中是否包含指定字段或没有某个字段
- # "exists": 是否包含指定字段
- POST renzu/_search
- {
- "query": {
- "exists": {
- "field": "email"
- }
- }
- }
没有命中的结果如下:
一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能。集群默认名“elasticsearch”
一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能。
一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中
ElasticSearch提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。