当前位置:   article > 正文

如何使用Elasticsearch构建强大的搜索和分析应用程序(2023年最新ES新手教程)_es搜索引擎的使用教程

es搜索引擎的使用教程

1.Elasticsearch

非常强大的开源搜索引擎,可以帮助我们从海量数据中快速找到需要的内容

什么是elasticsearch

一个开源的分布式搜索引擎,可以用来实现搜索、日志统计、分析、系统监控

什么是elastic stack

是以elasticsearch为核心的技术栈,包括beats、Logstash、Kibana、elasticsearcg

什么是Lucene

是Apache的开源搜索引擎类库,提供了搜索引擎的核心API

1.1.正向索引和倒排索引

1.1.1.传统数据库(如MYSQL)采用正向索引

在这里插入图片描述

1.1.2.elasticsearch采用倒排索引:

  • 文档(document):每条数据就是一个文档
  • 词条(term):文档按照语义分成的词语

在这里插入图片描述

在这里插入图片描述

什么是文档和词条

  • 每一条数据就是一个文档(doc) 扩展,定义文档的规矩就是索引库
  • 对文档中的内容分词,得到的语句就是词条(terms)

什么是正向索引

  • 基于文档id创建索引,查询词条是必须先找到文档,而后判断是否包含词条

什么是倒排索引

  • 对文档内容分词,对词条创建索引,并记录词条所在文档的消息,查询时先根据词条查询到文档id,而后获取到文档

1.1.3.文档

elasticsearch是面向文档存储的,可以是数据库中的一条商品数据,一个订单消息。文档数据会把小米,华为转换为json格式后存储在elasticsearch中

在这里插入图片描述

1.1.4.索引

  • 索引(index):相同类型的文档集合(做相同的划分)
  • 映射(mapping):索引中文档的字段约束消息,类似表的结构约束
    在这里插入图片描述
MySqlElasticsearch说明
Table(table structure)Index索引,就是文档的集合,类似数据库的表
RowDocument文档,就是一条条的数据,类似数据库中的行(Row),文档都是JSON格式
ColumnField(字段)字段,就是JSON文档中的字段,类似数据库的列
SchemaMappingMapping是索引中文档的约束,列如字段类型约束。类似数据库的表结构
SQLDSLDSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD

1.1.5.结构

Mysql:擅长事务类型操作,可以确保数据的安全和一致性(适用于支付之类的需要安全的场景)

Elasticsearch:擅长海量数据的搜索、分析、计算(适用于页面搜索,查询商品之类的海量搜索)

在这里插入图片描述

1.2.总结(Elasticsearch):

  • 文档:一条数据就是一个文档,es中以json格式表示
  • 字段:Json文档中的字段
  • 索引:同类型文档的集合
  • 映射:索引中文档的约束,比如字段名称、类型

elasticsearch于数据库的关系:

  • 数据库负责事务类型操作
  • elasticsearch负责海量数据的搜索、分析、计算。

1.2.1.运行

部署kibana容器,因此需要让es和kibana容器互联,首先需要建立一个网络

docker network create es-net
  • 1

运行docker命令,部署单点es:

docker run -d \  
--name es \
-e "ES_JAVA_OPTS=-Xms1024m  -Xmx1024m" \
-e "discovery.type=single-node" \
-v /usr/elasticsearch/es-data:/usr/share/elasticsearch/data \
-v /usr/elasticsearch/es-plugins:/usr/share/elasticsearch/plugins  \
--privileged \
--network es-net \
-p 9200:9200 \
-p 9300:9300 \
elasticsearch:7.12.1	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

解释

docker run -d \  #-d后台运行
	--name es \ #指定名字
	-e "ES_JAVA_OPTS=-Xmx512m  -Xmx512m" \  #配置环境变量设置内存
	-e "discovery.type=single-node" \	#运行模式是单点模式,一般是集群模式
	-v es-data:/usr/share/elasticsearch/data \ #挂载数据卷,冒号前面的需要自己去虚拟机创建,如果不指定路径则会被创建到docker根目录的volume文件夹下
	-v es-plugins:/usr/share/elasticsearch/plugins  \
	--privileged \  
	--network es-net \ #让es容器加入网络
	-p 9200:9200 \ #暴露在http下的端口,供用户访问
	-p 9300:9300 \	#暴露在内部,es各个节点互联的端口
