项目要求:客户可以筛选不同的index 并且可以进行多条条件的筛选。
刚开始,一开始对于需求不够明确,认为搜索条件中的 第四个条件 “或or且”是针对于当前条件单独的描述。例如,名字等于xxx (且) 且表示当前条件必须成立。但是这样的理解是不正确的。
正确的查询:且表示上下俩条数据都必须存在,或表示当前条件可以存在可以不存在。
而后,被公司的大哥指点了一番,通过大集合小集合先对条件进行了处理,然后进行查询,完成的高级查询。
解决思路:首先以“或”作为条件的分割, 如果在条件列表中,出现了或,则证明或后面的条件是单独的条件,例如:A and B or C 那么证明我们想要的查询其实为(A and B) or C
对条件列表进行遍历, 当列表的长度为1或者2的时候单独处理,当条件条数大于3的时候,首先判断当前条件中 是 或or且 如果是且,那么将且封装进入一个小的list 然后进行下一次遍历,如果当前条件中需求为或,那么证明之前的条件+当前条件为组合,所以将当前条件装入小list 然后将小list装入大list中,每次遇到或。都进行将数据加入小list ,然后将小list加入大list 然后给小list重新创建对象(一开始是调用了list.clear,但是这个方法会将内存中所有小list的引用都清除,但是我们是要保存大list中小list的数据的),这样循环下去,将不同的条件都封装在大list中,大list中包含多个小list 小list中包含多个条件,各个小list之间的关系是or 小list中各个条件之间的关系为and
对应到es的dsl语句为:
POST mhdz,tlsmz,wbswxx,hotel/_search
{
"profile": "true",
"query": {
"bool": {
"should": [
{
"bool": {
"must": [
{
"multi_match": {
"query": "李四",
"fields": [
"xm",
"xm.keyword"
],
"boost": 0.5,
"minimum_should_match": "75%"
}
},
{
"match": {
"zjhm": {
"query": "3604251999061622",
"boost": 3,
"fuzziness": "AUTO"
}
}
}
]
}
},
{
"bool": {
"must": [
{
"multi_match": {
"query": "张三",
"fields": [
"xm",
"xm.keyword"
],
"boost": 0.5,
"minimum_should_match": "75%"
}
},
{
"match": {
"zjhm": {
"query": "3604251999061122",
"boost": 3,
"fuzziness": "AUTO"
}
}
}
]
}
}
]
}
}
}
private SearchRequest getSynQueryResultByCondition(HashMap<String, Object> bindParams) {
SearchRequest searchRequest;
// 获取查询的索引列表
String[] indexNames = (String[]) bindParams.get("indexKeys");
// 获取查询的条件列表
List<HashMap<String, String>> options = (List<HashMap<String, String>>) bindParams.get("conditions");
// 1.构建查询请求
if (indexNames != null && indexNames.length > 0) {
searchRequest = new SearchRequest(indexNames);
} else {
// 所有的名称
EsIndex esIndex = new EsIndex();
esIndex.setDelFlag(false);
String[] indexArray = esIndexService.select(esIndex).stream().map(EsIndex::getIndexKey).toArray(String[]::new);
searchRequest = new SearchRequest(indexArray);
}
// 4.构建最外面的boolQuery
BoolQueryBuilder query = QueryBuilders.boolQuery();
if (options != null && options.size() > 0) {
// 大list
ArrayList<List<HashMap<String, String>>> orList = Lists.newArrayList();
// 小list
ArrayList<HashMap<String, String>> andList = Lists.newArrayList();
for (int i = 0; i < options.size(); i++) {
// 如果只有一条数据,直接装入orList
if (options.size() == 1) {
andList.add(options.get(i));
orList.add(andList);
break;
}
String searchRelation = options.get(i).get("searchRelation");
// 如果有俩条数据,判断他们的关系
if (options.size() == 2) {
if (Integer.toString(SearchRelationEnum.Q.getKey()).equals(searchRelation)) {
orList.add(options);
} else {
andList.add(options.get(0));
orList.add(andList);
andList = new ArrayList<>();
andList.add(options.get(1));
orList.add(andList);
}
break;
}
// 三条以上 判断倒数第二条是且还是或,最后一条不考虑且或
if (i == options.size() - 2) {
if (Integer.toString(SearchRelationEnum.Q.getKey()).equals(searchRelation)) {
andList.add(options.get(i));
andList.add(options.get(i + 1));
orList.add(andList);
} else {
andList.add(options.get(i));
orList.add(andList);
andList = new ArrayList<>();
andList.add(options.get(i + 1));
orList.add(andList);
}
break;
}
// 如果是且 装入小list
if (Integer.toString(SearchRelationEnum.Q.getKey()).equals(searchRelation)) {
andList.add(options.get(i));
} else {
// 如果是或 先装入小的,然后装入大的,然后清空小的
andList.add(options.get(i));
orList.add(andList);
andList = new ArrayList<>();
}
}
orList.forEach(or -> {
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
for (HashMap<String, String> stringHashMap : or) {
// 获取搜索前提条件
String searchPremise = stringHashMap.get("searchPremise");
// 获取搜索符号
String searchSymbol = stringHashMap.get("searchSymbol");
// 获取搜索内容
String searchContent = stringHashMap.get("searchContent");
// 索取搜索关系
String analyzeType = stringHashMap.get("analyzeType");
//判断analyzeType 是否等于 0 如果等于0 则证明他是keyword 字段,不需要分词,
if ("0".equals(analyzeType)){
// 判断searchSymbol是等于还是包含。如果是等于 设置他的前提条件为searchSymbol
analyzeType = "whitespace";
}else {
if (Integer.toString(SearchSymbolEnum.DY.getKey()).equals(searchSymbol)){
analyzeType = "whitespace";
searchPremise = searchPremise + ".keyword";
}
}
// 查询query 姓名 等于 或
// 判断前提条件是否为姓名
if ("xm".equals(searchPremise)) {
QueryBuilder multiQuery;
// 判断搜索符号是等于还是包含
// 如果是等于
if (Integer.toString(SearchSymbolEnum.DY.getKey()).equals(searchSymbol)) {
// 对于xm字段,xm的keyword字段 进行多字段查询,当条件为必须等于的时候,将查询条件不分词完成必须完全匹配
multiQuery = QueryBuilders.multiMatchQuery(searchContent, "xm", "xm.keyword").analyzer(analyzeType).boost((float) 0.5);
boolQuery.must(multiQuery);
} else if (Integer.toString(SearchSymbolEnum.BH.getKey()).equals(searchSymbol)) {
// 包含关系中 姓名必须匹配75%以上,才会返回
multiQuery = QueryBuilders.multiMatchQuery(searchContent, "xm", "xm.keyword").minimumShouldMatch("75%").boost((float) 0.5);
// 如果是且
boolQuery.must(multiQuery);
}
} else if ("lxdh".equals(searchPremise)) {
// 手机号码查询设置查询时可以出错,可出错的个数由ES决定
QueryBuilder matchQuery = QueryBuilders.matchQuery("lxdh", searchContent).fuzziness("AUTO").boost(3);
boolQuery.must(matchQuery);
} else if ("zjhm".equals(searchPremise)) {
QueryBuilder matchQuery = QueryBuilders.matchQuery("zjhm", searchContent).fuzziness("AUTO").boost(5);
boolQuery.must(matchQuery);
} else{
QueryBuilder multiMatchQuery = QueryBuilders.multiMatchQuery(searchContent,searchPremise).analyzer(analyzeType);
boolQuery.must(multiMatchQuery);}
}
query.should(boolQuery);
});
}
//6.高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
// 所有查询出来的字段全部高亮
HighlightBuilder.Field highlightTitle = new HighlightBuilder.Field("*").requireFieldMatch(false);
highlightTitle.highlighterType("unified");
highlightBuilder.field(highlightTitle);
// 3.构建高亮
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().query(query).highlighter(highlightBuilder);
// 2.将查询构建器放入查询请求中
searchRequest.source(sourceBuilder);
return searchRequest;
}