• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

ElasticSearvch搜索引擎以和Spring Data Elasticsearch

武飞扬头像
啊啊啊杨
帮助3

Elasticsearch客户端

客户端介绍

在elasticSearch官网中提供了各种语言的客户端
https://www.elastic.co/guide/en/elasticsearch/clie
nt/index.html

1创建demo工程

初始化项目

学新通

pom文件

注意 我们这里直接导入了SpringBoot启动器。不过还需要手动引入elasticsearch的High-level-Rest-Client的依赖

<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>com.谷歌.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<!--Apache开源组织提供的用于操作JAVA BEAN的工具包-->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.1</version>
</dependency>
<!--ES高级Rest Client-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>6.4.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

学新通

2索引库及映射

创建索引库的同时,我们也会创建type及其映射关系 ,但是这些操作不建议使用java客户端完成,其原因如下:
索引库和映射往往是初始化完成,,不需要频繁操作,不如提前配置好
官方提供的创建索引库及映射非常繁琐,需要通过字符串拼接json结构
学新通
因此 我们还是建议是用之前学过的Rest风格API实现
我们通过一个商品数据库为例来创建索引库:

package com.ywc.es.pojo;
public class Product {
private Long id;
private String title; //标题
private String category;// 分类
private String brand; // 品牌
private Double price; // 价格
private String images; // 图片地址
}

分析一下数据结构
我们可以编写这样的映射配置:

PUT /lagou
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
},
"mappings": {
"item": {
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"category": {
"type": "keyword"
},
"brand": {
"type": "keyword"
},
"images": {
"type": "keyword",
"index": false
},
"price": {
"type": "double"
}
}
}
}
}
学新通

3.索引库操作

有了索引库,我们接下来看看如何新增索引数据
这操作MYSql数据库

  1. 获得数据库连接
  2. 完成数据的增删改查
  3. 释放资源

初始化客户端

先编写一个测试类

然后再@Before的方法中编写client初始化

public class ElasticSearchTest{
   private RestHighLevelClient client;
   //json工具
   private Gson gson= new Gson();

@Before 
public void init(){

//初始化HighLevel的客户端
client= new RestHighLevelClient(
RestClient。builder(
new HttpHost("127.0.0.1",9201,"http"),
new HttpHost("127.0.0.1",9201,"http"),
new HttpHost("127.0.0.1",9201,"http"))



);
}



@After
public void close() throws IOException 
{
//关闭客户端
client.close();
}



}
学新通

看下响应:
response = IndexResponse[
index=item,
type=docs,
id=1,
version=2,
result=created,
seqNo=1,
primaryTerm=1,
shards={“total”:2,“successful”:2,“failed”:0}
]

查看文档:
根据Rest风格 ,查看应该是根据id 进行get查询,难点是对结果的解析:


@Test
public void testFindIndex() throws IOException{
//创建get请求,并根据id指定
GetRequest request=new GetRequest("ywc","item","1");
// 查询,得到响应
GetResponse response = client.get(request, RequestOptions.DEFAULT);
// 解析响应,应该是json
String source = response.getSourceAsString();
// 转换json数据
Product item = gson.fromJson(source, Product.class);
System.out.println(item);
}

新增文档
实例:

@Test
public void testInsert() throws IOException {
//1.文档数据
Product product = new Product();
product.setBrand("华为");
product.setCategory("手机");
product.setId(1L);
product.setImages("http://image.huawei.com/1.jpg");
product.setPrice(5999.99);
product.setTitle("华为P50就是棒");
//2.将文档数据转换为json格式
String source = gson.toJson(product);
//3.创建索引请求对象 访问哪个索引库、哪个type、指定文档ID
//public IndexRequest(String index, String type, String id)
IndexRequest request = new
IndexRequest("ywc","item",product.getId().toString());
request.source(source, XContentType.JSON);
//4.发出请求
IndexResponse response = restHighLevelClient.index(request,
RequestOptions.DEFAULT);
System.out.println(response);
}
学新通

看下响应:

response = IndexResponse[
index=item,
type=docs,
id=1,
version=2,
result=created,
seqNo=1,
primaryTerm=1,
shards={“total”:2,“successful”:2,“failed”:0}
]

修改文档:

新增时,如果传递的Id是已经存在的,则会完成修改操作,如果不存在,则是新增

删除文档

根据Id删除


