【超容易理解】Elasticsearch学习笔记

安装

  1. 安装docker,步骤略
  2. 安装elasticsearch
docker pull elasticsearch:7.4.2
  1. 安装kibana
docker pull kibana:7.4.2
  1. 挂载目录,修改目录权限
mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data
echo "http.host: 0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml
chmod -R 777 /mydata/elasticsearch/
  1. 运行es和kibana
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
 -e "discovery.type=single-node" \
 -e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
 -v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
 -v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \
 -v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
 -d elasticsearch:7.4.2
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.56.10:9200 -p 5601:5601 \
 -d kibana:7.4.2

注:启动kibana的ip地址要换成自己的ip

入门

打开192.168.56.10:5601,进入kibana,打开下面的界面,这个界面就是执行api的界面,因为elasticsearch是对 Lucene 的封装,提供了 REST API 的操作接口,在不接触底层原理的情况下,学习es就是针对api的学习。
【超容易理解】Elasticsearch学习笔记_第1张图片

概念介绍

参照mysql中概念一一对应:

elasticsearch mysql
index(索引) database(库)
type(类型) table(表)
document(文档) row(每一行数据)
倒排索引

mysql中的索引是针对字段来的,类似主键索引是对主键id建立一个索引,然后去查数据。

elasticsearch是对导入的数据建立索引,将数据分词,正对每一个词建立索引,搜索的时候包含的词越多相关度也越高。

api学习

在打开的kibana中调试输入,查看效果

_cat 查看信息

#查看所有节点
GET /_cat/nodes
#查看 es 健康状况
GET /_cat/health
#查看主节点
GET /_cat/master
#查看所有索引
GET /_cat/indices

索引一个文档(保存)

#customer是索引,external是类型,1是唯一标识,这个请求的意思就是保存一个数据,内容是下如下
PUT customer/external/1
{ 
	"name": "John Doe"
}

执行返回结果

{
  "_index" : "customer", #表示索引名称
  "_type" : "external",  #表示类型名称
  "_id" : "1",           #文档唯一标识,在PUT方式下需要指定id,在POST方式下不需要指定,会随机生成
  "_version" : 1,		 #版本号,再次点击版本号会累加
  "result" : "created",	 #操作结果,create表示新增,updated表示修改,再次点击就是修改
  "_shards" : {			 #包含关于分片执行的信息,后续介绍
    "total" : 2,		 
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,		 #表示文档的序列号。用于实现乐观并发控制
  "_primary_term" : 1	 #表示文档所在分片的主要分片数。用于实现乐观并发控制
}
#和put一样,就是不需要指定id,会随机生成一个
POST customer/external
{ 
	"name": "John Doe"
}

执行返回结果

{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "i-4HNYwBxXNzdWrbY4el",  #会返回一个随机的id
  "_version" : 1,				   #再次点击不会新增版本号,每次都是新增一个
  "result" : "created",			   #每次点击都是created
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 2,				  #并发控制字段,每次更新就会+1,用来做乐观锁
  "_primary_term" : 1
}
#?if_seq_no=0&if_primary_term=1这一段是指在当前分片中进行并发控制,只有seq_no=0才执行修改
PUT customer/external/1?if_seq_no=0&if_primary_term=1
{ 
	"name": "John Doe"
}
总结

PUT 和 POST 都可以,
POST 新增。如果不指定 id,会自动生成 id。指定 id 就会修改这个数据,并新增版本号
PUT 可以新增可以修改。PUT 必须指定 id;由于 PUT 需要指定 id,我们一般都用来做修改
操作,不指定 id 会报错。

查询文档

GET customer/external/1

执行返回结果

{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 3,
  "_seq_no" : 4,	#并发控制字段,每次更新就会+1,用来做乐观锁
  "_primary_term" : 1,
  "found" : true,
  "_source" : {     #这里面就是之前存入的值
    "name" : "John Doe"
  }
}

更新文档

单纯更新文档
#会对比原来数据与原来一样就什么都不做,version和seq_no都不变
POST customer/external/1/_update
{ 
	"doc":{ 
		"name": "John Doew"
	}
}

或者

#不检查原来的数据,直接更新数据,version和seq_no都会变
POST customer/external/1
{ 
	"name": "John Doe2"
}

或者

#和上一个一样,不检查原来的数据,直接更新数据,version和seq_no都会变
PUT customer/external/1
{ 
	"name": "John Doe"
}
更新时同时增加属性
POST customer/external/1/_update
{ 
	"doc": { 
		"name": "Jane Doe",
		"age": 20 
	}
}

总结:
看场景;
对于大并发更新,不带 update;
对于大并发查询偶尔更新,带 update;对比更新,重新计算分配规则

删除文档&索引

#删除这个索引下的这个类型下的数据
DELETE customer/external/1
#删除整个索引
DELETE customer

批量操作

执行索引批量操作
POST customer/external/_bulk
{"index":{"_id":"1"}}   #一行操作
{"name": "John Doe" }	#一行数据
{"index":{"_id":"2"}}
{"name": "Jane Doe" }

执行返回结果

{
  "took" : 53,					#花费了多少毫秒
  "errors" : false,				#标识没有发生错误
  "items" : [					#多个数据之间相互不影响,及时第一个发生报错也不影响后面的操作
    {
      "index" : {				#index表示新增如果存在就替换,create也表示新增如果存在操作失败,update表示批量更新更新,delete表示批量删除
        "_index" : "customer",	#索引
        "_type" : "external",	#类型
        "_id" : "1",			#唯一标识
        "_version" : 8,			#版本号
        "result" : "updated",	#表示原来存在,批量操作后更新
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 10,
        "_primary_term" : 1,
        "status" : 200
      }
    },
    {
      "index" : {
        "_index" : "customer",
        "_type" : "external",
        "_id" : "2",
        "_version" : 1,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 11,
        "_primary_term" : 1,
        "status" : 201
      }
    }
  ]
}
不指定索引批量操作es
POST /_bulk
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}	#表示删除website索引下blog这个类型id为123的数据
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}	#表示新增website索引下blog这个类型id为123的数据
{ "title": "My first blog post" }	#新增的数据就是这个一行
{ "index": { "_index": "website", "_type": "blog" }}	#存在就替换上面新增的数据
{ "title": "My second blog post" }
{ "update": { "_index": "website", "_type": "blog", "_id": "123"} }	#更新上面新增的数据值为后面一行的内容
{ "doc" : {"title" : "My updated blog post"} }

执行返回结果

{
  "took" : 353,
  "errors" : false,
  "items" : [
    {
      "delete" : {					#表示删除操作
        "_index" : "website",
        "_type" : "blog",
        "_id" : "123",
        "_version" : 1,
        "result" : "not_found",		#表示没有找到要删除的值
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 0,
        "_primary_term" : 1,
        "status" : 404
      }
    },
    {
      "create" : {					#表示新增操作
        "_index" : "website",
        "_type" : "blog",
        "_id" : "123",
        "_version" : 2,
        "result" : "created",		#新增成功
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 1,
        "_primary_term" : 1,
        "status" : 201
      }
    },
    {
      "index" : {					#也是表示新增操作
        "_index" : "website",
        "_type" : "blog",
        "_id" : "ju6BNYwBxXNzdWrbzYfj",
        "_version" : 1,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 2,
        "_primary_term" : 1,
        "status" : 201
      }
    },
    {
      "update" : {					#表示更新操作
        "_index" : "website",
        "_type" : "blog",
        "_id" : "123",
        "_version" : 3,
        "result" : "updated",		#更新了上面由create创建的id为123的数据
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 3,
        "_primary_term" : 1,
        "status" : 200
      }
    }
  ]
}

这里可以去下载批量导入的数据

https://wws.lanzoui.com/iaoMKppmpzc

#这里批量导入bank这个索引account这个类型下数据
POST /bank/account/_bulk
。。。上面链接上下载的数据

【超容易理解】Elasticsearch学习笔记_第2张图片
导入进去后进入下一阶段

进阶

数据查询

两种基本方式检索

  • 一个是通过使用 REST request URI 发送搜索参数(uri+检索参数)
  • 另一个是通过使用 REST request body 来发送它们(uri+请求体)
    分别如下所示
