一、应用场景
实际项目开发中要求记录下用户的操作日志,创建一个时间轴展示相关操作日志信息,要求按时间降序显示。其中,操作日志采用ES存储。
二、实际代码
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.taobao.rigel.rap.model.CustomOperationLog;
import com.taobao.rigel.rap.utils.PrettyTimeTool;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping(value = "api/umeapiplus/log")
public class CustomOperationLogController {
private static final Gson gson = new Gson();
@Autowired
private RestHighLevelClient client;
public SearchHit[] findSearchHits(){
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.fetchSource(new String[]{"log_operation_datetime", "log_operation_object"}, new String[]{});
sourceBuilder.size(10000); //设置确定搜素命中返回数的size选项,默认为10
sourceBuilder.sort(new FieldSortBuilder("log_operation_datetime").order(SortOrder.DESC));
// sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
SearchRequest searchRequest = new SearchRequest("umeapi-logstash");
searchRequest.source(sourceBuilder);
try{
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = response.getHits();
SearchHit[] searchHits = hits.getHits();
System.out.println("searchHits数组长度:" + searchHits.length);
return searchHits;
}catch (IOException e){
e.printStackTrace();
}
return null;
}
@RequestMapping(value = "/getDynamicInformation", method = RequestMethod.GET)
public List<CustomOperationLog> getDynamicInformation(){
List<CustomOperationLog> returnList = new ArrayList<>();
SearchHit[] searchHits = findSearchHits();
for(SearchHit hit : searchHits){
System.out.println("search -> " + hit.getSourceAsString());
JSONObject totalJson = JSONObject.parseObject(hit.getSourceAsString());
String operationObjStr = (String)totalJson.get("log_operation_object");
System.out.println("operationObjStr: " + operationObjStr);
if(null != operationObjStr){
try {
CustomOperationLog temp = gson.fromJson(operationObjStr, CustomOperationLog.class);
temp.setHowLongBefore(PrettyTimeTool.getLastUpdateTimeStr(temp.getDateTime()));
returnList.add(temp);
}catch (Exception e){
e.printStackTrace();
}
}
}
return returnList;
}
}
代码分析:
sourceBuilder.sort(new FieldSortBuilder("log_operation_datetime").order(SortOrder.DESC));
这行代码实现了按自定义字段“log_operation_datetime”降序排序的需求
三、ES排序
ES提供了默认排序和自定义排序,默认排序通常都是按_score来排序的。自定义排序通常是使用sourceBuilder.sort(new FieldSortBuilder("字段名").order(SortOrder.DESC));
实现的。SearchSourceBuilder允许添加一个或多个SortBuilder实例。SortBuilder有四种特殊的实现,分别是:FieldSortBuilder、GeoDistanceSortBuilder、ScoreSortBuilder、ScriptSortBuilder
- FieldSortBuilder:根据某个特殊字段排序
- ScoreSortBuilder:根据score排序
- GeoDistanceSortBuilder:根据地理位置排序
- ScriptSortBuilder:根据自定义脚本排序
四、遇到问题
1、调用接口时,报如下错误
“Fielddata is disabled on text fields by default. Set fielddata=true on [name] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead.”
这个问题是由于我指定的字段“log_operation_datetime”是 text 类型。在ES搜索排序中,字段的类型必须是:integer、double、long或者keyword。否则使用该字段进行查询、排序、聚合时候,就会出现Fielddata is disabled on text fields by default.
解决办法:
- 把字段的索引类型改成keyword或者数值型(实际使用了本方法,解决了问题)
- 索引字段类型还是text,但是在mapping中加上fielddata=true。这种不推荐,因为这样加载时候,会占用过多的内存(自己没试过这种方法,仅供参考)