在订单搜索中,有时需要实现复合搜索,好比 ( A must B ) or ( C must D ) 可能 (A or C) must ( B or D ) 。 这就需要可以或许机动地组合条件,条件可以是原子的或复合的。可以利用组合模式来实现。
思路
要实现复合搜索条件的构建,需要办理两个问题:A. 如何暗示复合搜索条件; B. 如何将复合搜索条件转换为符合的ES查询工具。对付A来说,要害就是搜索条件可机动组合,昆山软件开发,用组合模式再符合不外;对付B来说,昆山软件公司,需要知道ES如何暗示这些复合搜索。
(A must B ) or ( C must D) 的 ES 暗示为:
{"query":{"bool":{"should":[{"bool":{"must":[{"term":{"shop_id":63077}},{"terms":{"state":[1,2,3,4,5]}}]}},{"bool":{"must":[{"term":{"shop_id":63077}},{"range":{"book_time":{"gt":1516550400}}},{"terms":{"order_tags":["IS_SECURED_TRANSACTIONS"]}}]}}],"minimum_should_match":1}},"from":0,"size":10}
( A or B ) must ( C or D ) 的 ES 暗示是:
bool:{must:[{bool:{should:[{A},{C}],minimum_should_match: 1}},{bool:{should:[{D},{B}], minimum_should_match: 1}}]}
组合模式的要点是:原子条件和复合条件具备沟通的行为接口,从而可以或许组合和叠加。
实现
STEP1: 首先界说 Condition 接口, 今朝仅支持 与 和 或 操纵,以及查询工具转换。
/**
* Created by shuqin on 18/2/7.
*/
public interface Condition {
Condition and(Condition c);
Condition or(Condition c, Integer shouldMinimumMatch);
Map expr(); // ES 查询工具
default String json() {
return JSON.toJSONString(this);
}
}
STEP2: 原子条件 EsCondition 实现
package zzz.study.patterns.composite.escondition;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import lombok.Data;
/**
* Created by shuqin on 18/2/8.
*/
@Data
public class EsCondition implements Condition, Serializable {
private static final long serialVersionUID = -209082552315760372L;
/** ES 字段名称 */
private String fieldName;
/** 匹配符 */
private Op op;
/**
*
* 要匹配的值,用于 eq, neq, range, in, match
*
* eq 传 单个值工具,好比 Integer, String , etc
* in 传 List 工具
* range 传 Range 工具
* match 传 Match 工具
*
*/
private Object value;
public EsCondition() {
}
public EsCondition(String fieldName, Op op, Object value) {
this.fieldName = fieldName;
this.op = op;
this.value = value;
}
public String getFieldName() {
return fieldName;
}
public Op getOp() {
return op;
}
public Object getValue() {
return value;
}
@Override
public String toString() {
return "EsCondition{" +
"fieldName='" + fieldName + '\'' +
", op=" + op +
", value=" + value +
'}';
}
@Override
public Condition and(Condition c) {
return new CompositeMustCondition(Lists.newArrayList(c, this));
}
@Override
public Condition or(Condition c, Integer shouldMinimumMatch) {
List<Condition> shouldConditions = Lists.newArrayList(c, this);
return new CompositeShouldCondition(shouldConditions, shouldMinimumMatch);
}
private static Map<String, String> op2EsKeyMap = ImmutableMap.of(
Op.eq.name(), "term",
Op.neq.name(), "term",
Op.in.name(), "terms",
Op.range.name(), "range",
Op.match.name(), "match"
);
@Override
public Map expr() {
return buildEsExpr(op2EsKeyMap.get(op.name()));
}
private Map buildEsExpr(String esKey) {
return ImmutableMap.of(esKey, ImmutableMap.of(fieldName, value));
}
}
STEP3: 复合 must 条件
package zzz.study.patterns.composite.escondition;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.Data;
/**
* Created by shuqin on 18/2/8.
*/
@Data
public class CompositeMustCondition implements Condition, Serializable {
private static final long serialVersionUID = 2546838275170403153L;
private List<Condition> multiConditions;
public CompositeMustCondition() { multiConditions = Lists.newArrayList(); }
public CompositeMustCondition(List<Condition> multiConditions) {
this.multiConditions = multiConditions;
}
@Override
public Condition and(Condition c) {
multiConditions.add(c);
return new CompositeMustCondition(multiConditions);
}
@Override
public Condition or(Condition c, Integer shouldMinimumMatch) {
List<Condition> shouldConditions = Lists.newArrayList(c, this);
return new CompositeShouldCondition(shouldConditions, shouldMinimumMatch);
}
@Override
public Map expr() {
List<Map> conditions = multiConditions.stream().map(Condition::expr).collect(Collectors.toList());
return ImmutableMap.of("bool", ImmutableMap.of("must", conditions));
}
}