elasticsearch:7.12.1	#最后指定版本
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

1.2.2.部署Kibana

kibana可以给我们提供一个elasticsearch的可视化界面,便于我们学习。Elasticsearch和Kibana的版本需要保持一致

运行docker命令,部署Kibana

docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=es-net \
-p 5601:5601 \
kibana:7.12.1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • –network es-net:加入一个名为es-net的网络中,于elasticsearch再同一网络中
  • -e ELASTICSEARCH_HOSTS=http://es:9200 :设置elasticsearch的地址,因为kibana已经与elasticsearch再一个网络,因此可以用容器名直接访问elasticsearch

1.2.3.分词器

es在创建倒排索引时需要对文档分词;在搜索时,需要对用户输入内容分词。但默认的分词规则对中文处理并不友好。我们在kibana的DevToos中测试

POST /_analyze
{
"analyzer":"standard",
"text":"今天天气不错"
}
  • 1
  • 2
  • 3
  • 4
  • 5

语法说明

  • POST请求方式
  • /代表 192.168.26.xx:9200定位elasticsearch的服务地址斜杠省略,因为前面和elasticsearch关联了主要就是在这个环境变量上-e ELASTICSEARCH_HOSTS=http://es:9200 \
  • 请求参数JSON
    • analyzer:分词器类型,默认是standard标准
    • text:要分词的内容

下载ik分词器

  • 在官网下载ik分词器 https://github.com/medcl/elasticsearch-analysis-ik/releases

  • 解压放到在es的data目录中

    这里的data目录是之前es所挂载的那个目录,如果没有挂载的话就要开启容器之后使用命令

    docker exec -it xxx(自己创建的容器名称) bash
    
    • 1
  • 重启es容器 docker restart es配置完成

ik分词器的分析器参数

  1. ik_smart :最少切分,占用空间小,分出每句话中的中文或者词语
  2. ik_max_word:最细切分,占用空间大,分出每句话中所有可能的中文和词语

底层就是在内部庞大的数据字典中检索

当需要拓展或限制ik分词器中的内容时,可以去ik分词器目录中的ikAnalyzer.cfg.xml文件中修改配置

1.3.总结(分词器)

1.3.1.分词器的作用是什么

  • 创建倒排索引时对文档分词
  • 用户搜索时,对输入内容分词

1.3.2.ik分词器有几种模式

  • 两种
    • ik_smart
    • ik_max_word

1.3.3.ik分词器如何拓展和停止词条

  • 在ik分词器中的config目录下配置ikAnalyzer.cfg.xml文件中配置本地或者远程的扩展和停止词条
  • 一个值一行

1.4.索引库操作

1.4.1.mapping映射属性

mapping映射是对索引库中文档的约束,常见的mapping属性包括

