了解Elasticsearch

Elasticsearch 介绍

Elasticsearch 是一个开源的搜索引擎,建立在一个全文搜索引擎库 Apache Lucene™ 基础之上。 Lucene 可以说是当下最先进、高性能、全功能的搜索引擎库—​无论是开源还是私有。

Elasticsearch 也是使用 Java 编写的,它的内部使用 Lucene 做索引与搜索,但是它的目的是使全文检索变得简单, 通过隐藏 Lucene 的复杂性,取而代之的提供一套简单一致的 RESTful API。

Elasticsearch 不仅仅是 Lucene,并且也不仅仅只是一个全文搜索引擎。 它可以被下面这样准确的形容:

  • 一个分布式的实时文档存储,每个字段 可以被索引与搜索
  • 一个分布式实时分析搜索引擎
  • 能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据

Elasticsearch 倒排索引结构

在网上找到了一篇介绍比较清晰的文档
https://www.cnblogs.com/cjsblog/p/10327673.html

Elasticsearch 安装

Elasticsearch 在5.x版本后依赖的jdk环境就是1.8,所以首先要保证自己本地的jdk环境是1.8

windows下安装包7.6.1,这里推荐在华为开源镜像站进行安装,官网下载超级慢。
地址:https://mirrors.huaweicloud.com/elasticsearch/7.6.1/
在这里插入图片描述
下载安装包后解压
在这里插入图片描述
进入bin 目录,看到 elasticsearch.bat文件,运行
在这里插入图片描述
看到started,表示启动成功,可以看到默认端口为9200
浏览器访问:

127.0.0.1:9200

在这里插入图片描述

Elasticsearch-head安装

PS:Elasticsearch-head依赖于node环境,安装es-head前需确保node环境安装完毕。

安装node

node.js安装包 找到msi后缀的安装
https://nodejs.org/en/download/
我安装的是当前最新版本
在这里插入图片描述

在这里插入图片描述

ps:此处可勾选,勾选后会安装两个模块工具,pythonchocolatey,勾选后默认会安装到C盘,所以也可以不勾选,之后自己自定义安装。

安装完成后进入命令控制台 输入node 可以看到当前安装的版本号
在这里插入图片描述
在这里插入图片描述


安装grunt

npm install -g grunt-cli
在这里插入图片描述
准备工作ok了,可以安装head插件了

安装es-head(7.6.1)

官网下载地址:
:https://github.com/mobz/elasticsearch-head
百度网盘下载地址:
https://pan.baidu.com/s/1vWhDMf1zrWYun_72D3AjYA
提取码:a47u

1.下载,解压
2.命令控制台进入head目录
在这里插入图片描述
3.npm install (ps:安装速度比较慢)
4.npm run start 启动

es-head安装完毕

PS:修改elasticsearch 的配置文件,配置可以跨域访问
在这里插入图片描述

在文件结尾加上:

# 是否支持跨域
http.cors.enabled: true

# *表示支持所有域名
http.cors.allow-origin: "*"

重新启动es
在浏览器访问: 127.0.0.1:9100
在这里插入图片描述

IK分词器安装

github下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.6.1

将ik分词器压缩包解压到elasticsearch的plugins插件下:

在这里插入图片描述
重启es

Kibana安装

安装包地址:https://pan.baidu.com/s/1hjwohKNqMYj3lMvIj15gFg
提取码:hscx
下载 安装 解压 后进入kibana.bat 运行
默认连接的es地址就是 localhost:9200

可进入config/kibana.yml文件下看到:
在这里插入图片描述
配置kibana可视化界面汉化
在配置文件末尾加上:
i18n.locale: “zh-CN”
在这里插入图片描述
在这里插入图片描述
浏览器访问:localhost:5601进入kibana可视化界面
在这里插入图片描述
在这里插入图片描述

Elasticsearch DSL

ES常用数据类型