@Test
public void testDeleteIndex(){
//准备删除的请求  ,参数为id
DeleteRequest request=new DeleteRequest("ywc","item","1");
//发起请求
DeleteResponse response=client.delete(request,RequestOptions.DEFAULT);
System.out.println(response);


}

搜索数据

查询所有的match_all
@Test
public void testMatchAll() throws IOException {
// 创建搜索对象
SearchRequest request = new SearchRequest();
// 查询构建工具
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 添加查询条件,通过QueryBuilders获取各种查询
sourceBuilder.query(QueryBuilders.matchAllQuery());
request.source(sourceBuilder);
// 搜索
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 解析
SearchHits hits = response.getHits();
SearchHit[] searchHits = hits.getHits();
for (SearchHit hit : searchHits) {
// 取出source数据
String json = hit.getSourceAsString();
// 反序列化
Product item = gson.fromJson(json, Item.class);
System.out.println("item = "   item);
}
}
学新通

关键字搜索match

private void basicQuery(SearchSourceBuilder sourceBuilder) throws IOException {
// 创建搜索对象
SearchRequest request = new SearchRequest();
request.source(sourceBuilder);
// 搜索
SearchResponse response = client.search(request,
RequestOptions.DEFAULT);
// 解析
SearchHits hits = response.getHits();
SearchHit[] searchHits = hits.getHits();
for (SearchHit hit : searchHits) {
// 取出source数据
String json = hit.getSourceAsString();
// 反序列化
Product item = gson.fromJson(json, Item.class);
System.out.println("item = "   item);
}
}
学新通
@Test
public void testMatchQuery() throws IOException {
// 查询构建工具
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 添加查询条件,通过QueryBuilders获取各种查询
sourceBuilder.query(QueryBuilders.matchQuery("title", "手机"));
basicQuery(sourceBuilder);
}

范围查询

学新通

@Test
public void testRangeQuery() throws IOException {
// 查询构建工具
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 添加查询条件,通过QueryBuilders获取各种查询
sourceBuilder.query(QueryBuilders.rangeQuery("price").gt(1000).lt(4000));
basicQuery(sourceBuilder);
}

source过滤

默认情况下,索引库中所有数据都会返回,如果我们想只返回部分字段,可以通过source filter来控
制。

@Test
public void testSourceFilter() throws IOException {
// 创建搜索对象
SearchRequest request = new SearchRequest();
// 查询构建工具
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 添加查询条件,通过QueryBuilders获取各种查询
sourceBuilder.query(QueryBuilders.matchAllQuery());
// 添加source过滤
sourceBuilder.fetchSource(new String[]{"id", "title", "price"}, null);
basicQuery(sourceBuilder);
}

排序

public void testSortQuery() throws IOException {
// 创建搜索对象
SearchRequest request = new SearchRequest();
// 查询构建工具
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 添加查询条件,通过QueryBuilders获取各种查询
sourceBuilder.query(QueryBuilders.matchAllQuery());
// 添加排序
sourceBuilder.sort("price", SortOrder.ASC);
basicQuery(sourceBuilder);
}

分页

分页需要视图层传递两个参数给我们:
当前页:page
每页大小:size
而elasticsearch中需要的不是当前页,而是起始位置,还好有公式可以计算出:
from–>起始位置,0表示第一条
起始位置:start = (page - 1) * size
第一页:(1-1)*5 = 0
第二页:(2-1)*5 = 5

@Test
public void testSortAndPageQuery() throws IOException {
// 创建搜索对象
SearchRequest request = new SearchRequest();
// 查询构建工具
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 添加查询条件,通过QueryBuilders获取各种查询
sourceBuilder.query(QueryBuilders.matchAllQuery());
// 添加排序
sourceBuilder.sort("price", SortOrder.ASC);
// 添加分页
int page = 1;
int size = 3;
int start = (page - 1) * size;
// 配置分页
sourceBuilder.from(start);
sourceBuilder.size(3);
basicQuery(sourceBuilder);
}
学新通

Spring Data Elasticsearch

Spring提供的elasticsearch组件:Spring Data Elasticsearch:
Spring Data Elasticsearch(以后简称SDE)是Spring Data项目下的一个子模块。
Spring Data 的使命是给各种数据访问提供统一的编程接口,不管是关系型数据库(如MySQL),还是非关系数据库(如Redis),或者类似Elasticsearch这样的索引数据库。从而简化开发人员的代码,提高开发效率。
Spring Data Elasticsearch的页面:https://projects.spring.io/spring-data-elasticsearch/
特征:
支持Spring的基于 @Configuration 的java配置方式,或者XML配置方式
提供了用于操作ES的便捷工具类 ElasticsearchTemplate 。包括实现文档到POJO之间的自动智能映射。
利用Spring的数据转换服务实现的功能丰富的对象映射
基于注解的元数据映射方式,而且可扩展以支持更多不同的数据格式,可以定义JavaBean:类名、属性
根据持久层接口自动生成对应实现方法,无需人工编写基本操作代码(类似mybatis,根据接口自动得到实现)。当然,也支持人工定制查询

配置SpringDataElasticsearch

我们在pom文件中,引入SpringDataElasticsearch的启动器:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

然后,只需要在resources下新建application.yml文件,引入elasticsearch的host和port即可

spring:
	data:
		elasticsearch:
			cluster-name: lagou-elastic
			cluster-nodes: 127.0.0.1:9301,127.0.0.1:9302,127.0.0.1:9303

需要注意的是,SpringDataElasticsearch底层使用的不是Elasticsearch提供的RestHighLevelClient,
而是TransportClient,并不采用Http协议通信,而是访问elasticsearch对外开放的tcp端口,我们之前
集群配置中,设置的分别是:9301,9302,9303
添加引导类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
	public class EsApplication {
	public static void main(String[] args) {
		SpringApplication.run(EsApplication.class,args);
}
}
//准备一个pojo对象
//然后准备一个新的实体类,作为下面与索引库对应的文档:
@Data
public class Product {
private Long id;
private String title; //标题
private String category;// 分类
private String brand; // 品牌
private Double price; // 价格
private String images; // 图片地址
}
//我们先创建一个测试类,然后注入ElasticsearchTemplate:
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringElasticsearchTest {
@Autowired
private ElasticsearchTemplate esTemplate;
}

发现没有,创建索引库需要指定的信息,比如:索引库名、类型名、分片、副本数量、还有映射信息都
没有填写,这是怎么回事呢?
实际上,与我们自定义工具类类似,SDE也是通过实体类上的注解来配置索引库信息的,我们需要在
Goods上添加下面的一些注解:

@Document(indexName = "lagou", type = "product", shards = 3, replicas = 1)
public class Product {
@Id
private Long id;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title; //标题
@Field(type = FieldType.Keyword)
private String category;// 分类
@Field(type = FieldType.Keyword)
private String brand; // 品牌
@Field(type = FieldType.Double)
private Double price; // 价格
@Field(type = FieldType.Keyword, index = false)
private String images; // 图片地址
}

几个用到的注解:
@Document:声明索引库配置
indexName:索引库名称
type:类型名称,默认是“docs”
shards:分片数量,默认5
replicas:副本数量,默认1
@Id:声明实体类的id
@Field:声明字段属性
type:字段的数据类型
analyzer:指定分词器类型
index:是否创建索引

创建索引库

创建索引有单个创建和批量创建之分 先来看单个创建

@Autowired
private GoodsRepository goodsRepository;
@Test
public void addDocument(){
Goods goods = new Goods(1L, "小米手机9", " 手机",
"小米", 3499.00, "http://image.ywc.com/13123.jpg");
// 添加索引数据
goodsRepository.save(goods);
}

再来看批量创建:

@Test
public void addDocuments(){
// 准备文档数据:
List<Goods> list = new ArrayList<>();
list.add(new Goods(1L, "小米手机7", "手机", "小米", 3299.00, "/13123.jpg"));
list.add(new Goods(2L, "坚果手机R1", "手机", "锤子", 3699.00, "/13123.jpg"));
list.add(new Goods(3L, "华为META10", "手机", "华为", 4499.00, "/13123.jpg"));
list.add(new Goods(4L, "小米Mix2S", "手机", "小米", 4299.00, "/13123.jpg"));
list.add(new Goods(5L, "荣耀V10", "手机", "华为", 2799.00, "/13123.jpg"));
// 添加索引数据
goodsRepository.saveAll(list);
}

原生查询


如果觉得上述接口依然不符合你的需求,SDE也支持原生查询,这个时候还是使用 ElasticsearchTemplate 而查询条件的构建是通过一个名为 NativeSearchQueryBuilder 的类来完成的,不过这个类的底层还 是使用的原生API中的 QueryBuilders 、 AggregationBuilders 、 HighlightBuilders 等工具。 需求: 查询title中包含小米手机的商品,以价格升序排序,分页查询:每页展示2条,查询第1页。 对查询结果进行聚合分析:获取品牌及个数


