中文文档地址:https://www.elastic.co/cn/what-is/elasticsearch
什么是Elastic Search
Elasticsearch 是一个开源的分布式 RESTful 搜索和分析引擎 , 适用于所有类型的数据,包括文本、数字、地理空间、结构化和非结构化数据。 Elasticsearch 以其简单的 REST 风格 API、分布式特性、速度和可扩展性而闻名,是 Elastic Stack 的核心组件;Elastic Stack 是适用于数据采集、充实、存储、分析和可视化的一组开源工具。人们通常将 Elastic Stack 称为 ELK Stack(代指 Elasticsearch、Logstash 和 Kibana),目前 Elastic Stack 包括一系列丰富的轻量型数据采集代理,这些代理统称为 Beats,可用来向 Elasticsearch 发送数据。
基本概念
Index(索引)
Elastic Search中的Index用来存放实际文档,数据的写入和查询是基于索引的。
Type(类型)
type建立在index之下,对index下的文档做虚拟分组;在es7以后版本,不再使用type,所有document文档数据直接存放在index下。
document(文档)
实际存放的数据,json格式文档。
Inverted Index (倒排索引)
通过文档ID,标记文档对应的所有关键词,这种方式是正向索引;而反过来,通过关键词,记录关键词出现过的文档,就是倒排索引。
数据类型
- 基本类型,如 text, keyword, date, long, double, boolean, ip
- 复合类型,如 object, nested
- 特殊类型,如 geo_point, geo_shape, completion.
基本增删改查接口
_cat
_cat/nodes:查看所有节点;
_cat/health:查看es健康状况;
_cat/master:查看主节点;
_cat/indices:查看所有索引;
/_cat/allocation
/_cat/shards
/_cat/shards/{index}
/_cat/master
/_cat/nodes
/_cat/tasks
/_cat/indices
/_cat/indices/{index}
/_cat/segments
/_cat/segments/{index}
/_cat/count
/_cat/count/{index}
/_cat/recovery
/_cat/recovery/{index}
/_cat/health
/_cat/pending_tasks
/_cat/aliases
/_cat/aliases/{alias}
/_cat/thread_pool
/_cat/thread_pool/{thread_pools}
/_cat/plugins
/_cat/fielddata
/_cat/fielddata/{fields}
/_cat/nodeattrs
/_cat/repositories
/_cat/snapshots/{repository}
/_cat/templates
新增
PUT(POST)😕{index}/{type}/{id}: 新增OR更新;消息体是json
PUT:http://192.168.0.207:9200/idx1/user/1
{
"_index": "idx1", // 所属索引
"_type": "user", // 所属类型
"_id": "1", // id
"_version": 1, // 版本
"result": "created", // 返回结果
"_shards": { // 分区信息
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0, // 序列号,常用于乐观锁
"_primary_term": 1 // 主分片
}
POST:/{index}/{type}: 新增,每次自动生成一个id;消息体是json
查询
GET:/{index}/{type}/{id}: 查看索引指定数据。
GET:http://192.168.0.207:9200/idx1/user/1
{
"_index": "idx1",
"_type": "user",
"_id": "1",
"_version": 2,
"_seq_no": 1,
"_primary_term": 1,
"found": true, // 查询结果
"_source": { // 实际查询到的数据
"user_name": "hello es"
}
}
修改
1,基本修改,和新增一致:PUT(POST)😕{index}/{type}/{id}: 新增OR更新;消息体是json
2,POST:_update更新;会检查文档是否需要更新。
POST {index}/{type}/{id}/_update
// http://192.168.0.207:9200/idx1/user/1/_update
// 消息体要带"doc"{"doc": { "user_name": "hello es2"}}
{
"_index": "idx1",
"_type": "user",
"_id": "1",
"_version": 3, // 版本未变
"result": "noop", // 结果是 no operation
"_shards": {
"total": 0,
"successful": 0,
"failed": 0
},
"_seq_no": 2, // seq未变
"_primary_term": 1
}
3,乐观锁更新:
PUT:/{index}/{type}/{id}?if_seq_no={_seq_no}&if_primary_term={_primary_term};消息体是json
// http://192.168.0.207:9200/idx1/user/1?if_seq_no=1&if_primary_term=1
{
"_index": "idx1",
"_type": "user",
"_id": "1",
"_version": 3, // 更新了版本号
"result": "updated", // 结果已更新
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2, // 更新了序列号
"_primary_term": 1
}
删除
DELETE:/{index}/{type}/{id}: 删除指定索引下指定type下指定id的文档。
DELETE:/{index}: 删除指定索引。会删除索引下所有文档。
// http://192.168.0.207:9200/idx1
{
"acknowledged": true
}
批量查询 mget
GET:/{index}/{type}/_mget: 查找指定索引指定类型下的文档。
// http://192.168.0.207:9200/idx1/user/_mget
// 参数体方式1
{
"docs": [
{
"_id": "1",
"_source": ["user_name","age"]
},
{
"_id": "2"
}
]
}
// 参数体方式2
{
"ids":[1,2]
}
GET:/_mget: 查找指定索引指定类型下的文档。
// http://192.168.0.207:9200/_mget
// 参数体
{
"docs": [
{
"_index": "idx1",
"_type": "user",
"_id": "1",
"_source": ["user_name","age"]
},
{
"_index": "idx1",
"_type": "user",
"_id": "2"
}
]
}
批量操作 bulk
bulk的格式:
{action:{metadata}}\n
{requstbody}\n (请求体)
action:(行为),包含create(文档不存在时创建)、update(更新文档)、index(创建新文档或替换已用文档)、delete(删除一个文档)。
create和index的区别:如果数据存在,使用create操作失败,会提示文档已存在,使用index则可以成功执行。
metadata:(行为操作的具体索引信息),需要指明数据的_index、_type、_id。
和mget一致,可以在路径带上index和type直接指向指定索引和类型。
注意设置header请求头: Content-Type : application/json
// 方式一:带index和type
// http://192.168.0.207:9200/idx1/user/_bulk
{"index":{"_id":1}}
{"user_name": "es1","age": 40}
{"index":{"_id":2}}
{"user_name":"es2","age": 41}
// 方式二:不带index和type
// http://192.168.0.207:9200/_bulk
{"index":{"_index":"idx1","_type":"user","_id":1}}
{"user_name": "es1","age": 40}
{"index":{"_index":"idx1","_type":"user","_id":2}}
{"user_name":"es2","age": 41}
检索 _search
可以通过_search发送GET请求,检索文档。通过参数或者请求体指定过滤条件和排序规则。
// GET /bank/_search
// 方式1
// http://192.168.0.207:9200/idx1/_search?q=*&sort=age:desc
// 方式2
// http://192.168.0.207:9200/idx1/_search
{
"query": { "match_all": {} },
"sort": [
{ "age": "desc" }
]
}
// 返回结果:
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": null,
"hits": [
{
"_index": "idx1",
"_type": "user",
"_id": "2",
"_score": null,
"_source": {
"user_name": "es2",
"age": 41
},
"sort": [
41
]
},
{
"_index": "idx1",
"_type": "user",
"_id": "1",
"_score": null,
"_source": {
"user_name": "es1",
"age": 40
},
"sort": [
40
]
}
]
}
}
Query DSL常用查询
基本语法
{
QUERY_NAME:{
ARGUMENT: VALUE,
ARGUMENT:VALUE,…
}
}
常用如下:
// match 分词
GET bank/_search
{
"query": {
"match": {
"address": "mill road"
}
},
"from": 0,
"size": 10,
"_source": ["firstname", "balance"]
}
// match_phrase 当成短语,不分词
GET bank/_search
{
"query": {
"match_phrase": {
"address": "mill road"
}
}
}
// multi_match 多字段匹配;会分词
GET bank/_search
{
"query": {
"multi_match": {
"query": "mill",
"fields": ["address", "city"]
}
}
}
// bool 组合多条件 复合查询
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"gender": "m"
}
},
{
"match": {
"address": "mill"
}
}
],
"must_not": [
{
"match": {
"age": 28
}
}
],
"should": [
{
"match": {
"lastname": "Wallace"
}
}
]
}
}
}
// filter 过滤(must_not)
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"gender": "m"
}
},
{
"match": {
"address": "mill"
}
}
],
"should": [
{
"match": {
"lastname": "Wallace"
}
}
],
"filter": {
"range": {
"balance": {
"gte": 1000,
"lte": 10000
}
}
}
}
}
}
// term (文本字段用must,精确值字段用term;term不做分词处理)
GET /bank/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"age": {
"value": "38"
}
}
},
{
"match": {
"address": "Street"
}
}
]
}
}
}
聚合查询
ES里面,聚合查询就是根据搜索查询提供聚合数据,ES除了提供强大、快速的搜索,也提供了类似数据库的聚合查询分析。ES一次查询,将结果返回,并完成聚合分析。聚合可以进行多个维度,聚合分析可也以嵌套进行,类似多层分组。
聚合包含各种聚合方式,总体分指标聚合和桶聚合。具体各种聚合类型可以参考文档。(https://learnku.com/docs/elasticsearch73/7.3/article-11/6889)
使用样例:
// 聚合查询样例
// 聚合 group by + avg
GET bank/_search
{
"size": 0,
"aggs": {
"group_by_keyword": {
"terms": {
"field": "state.keyword"
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
// 搜索并聚合
GET bank/_search
{
"query": {
"match": {
"address": "mill"
}
},
"aggs": {
"agecount": {
"terms": {
"field": "age",
"size": 10
}
},
"avgbalance":{
"avg": {
"field": "balance"
}
}
},
"size": 0
}
// 查询+聚合+子聚合avg
GET bank/_search
{
"query": {
"match_all": {}
},
"size": 0,
"aggs": {
"group_by_keyword": {
"terms": {
"field": "state.keyword",
"size": 10,
"order": {
"_count": "asc"
}
},
"aggs": {
"avgBalance": {
"avg": {
"field": "balance"
}
},
"genderAgg": {
"terms": {
"field": "gender.keyword"
},
"aggs": {
"avgBalance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
}
}
Mapping 映射
mapping是用来定义存储的文档及其属性是如何存储和索引的。
建索引,同时建mapping
// 新建索引并同步建mapping
PUT newidx1
{
"mappings" : {
"properties" : {
"age" : {
"type" : "integer"
},
"user_name" : {
"type" : "text"
}
}
}
}
给指定index新建mapping
// 已有index,增加mapping
PUT /testidx/_mapping
{
"properties": {
"test_property":{
"type": "keyword",
"index": false
}
}
}
更新索引的mapping
更新索引的mapping信息,只能通过创建新的索引,并对所有数据重新索引(_reindex)。
// reindex
POST _reindex
{
"source": {
"index": "bank"
},
"dest": {
"index": "newbank"
}
}
分词
所谓分词就是将完整的文本信息,分割为独立的词元,然后输出词元流。
ES默认带有很多分词器,但是这些分词器都不支持中文分词。一般使用开源分词器ik。
1,安装ik插件:直接通过官网找到下载.zip文件的地址。然后在linux中通过wget http***方式下载,然后解压到plugs下;重启es即可。
2,测试分词:
// ik_smart
// http://192.168.0.207:9200/_analyze
{
"analyzer": "ik_smart",
"text": "docker 安装ElasticSearch的中文分词器IK"
}
// ik_max_word 对比一下,这种分析器会组合更多的词元
// http://192.168.0.207:9200/_analyze
{
"analyzer": "ik_max_word",
"text": "docker 安装ElasticSearch的中文分词器IK"
}
3,扩展ik词库
ik词库两种模式提供的分词功能都能对中文文本进行分词,但是由于其默认使用的词库不一定满足所有要求,部分词元无法识别,可以通过扩展的方式让其识别。
修改es插件ik的配置文件:IKAnalyzer.cfg.xml
<?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.0.207/words.text</entry>
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
4,指定默认分词器
// 创建索引时,就指定默认分词器
PUT newidx2
{
"mappings" : {
"properties" : {
"age" : {
"type" : "integer"
},
"user_name" : {
"type" : "text"
}
}
},
"settings" : {
"index" : {
"analysis.analyzer.default.type": "ik_max_word"
}
}
}
spring boot 整合
es官方提供了对es rest api调用的客户端封装,只需要整合到spring boot项目就可以使用了。
1,添加依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.10.1</version>
</dependency>
2,修改spring-boot-dependencies中管理的elasticsearch版本:在properties中重新指定版本。
<properties>
<elasticsearch.version>7.10.1</elasticsearch.version>
</properties>
3,编写配置类
/**
* ElasticSearch配置类
*/
@Configuration
public class ElasticSearchConfig {
public static final RequestOptions COMMON_OPTIONS;
static {
RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
// builder.addHeader("Authorization", "Bearer " + TOKEN);
// builder.setHttpAsyncResponseConsumerFactory(
// new HttpAsyncResponseConsumerFactory
// .HeapBufferedResponseConsumerFactory(30 * 1024 * 1024 * 1024));
COMMON_OPTIONS = builder.build();
}
public RestHighLevelClient initRestHighLevelClient () {
return new RestHighLevelClient(
RestClient.builder(
new HttpHost("192.168.20.207", 9200, "http")));
}
}
4,使用client调用接口操作es:
Java High Level REST Client接口文档参考:
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.x/java-rest-high-supported-apis.html
@Autowired
RestHighLevelClient restHighLevelClient;
private void esIndexApiCall() throws IOException {
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("user", "kimchy");
jsonMap.put("postDate", new Date());
jsonMap.put("message", "trying out Elasticsearch");
IndexRequest indexRequest = new IndexRequest("posts")
.id("1").source(jsonMap);
IndexResponse indexResponse = restHighLevelClient.index(indexRequest, ElasticSearchConfig.COMMON_OPTIONS);
}
小结
ElasticSearch的简单使用,先总结到这里,其全文检索的实现原理待再进一步了解,先用起来!