ollUrl = this.serviceUrl + "?scroll=600000";
while (true){
DataQueryBuilder dataQuery = new DataQueryBuilder();
dataQuery.setScroll_id(scrollId);
JSONObject jsonResult = query(JSON.toJSONString(dataQuery), scrollUrl);
scrollId = jsonResult.getString("scroll_id");
List<T> tmpList = getData.apply(jsonResult);
if(tmpList.size() == 0){
break;
}
rsList.addAll(tmpList);
}
} catch (Exception e) {
logger.error("getESDataException", e);
}
return rsList;
}
咋一看,似乎无从下手。别急,一步步来。
提取依赖变量和依赖函数
很容易看到,这个方法两次调用了 query, 可以先将 query 隔离出来,变成:
public <T> List<T> queryEsData(QueryBuilder initQuery, Function<JSONObject, List<T>> getData) {
return queryEsDataInner(initQuery, this::query, getData);
}
不过, 外部实例变量 this.serviceUrl 还在 queryEsDataInner 里面,会破坏 queryEsDataInner 的纯粹性,因此,要把这两个URL提取出来,放到 queryEsData 里传入给 queryEsDataInner. 效果应该是这样:
public <T> List<T> queryEsData(QueryBuilder initQuery, Function<JSONObject, List<T>> getData) {
String initUrl = this.serviceUrl + "?search_type=scan&scroll=600000";
String scrollUrl = this.serviceUrl + "?scroll=600000";
return queryEsDataInner(initQuery, initUrl, scrollUrl, this::query, getData);
}
public <T> List<T> queryEsDataInner(QueryBuilder initQuery, String initUrl, String scrollUrl,
BiFunction<String, String, JSONObject> query,
Function<JSONObject, List<T>> getData) {
try {
JSONObject result = query.apply(initQuery.toJsonString(), initUrl);
logger.info("ES search init result: " + result.toJSONString());
String scrollId = result.getString("scroll_id");
if (scrollId == null) {
return new ArrayList<>();
}
List rsList = new ArrayList();
while (true){
DataQueryBuilder dataQuery = new DataQueryBuilder();
dataQuery.setScroll_id(scrollId);
JSONObject jsonResult = query.apply(JSON.toJSONString(dataQuery), scrollUrl);
scrollId = jsonResult.getString("scroll_id");
List<T> tmpList = getData.apply(jsonResult);
if(tmpList.size() == 0){
break;
}
rsList.addAll(tmpList);
}
return rsList;
} catch (Exception e) {
logger.error("getESDataException", e);
return new ArrayList<>();
}
}
queryEsData 不纯粹没关系,反正它就是个壳。 仅这一步,就让 queryEsDataInner 方法变成了独立方法,不再依赖于任何外部变量和外部service,也不依赖于调用函数。不过 queryEsDataInner 会有点丑:有五个参数。 丑是丑了点,不过单测相对比较好写了。这里的 getData 动态生成不同的结果以便退出,因此用了点技巧,见代码:
@Test
public void testQueryEsDataInner() {
HttpEsClient esClient = new HttpEsClient();
List empty = esClient.queryEsDataInner(new QueryBuilder(), "initUrl", "", (q, u) -> new JSONObject(), (jo) -> new ArrayList());
Assert.assertEquals(0, empty.size());
JSONObject jo = new JSONObject();
List<Integer> list = Arrays.asList(1,2,3);
jo.put("scroll_id", "1");
jo.put("list", list);
List result = esClient.queryEsDataInner(new QueryBuilder(), "initUrl", "scrollUrl", (q, u) -> jo, this::dyGetData);
Assert.assertArrayEquals(new Integer[]{1,2,3,1,2,3}, result.toArray(new Integer[0]));
}
private static int i = 2;
private List<Integer> dy