@Test
public void testNativeQuery(){
// 原生查询构建器
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 1.1 source过滤
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[0], new
String[0]));
// 1.2搜索条件
queryBuilder.withQuery(QueryBuilders.matchQuery("title", "小米手机"));
// 1.3分页及排序条件
queryBuilder.withPageable(
PageRequest.of(0, 2,
Sort.by(Sort.Direction.ASC, "price")));
// 1.4高亮显示
// queryBuilder.withHighlightBuilder(new HighlightBuilder().field("title"));
// 1.5聚合
queryBuilder.addAggregation(AggregationBuilders.terms("brandAgg").field("brand"
));
// 构建查询条件,并且查询
AggregatedPage<Goods> result = esTemplate.queryForPage(queryBuilder.build(),
Goods.class);
// 2、解析结果:
// 2.1分页结果
long total = result.getTotalElements();
int totalPages = result.getTotalPages();
List<Goods> list = result.getContent();
System.out.println("总条数 = "   total);
System.out.println("总页数 = "   totalPages);
System.out.println(list);
// 2.2.聚合结果
Aggregations aggregations = result.getAggregations();
Terms terms = aggregations.get("brandAgg");
terms.getBuckets().forEach(b -> {
System.out.println("品牌 = "   b.getKeyAsString());
System.out.println("count = "   b.getDocCount());
});
}



学新通

高亮展示:

自定義搜索結果映射

public class EsResultMapper implements SearchResultMapper {
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T>
aClass, Pageable pageable) {
// 记录总条数
long totalHits = response.getHits().getTotalHits();
// 记录列表(泛型) - 构建Aggregate使用
List<T> list = new ArrayList<>();
// 获取搜索结果(真正的的记录)
SearchHits hits = response.getHits();
for (SearchHit hit : hits) {
if (hits.getHits().length <= 0) {
return null;
}
// 将原本的JSON对象转换成Map对象
Map<String, Object> map = hit.getSourceAsMap();
// 获取高亮的字段Map
Map<String, HighlightField> highlightFields =
hit.getHighlightFields();
for (Map.Entry<String, HighlightField> highlightField :
highlightFields.entrySet()) {
// 获取高亮的Key
String key = highlightField.getKey();
// 获取高亮的Value
HighlightField value = highlightField.getValue();
// 实际fragments[0]就是高亮的结果,无需遍历拼接
Text[] fragments = value.getFragments();
// 因为高亮的字段必然存在于Map中,就是key值
map.put(key, fragments[0].toString());
}
// 把Map转换成对象
Gson gson = new Gson();
T item = gson.fromJson(gson.toJson(map), aClass);
list.add(item);
}
// 返回的是带分页的结果
return new AggregatedPageImpl<>(list, pageable, totalHits);
}
}
学新通

高亮实现:

@Test
public void testNativeQuery() {
// 原生查询构建器
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 1.1 source过滤
//queryBuilder.withSourceFilter(new FetchSourceFilter(new String[0], new
String[0]));
// 1.2搜索条件
queryBuilder.withQuery(QueryBuilders.matchQuery("title", "小米手机"));
// 1.3分页及排序条件
queryBuilder.withPageable(
PageRequest.of(0, 6,
Sort.by(Sort.Direction.ASC, "price")));
// 1.4高亮显示
//queryBuilder.withHighlightBuilder(new
HighlightBuilder().field("title"));
// 构建高亮查询
HighlightBuilder.Field field = new
HighlightBuilder.Field("title").preTags("<font style='color:red'>").postTags("
</font>");
queryBuilder.withHighlightFields(field); // 名字高亮
NativeSearchQuery build = queryBuilder.build();
// 1.5聚合
queryBuilder.addAggregation(AggregationBuilders.terms("brandAgg").field("brand"
));
// 构建查询条件,并且查询
AggregatedPage<Product> result =
template.queryForPage(queryBuilder.build(), Product.class,new EsResultMapper());
// 2、解析结果:
long total = result.getTotalElements();
int totalPages = result.getTotalPages();
List<Product> list = result.getContent();
list.stream().forEach(product -> System.out.println(product));
}
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfhikge
系列文章
更多 icon
同类精品
更多 icon
继续加载