类型分类子分类具体类型
核心类型字符串text,keyword
整数byte,short,integer,long
浮点double,float,half_float,scale_float
逻辑boolean
日期date
范围range
二进制binary
复合类型数组array
对象object
嵌套nested
地理类型经纬度ge_point
特殊类型ipip
joinjoin

索引操作

创建索引

PUT /test01
{
  "settings": {
    "number_of_replicas": 1,
    "number_of_shards": 1
  },
  "mappings": {
    "properties": {
        "name":{
          "type": "keyword"
        },
        "phone":{
          "type": "keyword"
        },
        "age":{
          "type": "integer"
        },
        "ip":{
          "type": "ip"
        },
        "dec":{
          "type": "text"
        },
        "birth":{
          "type": "date"
        }
    }
  }
}

这里的setting主要是设置索引的分片和备份,mapping映射主要是配置文档中字段名(类似于mysql表的字段)以及对应的类型(字段类型)。
在这里插入图片描述
在这里插入图片描述
可以看到我们默认的文档类型是_doc。

ps:
在5版本及之前一个索引可以对应多个类型。
在6.x版本一个索引可以对应一个类型。
在7.x之后取消了类型。

当然映射其实不需要我们去手动映射,因为在我们添加文档的时候,elasticsearch会自动猜测我的文档数据为我们创建映射。

创建索引(这样也是ok的)

PUT /test01

删除索引

DELETE /test01

文档操作

创建文档

  • 创建文档(指定文档id)
PUT /zhongtou_uat/_doc/1
{
  "name": "张三",
  "age": 18,
  "score": 95
}
  • 创建文档,使用默认id
POST /zhongtou_uat/_doc
{
  "name": "王东林",
  "age": 23
}

此处必须要用POST请求方式,使用PUT请求,则会报错

PUT /zhongtou_uat/_doc
{
  "name":"王东林",
  "age":23
}
{
  "error" : "Incorrect HTTP method for uri [/zhongtou_uat/_doc?pretty=true] and method [PUT], allowed: [POST]",
  "status" : 405
}

修改文档

  • 覆盖修改
    覆盖修改即采用添加的方式,覆盖掉原有的文档数据,但是有一个弊端,当漏掉属性时,这个属性也会去掉。
    在这里插入图片描述
PUT /zhongtou_uat/_doc/1
{
  "name": "李四",
  "age": 18,
  "score": 95
}

在这里插入图片描述

  • 指定属性修改

此处需要指定POST类型 具体语法:

POST /索引/类型/文档id/_update 类型默认都是_doc

POST /zhongtou_uat/_doc/1/_update
{
  "doc":{
    "name": "王五"
  }
}

删除文档

删除文档需要指定 DELETE类型 具体语法

DELETE /索引/文档类型/文档id

DELETE /zhongtou_uat/_doc/Jgt4T3UBlIJHe9k23-YO

在这里插入图片描述

查询文档

基本的条件查询

在这里插入图片描述

GET /zhongtou_uat/_doc/_search?q=name:"王"
复杂的条件查询
  • match
    match 查询类似于模糊查询,因为match查询的属性值(类型为keyword的除外) 首先会被分词器分词,然后去文档中匹配,例如 使用match操作搜索 name=王五的文档,可以搜到 name等于王五和王小二的两个文档。

在这里插入图片描述
可以使用_source来限制查询的字段

 "_source": ["name","age"]

可以使用sort进行排序

 "sort": [
    {
      "age": {
        "order": "desc"
      }
    }
  ]

可以使用from size进行分页

"form": 0 #起始条数
"size": 10 #每页展示条数
  • multi_match
    匹配多个字段(age,score中有等于18的文档),注意fields中的类型必须保持一致。否则会出现异常。
GET /zhongtou_uat/_search
{
  "query": {
    "multi_match": {
      "query": 18,
      "fields": ["age","score"]
    }
  }
}

使用multi_match ,同一个关键字查询多个字段时,可以增加权重,可以使_score乘以相应的倍数,如图,增加score的权重,则id=2的文档的相关性就大大提高了。

