什么是Elaticsearch?
总结:
1、elasticsearch是一个基于Lucene的高扩展的分布式搜索服务器,支持开箱即用。
2、elasticsearch隐藏了Lucene的复杂性,对外提供Restful 接口来操作索引、搜索。
突出优点:
1.扩展性好,可部署上百台服务器集群,处理PB级数据。
2.近实时的去索引数据、搜索数据。
es和solr选择哪个?
1.如果你公司现在用的solr可以满足需求就不要换了。
2.如果你公司准备进行全文检索项目的开发,建议优先考虑elasticsearch,因为像Github这样大规模的搜索都在用它
Elaticsearch与数据库的区别:
遍历方式:
ES有分片的概念,一个大的索引会被分成多个分片来进行存储数据,使用分布式的架构对分片进行并行搜索(基于倒排)
传统数据库的遍历,属于正向全表扫描
选用:
在面对大数据量简单计算的时候es的效率原高于mysql等传统数据库,但是在定位某一个唯一值(如用会员id找会员)时并不需要es
但在大数据的相似计算与查找或简单计算时,es的分布式并行计算有绝对的优势
参考https://www.cnblogs.com/yanxiaoge/p/11117513.html小名的同学
一、安装配置
1、新版本要求至少jdk1.8以上。
2、支持tar、zip、rpm等多种安装方式(在windows下开发建议使用ZIP安装方式)
3、支持docker方式安装
4、下载地址 https://www.elastic.co/downloads/past-releases
Elasticsearcha文件目录介绍
bin:脚本目录,包括:启动、停止等可执行脚本
config:配置文件目录
data:索引目录,存放索引文件的地方
logs:日志目录
modules:模块目录,包括了es的功能模块
plugins :插件目录,es支持插件机制
Elasticsearch配置文件(config目录下)
1、elasticsearch.yml : 用于配置Elasticsearch运行参数
2、 jvm.options : 用于配置Elasticsearch JVM设置
3、log4j2.properties: 用于配置Elasticsearch日志
elasticsearch.yml配置文件
#集群名称,默认是elasticsearch,建议修改成一个有意义的名称
cluster.name: xuecheng
#节点名,通常一台物理服务器就是一个节点,es会默认随机指定名称,建议指定一个有意义的名称
node.name: xc_node_1
#设置绑定主机的ip地址,设置为0.0.0.0表示绑定任何ip,允许外网访问,生产环境建议设置为具体
#的ip
network.host: 0.0.0.0
#设置对外服务的http端口,默认为9200
http.port: 9200
#集群结点之间通信端口
transport.tcp.port: 9300
#指定该节点是否有资格被选举成为master结点,默认是true,如果原来的master宕机会重新选举新
#的master
node.master: true
#指定该节点是否存储索引数据,默认为true
node.data: true
#设置集群中master节点的初始列表
discovery.zen.ping.unicast.hosts: ["0.0.0.0:9300", "0.0.0.0:9301", "0.0.0.0:9302"]
#主结点数量的最少值 ,此值的公式为:(master_eligible_nodes / 2) + 1 ,比如:有3个符合要求的主结#点,那么这
#里要设置为2
discovery.zen.minimum_master_nodes: 1
#内存锁定:true表示该内存只被Elasticsearch使用,防止其它进程使用,避免与swap交换数据
bootstrap.memory_lock: false
#单机允许的最大存储结点数,通常单机启动一个结点建议设置为1,开发环境如果单机启动多个节点可设置大于1
node.max_local_storage_nodes: 1
#设置索引数据的存储路径
path.data: D:\ElasticSearch\elasticsearch‐6.2.1\data
#设置日志文件的存储路径
path.logs: D:\ElasticSearch\elasticsearch‐6.2.1\logs
#开启cors跨域访问支持,默认为false
http.cors.enabled: true
##跨域访问允许的域名地址,(允许所有域名)以上使用正则
http.cors.allow‐origin: /.*/
jvm.options配置文件
设置最小及最大的JVM堆内存大小:
在jvm.options中设置 -Xms和-Xmx:
1) 两个值设置为相等
2) 将 Xmx 设置为不超过物理内存的一半
log4j2.properties配置文件
日志文件设置,ES使用log4j,注意日志级别的配置(error)
启动报错
参考https://www.cnblogs.com/yanxiaoge/p/11117513.html小名的同学
elasticsearch.yml配置文件配置了 network.host: 0.0.0.0,报错:JVM is using the client VM [Java HotSpot™ Client VM] but should be using a server VM for the best performance
解决修改java运行环境 D:\java\jdk1.8.0_201\jre\lib\i386\jvm.cfg
测试是否启动成功:
浏览器输入http://localhost:9200/显示
Elasticsearch可视化管理插件使用kibana
可用于用来监视ES的状态,创建映射、创建索引等
二、ES数据储存结构理解
注意:6.0之前的版本有type(类型)概念,type相当于关系数据库的表,ES官方将在ES9.0版本中彻底删除type,但现在这样理解也没什么大碍
Elasticsearch采取的索引方法是倒排索引
正排索引:搜索内容——>文章是否含有该内容——>筛选——>输出(当数据量大的时候反应速度慢)
倒排索引:(文档或者说数据的数量肯定比词语的数量多,因为多个文档可能包含同一个词语,所以倒排索引就是通过文档内容中的词语作为索引实现快速搜索的功能)(实现能根据搜索的关键字快速查找到匹配度最高的文章,关键是IK分词器)
所以根据倒排索引来搜索,再根据匹配度来筛选的话,最后得到的最优结果应该是包含“包含我爱你中国”的这个文档
三、postman测试Elasticsearch
1、创建索引(相当创建数据库,配置参数等)
put http://localhost:9200/索引库名称
{
"settings":{
"index":{
"number_of_shards":1,
"number_of_replicas":0
}
}
}
number_of_shards:设置分片的数量,在集群中通常设置多个分片,表示一个索引库将拆分成多片分别存储不同的结点,提高了ES的处理能力和高可用性,入门程序使用单机环境,这里设置为1。
number_of_replicas:设置副本的数量,设置副本是为了提高ES的高可靠性,单机环境设置为0
2、创建映射(相当创建表的列名,数据类型)
post http://localhost:9200/索引库名称/类型名称/_mapping
{
"properties": {
"name": {
"type": "text"
},
"description": {
"type": "text"
},
"studymodel": {
"type": "keyword"
}
}
}
properties代表列参数,固定写法
name、description、studymodel是列名
3、创建文档(相当创建表数据)
发送:put 或Post http://localhost:9200/xc_course/doc/id值(如果不指定id值ES会自动生成ID)
{
"name": "Bootstrap开发框架已更改",
"description": "Bootstrap是由Twitter推出的一个前台页面开发框架,在行业之中使用较为广泛。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
"studymodel": "201001"
}
4、CRUD操作
“q=”是固定写法,后面加条件
1、根据课程id查询文档
发送:get http://localhost:9200/xc_course/doc/4028e58161bcf7f40161bcf8b77c0000
2、查询所有记录
发送 get http://localhost:9200/xc_course/doc/_search
3、查询名称中包括spring 关键字的的记录
发送:get http://localhost:9200/xc_course/doc/_search?q=name:bootstrap
4、查询学习模式为201001的记录
发送 get http://localhost:9200/xc_course/doc/_search?q=studymodel:201001
5、Elasticsearch不支持修改已有映射属性,只能删除索引库重新添加(除非你是添加新项)
delete http://localhost:9200/xc_course
6、添加某类型的映射
http://localhost:9200/xc_course/doc/_mapping
7、根据条件删除文档
post http://localhost:9200/xc_course/doc/_delete_by_query
{
"query":{
"match":{
"host":"DESKTOP-4MCSL0K"
}
}
}
查询结果分析
took:本次操作花费的时间,单位为毫秒。
timed_out:请求是否超时
_shards:说明本次操作共搜索了哪些分片
hits:搜索命中的记录
hits.total : 符合条件的文档总数 hits.hits :匹配度较高的前N个文档
hits.max_score:文档匹配得分,这里为最高分
_score:每个文档都有一个匹配度得分,按照降序排列。
_source:显示了文档的原始内容
四、IK分词器
Elasticsearch 默认的分词器对中文不友好,明明是一个词的默认分词器全把它分成一个个字
如:“测试”明明是一个词却被拆分了
post localhost:9200/_analyze
body
{"text":"测试分词器"}
{
"tokens": [
{
"token": "测",
"start_offset": 0,
"end_offset": 1,
"type": "<IDEOGRAPHIC>",
"position": 0
},
{
"token": "试",
"start_offset": 1,
"end_offset": 2,
"type": "<IDEOGRAPHIC>",
"position": 1
},
{
"token": "分",
"start_offset": 2,
"end_offset": 3,
"type": "<IDEOGRAPHIC>",
"position": 2
},
{
"token": "词",
"start_offset": 3,
"end_offset": 4,
"type": "<IDEOGRAPHIC>",
"position": 3
},
{
"token": "器",
"start_offset": 4,
"end_offset": 5,
"type": "<IDEOGRAPHIC>",
"position": 4
}
]
}
所以我们要引用一个插件分词器IK分词器,这个分词器对中文词语有一定的识别
下载IK分词器,将文件解压到elasticsearch下的plugins文件夹下,重启elasticsearch
再用postman测试一次
post localhost:9200/_analyze
加上 “analyzer”:“ik_max_word” 指定分词器
{"text":"测试分词器", "analyzer":"ik_max_word"}
{
"tokens": [
{
"token": "测试",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
},
{
"token": "分词器",
"start_offset": 2,
"end_offset": 5,
"type": "CN_WORD",
"position": 1
},
{
"token": "分词",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 2
},
{
"token": "器",
"start_offset": 4,
"end_offset": 5,
"type": "CN_CHAR",
"position": 3
}
]
}
ik分词器有两种分词模式:ik_max_word和ik_smart模式
1、ik_max_word
会将文本做最细粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、大会堂、大会、会堂等词语。
2、ik_smart
会做最粗粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为中华人民共和国、人民大会
五、自定义分词器
因为IK分词器已有的中文词语辨识库有限,如我们一些专有名词是没有添加到的,所以我们可以自定义词库。
IKAnalyzer.cfg.xml:配置自定义词库
main.dic:iK分词器自带一个main.dic的文件,此文件为词库文件
my.dic :这是自己新创建的自定义词库
在my.dic添加自己想搜索的专有名词
打开IKAnalyzer.cfg.xml文件,将my.dic写入,重启Elasticsearch即可
这样就可以完整搜索到一个词了
{
"tokens": [
{
"token": "黑马程序员",
"start_offset": 0,
"end_offset": 5,
"type": "CN_WORD",
"position": 0
},
{
"token": "黑马",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 1
},
{
"token": "程序员",
"start_offset": 2,
"end_offset": 5,
"type": "CN_WORD",
"position": 2
},
{
"token": "程序",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 3
},
{
"token": "员",
"start_offset": 4,
"end_offset": 5,
"type": "CN_CHAR",
"position": 4
}
]
}
六、映射类型
{
"properties": {
"description": {
"type": "text", //text全文分词检索(支持分词器检索)
"analyzer": "ik_max_word", //文档建立索引时采用高度分词器模式(扩大该文档的索引数量,有利于被搜索;如“我爱你中国”,浏览器输入“我”、“爱你”、“我爱你中国”都可以搜索到)
"search_analyzer": "ik_smart" //搜索文档时的搜索内容采用低度分词器模式(减少分词数量,避免搜到无关内容;如我搜“我爱你中国”,分词成“我爱你”和“中国”,就不会用到“我”,“你”的索引了)
},
"name": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
},
"pic":{
"type":"text",
"index":false
},
"price": {
"type": "float" //浮点数
},
"studymodel": {
"type": "keyword" //精确搜索(如我搜“java开发”,那么studymodel=“java基础”这个就不完全符合了,所以不会被检索到,只有完全一模一样才能搜到)
},
"timestamp": {
"type": "date", //日期类型,有三种
"format": "yyyy‐MM‐dd HH:mm:ss||yyyy‐MM‐dd||epoch_millis"
}
}
}
七、结合SpringBoot、Elasticsearch
1、引入依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch‐rest‐high‐level‐client</artifactId>
<version>6.2.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>6.2.1</version>
</dependency>
2、配置文件
server:
port: ${port:40100}
spring:
application:
name: xc-search-service
xuecheng:
elasticsearch:
hostlist: ${eshostlist:127.0.0.1:9200} #配置ES服务IP和端口,提供ES客户端连接ES服务;多个结点中间用逗号分隔
3、配置ES客户端连接(将配置文件中的IP和端口处理然后写入ES客户端)
ES提供多种不同的客户端:
1、TransportClient
ES提供的传统客户端,官方计划8.0版本删除此客户端。
2、RestClient
RestClient是官方推荐使用的,它包括两种:Java Low Level REST Client和 Java High Level REST Client。
ES在6.0之后提供 Java High Level REST Client, 两种客户端官方更推荐使用 Java High Level REST Client,不过当前它还处于完善中,有些功能还没有。
所以下面配置的是restclient的低版本和高版本,低版本用于弥补高版本的某些功能的缺乏,除此之外使用高版本的ES客户端
/**
* @author Administrator
* @version 1.0
**/
@Configuration
public class ElasticsearchConfig {
@Value("${xuecheng.elasticsearch.hostlist}")
private String hostlist;
//高版本客户端
@Bean
public RestHighLevelClient restHighLevelClient(){
//解析hostlist配置信息
String[] split = hostlist.split(",");
//创建HttpHost数组,其中存放es主机和端口的配置信息
HttpHost[] httpHostArray = new HttpHost[split.length];
for(int i=0;i<split.length;i++){
String item = split[i];
httpHostArray[i] = new HttpHost(item.split(":")[0], Integer.parseInt(item.split(":")[1]), "http");
}
//创建RestHighLevelClient客户端
return new RestHighLevelClient(RestClient.builder(httpHostArray));
}
//低版本客户端
@Bean
public RestClient restClient(){
//解析hostlist配置信息
String[] split = hostlist.split(",");
//创建HttpHost数组,其中存放es主机和端口的配置信息
HttpHost[] httpHostArray = new HttpHost[split.length];
for(int i=0;i<split.length;i++){
String item = split[i];
httpHostArray[i] = new HttpHost(item.split(":")[0], Integer.parseInt(item.split(":")[1]), "http");
}
return RestClient.builder(httpHostArray).build();
}
}
3、测试(索引库的创建、删除;映射的创建;文档的CRUD)
/**
* @author Huang
* @version 1.0
* @date 2020/4/19 12:04
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestIndex {
@Autowired
RestHighLevelClient restHighLevelClient; //高级ES客户端
@Autowired
RestClient restClient; //低级ES客户端,用于弥补高级客户端缺乏某些功能
/**
* 创建索引库,添加(type)映射
*/
@Test
public void test01() throws IOException {
//创建添加索引库对象并且指定索引库名称
CreateIndexRequest createIndexRequest = new CreateIndexRequest("xc_course");
/**
* {
* "settings":{
* "index":{
* "number_of_shards":1,
* "number_of_replicas":0
* }
* }
* }
*/
//设置索引库配置属性参数
createIndexRequest.settings(Settings.builder().put("number_of_shards",1).put("number_of_replicas",0));
//设置(type)映射
createIndexRequest.mapping("doc", " {\n" +
" \t\"properties\": {\n" +
" \"name\": {\n" +
" \"type\": \"text\",\n" +
" \"analyzer\":\"ik_max_word\",\n" +
" \"search_analyzer\":\"ik_smart\"\n" +
" },\n" +
" \"description\": {\n" +
" \"type\": \"text\",\n" +
" \"analyzer\":\"ik_max_word\",\n" +
" \"search_analyzer\":\"ik_smart\"\n" +
" },\n" +
" \"studymodel\": {\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"price\": {\n" +
" \"type\": \"float\"\n" +
" }\n" +
" }\n" +
"}", XContentType.JSON);
//利用ES客户端创建操作索引的对象indices
IndicesClient indices = restHighLevelClient.indices();
//indices对象执行添加请求createIndexRequest
CreateIndexResponse createIndexResponse = indices.create(createIndexRequest);
//获取响应acknowledged,确认是否执行成功
boolean acknowledged = createIndexResponse.isAcknowledged();
System.out.println(acknowledged);
}
/**
* 删除索引库
*/
@Test
public void test02() throws IOException {
//创建删除索引库对象并且指定索引库名称
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("xc_course");
//利用ES客户端创建操作索引的对象indices
IndicesClient indices = restHighLevelClient.indices();
//indices对象执行删除请求deleteIndexRequest
DeleteIndexResponse delete = indices.delete(deleteIndexRequest);
//获取响应acknowledged,确认是否执行成功
boolean acknowledged = delete.isAcknowledged();
System.out.println(acknowledged);
}
/**
* 添加文档
*/
@Test
public void test03() throws IOException {
//用Map储存文档内容
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("name", "spring cloud实战");
jsonMap.put("description", "本课程主要从四个章节进行讲解: 1.微服务架构入门 2.spring cloud 基础入门 3.实战Spring Boot 4.注册中心eureka。");
jsonMap.put("studymodel", "201001");
SimpleDateFormat dateFormat =new SimpleDateFormat("yyyy‐MM‐dd HH:mm:ss");
jsonMap.put("timestamp", dateFormat.format(new Date()));
jsonMap.put("price", 5.6f);
//创建请求对象,指定索引库和类型和文档ID,不指定ID则随机设置
IndexRequest indexRequest = new IndexRequest("xc_course", "doc", "huang01");
//指定创建的文档内容
indexRequest.source(jsonMap);
//通过restHighLevelClient高级ES客户端执行请求并返回结果
IndexResponse indexResponse = restHighLevelClient.index(indexRequest);
//根据indexResponse获取是否执行成功
DocWriteResponse.Result result = indexResponse.getResult();
System.out.println(result);
}
/**
* 查询文档
*/
@Test
public void test04() throws IOException {
//指定查询的索引库、类型、文档ID
GetRequest getRequest = new GetRequest("xc_course", "doc", "huang01");
//利用ES客户端执行请求后获取结果
GetResponse getResponse = restHighLevelClient.get(getRequest);
//验证结果
boolean exists = getResponse.isExists();
//从结果中获取文档
Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
System.out.println(sourceAsMap + " " + exists);
}
/**
* 更新文档(部分字段更新,不是覆盖更新)
*/
@Test
public void test05() throws IOException {
//指定更新文档的索引库、类型、文档ID
UpdateRequest updateRequest = new UpdateRequest("xc_course", "doc", "huang01");
//使用Map储存指定更新内容
Map<String, String> map = new HashMap<>();
map.put("name", "spring cloud实战");
//指定更新内容
updateRequest.doc(map);
//利用ES客户端执行更新请求
UpdateResponse updateResponse = restHighLevelClient.update(updateRequest);
//确认执行是否成功
DocWriteResponse.Result result = updateResponse.getResult();
System.out.println(result);
}
/**
* 删除文档
*/
@Test
public void test06() throws IOException {
//指定更新文档的索引库、类型、文档ID
DeleteRequest deleteRequest = new DeleteRequest("xc_course", "doc", "huang01");
//利用ES客户端执行更新请求
DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest);
//确认执行是否成功
DocWriteResponse.Result result = deleteResponse.getResult();
System.out.println(result);
}
}
八、DSL搜索
什么是DSL?
DSL(Domain Specific Language)是ES提出的基于json的搜索方式,在搜索时传入特定的json格式的数据来完成不
同的搜索需求。
DSL比URI搜索方式功能强大,在项目中建议使用DSL方式来完成搜索
URL搜索(查询条件比较单一局限,不好扩展)
Post http://localhost:9200/xc_course/doc/_search?q=name:bootstrap
DSL搜索(查询条件是json格式,查询条件多样,容易扩展)
Post http://localhost:9200/xc_course/doc/_search
查询条件:
{
"query": {
"match_all": {}
},
"_source" : ["name","studymodel"]
}
结合SpringBoot实现DSL搜索
/**
* @author Huang
* @version 1.0
* @date 2020/4/19 16:31
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestSearch {
@Autowired
RestHighLevelClient restHighLevelClient; //高版本的ES客户端
@Autowired
RestClient restClient; //低版本的ES客户端
/**
* 搜索文档方式
*/
@Test
public void test01() throws IOException, ParseException {
int page = 1;
int size = 5;
int from = (page - 1) * size; //计算起始下标
//创建搜索对象(指定索引库、类型)
SearchRequest searchRequest = new SearchRequest("xc_course").types("doc");
//创建搜索源(作用是设置搜索条件和设置搜索结果的显示的格式,如显示哪一列)
String[] split = new String[]{"1","2"}; //根据id查询文档的id数组
List<String> ids = Arrays.asList(split); //将String[]转换成列表集合类型
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
.fetchSource(new String[]{"name", "studymodel", "price", "timestamp"}, new String[]{}) //设置源字段过滤(第一个参数:包括哪些字段;第二个参数:不包括哪些字段)
.from(from) //分页搜索:from(0)设置起始下标,从0开始,size(1)每页显示个数
.size(size)
.query(QueryBuilders.matchQuery("description", "spring开发框架").minimumShouldMatch("80%"));//全文检索:进行分词检索 spring、开发、框架;minimumShouldMatch("80%")表示3*0.8=2,至少两个分词匹配到该文档才可以被筛选
// .query(QueryBuilders.matchAllQuery()) //QueryBuilders.matchAllQuery()表示搜索doc类型下所有文档
// .query(QueryBuilders.matchQuery("description", "spring开发框架").operator(Operator.OR)); //全文检索:进行分词检索 spring、开发、框架;Operator.OR表示只要有一个文档含有该分词则可以被筛选 AND 表示分词全部要有的文档才可以被筛选
// .query(QueryBuilders.termsQuery("_id", ids)); //terms根据id查询(可多个id)
// .query(QueryBuilders.termQuery("name", "spring")); //term精确查询:搜索内容不进行分词(搜索姓名、学号、身份证)
//搜索请求设置搜索源
searchRequest.source(searchSourceBuilder);
//利用ES客户端执行该搜索请求
SearchResponse searchResponse = restHighLevelClient.search(searchRequest);
//获取匹配结果
SearchHits hits = searchResponse.getHits();
//匹配到的文档数量
long totalHits = hits.getTotalHits();
//得到匹配度高的文档
SearchHit[] hits1 = hits.getHits();
//遍历文档数组hits1
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy‐MM‐dd hh:mm:ss");
for(SearchHit searchHit : hits1){
//文档ID
String id = searchHit.getId();
//获取源文档(_source)
Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
//获取姓名属性
String name = (String) sourceAsMap.get("name");
//由于设置的源文档字段过滤,所以deciption字段属性是获取不到的
//获取学习模式属性
String studymodel = (String) sourceAsMap.get("studymodel");
//获取价格属性
double price = (double) sourceAsMap.get("price");
//获取日期属性
Date timestamp = simpleDateFormat.parse((String) sourceAsMap.get("timestamp"));
}
}
}
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()下搜索方式的解释
1、过滤结果(设置文档显示的列(第一个参数)和不想显示的列(第二个参数))
.fetchSource(new String[]{"name", "studymodel", "price", "timestamp"}, new String[]{})
2、分页搜索(从搜索到的文档第一个开始显示,每页显示5个)
.from(0)
.size(5)
3、搜索(doc类型)所有文档
.query(QueryBuilders.matchAllQuery())
4、分词搜索 + 匹配度百分比
进行分词检索 spring、开发、框架;minimumShouldMatch(“80%”)表示3*0.8=2,至少两个分词匹配到该文档才可以被筛选
.query(QueryBuilders.matchQuery("description", "spring开发框架").minimumShouldMatch("80%"));
5、分词搜索 + or(and)
or:文档只要含有一个分词即可被筛选
and:文档要含有全部分词才可以被筛选
.query(QueryBuilders.matchQuery("description", "spring开发框架").operator(Operator.OR));
6、根据ID查询(terms精确多个查询,表示这些ID不会被分词,要作为一个完整的词进行检索)
ids是ID数组
.query(QueryBuilders.termsQuery("_id", ids)); //terms根据id查询(可多个id)
7、term精确查询(和第6个语句差不多,只是这个只能精确查询一个搜索内容,该搜索内容也不会被分词,作为一个完整的词语进行检索)(一般用于搜索姓名、学号、身份证)
.query(QueryBuilders.termQuery("name", "spring"));
8、综合搜索bool查询(多条件查询、过滤器、排序、高亮)
什么是bool查询?
布尔查询对应于Lucene的BooleanQuery查询,实现将多个查询组合起来。
三个参数:
must:文档必须匹配must所包括的查询条件,相当于 “AND”
should:文档应该匹配should所包括的查询条件其中的一个或多个,相当于 “OR”
must_not:文档不能匹配must_not所包括的该查询条件,相当于“NOT”
POST http://localhost:9200/xc_course/doc/_search
{
"_source" : [ "name", "studymodel", "description","price"],
"query": {
"bool" : {
"must":[
{
"multi_match" : {
"query" : "spring框架",
"minimum_should_match": "50%",
"fields": [ "name^10", "description" ]
},
"match": {
"name": {
"query":"张三"
}
}
}
],
"filter": [
{ "term": { "studymodel": "201001" }},
{ "range": { "price": { "gte": 60 ,"lte" : 100}}}
]
}
},
"sort" : [
{
"studymodel" : "desc"
},
{
"price" : "asc"
}
],
"highlight": {
"pre_tags": ["<tag1>"],
"post_tags": ["</tag2>"],
"fields": {
"name": {},
"description":{}
}
}
}
1、_source:过滤字段;只显示文档"name", “studymodel”, “description”,"price"列
2、must:里面写多个match语句
3、multi_match:搜索内容匹配 fields 中name、description字段(相对的是只匹配一个字段name)
4、minimum_should_match: 匹配度(两个分词至少要符合一个)
5、filter:过滤器(针对搜索后的结果进行条件过滤,不进行打分和分词,所以性能比搜索高)
6、term:针对某字段进行精确查询,不进行分词
7、range:范围查询
8、sort:针对某个字段进行排序
9、highlight:高亮显示
结合SpringBoot和ES客户端的代码实现
/**
* 综合搜索bool查询(多条件查询)
*/
@Test
public void test02() throws IOException, ParseException {
int page = 1;
int size = 5;
int from = (page - 1) * size; //计算起始下标
//创建搜索对象(指定索引库、类型)
SearchRequest searchRequest = new SearchRequest("xc_course").types("doc");
//创建搜索源(作用是设置搜索条件和设置搜索结果的显示的格式,如显示哪一列)
String[] split = new String[]{"1","2"}; //根据id查询文档的id数组
List<String> ids = Arrays.asList(split); //将String[]转换成列表集合类型
/**must:必须同时满足"multi_match"查询条件和"term"查询条件
* {
* "_source" : [ "name", "studymodel", "description"],
* "from" : 0, "size" : 1,
* "query": {
* "bool" : {
* "must":[
* {
* "multi_match" : {
* "query" : "spring框架",
* "minimum_should_match": "50%",
* "fields": [ "name^10", "description" ]
* }
* },
* {
* "term":{
* "studymodel" : "201001"
* }
* }
* ]
* }
* }
* }
*/
//先创建一个multi_match对象(用于搜索)
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("spring java", "name", "description").minimumShouldMatch("50%").field("name", 10);
//再创建一个term对象(用于过滤器)
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("studymodel", "201001"); //精确查询studymodel等于201001
//在创建一个范围查询对象(用于过滤器)
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price").gte("70").lte("100"); //price 大于60小于100
//最后创建bool对象,设置must的条件和过滤器
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
.must(multiMatchQueryBuilder) //定义搜索条件
.filter(termQueryBuilder) //定义过滤器(过滤器是对搜索结果进行条件过滤,不进行分词和打分,纯粹的过滤,所以过滤器性能比搜索性能高,所以搜索文档时尽量配合搜压缩与过滤器使用)
.filter(rangeQueryBuilder); //定义过滤器(过滤器是对搜索结果进行条件过滤,不进行分词和打分,纯粹的过滤,所以过滤器性能比搜索性能高,所以搜索文档时尽量配合搜压缩与过滤器使用)
//创建高亮对象并设置高亮标签
HighlightBuilder highlightBuilder = new HighlightBuilder()
.preTags("<tag>")
.postTags("</tag>");
//设置高亮字段
highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
highlightBuilder.fields().add(new HighlightBuilder.Field("description"));
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
.fetchSource(new String[]{"name", "studymodel", "price", "timestamp"}, new String[]{})
.from(from)
.size(size)
.query(boolQueryBuilder) //最后将boolQueryBuilder放入searchSourceBuilder执行
.sort("studymodel", SortOrder.DESC) //根据文档字段排序-降序
.sort("price", SortOrder.ASC) //根据文档字段排序-升序
.highlighter(highlightBuilder); //设置高亮
//搜索请求设置搜索源
searchRequest.source(searchSourceBuilder);
//利用ES客户端执行该搜索请求
SearchResponse searchResponse = restHighLevelClient.search(searchRequest);
//获取匹配结果
SearchHits hits = searchResponse.getHits();
//匹配到的文档数量
long totalHits = hits.getTotalHits();
//得到匹配度高的文档
SearchHit[] hits1 = hits.getHits();
//遍历文档数组hits1
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy‐MM‐dd hh:mm:ss");
for(SearchHit searchHit : hits1){
//文档ID
String id = searchHit.getId();
//获取源文档(_source)
Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
//获取姓名属性
String name = (String) sourceAsMap.get("name");
//由于设置的源文档字段过滤,所以deciption字段属性是获取不到的
//获取学习模式属性
String studymodel = (String) sourceAsMap.get("studymodel");
//获取价格属性
double price = (double) sourceAsMap.get("price");
//获取日期属性
Date timestamp = simpleDateFormat.parse((String) sourceAsMap.get("timestamp"));
//匹配分数
float score = searchHit.getScore();
//获取高亮字段
Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
//判断该文档有无高亮字段
if(null!=highlightFields){
HighlightField name_highlightField = highlightFields.get("name");
HighlightField description_highlightField = highlightFields.get("description");
//判断该字段有无高亮
if(null!=name_highlightField){
Text[] name_highlightFieldFragments = name_highlightField.getFragments();
StringBuffer stringBuffer = new StringBuffer();
for(Text s : name_highlightFieldFragments){
stringBuffer.append(s.toString());
}
name = stringBuffer.toString();
}
//判断该字段有无高亮
if(null!=description_highlightField){
Text[] description_highlightFieldFragments = description_highlightField.getFragments();
StringBuffer stringBuffer = new StringBuffer();
for(Text s : description_highlightFieldFragments){
stringBuffer.append(s.toString());
}
String description = stringBuffer.toString();
}
}
}
}
这里要注意语句的层次,searchSourceBuilders > boolqueryBuilders > queryBuilders
用图说明一下用代码实现ES搜索的流程(不要看代码冗长,其实是数据处理冗杂,关键步骤只有几步)
九、ES集群(多个ES服务结点)
结点分类
主结点:master节点主要用于集群的管理及索引 比如新增结点、分片分配、索引的新增和删除等。 数据结点:
data 节点上保存了数据分片,它负责索引和搜索操作。 客户端结点:client 节点仅作为请求客户端存在,client的
作用也作为负载均衡器,client 节点不存数据,只是将请求均衡转发到其它结点。
通过下边两项参数来配置结点的功能:
node.master: #是否允许为主结点
node.data: #允许存储数据作为数据结点
node.ingest: #是否允许成为协调节点,
四种组合方式:
master=true,data=true:即是主结点又是数据结点
master=false,data=true:仅是数据结点
master=true,data=false:仅是主结点,不存储数据
master=false,data=false:即不是主结点也不是数据结点,此时可设置ingest为true表示它是一个客户端
ES集群很简单
1、准备两个ES文件
2、elasticsearch-6.2.1-1的 elasticsearch.yml(有标记的地方注意修改)
cluster.name: xuecheng
node.name: xc_node_1 **
network.host: 0.0.0.0
http.port: 9200 **
transport.tcp.port: 9300 **
node.master: true
node.data: true
discovery.zen.ping.unicast.hosts: ["0.0.0.0:9300", "0.0.0.0:9301"]
discovery.zen.minimum_master_nodes: 1
node.ingest: true
bootstrap.memory_lock: false
node.max_local_storage_nodes: 2
path.data: F:\elasticsearch\elasticsearch-6.2.1-1\data **
path.logs: F:\elasticsearch\elasticsearch-6.2.1-1\logs **
http.cors.enabled: true
http.cors.allow-origin: /.*/
3、elasticsearch-6.2.1-2的 elasticsearch.yml
cluster.name: xuecheng
node.name: xc_node_2 **
network.host: 0.0.0.0
http.port: 9201 **
transport.tcp.port: 9301 **
node.master: true
node.data: true
discovery.zen.ping.unicast.hosts: ["0.0.0.0:9300", "0.0.0.0:9301"]
discovery.zen.minimum_master_nodes: 1
node.ingest: true
bootstrap.memory_lock: false
node.max_local_storage_nodes: 2
path.data: F:\elasticsearch\elasticsearch-6.2.1-2\data **
path.logs: F:\elasticsearch\elasticsearch-6.2.1-2\logs **
http.cors.enabled: true
http.cors.allow-origin: /.*/
注意:
discovery.zen.ping.unicast.hosts: [“0.0.0.0:9300”, “0.0.0.0:9301”]
表示这个节点可以通信"0.0.0.0:9301(自己)和 0.0.0.0:9300的节点(ES集群节点之间都要保持通信,即使分片数据不在该节点上,也能通过转发到达对应节点处)
4、注意添加分词器插件
5、启动两个ES服务
6、访问 http://localhost:9200 和 http://localhost:9201 查看是否都启动成功
发送http://localhost:9201/_cluster/health 查看Elasticsearch 的集群健康情况。
集群后创建索引的分片和副本都会均匀的分布到连接的节点上
什么是分片?
{
"settings":{
"index":{
"number_of_shards":2,
"number_of_replicas":1
}
}
}
注:在ES中, 索引是一组文档的集合
分片是创建索引时将该索引分成一片片(将文档集合分成一片片),如 “number_of_shards”:2,就是将该索引的数据分成两部分,分别储存到两个不同的节点上。
什么是副本?
副本就是每一个分片的拷贝,也就是文档集合的复制,如 “number_of_replicas”:1,也会随着创建索引时分别储存到不同的节点上。
十、学习总结
1、ES是一个运用倒排索引进行检索的服务,实现快速检索功能。
2、ES关键要掌握DSL搜索语法的运用,分词检索、精确检索、过滤、字段权重、匹配度等
3、ES集群通过配置文件配置通信端口实现节点互通,即使检索文档数据不在该节点上也能通过转发到相应节点获取数据。通过集群实现扩大数据量。