前言
今天开始我们学习如何使用Java来操作ES,这里会讲两种操作方式,一是使用ES提供的jar包来操作,二是使用SpringBootData来操作,第二种方式是企业里面使用的较多的方式了。
Java 操作ES
环境集成
创建一个普通的Java工程,首先我们需要导入ES的jar包
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>6.8.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
然后创建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<String,Object> 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<String,Object> 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<String,Object> 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<String,Object> 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<String,Object> 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<BulkItemResponse> 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=[]}]
DSL查询
案例:查询订单标题中包含:鼠标,价格在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<QueryBuilder> must = boolQueryBuilder.must();
must.add(QueryBuilders.matchQuery("title" , "鼠标"));
//dsl过滤
List<QueryBuilder> 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
<!--SpringBoot-->
<parent>
<groupId> org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
第二步:然后需要在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;
/**
* 标题需要分词,指定为text;并使用IK分词器
* 一般需要作为关键字搜索的字段都要指定为text,因为需要分词且创建索引
*/
@Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
//@Field(type = FieldType.Keyword)
private String title;
/**
* 指定为integer类型
*/
@Field(type = FieldType.Integer)
private int count;
/**
* 状态指定为 integer类型
*/
@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<OrderDoc,Long> {
}
注意:这里的泛型是当前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<OrderDoc> 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<OrderDoc> page = orderRepository.search(builder.build());
//获取条数
System.out.println("总元素个数:"+page.getTotalElements());
//打印列表
page.getContent().forEach(System.out::print);
}
这里需要根据业务需求组装查询条件,我们之前见过的查询条件都可以通过它来构建,如:
打印效果
条数:3
{amount=200, count=1, id=2, title=买了一个鼠标, status=1}
{amount=200, count=1, id=1, title=买了一个鼠标, status=1}
{amount=200, count=1, id=3, title=买了一个鼠标, status=1}