GET /zhongtou_uat/_search
{
  "query": {
    "multi_match": {
      "query": 18,
      "fields": ["age","score^3"]
    }
  }
}

在这里插入图片描述

  • must/must not /should
    查询score等于60或者age等于20的数据,并且age不等于18的数据

must 相当于 and ,must not 就相当于 not 。should相当于or

GET /zhongtou_uat/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "bool": {
            "should": [
              {"match": {"score": "60"}},
              {"match": {"age": "20"}}
            ]
          }
        }
      ]
      , "must_not": [
        {"bool": {
          "filter": {
            "term": {
              "age": 18
            }
          }
        }}
      ]
    }
  }
}

在这里插入图片描述
在这里插入图片描述

  • 通配符查询
    ?代表匹配任意一个字符,*代表匹配0个或者多个字符
GET /zhongtou_uat/_search
{
  "query": {
    "wildcard": {
      "job": {
        "value": "*医*"
      }
    }
  }
}
  • 正则查询…

  • query_string

GET /zhongtou_uat/_search
{
  "query": {
    "query_string": {
      "default_field": "job",
      "query": "志愿医务工作者 OR 医生"
    }
  }
}

sort 排序

GET /zhongtou_uat/_search
{
  "query": {
   "match": {
     "name": {
       "query": "王五 赵四",
       "operator": "and"
     }
   }
  },
  "_source": ["name","age"],
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    }
  ]
}

range查询 filter查询

查询job为医生且score大于等于60小于等于100的文档

GET /zhongtou_uat/_search
{
  "query": {
   "bool": {
     "must": [
       {"match": {
         "job": "医生"
       }}
     ],
    "filter": {
      "range": {
        "score": {
          "gte": 60,
          "lte": 100
        }
      }
    }
   }
  }
}

sum求和,avg求平均数

“aggs”代表聚合操作,一个aggs下面可以跟多个聚合操作。sum_score代表聚合后结果名字,“sum”代表聚合操作为统计求和,field:代表是对score进行求和

avg max min 同理

GET /zhongtou_uat/_search
{
  "aggs": {
    "sum_score": {
      "sum": {
        "field": "score"
      }
    },
    "avg_score":{
      "avg": {
        "field": "score"
      }
    }
  }
}

在这里插入图片描述

max最大值 top榜 查询score最大值

GET /zhongtou_uat/_search
{
  "aggs": {
    "max_score": {
      "max": {
        "field": "score"
      }
    }
  }
}

min最小值 top 查询score最小值

GET /zhongtou_uat/_search
{
  "aggs": {
    "min_score": {
      "min": {
        "field": "score"
      }
    }
  }
}

Elasticsearch Java API

API链接地址:https://artifacts.elastic.co/javadoc/org/elasticsearch/client/elasticsearch-rest-high-level-client/7.6.1/index.html

准备工作(创建maven项目)

导入依赖(pom.xml)

<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.elasticsearch/elasticsearch -->
		<dependency>
			<groupId>org.elasticsearch</groupId>
			<artifactId>elasticsearch</artifactId>
			<version>7.6.1</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.elasticsearch.client/elasticsearch-rest-high-level-client -->
		<dependency>
			<groupId>org.elasticsearch.client</groupId>
			<artifactId>elasticsearch-rest-high-level-client</artifactId>
			<version>7.6.1</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.73</version>
		</dependency>

创建es客户端连接

package com.pec.es.uitl;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;

public class EsClient {
	
	public static RestHighLevelClient getHighLevelClient() {
		//指定elastic search的ip和端口
		HttpHost host= new HttpHost("127.0.0.1",9200,"http");
		return new RestHighLevelClient(RestClient.builder(host));
	}

}

辅助实体类

package com.pec.es.entity;

public class User {
	private String userName;
	private Integer age;
	private String sex;
	