#bank就是之前批量导入进去的索引,_search是固定写法表示查询,?之后都是参数q=*表示查询所有,sort=account_number:asc查询到的数据按照account_number这个字段升序排序
GET bank/_search?q=*&sort=account_number:asc
GET bank/_search
{ 
	"query": { 				#查询条件
		"match_all": {}		#查询所有
	},
	"from":0,				#和mysql中的limit类似,从第几条开始
	"size":5,				#展示多少条
	"sort": [				#排序条件
		{ 
			"account_number": { 
				"order": "desc"	#这个字段升序排序
			}
		},
		{
			"balance":{
				"order":"asc"	
			}
		}
	],
	"_source":["age","balance"]	#表示查询结果只返回这几个字段
}

执行返回结果

{
  "took" : 385,					#执行耗时
  "timed_out" : false,			#是否超时
  "_shards" : {					#分片信息
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {					#命中的记录
    "total" : {
      "value" : 1000,			#表示有多少条记录被匹配到了,虽然这里匹配了1000条但是es只给我们展示了10条类似分页查询
      "relation" : "eq"			#检索的关系,eq就是等于
    },
    "max_score" : null,			#得分,本次查找是查全文不存在相关性得分
    "hits" : [
      {
        "_index" : "bank",
        "_type" : "account",
        "_id" : "0",
        "_score" : null,
        "_source" : {			#当时保存记录的元信息
          "account_number" : 0,
          "balance" : 16623,
          "firstname" : "Bradshaw",
          "lastname" : "Mckenzie",
          "age" : 29,
          "gender" : "F",
          "address" : "244 Columbus Place",
          "employer" : "Euron",
          "email" : "[email protected]",
          "city" : "Hobucken",
          "state" : "CO"
        },
        "sort" : [				#排序,第一个
          0
        ]
      }
    ]
  }
}

匹配查询、短语匹配、多字段匹配(match、match_phrase、multi_match)

GET bank/_search
{
  "query": {
    "match": {							#模糊匹配,只要包含这个字段就展示
      "account_number": "20"			#指定某个字段的值,类似sql中的where后的查询条件
      "address":"Kings"					#可以模糊查询,只要包含Kings的都会查出来
    }"match_phrase":{					#短语匹配,必须包含这个短语才展示
    	"address":"mill road"			#只有包含mill road这个短语才匹配
    },
    "multi_match": { 					#多字段匹配,可以选择多个字段包含指定的值
    	"query": "mill road", 			#表示我要查询包含	mill 或者 road的数据
    	"fields": ["city","address"]	#city字段或者address字段包含mill 或者 road都展示出来
	}
  }
}

执行返回结果

{
  "took" : 9,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.0,				#下面数字中最大的得分
    "hits" : [
      {
        "_index" : "bank",
        "_type" : "account",
        "_id" : "20",
        "_score" : 1.0,				#数字越大表示相关性越高,数据展示就是按照这个字段排序
        "_source" : {
          "account_number" : 20,
          "balance" : 16418,
          "firstname" : "Elinor",
          "lastname" : "Ratliff",
          "age" : 36,
          "gender" : "M",
          "address" : "282 Kings Place",
          "employer" : "Scentric",
          "email" : "[email protected]",
          "city" : "Ribera",
          "state" : "WA"
        }
      }
    ]
  }
}

复合查询(bool),结果过滤(filter)

bool 用来做复合查询:
复合语句可以合并 任何 其它查询语句,包括复合语句,了解这一点是很重要的。这就意味着,复合语句之间可以互相嵌套,可以表达非常复杂的逻辑。

结果过滤filter:
并不是所有的查询都需要产生分数,特别是那些仅用于 “filtering”(过滤)的文档。为了不计算分数 Elasticsearch 会自动检查场景并且优化查询的执行。

GET bank/_search
{ 
	"query": { 
		"bool": { 										#表示复合查询,并且可以相互嵌套
			"must": [									#表示必须匹配
				{"match": {"address": "mill"}}, 
				{"match": {"gender": "M"}},
				{"range": {"age":{"gte":20,"lte":30}}}	#表示查询年龄范围的数据
			],
			"should": [									#表示应该匹配,不匹配也会展示,匹配的话_score会更高
				{"match": {"address": "lane"}}
			],
			"must_not": [								#表示必须不匹配
				{"match": {"email": "baluba.com"}}
			],
			"filter": {									#和must的区分是filter不计算_score相关性得分
			  "range": {
			    "age": {
			      "gte": 20,
			      "lte": 30
			    }
			  }
			}
		}
	}
}

非文本字段查询(term)、(keyword)

