(點選上方公眾號,可快速關註)
來源:琴水玉 ,
www.cnblogs.com/lovesqcc/p/8432981.html
在訂單搜尋中,有時需要實現複合搜尋,比如 ( 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
shouldConditions = Lists.newArrayList(c, this); return new CompositeShouldCondition(shouldConditions, shouldMinimumMatch);
}
private static Map
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
multiConditions;
public CompositeMustCondition() { multiConditions = Lists.newArrayList(); }
public CompositeMustCondition(List
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
shouldConditions = Lists.newArrayList(c, this); return new CompositeShouldCondition(shouldConditions, shouldMinimumMatch);
}
@Override
public Map expr() {
List
return ImmutableMap.of(“bool”, ImmutableMap.of(“must”, conditions));
}
}
STEP4: 複合 或 查詢
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 CompositeShouldCondition implements Condition, Serializable {
private static final long serialVersionUID = -3706269911758312468L;
private List
conditions;
private Integer shouldMatchMinimum = 1;
public CompositeShouldCondition() {
this.conditions = Lists.newArrayList();
this.shouldMatchMinimum = 1;
}
public CompositeShouldCondition(List
conditions, Integer shouldMinimumMatch) { this.conditions = conditions;
this.shouldMatchMinimum = shouldMinimumMatch;
}
@Override
public Condition and(Condition c) {
return new CompositeMustCondition(Lists.newArrayList(c, this));
}
@Override
public Condition or(Condition c, Integer shouldMinimumMatch) {
return new CompositeShouldCondition(Lists.newArrayList(c, this),
shouldMinimumMatch);
}
@Override
public Map expr() {
List
Collectors.toList());
return ImmutableMap.of(“bool”, ImmutableMap.of(“should”, conditions, “minimum_should_match”, shouldMatchMinimum));
}
}
通用方法抽離
事實上,發現 or 的實現基本相同,可以寫在介面的預設方法裡:
package zzz.study.patterns.composite.escondition;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.Map;
/**
* Created by shuqin on 18/2/8.
*/
public interface Condition {
Condition and(Condition c);
Map expr(); // ES 查詢物件
default String json() {
return JSON.toJSONString(this);
}
default Condition or(Condition c, Integer shouldMinimumMatch) {
List
shouldConditions = Lists.newArrayList(c, this); return new CompositeShouldCondition(shouldConditions, shouldMinimumMatch);
}
default Condition or(List
conds, Integer shouldMinimumMatch) { List
shouldConditions = Lists.newArrayList(this); shouldConditions.addAll(conds);
return new CompositeShouldCondition(shouldConditions, shouldMinimumMatch);
}
}
工廠樣式
使用 new EsCondition 顯得比較“硬”一點,可以使用工廠樣式使得API更加友好一點。
package zzz.study.patterns.composite.escondition;
import java.util.List;
/**
* Created by shuqin on 18/2/11.
*/
public class ConditionFactory {
public static Condition eq(String fieldName, Object value) {
return new EsCondition(fieldName, Op.eq, value);
}
public static Condition neq(String fieldName, Object value) {
return new EsCondition(fieldName, Op.neq, value);
}
public static Condition in(String fieldName, List value) {
return new EsCondition(fieldName, Op.in, value);
}
public static Condition range(String fieldName, Range range) {
return new EsCondition(fieldName, Op.range, range);
}
public static Condition match(String fieldName, Match match) {
return new EsCondition(fieldName, Op.match, match);
}
}
示例
package zzz.study.patterns.composite.escondition;
import com.google.common.collect.Lists;
import static zzz.study.patterns.composite.escondition.ConditionFactory.*;
/**
* Created by shuqin on 18/2/8.
*/
public class ComplexConditionTest2 {
public static void main(String[] args) {
Condition c1 = eq(“shop_id”, “55”);
Condition c2 = eq(“order_no”, “E2001”);
Condition c3 = range(“book_time”, new Range(15100000000L, 15200000000L));
Condition c4 = in(“state”, Lists.newArrayList(5, 6));
Condition c5 = match(“goods_title”, new Match(“商品標題的一部分”, “90%”));
Condition c1mustc2mustc3 = c1.and(c2).and(c3);
//System.out.println(“c1 must c2 must c3 json: \n” + c1mustc2mustc3.json());
System.out.println(“c1 must c2 must c3 expr: \n” + c1mustc2mustc3.expr());
Condition c1c2orc3c4 = c1.and(c2).or(c3.and(c4), 1);
//System.out.println(“( c1 must c2 )or( c3 must c4 ) json:\n” + c1c2orc3c4.json());
System.out.println(“\n( c1 must c2 )or( c3 must c4 ) expr:\n” + c1c2orc3c4.expr());
Condition c2orc3mustc1orc4orc5 = (c2.or(c3, 1)).and(c4.or(Lists.newArrayList(c1,c5),2));
//System.out.println(“( c2 or c3 ) must ( c4 or c5 ) json:\n” + c2orc3mustc4orc5.json());
System.out.println(“\n( c2 or c3 ) must ( c1 or c4 or c5 ) expr:\n” + c2orc3mustc1orc4orc5.expr());
Condition complexCond = ((c1.and(c2)).or(c3.and(c4), 1)).and(c5.or(Lists.newArrayList(c2,c3), 2));
System.out.println(“\n(( c1 must c2 ) or ( c3 must c4 )) must (c5 or c2 or c3) expr:\n” + complexCond.expr());
}
}
小結
透過組合樣式,清晰地實現了複合搜尋條件的構建;透過工廠樣式,建立條件物件更加簡潔友好。
看完本文有收穫?請轉發分享給更多人
關註「ImportNew」,提升Java技能