初始化
查看官方网站文档
ES支持很多中语言的客户端,使用java REST Client
选择对应的版本。选择7.6。
提供了两个级别的客户端,选择高级的。
项目开始:
添加maven依赖之前,首先要是spring项目,我们直接使用springboot。
我们使用springboot,只需要添加对应的场景启动器,springboot会自动添加对应的依赖。
但是要注意的是,springboot仲裁了依赖版本,可能和我们需要的版本是不一致的,我们要自己调整版本。
<!-- 添加ES的场景启动器的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
如果依赖的版本和我们使用的ES的版本一致,不需要做任何调整,如果不一致,需要调整。
创建一个工程:
pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
</parent>
<groupId>com.st</groupId>
<artifactId>ES-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<!--覆盖mysql驱动版本-->
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 单元测试场景启动 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--添加ES的场景启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
</dependencies>
</project>
ESClient的使用**
准备一个索引:
PUT product
{
"mappings": {
"properties": {
"title":{
"type":"text",
"analyzer": "ik_max_word"
},
"sellpoint":{
"type":"text",
"analyzer": "ik_max_word"
},
"price":{
"type":"integer"
},
"created":{
"type": "date"
}
}
}
}
[1]添加索引**
/**
* @author:刘倩云
* @createTime:2021-04-13
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class ESApplicationTest {
//注入客户端
@Autowired
private RestHighLevelClient client;
//添加索引
@Test
public void testAddIndex() throws IOException {
//创建索引请求对象 POST /索引名字/类型/文档id
IndexRequest request = new IndexRequest("product");
// 设置文档id,如果不设置id会随机生成一个id
request.id("1");
//设置类型 在7.x之后,就建议使用默认的类型,而不需要设置类型,默认类型是"_doc" \
// request.type("emp");
//创建json数据
Map data = new HashMap<>();
data.put("title","Populele2二代智能尤克里里23英寸复合碳纤维材质小吉他学生乌克丽丽初学者ukulele 黑色");
data.put("sellpoint","【智能尤克里里】【7天价保】直降立省50元,到手价仅549!联系客服另赠惊喜好礼!八仓发货,极速送达!【立即抢购】");
data.put("price",500);
data.put("created",new Date());
//将map转换成json数据
String json = new ObjectMapper().writeValueAsString(data);
//添加数据 (json字符串,source类型)
request.source(json, XContentType.JSON);
//提交索引
IndexResponse response = client.index(request, RequestOptions.DEFAULT);
//这里的IndexResponse提供了大量的API方便我们获取添加之后响应的json数据
System.out.println(response);
}
}
上一个案例中,将map转换为json添加,实际上可以直接将map添加到索引库:
//添加索引 Map
@Test
public void testAddIndexMap() throws IOException {
//创建索引请求对象 POST /索引名字/类型/文档id
IndexRequest request = new IndexRequest("product");
// 设置文档id,如果不设置id会随机生成一个id
request.id("2");
// 创建数据
Map data = new HashMap();
data.put("title","KAKA卡卡 KUC-25D 尤克里里乌克丽丽ukulele单板桃花心木迷你小吉他23英寸款");
data.put("sellpoint","4.11大牌闪购~暖春特惠】【下单送首购礼金】【进阶款单板升级款相思木】【加购送教学陪练和精品豪礼】【京库配送】【蕞快当日达】~点击抢购");
data.put("price",498);
data.put("created",new Date());
//将数据添加到source
request.source(data);
//提交索引
IndexResponse response = client.index(request,RequestOptions.DEFAULT);
//这里的IndexResponse提供了大量的API方便我们获取添加之后响应的json数据
System.out.println(response);
}
RestHighLevelClient的index方法会返回一个IndexResponse对象,这个对象就是一个添加之后响应的json字符串。
提供了api可以让我们获取这个json中的所有的数据
直接key-value形式添加文档
//添加索引 key-value
@Test
public void testAddIndexKeyAndValue() throws IOException {
//创建索引请求对象 POST /索引名字/类型/文档id
IndexRequest request = new IndexRequest("product");
// 设置文档id,如果不设置id会随机生成一个id
request.id("3");
// 添加数据
request.source(
"title","雅马哈(YAMAHA)雅马哈吉他FG800VN美国型号单板民谣吉他木吉它复古木色亮光41英寸",
"sellpoint","【闪购狂欢】北美爆款FG800VN,闪购优惠260元~手价仅1939元,活动时间(4月11号0点-4月13号23点)抓紧加购!!点击",
"price",1999,
"created",new Date()
);
//提交索引
IndexResponse response = client.index(request,RequestOptions.DEFAULT);
//这里的IndexResponse提供了大量的API方便我们获取添加之后响应的json数据
System.out.println(response);
}
其他设置
设置超时时间:
//设置超时时间 为10s request.timeout(“10s”);
springboot对ESClient的自动配置
上面config的配置类手动创建的,实际的时候可以使用springboot自动配置。
在springboot的自动配置类:ElasticsearchRestClientAutoConfiguration
配置方式的查看:
我们可以在springboot的配置文件中添加配置:
spring:
#ES客户端配置#
elasticsearch:
rest:
#ES的服务器地址列表,如果是集群,就需要配置多个#
uris:
- http://192.168.37.190:9200
read-timeout: 30s
connection-timeout: 5s
将我们自己配置的ESClient注释。再测试。
批量的添加索引
//批量添加索引
@Test
public void testBulk() throws IOException{
//准备两套数据
Map data1 = new HashMap<>();
data1.put("title","Gibson吉普森Firebird火鸟Gallery美术馆3号 蜂鸟Doves鸽子民谣吉他 木吉他 SJ200 Silver Gallery");
data1.put("sellpoint","4.11大牌闪购~暖春特惠】【下单送首购礼金】【进阶款单板升级款相思木】【加购送教学陪练和精品豪礼】【京库配送】【蕞快当日达】~点击抢购");
data1.put("price",15000);
data1.put("created",new Date());
Map data2 = new HashMap<>();
data2.put("title","戴尔dell成就3681办公商用台式机电脑主机(英特尔酷睿i3-10100 8G 1T 三年上门售后)+21.5英寸电脑显示器");
data2.put("sellpoint","全新升级版十代酷睿爆款小机箱上市,更强动力,成就高效,提前预定更享首发好价,点击抢先体验");
data2.put("price",5000);
data2.put("created",new Date());
List<Map> list = new ArrayList<>();
list.add(data1);
list.add(data2);
//准备一个批处理的对象
BulkRequest bulkRequest = new BulkRequest();
for(Map map : list){
//使用IndexRequest循环添加 这个不是批处理,就是循环一个一个的添加
// IndexRequest request = new IndexRequest("product");
// request.source(map);
// client.index(request,RequestOptions.DEFAULT);
//--批处理操作方式--
//创建IndexRequest
IndexRequest request = new IndexRequest("product");
request.source(map);
//将request加入批处理对象
bulkRequest.add(request);
}
//批处理添加
BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(response);
}
修改和删除索引
修改
只要索引的id存在就是修改。 使用UpdateRequest
//修改索引
@Test
public void testUpdateIndex() throws IOException {
UpdateRequest request = new UpdateRequest("product","3");
Map map = new HashMap();
map.put("title","雅马哈(YAMAHA)雅马哈吉他FG800VN美国型号单板民谣吉他木吉它复古木色亮光42英寸");
//将map加入request
request.doc(map);
UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
System.out.println(response);
}
删除
//删除索引
@Test
public void testDeletIndex() throws IOException {
DeleteRequest request = new DeleteRequest("product","J2JCyXgBaEE5g_LwIR-w");
DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
System.out.println(response);
}
通过ID查询
GET API 查询之后得到是一个GetResponse,其中提供了大量API获取查询的结果
//根据ID查询
@Test
public void testQueryById(){
GetRequest request = new GetRequest("product","2");
GetResponse response = null;
try {
response = client.get(request, RequestOptions.DEFAULT);
System.out.println(response);
} catch (IOException e) {
System.out.println(e);
}
}
查询之后得到的response对象的toString结构:
{
"_index": "product",
"_type": "_doc",
"_id": "2",
"_version": 2,
"_seq_no": 7,
"_primary_term": 1,
"found": true,
"_source": {
"sellpoint": "4.11大牌闪购~暖春特惠】【下单送首购礼金】【进阶款单板升级款相思木】【加购送教学陪练和精品豪礼】【京库配送】【蕞快当日达】~点击抢购",
"price": 498,
"created": "2021-04-13T03:23:35.823Z",
"title": "KAKA卡卡 KUC-25D 尤克里里乌克丽丽ukulele单板桃花心木迷你小吉他23英寸款"
}
}
这个结果就是我们在kibana中使用 GET /product/_doc/2查询的结果:
其中_source中存储的是查询的结果数据。
GetResponse提供了专门的API可以轻松获取其中的数据:
可以这样处理:
//根据ID查询
@Test
public void testQueryById(){
GetRequest request = new GetRequest("product","2");
GetResponse response = null;
try {
response = client.get(request, RequestOptions.DEFAULT);
if(response.isExists()) {
//直接得到map
Map<String, Object> source = response.getSource();
System.out.println(source);
}else{
System.out.println("没有查询到任何数据");
}
} catch (IOException e) {
System.out.println(e);
}
}
Search查询一个列表
简单的查询
不设置任何查询条件,查询所有数据
//使用search查询所有
@Test
public void testSearchAll() throws IOException {
SearchRequest request = new SearchRequest("product");
//不设置条件
SearchResponse respons = client.search(request, RequestOptions.DEFAULT);
System.out.println(respons);
}
response结果
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 6,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_index": "product",
"_type": "_doc",
"_id": "2",
"_score": 1.0,
"_source": {
"sellpoint": "4.11大牌闪购~暖春特惠】【下单送首购礼金】【进阶款单板升级款相思木】【加购送教学陪练和精品豪礼】【京库配送】【蕞快当日达】~点击抢购",
"price": 498,
"created": "2021-04-13T03:23:35.823Z",
"title": "KAKA卡卡 KUC-25D 尤克里里乌克丽丽ukulele单板桃花心木迷你小吉他23英寸款"
}
},
.......
]
}
}
查询的结果是存储在hits里面的hits中:
返回的结果对象SearchResponse中提供了大量的API,用来获取返回的结果。
查询的数据是放在SearchResponse对象的属性hits中的,源码中的类型是:
按照迭代器的方式迭代结果集:
//使用search查询所有
@Test
public void testSearchAll() throws IOException {
SearchRequest request = new SearchRequest("product");
//不设置条件
SearchResponse respons = client.search(request, RequestOptions.DEFAULT);
System.out.println(respons);
//得到hits
SearchHits hits = respons.getHits();
System.out.println("查询的总条数:"+hits.getHits().length);
//按照iterator的方式迭代
for(SearchHit hit : hits){
System.out.println("分数:"+hit.getScore());
System.out.println("结果:"+hit.getSourceAsMap());
}
}
带条件查询
可以组合各种查询条件,然后将条件装入:SearchSourceBuilder。 SearchSourceBuilder放入request进行查询。
//使用search进行条件查询
@Test
public void testQuery() throws IOException {
//创建SearchRequest (传入索引库的名字)
SearchRequest request = new SearchRequest("product");
//使用SearchSourceBuilder设置查询条件条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//这里需要传入QueryBuilder
searchSourceBuilder.query(new MatchQueryBuilder("title","电脑"));
//设置查询体
request.source(searchSourceBuilder);
//查询
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
System.out.println(response);
//得到hits
SearchHits hits = response.getHits();
System.out.println("查询的总条数:"+hits.getHits().length);
//按照iterator的方式迭代
// for(SearchHit hit : hits.getHits()){
for(SearchHit hit : hits){
System.out.println("分数:"+hit.getScore());
System.out.println("结果:"+hit.getSourceAsMap());
}
}
QueryBuilder说明: 所有条件对象的统一接口。
我们可以创建各种不同的条件,也可以按照需求进行组合。
比如:
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(new MatchQueryBuilder("title","吉他"));
boolQueryBuilder.must(new MatchQueryBuilder("title","尤克里里"));
boolQueryBuilder.should(new MatchQueryBuilder("title","泰勒"));
searchSourceBuilder.query(boolQueryBuilder);
在kibana中的写法:
GET product/_search
{
"query": {
"bool": {
"must": [
{"match": {
"title": "吉他"
}
}
,
{ "match": {
"title": "尤克里里"
}
}
],"should": [
{"match": {
"title": "泰勒"
}}
]
}
}
}
使用工具类QueryBuilders完成条件的设置
范围查询
//使用search进行条件查询
@Test
public void testQueryRange() throws IOException {
//创建SearchRequest (传入索引库的名字)
SearchRequest request = new SearchRequest("product");
//使用SearchSourceBuilder设置查询条件条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//使用QueryBuilders创建条件 范围查询
searchSourceBuilder.query(QueryBuilders.rangeQuery("price").from(500,true).to(5000,false));
//设置查询体
request.source(searchSourceBuilder);
//查询
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
System.out.println(response);
//得到hits
SearchHits hits = response.getHits();
System.out.println("查询的总条数:"+hits.getHits().length);
}
模糊查询
模糊查询表明在查询的关键字拼写错误时,也可以查询。
错误长度是有限是的, 默认是 0~1,也就是最多错1个,可以匹配和识别。
java程序的实现
//使用search进行条件查询 模糊查询
@Test
public void testQueryFuzzy() throws IOException {
//创建SearchRequest (传入索引库的名字)
SearchRequest request = new SearchRequest("product");
//使用SearchSourceBuilder设置查询条件条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//使用QueryBuilders创建条件 范围查询
searchSourceBuilder.query(QueryBuilders.fuzzyQuery("title","Custoa").maxExpansions(1));
//设置查询体
request.source(searchSourceBuilder);
//查询
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
System.out.println(response);
//得到hits
SearchHits hits = response.getHits();
System.out.println("查询的总条数:"+hits.getHits().length);
}
容错的范围:只能是0或者1。
查询结果排序
像sql查询一样给查询结果排序
GET product/_search
{
"sort": [
{
"price": {
"order": "desc"
},
"created": {
"order": "asc"
}
}
]
}
java程序的写法
//使用search查询所有 排序
@Test
public void testSearchAllByOrder() throws IOException {
SearchRequest request = new SearchRequest("product");
SearchSourceBuilder builder= new SearchSourceBuilder();
//没有条件
// builder.sort("price");//默认降序
// builder.sort("price", SortOrder.DESC);
builder.sort(new FieldSortBuilder("price").order(SortOrder.DESC));
builder.sort(new FieldSortBuilder("created").order(SortOrder.ASC));
request.source(builder);
//不设置条件
SearchResponse respons = client.search(request, RequestOptions.DEFAULT);
//得到hits
SearchHits hits = respons.getHits();
//按照iterator的方式迭代
// for(SearchHit hit : hits.getHits()){
for(SearchHit hit : hits){
System.out.println("分数:"+hit.getScore());
System.out.println("结果:"+hit.getSourceAsMap());
}
}
分页查询
GET product/_search
{
"from": 3,
"size": 2
}
只要设置了SearchSourceBuilder的from和size就行
//使用search查询所有 分页
@Test
public void testSearchAllByOrderPage() throws IOException {
SearchRequest request = new SearchRequest("product");
SearchSourceBuilder builder= new SearchSourceBuilder();
//没有条件 - 按照价格排序
builder.sort("price", SortOrder.DESC);
//设置分页信息
builder.from(2);
builder.size(2);
request.source(builder);
//不设置条件
SearchResponse respons = client.search(request, RequestOptions.DEFAULT);
//得到hits
SearchHits hits = respons.getHits();
//按照iterator的方式迭代
// for(SearchHit hit : hits.getHits()){
for(SearchHit hit : hits){
System.out.println("分数:"+hit.getScore());
System.out.println("结果:"+hit.getSourceAsMap());
}
}
高亮查询
java实现
//高亮显示查询
@Test
public void testSearchHightlight() throws IOException {
SearchRequest request = new SearchRequest("product");
//设置条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.matchQuery("title","吉他"));
//设置高亮显示
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags("<font style='color:red'>");//前缀
highlightBuilder.postTags("</font>");//后缀
highlightBuilder.field("title");
builder.highlighter(highlightBuilder);
request.source(builder);
//不设置条件
SearchResponse respons = client.search(request, RequestOptions.DEFAULT);
//得到hits
SearchHits hits = respons.getHits();
//按照iterator的方式迭代
// for(SearchHit hit : hits.getHits()){
for(SearchHit hit : hits){
System.out.println("分数:"+hit.getScore());
//取出原始数据
Map<String, Object> sourceMap = hit.getSourceAsMap();
//处理高亮显示的结果
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
HighlightField title = highlightFields.get("title");
if(title!=null) {
Text[] fragments = title.getFragments();
if (fragments != null && fragments.length > 0) {
//修改sourceMap中title为高亮显示的结果
sourceMap.put("title", fragments[0]);
}
}
System.out.println("结果:"+sourceMap);
}
}