{
"age":xxx
"weight":xxx
"isMarried":xxxx
"info":xxx
"name":{
		"firstname":xxx
		"lastname":xxx
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • type:字段数据类型,常见的简单类型有
    • 字符串:text(可分词的文本)、keyword(精准值,列如:品牌、国家、ip地址)
    • 数值:long、integer、short、byte、double、float
    • 布尔:boolean
    • 日期:date
    • 对象:object
      • properties:该字段的子属性
  • index:是否创建索引,默认为true
  • analyzer:使用那种分词器

1.4.1.总结(mapping和type)

  • mapping常见的属性有那些
    • type:数据类型
    • index:是否索引,默认true
    • analyzer:分词器
    • properties:子字段
  • type常见的有那些
    • 字符串:text,keyword
    • 数字:long、integer、short、byte、double、float
    • 布尔:boolean
    • 日期:date
    • 对象:object (对象下面有properties子对象)

1.4.2.索引库的CRUD

创建索引库

PUT /索引名
{
	"mappings":{
		"properties":{
			根据自己的需要创建字段
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

查看、删除索引库

GET /索引名

DELETE /索引名
  • 1
  • 2
  • 3

修改索引库

索引库和mapping一旦创建无法修改,但是可以添加新的字段

PUT /索引名/_mapping
{
	"properties":{
		"新字段名":{
			"type":"integer"
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

1.4总结(索引库的操作)

索引库操作有那些

  • 创建索引库:PUT /索引名
  • 查询索引库:GET /索引名
  • 删除索引库:DELETE /索引名
  • 添加字段:PUT/索引名/_mapping ,但是不能修改内容,只能添加

1.5.文档操作

1.5.1.添加文档(创建到哪个库中)

新增文档的DSL语法如下:

POST /索引库名(实际上类似于表,这个索引规定了表结构)/_doc/1(这是指定的id,方便以后删除和查找)
  • 1

1.5.2.查看文档语法

GET /索引库名/_doc/1(刚才创建文档所指定的id)
  • 1

1.5.3.删除文档语法

DELETE /索引库名/_doc/1(刚才创建文档所指定的id)
  • 1

1.5.4.修改文档

方式一:全量修改,会删除旧文档,添加新文档(特点,既能新增也能修改,检索对应id来修改,如果没有对应的id则直接新建)

PUT /索引名/_doc/id
{
	"字段1":xxx,
	"字段2":xxx,
	etc
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

方式二:局部修改,修改指定字段值(优先使用)

POST /索引名/_update/文档id
{
	"doc":{
		"字段名":"新的值"
		etc
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

1.5.总结(文档操作有那些)

  • 创建文档:POST /索引名/__doc/文档id 或者PUT /索引名/_doc/文档id

    后者是先删除在创建,实际开发中选择POST
    
    • 1
  • 查询文档:GET /索引名/_doc/文档id

  • 删除文档:DELETE /索引名/_doc/文档id

  • 修改文档:

    • 全局修改:PUT /索引名/_doc/文档id
    • 局部修改:POST /索引名/_update/文档id{“doc”:{字段}}

1.6.RestClient操作索引库(简化操作的三方工具)

ES官方提供了各种不同语言的客户端,用来操作ES,这些客户端的本质就是组装DSL语句,通过http请求发送给ES。

1.6.1.案例利用JavaRestClient实现创建、删除索引库,判断索引库是否存在

  1. 导入资料demo

  2. 分析数据结构,定义mapping属性

    mapping要考虑的问题

    ​ 字段名、数据类型、是否参与搜索、是否分词、如果分词,分词器是什么

    ES中支持两种地理坐标数据类型
    	geo_point:由维度(latitude)和经度(longitude)确定的一个点列如”xx.xxxx ,xx.xxxx“
    	
    	geo_shape:有多个geo_point组成的复杂几何图形,列如一条直线
    	linestring(-77.xxxxxxx 38.xxxxx, -77.xxxxx  38.xxxxxx)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    字段拷贝可以使用copy_to属性将当前字段拷贝到指定字段。
    "all":{
    	"type":"text",
    	"analyzer":"ik_max_word"
    },
    
    "brand":{
    	"type":"keyword",
    	"copy_to":"all"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  3. 初始化JavaRestClient

    1. 引入es的RestHighLevelClient依赖

        <dependency>
                  <groupId>org.elasticsearch.client</groupId>
                  <artifactId>elasticsearch-rest-high-level-client</artifactId>
              </dependency>
      
      • 1
      • 2
      • 3
      • 4
    2. 因为SpringBoot默认的ES版本是7.6.2,所以我们需要覆盖默认的ES版本

      在pom文件中加入这句话强制控制版本,版本不一会导致报错
      
      <properties>
      	<elasticsearch.version>7.12.1</elasticsearch.version>
      </properties>
      
      • 1
      • 2
      • 3
      • 4
      • 5
    3. 初始化RestHighLevelClient

       private RestHighLevelClient client;
      
          @Test
          void testInit(){
              System.out.println(client);
          }
      
          //当你需要这个RestHighLevelClient的时候,就不用每次都去创建一个关联的连接
          @BeforeEach
          void setUp() {
              this.client=new RestHighLevelClient(RestClient.builder(
                 HttpHost.create("http://192.168.26.131:9200")
              ));
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
  4. 利用JavaRestClient创建索引库

    //1.创建Request对象
    CteateIndexRequest request=new CreateIndexRequest("索引名");  //这里这个名字就是索引的名字
    //2.请求参数,MAPPING_TEMPLATE是静态字符串常量,内容是创建索引库的DSL语句
    request.source(MAPPING_TEMPLATE,XCntentType.JSON);
    //3.发起请求
    client.indices().create(request,RequestOptions.DEFAULT);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  5. 利用JavaRestClient删除索引库

    //创建一个DeleteIndexRequest
    DeleteIndexRequest delete=new DeleteIndexRequest("索引名");
    //调用client的删除命令
    client.indices().delete(delete,RequestOptions.DEFAULT);
    
    • 1
    • 2
    • 3
    • 4
  6. 利用JavaRestClient判断索引库是否存在

    //创建一个GetIndexRequest
    GetIndexRequest get=new GetIndexRequest("索引名");
    client.indices().exists(get,RequestOptions.DEFAULT);
    
    • 1
    • 2
    • 3

1.7.RestClient操作文档(SpringBoot)

1.7.1.新增文档

//1.首先准备数据,先从数据库中查询数据
调用方法得到比如说hotel数据
//2.准备request对象
IndexRequest request=new IndexRequest("索引名").id("文档id");
//3.准备JSON文档
request.source(JSON.toJSONString(hotel),XContentType.JSON);
//4.发送请求
client.index(request,RequestOptions.DEFAULT);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

1.7.2.查询文档

//1.准备request对象
GetRequest request=new GetRequest("索引名").id("文档id");
//2.发送请求
GetResponse response=client.get(request,RequestOptions.DEFAULT);
//3.解析响应结果
String json=response.getSourceAsString();
HotelDoc result=JSON.parseObject(json,HotelDoc.class);
Sysem.out.print(result);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

1.7.3.删除文档

//准备request对象
DeleteRequest request=new DeleteRequest("索引名","文档id");

//发送请求
client.delete(request,RequestOptions.DEFAULT);
  • 1
  • 2
  • 3
  • 4
  • 5

1.7.4.修改文档

局部更新,这个不会删除
POST /hotel/_update/id
{
	"doc":{
		字段名
	}
}
全局更新,先删除在创建
PUT /hotel/_doc/id
{
	"字段1":{
	
	},
	"字段2":{
	
	}
}
以上是在es内部的写法

在JAVA中
//1.准备request对象
UpdateRequest request=new UpdateRequest("hotel","xxxx");   //第二个参数是需要更新的id
//2.修改的数据是什么
request.doc("price",12313,"city","Washington");  //格式key value对应,逗号分隔
//3.发送请求
client.update(request,RequestOptions.Default);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

1.7.5.批量导入文档

//从数据库中批量查询数据
Hotel hotel=xxxservice.list();
//1.创建request
BulkRequest request=new BulkRequest();
//2.准备参数,添加多个新增的Request
for(Hotel hotel : hotels){
	HotelDoc hotelDoc=new HotelDoc(hotel);
	request.add(new IndexRequest("hotel")
	.id(hotel.getId().toString())
	.source(JSON.toJSONString(hotelDoc),XContentType.JSON)) //转换文档类型
}

//3.发送请求
client.bulk(request,RequestOptions.DEFAULT)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

fastJSON可以把数据序列化为JSON的格式具体操作为

JSON.toJSONString()   把想要序列化的值放入其中
  • 1

在es中批量查询的语句

GET /索引名/_search
  • 1

1.8.Elasticsearch搜索功能

1.8.1.DSL查询分类

分类用法命令
查询所有查询出所有数据,一般测试用match_all
全文检索full-text利用分词器对用户输入内容分词,然后再去倒排索引库中匹配match_query multi_match_query
精确查询根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段ids range term
地理查询根据经纬度查询geo_distance geo_bounding_box
复合查询复合查询可以将上述各种查询天配件结合起来,合并查询条件bool function_score

1.8.1.总结

DSL的基本语法

GET /索引名/_search
{
	"query":{
		"查询类型":{"FIELD":"TEXT"}    
		}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

1.8.2.全文检索查询

match查询:全文检索查询的一种,会对用户输入内容分词,然后去倒排索引库检索

GET /索引名/_search
{
	"query":{
		"match":{
			"FIELD":"TEXT"
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

multi_match:于match查询类似,但他允许同时查询多个字段

GET /索引名/_search
{
	"query":{
		"multi_match":{
			"query":"",
			"字段名1":["查询条件1","查询条件2"]
			
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

1.8.2.总结

match和multi_match的区别是什么

  • match:根据一个字段查询
  • multi_match:根据多个字段查询,参与查询字段越多,查询性能越差,所以在创建索引时就要把今后可能需要的查询的东西copy到一个中,得以提升效率,copy_to

1.8.3.精准查询

查找keyword、数值、日期、boolean等类型字段,所以不会对搜索条件分词,常见的类型如下

命令解释
term根据词条精确值查询
range根据值的范围查询

语法

term:
GET /索引名/_search
{
	"query":{
		"term":{
			"field":{
				"value":想要精确查找的值
			}
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
range
GET /索引名/_search
{
	"query":{
		"range":{
			"field":{
				"gte":大于等于的数的范围,   # greater than equal      这里还可以选择仅大于就是gt  greater than	
				"lte":小于等于的数的范围    # less than equal         同理小于就是lt       	 less than	
			}
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

1.8.3.总结

精确查询常见的有那些

  • term查询:根据词条精确匹配,一般搜索keyword类型、数值类型、布尔类型、日期类型字段
  • range查询:根据数值范围查询,可以是数值、日期的范围

1.8.4.地理坐标查询

根据经纬度查询。常见的使用常见包含

  • 搜索附件的酒店

在这里插入图片描述

  • 搜索附近的出租车

在这里插入图片描述

  • 搜索附近的人

根据经纬度查询,官方文档

  • geo_bounding_box(geography bounding box 地理边界框):查询geo_point值落在某个矩形范围的所有文档

ES中的语法

GET /索引名/_search
{
	"query":{
		"geo_bounding_box":{
			"FIELD":{
				"top_left":{     #左上角的坐标
					"lat":xxx,   #latitude 纬度
					"lon":xxx	 #longitude 经度
				},
				"bottom_right":{ #右下角的坐标
					"lat":xxx,
					"lon":xxx
				}
			}
		}
	}
}
解析:得到左上和右下的坐标之后四条线连接这两个点得到一个检索的范围,如下图,方框内则是你定位坐标内的范围
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在这里插入图片描述

  • geo_distance:查询到指定中心点小于某个距离值的所有文档

ES中的用法

GET /索引名/_search
{
	"query":{
		"geo_distance":{
			"distance":"xxxkm",
			"FIELD":"xxx.xx,xxx.xx" #类型为geo_point
		}
	}
}
解析:以一个坐标点为中心,以定义的distance为半径所画圆的范围,如下图
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

1.8.5.复合查询

复合(compound)查询:复合查询可以将其他简单查询组合起来,实现更复杂的搜索逻辑,比如

  • function score:算分函数查询,可以控制文档相关性算分,控制文档排名

使用function score query

GET /索引名/_search
{
	"query":{
		"function_score":{
			"query":{"match":{"all":"外滩"}},
			"functions":[
				{
					"filter":{"term":{"id":"1"}},
					"weight":10
				}
			],
			"boost_mode":"multiply"
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述

复合查询Boolean Query

布尔查询是一个或多个子句的组合,子查询的组合方式有:

  • must:必须匹配每个子查询,类似”与“

  • should:选择性匹配子查询,类似”或“

  • must_not:必须不匹配,不参与算分,类似”非“

  • filter:必须匹配,不参与算分

    GET /hotel/_search
    {
    	"query":{
    		"bool":{
    			"must":[],
    			"should":[],
    			"must_not":[],
    			"filter":[]
    		}
    	}
    }
    再组合查询pool中的子查询中可以写精确查询比如,Term和Range
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

1.8.5.总结

elasticsearch中的相关性打分算法是什么

  • TF-IDF:在elasticsearch5.0之前,会随着词频增加而越来越大
  • BM25:在elasticsearch5.0之后,会随着词频增加而增大,但增长曲线会趋于水平

function_score定义的三要素

  • 过滤条件:那些文档要加分
  • 算分函数:如何计算function score
  • 加权方式:function score于query score如何计算

1.9.elasticsearch搜索排序处理

elasticsearch支持对搜索结果排序,默认是根据相关度算分(_score)来排序,可以排序字段类型有,keyword类型、数值类型、地理坐标类型、日期类型等。

1.9.1.排序

GET /indexName/_search
{
	"query":{
		"match_all":{}
	},
	"sort":{
		"FIELD":"desc"   //排序字段的方式desc降序和ASC升序
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

根据地理位置排序

GET /indexName/_search
{
	"query":{
		"match_all":{}
	},
	"sort":[
		{
			"_geo_distance":{
				"FIELD":"维度lat","经度lon"
				"order":"desc/asc",
				"unit":"xxxkm"
			}
		}
	]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

案例:对酒店数据按照用户评价降序排序,评价相同按照价格升序排序

GET /hotel/_search
{
	"query":{"match_all":{}},

	"sort":[
		{"score":"desc"},
		
		{"price":asc}
	]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

案例:实现对酒店数据按照到你的位置坐标的距离升序排序

GET /index/_search
{
	"query":{"match_all":{}},
	
	"sort":[
		{"_geo_distance":{
			"order":"asc",    //absc降序   asc升序
			"unit":"km",      //距离单位
			"location":{"lat":33.1,"lon":121.5} // 字段,维度和经度
		}}
		
	]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

1.9.2.分页

elasticsearch默认情况下只返回top10的数据,而如果要查询更多的数据就需要修改分页参数了。

作为分布式第三方插件,ES会面临深度分页的问题,列如按price排序后,获取from=990,size=10的数据

在这里插入图片描述

ES的处理逻辑是

  1. 首先将每个数据分片上都排序并查询前1000条文档
  2. 然后将所有节点的结果聚合,在内存中重新排序选出前1000条文档
  3. 最后从这1000条数据再按照从第990条开始的10条数据

如果搜索页数过深,或者结果集(From+size)越大,对内存和CPU的消耗也越高,因此ES设定结果集查询的上限是10000条。

ES深度分页解决方案

  • search after:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据
  • scroll:原理将排序数据形成快照,保存的内存

1.9.2.总结

from+size

  • 优点:支持随机翻页
  • 缺点:深度分页问题,默认查询上线(from+size)=10000
  • 场景:百度,京东,谷歌,淘宝

after search:

  • 优点:没有查询上限(单次查询的size不超过10000)
  • 缺点:只能向后逐页查询,不支持随机翻页
  • 场景:没有随机翻页需求的搜索,例如手机向下滚动翻页

scroll:

  • 优点:没有查询上限(单次查询的size不超过10000)
  • 缺点:会有额外内存消耗,并且搜索结果是非实时的
  • 场景:海量数据的获取或迁移,从ES7.1开始不推荐,建议用after search方案

1.9.3.ES中高亮显示

再搜索结果中把搜索关键字突出显示

  • 将搜索结果中的关键字用标签标记出来
  • 再页面中给标签添加css样式

2.RestClient查询文档(Springboot中使用ES)

2.1.快速入门

//准备request
        SearchRequest request=new SearchRequest("hotel");
        //准备DSL语句
        request.source().query(QueryBuilders.matchAllQuery());
        //发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        //解析结果
        SearchHits searchHits = response.getHits();
        //查询的总条数
        TotalHits totalHits = searchHits.getTotalHits();
        //查询的结果数组
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            //得到source
            String json = hit.getSourceAsString();
            System.out.println(json);

        }
        System.out.println(response);
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在这里插入图片描述

2.1.总结

  • 准备searchRequest对象

  • 准备Request.source(),相当于ES中内部的DSL语句

    DSL语句:
    GET /索引名/_search
    {
    	"query":{}
    }
    JAVA代码
    request.source().query(QueryBuilders.matchAll)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 发送请求

    SearchResponse response=client.search(request,RequestOptions.DEFAULT);
    
    • 1
  • 得到ES端发过来的JSON数据类似于

    反馈的数据
    took133
    timed_outfalse
    _shards
    total1
    successful1
    skipped0
    failed0
    hits
    total
    max_score2.0645075
    hits
    0
    _indexhotel
    _type_doc
    _id339952837
    _score2.0645075
    _source
    address良乡西路7号
    brand如家
    business房山风景区
    city北京
    id339952837
    location39.73167, 116.132482
    name如家酒店(北京良乡西路店)
    pichttps://m.tuniucdn.com/fb3/s1/2n9c/3Dpgf5RTTzrxpeN5y3RLnRVtxMEA_w200_h200_c1_t0.jpg
    price159
    score46
    starName二钻
    highlight
    name
    <em>如家</em>酒店(北京良乡西路店)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
  • 通过JAVA取出值

    SearchHist searchHits=response.getHits();
    for (SearchHit searchHit: searchHits){
    //得到所有的数据			  System.out.println(searchHit.getSourceAsString());
    }
    
    • 1
    • 2
    • 3
    • 4

2.2.match查询

分为match和multi_match两种

QuerySyntax on JAVASyntax on ES
单字段查询queryBuilders.matchQuery(“FIeld”,“Text”)“match”:{“FIELD”:“Text”}
多字段查询queryBuilders.multiMatchQuery(“Text”,“FIeld1”,“Field2”)“multi_match”:{“Text”,“FIELD1”,“FIELD2”}

Ctrl+Alt+M 抽取JAVA中的代码

2.3.精确查询

常见的精确查询是term和range

QuerySyntax on JAVASyntax on ES
TermQueryBuilders.termQuery(“FIELD”,“Text”)“term”:{“FIELD”:{“indexName”:“query condition”}}
RangeQueryBuilders.RangeQuery(“FIELD”).gte(“xxx”).lte(“xxx”)“range”:{“FIELD”:{“gte”:xxx,“lte”:xxx}}

2.4.复合查询

首先需要创建一个复合查询

BoolQuery boolQuery=QueryBuilders.boolQuery(); 这里涉及到嵌套写法,是再bool的基础语法上嵌套精确查询

QuerySyntax on JAVASytax on ES
MustboolQuery.must(QueryBuilders.termQuery(“Field”,“Text”));
FilterboolQuery.Filter(QueryBuilders.rangeQuery(“Field”).lte(xxx))

构建查询条件,只需要一个类QueryBuilders

2.5.排序、分页

搜索结果的排序和分页是于query同级的参数

//查询
request.source().query(QueryBuilders.matchAllQuery());
//分页
rquest.source().from(0).size(5);
//价格排序
request.source().sort("price",desc)
abscent   descent
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
GET /indexName/_search
{
	"query":{
		"match_all":{}
	},
	
	"from":0,
	"size":5,
	"sort":[
		{
			"FIELD":"desc"
		},	
	]

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2.6.高亮

高亮API包括请求DSL构建和结果解析两部分。

request.source().highlighter(new HightBuilder()
.field("name")
//是否需要于查询字段匹配
.requireFieldMatch(false)
)
  • 1
  • 2
  • 3
  • 4
  • 5
GET /hotel/_search
{
	"query":{
		"match":{
			"all":"如家"
		}
	},
	"highlight":{
		"fields":{
			"name":{
				"require_field_match":"false"
			}
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

完成了基于RestClient的语句实现的排序、分页和高亮

completed order、separate page、highlight based on the statement of RestClient implemented

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/421686
推荐阅读
相关标签
  

闽ICP备14008679号