今天开始我们学习如何使用Java来操作ES,这里会讲两种操作方式,一是使用ES提供的jar包来操作,二是使用SpringBootData来操作,第二种方式是企业里面使用的较多的方式了。
Java 操作ES 环境集成创建一个普通的Java工程,首先我们需要导入ES的jar包
org.elasticsearch.client transport 6.8.6 junit junit 4.12 compile
然后创建ES的客户端
public class ESClientUtil {
public static TransportClient getClient(){
TransportClient client = null;
Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
try {
client = new PreBuiltTransportClient(settings).addTransportAddress(
new TransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
} catch (UnknownHostException e) {
e.printStackTrace();
}
return client;
}
}
添加文档
编写测试类 ,往指定的索引库中添加文档
@Test
public void testAdd() {
//获取客户端对象
TransportClient client = ESClientUtil.getClient();
//创建索引
IndexRequestBuilder indexRequestBuilder = client.prepareIndex("orders", "_doc", "1");
Map data = new HashMap<>();
data.put("id",1);
data.put("title","买了一个鼠标");
data.put("count",1);
data.put("amount",100.00);
data.put("status",1);
//获取结果
IndexResponse indexResponse = indexRequestBuilder.setSource(data).get();
//打印给过
System.out.println(indexResponse);
client.close();
}
打印结果如下 :
IndexResponse[index=orders,type=_doc,id=1,version=18,result=updated,seqNo=19,primaryTerm=3,shards={“total”:2,“successful”:1,“failed”:0}]
获取一个文档@Test
public void testGet() {
//获取客户端对象
TransportClient client = ESClientUtil.getClient();
//获取文档
GetResponse response = client.prepareGet("orders", "_doc", "1").get();
//打印结果
System.out.println(response);
}
打印结果如下:{amount=100.0, count=1, id=1, title=买了一个鼠标, status=1}
更新一个文档@Test
public void testUpdate(){
//获取客户端对象
TransportClient client = ESClientUtil.getClient();
//修改索引
UpdateRequestBuilder updateRequestBuilder = client.prepareUpdate("orders", "_doc", "1");
Map data = new HashMap<>();
data.put("id",1);
data.put("title","买了一个鼠标");
data.put("count",2);
data.put("amount",100.00);
//data.put("status",1);
//获取结果设置修改内容
UpdateResponse updateResponse = updateRequestBuilder.setDoc(data).get();
System.out.println(updateResponse);
client.close();
}
打印结果如下:UpdateResponse[index=orders,type=_doc,id=1,version=19,seqNo=20,primaryTerm=3,result=updated,shards=ShardInfo{total=2, successful=1, failures=[]}]
注意:这种修改方式是不会影响到未修改的字段的,比如上面注释掉status字段,status字段不会被修改。也就是说这种方式是局部修改。
删除一个文档 @Test
public void testDelete() {
//获取客户端对象
TransportClient client = ESClientUtil.getClient();
//执行删除
DeleteResponse response = client.prepareDelete("orders", "_doc", "1").get();
//打印结果
System.out.println(response);
}
打印结果:DeleteResponse[index=orders,type=_doc,id=1,version=20,result=deleted,shards=ShardInfo{total=2, successful=1, failures=[]}]
批量操作同时提交多个操作
@Test
public void testBuilkAdd(){
//获取客户端对象
TransportClient client = ESClientUtil.getClient();
BulkRequestBuilder bulkRequestBuilder = client.prepareBulk();
Map data1 = new HashMap<>();
data1.put("id",1);
data1.put("title","买了一个鼠标");
data1.put("count",2);
data1.put("amount",100.00);
//添加操作
bulkRequestBuilder.add(client.prepareIndex("orders", "_doc", "1").setSource(data1));
Map data2 = new HashMap<>();
data2.put("id",2);
data2.put("title","买了一个鼠标");
data2.put("count",2);
data2.put("amount",100.00);
//添加操作
bulkRequestBuilder.add(client.prepareIndex("orders", "_doc", "2").setSource(data2));
Map data3 = new HashMap<>();
data3.put("id",2);
data3.put("title","买了一个鼠标");
data3.put("count",2);
data3.put("amount",200.00);
//修改操作
bulkRequestBuilder.add(client.prepareUpdate("orders", "_doc", "2").setDoc(data3));
BulkResponse bulkItemResponses = bulkRequestBuilder.get();
Iterator iterator = bulkItemResponses.iterator();
while(iterator.hasNext()){
BulkItemResponse next = iterator.next();
System.out.println(next.getResponse());
}
client.close();
}
打印结果
IndexResponse[index=orders,type=_doc,id=1,version=1,result=created,seqNo=22,primaryTerm=3,shards={“total”:2,“successful”:1,“failed”:0}]
IndexResponse[index=orders,type=_doc,id=2,version=4,result=updated,seqNo=3,primaryTerm=3,shards={“total”:2,“successful”:1,“failed”:0}]
UpdateResponse[index=orders,type=_doc,id=2,version=5,seqNo=4,primaryTerm=3,result=updated,shards=ShardInfo{total=2, successful=1, failures=[]}]
案例:查询订单标题中包含:鼠标,价格在10-2000之间,状态为1,按照价格正排序
@Test
public void testSearch(){
//获取客户端对象
TransportClient client = ESClientUtil.getClient();
SearchRequestBuilder searchRequestBuilder = client.prepareSearch("orders");
//设置分页
searchRequestBuilder.setFrom(0);
searchRequestBuilder.setSize(10);
searchRequestBuilder.addSort("amount", SortOrder.ASC);
//查询条件
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//dsl查询
List must = boolQueryBuilder.must();
must.add(QueryBuilders.matchQuery("title" , "鼠标"));
//dsl过滤
List filter = boolQueryBuilder.filter();
filter.add(QueryBuilders.rangeQuery("amount").lte(2000).gte(10));
filter.add(QueryBuilders.termQuery("status",1));
searchRequestBuilder.setQuery(boolQueryBuilder);
SearchResponse searchResponse = searchRequestBuilder.get();
SearchHits hits = searchResponse.getHits();
System.out.println("条数:"+hits.getTotalHits());
for (SearchHit hit : hits.getHits()) {
System.out.println(hit.getSourceAsMap());
}
client.close();
}
SpringBootData操作ES
环境集成
第一步:导入SpringBoot提整合ES的依赖 spring-boot-starter-data-elasticsearch
org.springframework.boot spring-boot-starter-parent 2.2.5.RELEASE org.springframework.boot spring-boot-starter-data-elasticsearch org.springframework.boot spring-boot-starter-test org.projectlombok lombok
第二步:然后需要在yml中对ES进行配置 , 如果是集群配置增加uri即可,单个配置如下:
spring:
elasticsearch:
rest:
uris:
- http://localhost:9200
第三步:编写启动类
@SpringBootApplication
public class SearchStart {
public static void main(String[] args) {
SpringApplication.run(SearchStart.class);
}
}
创建document对象
第三步:编写document对象 ,该对象是对存储到ES中的数据的封装,同时文档映射也是通过它来实现
//标记该对象是ES的文档对象
//indexName 索引库
//type 类型
@document(indexName = "orders",type = "_doc")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderDoc {
//标记为文档ID,该ID的值会作为document的id值
@Id
private Long id;
@Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
//@Field(type = FieldType.Keyword)
private String title;
@Field(type = FieldType.Integer)
private int count;
@Field(type = FieldType.Integer)
private int status;
@Field(type = FieldType.Double)
private BigDecimal amount;
}
创建Repository
第四步:SpringBootData提供了ElasticsearchRepository 来操作ES,该接口中包含了针对ES的CRUD方法,我们编写接口集成它即可使用
@Repository public interface OrderRepository extends ElasticsearchRepository{ }
注意:这里的泛型是当前Repository所要管理的实体类,也就是OrderDoc,Long是实体类ID的类型
创建索引和映射编写一个基于SpringBoot的测试类如下:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SearchStart.class)
public class OrderESTest {
//操作ES的template模板
@Autowired
private ElasticsearchRestTemplate template;
@Autowired
private OrderRepository orderRepository;
@Test
public void test(){
//创建索引
template.createIndex(CourseDoc.class);
//创建映射
template.putMapping(CourseDoc.class);
}
}
这里我注入了一个 ElasticsearchRestTemplate 工具类,通过它的 createIndex 可以创建索引库,SpringBoot指定解析 CourseDoc上的@document注解中的index来确定索引库的名字。创建映射也是同样的道理。
基础CRUD在SpringBoot中对ES的Crud显得极其简单,直接调用repository内置方法即可完成
@Test
public void testAdd(){
orderRepository.save(new OrderDoc(1L,"买了一个表",1,1,new BigDecimal("200")));
}
@Test
public void testGet(){
Optional optional = orderRepository.findById(1l);
System.out.println(optional.get());
}
@Test
public void testDelete(){
orderRepository.deleteById(1L);
}
注意:这里并没有update方法,因为save对象的时候如果ID已经存在,就会执行update操作。repository中还有很多的方法可以使用,你可以自己去调试。
DSL查询在SpringBoot中我们通过 NativeSearchQueryBuilder 来构建查询条件,调用repository.search来执行查询。
案例:查询标题中包含鼠标 ,状态为1,金额在10-2000之间,按照金额倒排,查询第2页数据,每页10条
@Test
public void testSearch(){
//查询构建器
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
//设置分页: 第2页 (0开始), 每页10数
builder.withPageable(PageRequest.of(1,10));
//设置排序 : 金额倒排
builder.withSort(SortBuilders.fieldSort("amount").order(SortOrder.DESC));
//构建组合查询
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//标题包含鼠标
boolQuery.must(QueryBuilders.matchQuery("title","鼠标"))
//状态值查询
.filter(QueryBuilders.termQuery("status",1))
//金额范围查询
.filter(QueryBuilders.rangeQuery("amount").gte(10).lte(2000));
//添加查询条件
builder.withQuery(boolQuery);
//执行搜索
Page page = orderRepository.search(builder.build());
//获取条数
System.out.println("总元素个数:"+page.getTotalElements());
//打印列表
page.getContent().forEach(System.out::print);
}
这里需要根据业务需求组装查询条件,我们之前见过的查询条件都可以通过它来构建,如:
在关键字搜索的时候,结果列表通常会把包含关键字的内容进行高亮,比如在京东商品列表搜搜“娃娃”效果如下
使用ES可以轻松做到高亮功能,第一步:增加结果处理器,在集成SpringBoot之后高亮功能失效,需要做高亮结果处理。
@Component
public class HighlightResultMapper implements SearchResultMapper {
@Override
public AggregatedPage mapResults(SearchResponse response, Class aClass, Pageable pageable) {
// 记录总条数
long totalHits = response.getHits().getTotalHits();
// 记录列表(泛型) - 构建Aggregate使用
List list = Lists.newArrayList();
// 获取搜索结果(真正的的记录)
SearchHits hits = response.getHits();
for (SearchHit hit : hits) {
if(hits.getHits().length <= 0){
return null;
}
// 将原本的JSON对象转换成Map对象
Map map = hit.getSourceAsMap();
// 获取高亮的字段Map
Map highlightFields = hit.getHighlightFields();
for (Map.Entry highlightField : highlightFields.entrySet()) {
// 获取高亮的Key
String key = highlightField.getKey();
// 获取高亮的Value
HighlightField value = highlightField.getValue();
// 实际fragments[0]就是高亮的结果,无需遍历拼接
Text[] fragments = value.getFragments();
StringBuilder sb = new StringBuilder();
for (Text text : fragments) {
sb.append(text);
}
// 因为高亮的字段必然存在于Map中,就是key值
// 可能有一种情况,就是高亮的字段是嵌套Map,也就是说在Map里面还有Map的这种情况,这里没有考虑
map.put(key, sb.toString());
}
// 把Map转换成对象
T item = JSON.parseObject(JSONObject.toJSONString(map),aClass);
list.add(item);
}
// 返回的是带分页的结果
return new AggregatedPageImpl<>(list, pageable, totalHits,response.getAggregations());
}
public T mapSearchHit(SearchHit searchHit, Class aClass) {
return null;
}
}
第二步:给搜索的字段设置高亮
...省略...
//给title字段设置高亮,使用color:red来高亮
HighlightBuilder.Field highlightField = new HighlightBuilder.Field("title")
.preTags("")
.postTags("");
builder.withHighlightFields(highlightField);
第三步:修改查询方式,使用template查询,且指定结果映射器
@Autowired
HighlightResultMapper highlightResultMapper;
@Test
public void testSearch(){
...省略...
//设置高亮
HighlightBuilder.Field highlightField = new HighlightBuilder.Field("title")
.preTags("")
.postTags("");
builder.withHighlightFields(highlightField);
//执行搜索
//Page page = orderRepository.search(builder.build());
Page page = template.queryForPage(builder.build(), OrderDoc.class, highlightResultMapper);
//获取条数
System.out.println("总元素个数:"+page.getTotalElements());
//打印列表
page.getContent().forEach(System.out::print);
}
打印结果如下:
总元素个数:3
OrderDoc(id=2, title=买了一个鼠标, count=1, status=1, amount=200)
OrderDoc(id=1, title=买了一个鼠标, count=1, status=1, amount=200)
OrderDoc(id=3, title=买了一个鼠标, count=1, status=1, amount=200)
我们看到 鼠标 说明高亮成功
文章结束,如果对你有所帮助,请点赞收藏加评论



