需求说明
近期项目碰到一个问题,需要使用ES中的java api中的ElasticsearchRestTemplate,调用
org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate#search方法,需要实现DSL语句日志打印;
调研阶段
其中跟踪源码发现
@Override
public <T> SearchHits<T> search(Query query, Class<T> clazz, IndexCoordinates index) {
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
SearchResponse response = execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
return callback.doWith(SearchDocumentResponseBuilder.from(response, getEntityCreator(documentCallback)));
}
在SearchRequest 这个对象中,有一个source的属性,包含了去es请求的完整dsl语句;
为了获取他,接着发现一个问题:
protected RequestFactory requestFactory;
RequestFactory为私有类,无法直接调用,虽然ElasticsearchRestTemplate提供了getRequestFactory()这个方法,但依然无法编译运行
public RequestFactory getRequestFactory() {
return requestFactory;
}
解决方案
去拿到这个requestFactory,目前有且仅有的方法就是通过Java反射的方法,然后实现了拿到
SearchRequest 这个对象;
Method searchRequestMethod = ReflectionUtils.findMethod(Class.forName("org.springframework.data.elasticsearch.core.RequestFactory"), "searchRequest", Query.class, Class.class, IndexCoordinates.class);
searchRequestMethod.setAccessible(true);
SearchRequest searchRequest = (SearchRequest) ReflectionUtils.invokeMethod(searchRequestMethod, getRequestFactory(), query, clazz, index);
接着通过代码提示发现没有source的get关键字的方法,但实际上还是有提供同类的方法,只不过没有get关键字而已
public SearchSourceBuilder source() {
return source;
}
好了,接下来该有的准备都有了,只需要对ElasticsearchRestTemplate进行扩展即可,以下是实现代码:
package com.example.methodtest;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponseBuilder;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
/**
* 实现dsl日志打印
*
* @author AaronCgt
* @since 2024/3/12/012
*/
@Component
@Slf4j
public class CustomTemplate extends ElasticsearchRestTemplate {
public CustomTemplate(RestHighLevelClient client, ElasticsearchConverter elasticsearchConverter) {
super(client, elasticsearchConverter);
}
public <T> SearchHits<T> search(Query query, Class<T> clazz, boolean isDebug) {
IndexCoordinates index=getIndexCoordinatesFor(clazz);
try {
if (!isDebug){
return search(query,clazz,index);
}else{
Method searchRequestMethod = ReflectionUtils.findMethod(Class.forName("org.springframework.data.elasticsearch.core.RequestFactory"), "searchRequest", Query.class, Class.class, IndexCoordinates.class);
searchRequestMethod.setAccessible(true);
SearchRequest searchRequest = (SearchRequest) ReflectionUtils.invokeMethod(searchRequestMethod, getRequestFactory(), query, clazz, index);
log.info("DSL:{}", searchRequest.source().toString());
SearchResponse response = execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
return callback.doWith(SearchDocumentResponseBuilder.from(response, getEntityCreator(documentCallback)));
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
至此,需求实现。可以根据实际需求,调用这个类,进行日志打印,例如平时开发接口,预留打印日志参数,需要时传入,实现接口级别DSL语句打印,也可以用于生产坏境出现问题时候对DSL的复现和跟踪!
jar包版本说明
为了准确性,以下提供jar包版本仅供各位参考: