springboot-elasticsearch
基于springboot的web項目,通過elasticsearch提供的Java API 進行查詢操作.
起因
項目在一個查詢要在亞秒級計算(分組、累加、平均)大量數據的結果。官方提供的API過于簡單,自己在做項目中遇到了一些坑,并總結了一些API的使用,簡單分享一下。
前置條件
有一個elasticsearch服務
配置
demo是基于springboot快速構建了一個web應用。elasticsearch提供了一個客戶端:
TransportClient
,首先我們來配置一下
如果在main方法里面執行可以直接創建一個客戶端連接
TransportClient client = new PreBuiltTransportClient(Settings.EMPTY)
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("172.16.3.121"), 9300));
如果在springboot項目中。配置一下,這樣我們可以直接注入使用了。
@Configuration
public class ElasticSearchConfig {
@Value("${spring.elasticsearch.host}")
private String host;//elasticsearch的地址
@Value("${spring.elasticsearch.port}")
private Integer port;//elasticsearch的端口
private static final Logger LOG = LogManager.getLogger(ElasticSearchConfig.class);
@Bean
public TransportClient client(){
TransportClient client = null;
try {
client = new PreBuiltTransportClient(Settings.EMPTY)
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(host), port));
} catch (UnknownHostException e) {
LOG.error("創建elasticsearch客戶端失敗");
e.printStackTrace();
}
LOG.info("創建elasticsearch客戶端成功");
return client;
}
}
配置完畢之后,我們就可以使用了。這里寫了一個簡單的demo,匯總了常用的一些API使用。
代碼示例
這個代碼示例滿足了查詢所需,查詢條件,分組計算,分組排序等
Map<String,Object> map = Collections.emptyMap();
Script script = new Script(ScriptType.INLINE, "painless","params._value0 > 0",map); //提前定義好查詢銷量是否大于1000的腳本,類似SQL里面的having
long beginTime = System.currentTimeMillis();
SearchResponse sr = client.prepareSearch("adele").setTypes("sale")
.setQuery(QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("store_name.keyword", "xxx旗艦店")) //挨個設置查詢條件,沒有就不加,如果是字符串類型的,要加keyword后綴
.must(QueryBuilders.termQuery("department_name.keyword", "玎"))
.must(QueryBuilders.termQuery("category_name.keyword", "T恤"))
.must(QueryBuilders.rangeQuery("pay_date").gt("2017-03-07").lt("2017-07-09"))
).addAggregation(
AggregationBuilders.terms("by_product_code").field("product_code.keyword").size(500) //按貨號分組,最多查500個貨號.SKU直接改字段名字就可以
.subAggregation(AggregationBuilders.terms("by_store_name").field("store_name.keyword").size(50) //按店鋪分組,不顯示店鋪可以過濾掉這一行,下邊相應減少一個循環
.subAggregation(AggregationBuilders.sum("total_sales").field("quantity")) //分組計算銷量匯總
.subAggregation(AggregationBuilders.sum("total_sales_amount").field("amount_actual")) //分組計算實付款匯總,需要加其他匯總的在這里依次加
.subAggregation(PipelineAggregatorBuilders.bucketSelector("sales_bucket_filter",script,"total_sales")))//查詢是否大于指定值
.order(Terms.Order.compound(Terms.Order.aggregation("total_calculate_sale_amount",false)))) //分組排序
.execute().actionGet();
Terms terms = sr.getAggregations().get("by_product_code"); //查詢遍歷第一個根據貨號分組的aggregation
System.out.println(terms.getBuckets().size());
for (Terms.Bucket entry : terms.getBuckets()) {
System.out.println("------------------");
System.out.println("【 " + entry.getKey() + " 】訂單數 : " + entry.getDocCount() );
Terms subTerms = entry.getAggregations().get("by_store_name"); //查詢遍歷第二個根據店鋪分組的aggregation
for (Terms.Bucket subEntry : subTerms.getBuckets()) {
Sum sum1 = subEntry.getAggregations().get("total_sales"); //取得銷量的匯總
double total_sales = sum1.getValue();
System.out.println(subEntry.getKey() + " 訂單數: " + subEntry.getDocCount() + " 銷量: " + total_sales); //店鋪和訂單數量和銷量
}
}
long endTime = System.currentTimeMillis();
System.out.println("查詢耗時" + ( endTime - beginTime ) + "毫秒");
demo地址
- 啟動項目
- 訪問http://localhost:9999/test
- 查看后端打印
參考鏈接
有一些坑是我領導踩得,部分代碼已得授權。