	public User() {
		super();
	}
	public User(String userName, Integer age, String sex) {
		super();
		this.userName = userName;
		this.age = age;
		this.sex = sex;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
}

测试

测试客户端连接

/**
	 * 测试连接
	 */
	@Test
	public void testConnect() {
		RestHighLevelClient client=EsClient.getHighLevelClient();
		System.out.println(client);
		
	}

索引

创建索引
/**
	 * 创建索引,分片setting 映射 mapping 创建文档
	 * @throws IOException 
	 */
	@Test
	@SuppressWarnings("deprecation")
	public void createIndex() throws IOException {
		RestHighLevelClient client=EsClient.getHighLevelClient();
		CreateIndexRequest createIndexRequest = new CreateIndexRequest("shop_jifen");
		//配置Setting 分片 备份
				Settings.Builder setting = Settings.builder()
				.put("number_of_shards",1)
				.put("number_of_replicas",1);
		createIndexRequest.settings(setting);
		//mapping映射 ...
		CreateIndexResponse create = client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
		System.out.println(create);
	}
查看索引是否存在
/**
	 * @throws IOException 
	 * 查看索引是否存在 
	 */
	@SuppressWarnings("deprecation")
	@Test
	public void getIndex() throws IOException {
		RestHighLevelClient client=EsClient.getHighLevelClient();
		GetIndexRequest getIndexRequest = new GetIndexRequest();
		getIndexRequest.indices("shop_jifen");
		boolean b = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
		System.out.println(b);
	}
删除索引
/**
	 * 
	 * 删除索引 
	 */
	@Test
	public void deleteIndex() throws IOException {
		RestHighLevelClient client=EsClient.getHighLevelClient();
		DeleteIndexRequest request = new DeleteIndexRequest();
		request.indices("shop_jifen");
		AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
		System.out.println(delete.toString());
	}

文档

创建文档
/**
	 * @throws IOException 
	 * 创建文档
	 */
	@Test
	public void createDoc() throws IOException {
		RestHighLevelClient client=EsClient.getHighLevelClient();
		User u =new User("黎明",40,"男");
		IndexRequest request = new IndexRequest("shop_jifen");
		request.id("2");
		request.timeout(TimeValue.timeValueSeconds(1));
		request.source(JSON.toJSONString(u),XContentType.JSON);
		IndexResponse response = client.index(request, RequestOptions.DEFAULT);
		System.out.println(response.status());
	}
修改文档
/**
	 * 修改文档
	 * @throws IOException 
	 * 此处修改文档,会修改文档中的某个属性,不会覆盖
	 */
	@Test
	public void updateDoc() throws IOException {
		RestHighLevelClient client=EsClient.getHighLevelClient();
		UpdateRequest request = new UpdateRequest("shop_jifen","1");
		request.timeout(TimeValue.timeValueSeconds(1));
		User user = new User();
		user.setUserName("张三");
		user.setAge(18);
		request.doc(JSON.toJSONString(user),XContentType.JSON);
		UpdateResponse response =client.update(request,RequestOptions.DEFAULT);
		System.out.println(response.status());
	}
删除文档
/**
	 * 修改文档
	 * @throws IOException 
	 * 此处修改文档,会修改文档中的某个属性,不会覆盖
	 */
	@Test
	public void updateDoc() throws IOException {
		RestHighLevelClient client=EsClient.getHighLevelClient();
		UpdateRequest request = new UpdateRequest("shop_jifen","1");
		request.timeout(TimeValue.timeValueSeconds(1));
		User user = new User();
		user.setUserName("张三");
		user.setAge(18);
		request.doc(JSON.toJSONString(user),XContentType.JSON);
		UpdateResponse response =client.update(request,RequestOptions.DEFAULT);
		System.out.println(response.status());
	}
查询文档(根据文档id)
/**
	 * @throws IOException 
	 * 查询文档 根据文档id
	 */
	@Test
	public void getDoc() throws IOException {
		RestHighLevelClient client=EsClient.getHighLevelClient();
		GetRequest request = new GetRequest("shop_jifen","1");
		GetResponse response = client.get(request, RequestOptions.DEFAULT);
		System.out.println(response.toString());
//		1.若索引库里面没有数据{"_index":"shop_jifen","_type":"_doc","_id":"1","found":false}
//		2.若索引库里面有数据{"_index":"shop_jifen","_type":"_doc","_id":"1","_version":1,"_seq_no":3,"_primary_term":1,"found":true,"_source":{"age":40,"sex":"男","userName":"黎明"}}
		System.ou.println(response.getSource());
	}
查询文档

QueryBuilders.boolQuery();
QueryBuilders.rangeQuery(name);
QueryBuilders.existsQuery(name);
QueryBuilders.fuzzyQuery(name, value);
QueryBuilders.matchAllQuery();
QueryBuilders.matchQuery(name, text);
QueryBuilders.multiMatchQuery(text, fieldNames);
QueryBuilders.termQuery(name, value);
//获取相似文章,根据关键词匹配。
QueryBuilders.moreLikeThisQuery(likeTexts, likeItems);
QueryBuilders.prefixQuery(name, prefix)

term精准查询job为学生的,并且age大于等于18且小于等于20 并且name(text类型,默认会分词)不是王五的数据

/**
	 * boolQuery must must not filter termQuery range 
	 * @throws IOException 
	 * term精准查询job为学生的,并且age大于等于18且小于等于20  并且name不能包含王、五的数据
	 */
	@Test
	public void search02() throws IOException {
		RestHighLevelClient client=EsClient.getHighLevelClient();
		BoolQueryBuilder builder = QueryBuilders.boolQuery();
		//此处可以写多个builder
		builder.must(QueryBuilders.termQuery("job", "学生"))
			.filter(QueryBuilders.rangeQuery("age").gte(18))
			.filter(QueryBuilders.rangeQuery("age").lte(20));
		
        builder.mustNot(QueryBuilders.matchQuery("name", "王五"));
        
		SearchSourceBuilder ssb =new SearchSourceBuilder();
		query = ssb.query(builder);
		SearchRequest sr =new SearchRequest().indices("zhongtou_uat").source(query);
		SearchResponse response = client.search(sr, RequestOptions.DEFAULT);
		SearchHit[] hits = response.getHits().getHits();
		for (SearchHit hit : hits) {
			System.out.println(hit.getSourceAsString());
		}
	}

高亮显示

@Test
	public void search03() throws IOException {
		RestHighLevelClient client=EsClient.getHighLevelClient();
		SearchRequest request =new SearchRequest().indices("zhongtou_uat");
		SearchSourceBuilder ssb =new SearchSourceBuilder();
		//查询所有构造器
		MatchAllQueryBuilder matchAllQuery = QueryBuilders.matchAllQuery();
		ssb.query(matchAllQuery);
		//高亮构造器
		HighlightBuilder highlightBuilder=new HighlightBuilder();
	    highlightBuilder.preTags("<span style='color:red'>");
	    highlightBuilder.postTags("</span>");
	    highlightBuilder.field("job");
		ssb.highlighter(highlightBuilder);
		
		request.source(ssb);
		SearchResponse searchResponse = client.search(request, RequestOptions.DEFAULT);
		for (SearchHit hit : searchResponse.getHits().getHits()) {
			Map<String, HighlightField> highlightFields = hit.getHighlightFields();
			HighlightField jobValue = highlightFields.get("job");
			//将值 前缀 后缀拼接在一起
			Map<String, Object> sourceAsMap = hit.getSourceAsMap();
			if(jobValue !=null) {
				StringBuilder newValue=new StringBuilder();
				Text[] fragments = jobValue.getFragments();
				for (Text text : fragments) {
					newValue.append(text);
				}
				sourceAsMap.put("job", newValue.toString());
			}
			String string = JSON.toJSONString(sourceAsMap);
			System.out.println(string);
		}
	}

未完待续…

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