GET bank/_search
{
  "query": {
    "term": {		#用于查询精确的值,不适用文本检索,因为文本在存入的时候会进行数据分析(分词等)操作
      "age":20
    }
  }
}
GET bank/_search
{
  "query": {
    "match": {
      "address.keyword":"McDonald"  #字段后面加上.keyword表示只address的值等于McDonald不会模糊查询
    }
  }
}

aggregations(执行聚合)

聚合提供了从数据中分组和提取数据的能力。最简单的聚合方法大致等于SQL GROUPBY 和 SQL 聚合函数。在 Elasticsearch 中,您有执行搜索返回 hits(命中结果),并且同时返回聚合结果,把一个响应中的所有 hits(命中结果)分隔开的能力。这是非常强大且有效的,您可以执行查询和多个聚合,并且在一次使用中得到各自的(任何一个的)返回结果,使用一次简洁和简化的 API 来避免网络往返。

#搜索 address 中包含 mill 的所有人的年龄分布以及平均年龄,但不显示这些人的详情
GET bank/_search
{
  "query": {				#这个先查询出所有address里包含mill的数据
    "match": {
      "address": "mill"
    }
  },
  "aggs": {					#每次需要分组聚合查询的时候都需要加上aggs
    "ageAgg": {				#这个是可以自己命名的
      "terms": {			#是一种聚合方式,表示统计分布,每个年龄占多少
        "field": "age",		#统计age字段
        "size": 10			#限制分组数量,只展示10个分组
      }
    },
    "avgAgg":{				#自己定义的
      "avg": {				#一种聚合方式,求平均值
        "field": "age"		#表示根据age的平均值
      }
    },
    "balanceAgg":{			#自己定义的
      "avg": {				#一种聚合方式,求平均值
        "field": "balance"	#表示根据balance的平均值
      }
    }
  },
  "size": 0					#为0表示不展示搜索出来的数据,仅展示聚合结果,如下
}

执行返回结果

{
  "took" : 19,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 4,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]							#size=0的时候hits就是空的
  },
  "aggregations" : {						#聚合操作后的数据都在这里
    "ageAgg" : {							#自己定义的结果
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : 38,						#表示年龄是38的有2个
          "doc_count" : 2
        },
        {
          "key" : 28,
          "doc_count" : 1
        },
        {
          "key" : 32,
          "doc_count" : 1
        }
      ]
    },
    "balanceAgg" : {						#自己定义的结果
      "value" : 25208.0	
    },
    "avgAgg" : {							#自己定义的结果
      "value" : 34.0
    }
  }
}

再列举一些更复杂的操作

#按照年龄聚合,并且请求这些年龄段的这些人的平均薪资
GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "ageAgg": {					#自己定义的
      "terms": {				#聚合操作,按照age分组取1000个
        "field": "age",
        "size": 1000
      },
      "aggs": {					#在上面的值中继续聚合操作,嵌套在里面
        "ageAvg": {
          "avg": {				#按照balance求平均值
            "field": "balance"
          }
        }
      }
    }
  }
}
聚合操作

聚合操作有很多上面使用了Terms Aggregation和Average Aggregation,其他的可以看看,学习学习

  1. Bucket Aggregations(桶聚合):
  • Terms Aggregation: 将结果分组为桶,每个桶表示具有相同或相似值的文档集合。
  • Date Histogram Aggregation: 根据日期字段将文档分组为时间桶,用于时间序列分析。
  • Histogram Aggregation: 将数值字段的范围划分为桶,每个桶包含特定范围的文档。
  1. Metric Aggregations(度量聚合):
  • Average Aggregation (Avg): 计算数值字段的平均值。
  • Sum Aggregation: 计算数值字段的总和。
  • Min and Max Aggregations: 分别计算数值字段的最小值和最大值。
  • Cardinality Aggregation: 估算唯一值的数量。
  • Stats Aggregation: 提供平均值、总和、最小值、最大值和文档数量的统计信息。
  1. Pipeline Aggregations(管道聚合):
  • Bucket Script Aggregation: 允许使用脚本计算桶内的值。
  • Moving Function Aggregation: 通过应用移动函数计算桶内的度量。
  • Derivative Aggregation: 计算时间序列数据的导数。
  1. Matrix Aggregations(矩阵聚合):
  • Matrix Stats Aggregation: 提供关于矩阵中值的统计信息。
  1. Geo Aggregations(地理聚合):
  • Geo Bounds Aggregation: 计算地理点的边界框。
  • Geo Centroid Aggregation: 计算地理点的中心点。
  1. Scripted Aggregations(脚本聚合):
  • Scripted Metric Aggregation: 使用脚本计算度量。
  • Scripted Bucket Aggregation: 使用脚本定义自定义的桶逻辑。

映射(Mapping)

这个映射就和mysql中的DDL语句类似,用于创建、查看索引的字段的类型
**注意:**只能创建和查看,不能修改已经存在的字段的类型,如果想修改只能用数据迁移

查看字段类型

#查看bank索引下所有字段的类型
GET /bank/_mapping

执行结果返回

{
  "bank" : {							#索引名
    "mappings" : {
      "properties" : {
        "account_number" : {			#字段名
          "type" : "long"				#字段类型
        },
        "address" : {
          "type" : "text",
          "fields" : {					#address字段的子类型
            "keyword" : {				#表示可以使用address.keyword做全词匹配
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "age" : {						#下面都是类似。。。。
          "type" : "long"
        },
        "balance" : {
          "type" : "long"
        },
        "city" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "email" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "employer" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "firstname" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "gender" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "lastname" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "state" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

注:es7及以上移除了type的概念,统一替换成了_doc,也就是你创建一个索引后,默认跟这这个

字段类型

elasticsearch的字段类型有很多,下面列举一二

  1. Text Types(文本类型):
  • Text: 用于索引长文本。支持全文搜索,但不支持排序和聚合。
  • Keyword: 用于索引短文本,通常用于精确匹配、排序和聚合。
  1. Numeric Types(数值类型):
  • Integer: 32 位整数。
  • Long: 64 位整数。
  • Short: 16 位整数。
  • Byte: 8 位整数。
  • Double: 双精度浮点数。
  • Float: 单精度浮点数。
  • Half Float: 半精度浮点数。
  1. Date Types(日期类型):
  • Date: 日期类型,存储日期和时间。
  1. Boolean Type(布尔类型):
  • Boolean: 存储布尔值。
  1. Binary Type(二进制类型):
  • Binary: 存储二进制数据。
  1. Range Types(范围类型):
  • Integer Range: 整数范围。
  • Float Range: 浮点数范围。
  • Long Range: 长整数范围。
  • Double Range: 双精度浮点数范围。
  • Date Range: 日期范围。
  1. Geo Types(地理类型):
  • Geo-point: 存储经纬度信息。
  • Geo-shape: 存储复杂的地理形状。
  1. Specialized Types(专用类型):
  • Ip: 存储 IPv4 或 IPv6 地址。
  • Completion: 用于自动完成建议。
  • Token count: 用于存储分析过的文本中的标记数量。
  1. Join Type(关联类型):
  • Join: 用于处理父子文档关系

新建索引并设置字段类型、新增字段类型

创建映射
PUT /my-index
{ 
	"mappings": { 
		"properties": {
			"age": { "type": "integer" }, 
			"email": { "type": "keyword" }, 
			"name": { "type": "text" }
		}
	}
}
添加新字段的映射
PUT /my-index/_mapping
{ 
	"properties": { 
		"employee-id": { 
			"type": "keyword", 
			"index": false		#默认为true,false表示该字段内容不会被索引,不可搜索
		}
	}
}
更新映射

对于已经存在的映射字段,我们不能更新。更新必须创建新的索引进行数据迁移

数据迁移

可以先创建新的索引

PUT /newbank
{ 
	"mappings": { 
		"properties": {
			"age": { "type": "integer" }, 
			"email": { "type": "keyword" }, 
			"name": { "type": "text" }
		}
	}
}

然后将老的数据迁移到新的索引下

POST _reindex
{
  "source": {
    "index": "bank",		#老的索引名
    "type": "account"		#es7之前有type这里就要写上,如果没有type这里就不写
  },
  "dest": {
    "index": "newbank"		#这里就是新的索引名
  }
}

分词

什么是分词,es在创建索引的时候会对数据内容进行分词,如果你的数据中有大量中文需要检索,使用默认的分词器效果非常不好,因为es是外国佬干出来的,默认的分词工具对中文并不友好,所以需要安装中文的分词器。

IK分词器

因为原文使用的es版本是7.4.2,所以IK分词器的版本也要与之对应,IK分词器下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip

#下载到linux上/mydata/elasticsearch/plugins目录下(因为已经和容器内的目录挂载了)
#没安装unzip的自行安装
unzip elasticsearch-analysis-ik-7.4.2.zip
#切记解压完要删除elasticsearch-analysis-ik-7.4.2.zip,不然后面会报错
rm -f elasticsearch-analysis-ik-7.4.2.zip
#创建ik目录,然后把解压的文件都放进取
mkdir ik
...
#都移入后进入重启es
docker restart elasticsearch
#进入容器内部
docker exec -it elasticsearch /bin/bash
#切换到bin
cd bin
#检查ik是否安装成功
elasticsearch-plugin list
#显示ik说明成功
测试分词
默认分词
#默认分词
POST _analyze
{
  "text": "这个B班谁爱上谁上"
}

执行返回结果

#都是一个一个字拆分的
{
  "tokens" : [
    {
      "token" : "这",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "",
      "position" : 0
    },
    {
      "token" : "个",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "",
      "position" : 1
    },
    {
      "token" : "b",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "",
      "position" : 2
    },
    {
      "token" : "班",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "",
      "position" : 3
    },
    {
      "token" : "谁",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "",
      "position" : 4
    },
    {
      "token" : "爱",
      "start_offset" : 5,
      "end_offset" : 6,
      "type" : "",
      "position" : 5
    },
    {
      "token" : "上",
      "start_offset" : 6,
      "end_offset" : 7,
      "type" : "",
      "position" : 6
    },
    {
      "token" : "谁",
      "start_offset" : 7,
      "end_offset" : 8,
      "type" : "",
      "position" : 7
    },
    {
      "token" : "上",
      "start_offset" : 8,
      "end_offset" : 9,
      "type" : "",
      "position" : 8
    }
  ]
}
ik分词
POST _analyze
{
  
  "analyzer": "ik_max_word",  #这个替换成"analyzer": "ik_smart", 也是可以的
  "text": "这个B班谁爱上谁上"
}

执行返回结果

{
  "tokens" : [
    {
      "token" : "这个",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "b",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "ENGLISH",
      "position" : 1
    },
    {
      "token" : "班",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "CN_CHAR",
      "position" : 2
    },
    {
      "token" : "谁",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "CN_CHAR",
      "position" : 3
    },
    {
      "token" : "爱上",
      "start_offset" : 5,
      "end_offset" : 7,
      "type" : "CN_WORD",
      "position" : 4
    },
    {
      "token" : "谁上",
      "start_offset" : 7,
      "end_offset" : 9,
      "type" : "CN_WORD",
      "position" : 5
    }
  ]
}
自定义分词
#ik分词配置目录
cd /mydata/elasticsearch/plugins/ik/config
#修改IKAnalyzer.cfg.xml
vi IKAnalyzer.cfg.xml  
#可以配置本地字典、远程字段等,这个字典可以是txt文本形式,然后把指定文字输入其中,分词的时候就会根据这个去创建倒排索引了
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
        <comment>IK Analyzer 扩展配置</comment>
        <!--用户可以在这里配置自己的扩展字典 -->
        <entry key="ext_dict"></entry>
         <!--用户可以在这里配置自己的扩展停止词字典-->
        <entry key="ext_stopwords"></entry>
        <!--用户可以在这里配置远程扩展字典 -->
        <entry key="remote_ext_dict">http://192.168.56.10/es/fenci.txt</entry>
        <!--用户可以在这里配置远程扩展停止词字典-->
        <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

额外的logstash(监听数据库变化,导入数据到es)

#下载镜像
docker pull logstash:7.4.2
#新建logstash启动配置文件依旧是mydata文件夹下
mkdir logstash
#然后依次创建如下文件夹并修改权限

在这里插入图片描述

#这个文件夹下的所有文件都加一下权限
chmod -R 777 logstash
#在config下增加logstash.conf、logstash.yml、pipelines.yml
vi logstash.yml
#修改成如下内容
node.name: logstash-1
path.logs: /usr/share/logstash/logs
config.test_and_exit: false
config.reload.automatic: false
config.reload.interval: 60s
config.debug: true
log.level: debug
http.host: 0.0.0.0
######################分割线#######################
vi pipelines.yml
#修改成如下内容
- pipeline.id: main
  path.config: /usr/share/logstash/config/logstash.conf
######################分割线#######################
vi logstash.conf
#修改成如下内容 
input{
	#这个jdbc是做全量更新,表示logstash启动后执行一次,用sql查询语句查出数据全部导入es
	jdbc { 
		#指定mysql驱动位置,这个位置是指docker容器内部的位置
		jdbc_driver_library => "/usr/share/logstash/driver/mysql-connector-java-8.0.30.jar"
		jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
		#监听的mysql地址
		jdbc_connection_string => "jdbc:mysql://192.168.17.98:3306/es_test?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=UTF-8"
		jdbc_user => "root"
		jdbc_password => "root"
		jdbc_paging_enabled => true
		jdbc_page_size => "50000"  
		#全量查询的sql文件保存地址,这个文件里的内容就是一行sql:SELECT * FROM 表名
		statement_filepath => "/usr/share/logstash/pipeline/all.sql"
	}
  
	#这个是增量更新
	jdbc {
		jdbc_driver_library => "/usr/share/logstash/driver/mysql-connector-java-8.0.30.jar"
		jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
		jdbc_connection_string => "jdbc:mysql://192.168.17.98:3306/es_test?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=UTF-8"
		jdbc_user => "root"
		jdbc_password => "root"
		jdbc_paging_enabled => true
		jdbc_page_size => "50000"
		#增量sql文件保存地址,文件内容也是一行sql:SELECT * FROM document WHERE update_time > :sql_last_value
		statement_filepath => "/usr/share/logstash/pipeline/incr.sql"
		#表示开启监听字段的功能
		use_column_value => true
		#表示执行的频率,全*默认就是一分钟一次
		schedule => "* * * * *"
		#指定要监听的数据库字段
		tracking_column => "update_time"
	}
}
output {
	#设置es目标地址
	elasticsearch {
		hosts => ["192.168.56.10:9200"]
		#要导入的索引是哪个
		index => "document"
		#索引id取数据库的哪个字段
		document_id => "%{id}"
	}
	#输出到控制台,使用docker logs logstash可以看到打印的信息
	stdout {
        	codec => json_lines
	}
}

配置好这些后重启docker,在你的表中加入一些数据,然后查看容器日志,再查询索引中的数据是否更新上来

整合springboot

引入依赖,版本最好和你使用的es版本相同

<dependency>
	<groupId>org.elasticsearch.clientgroupId>
	<artifactId>elasticsearch-rest-high-level-clientartifactId>
	<version>7.4.2version>
dependency>

配置类

@Configuration
public class ElasticsearchConfig {

    public static final RequestOptions COMMON_OPTIONS;
    static {
        RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
        COMMON_OPTIONS = builder.build();
    }

    @Bean
    public RestHighLevelClient restHighLevelClient() {
        return new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.56.10", 9200, "http")));
    }
}

测试类

@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
@Slf4j
public class ESTest {

    @Resource
    private DocumentMapper documentMapper;

    @Resource
    private RestHighLevelClient client;

    /**
     * 查询数据库数据批量导入es
     * @throws IOException
     */
    @Test
    public void runTest() throws IOException {
        List<Document> documents = documentMapper.selectList(null);
        BulkRequest bulkRequest = new BulkRequest();
        documents.forEach(document -> {
            bulkRequest.add(new IndexRequest("document").id(document.getId()).source(JSON.toJSONString(document), XContentType.JSON));
        });
        BulkResponse bulk = client.bulk(bulkRequest, ElasticsearchConfig.COMMON_OPTIONS);
        System.out.println(JSON.toJSONString(bulk));


    }


    /**
     * 查询es数据批量导入数据库
     * @throws IOException
     */
    @Test
    public void runTest2() throws IOException {
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("document");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //只取50条数据导入数据库
        sourceBuilder.size(50);
        searchRequest.source(sourceBuilder);
        SearchResponse search = client.search(searchRequest, ElasticsearchConfig.COMMON_OPTIONS);
        System.out.println(JSON.toJSONString(search));
        SearchHit[] hits = search.getHits().getHits();
        for (SearchHit hit : hits) {
            Document document = JSON.parseObject(hit.getSourceAsString(), Document.class);
            documentMapper.insert(document);
        }
    }
}

这里使用官方封装的api便捷操作es

你可能感兴趣的:(随记,elasticsearch,学习,笔记)