The Builder pattern is particular effective when the goal is to assemble complex objects step-by-step. Below is a implementation tailored for constructing Elasticsearch search queries based on different search strategies.
Filter Inetrface Definition
interface QueryFilterStrategy {
QueryBuilder assemble();
static void applyDefaultFilters(BoolQueryBuilder mainBuilder) {
mainBuilder.must(QueryBuilders.termQuery("deleted", 0));
mainBuilder.must(QueryBuilders.termQuery("hasImage", true));
}
}
Query Director Implementation
class ElasticQueryDirector {
private SearchCriteriaVO criteria;
private SearchRequest request;
public ElasticQueryDirector(SearchCriteriaVO criteria, IndexNameEnum index) {
this.criteria = criteria;
this.request = new SearchRequest(index.getName());
}
public String construct() {
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// Determine strategy and build 'where' clause
QueryFilterStrategy chosenStrategy = resolveStrategy(criteria.getType());
sourceBuilder.query(chosenStrategy.assemble());
// Pagination settings
int offset = (criteria.getPageNum() - 1) * criteria.getPageSize();
sourceBuilder.from(offset);
sourceBuilder.size(criteria.getPageSize());
// Sorting logic
String sortOrder = criteria.getSortOrder();
if (StringUtils.isNotBlank(sortOrder)) {
if ("DESC".equalsIgnoreCase(sortOrder)) {
sourceBuilder.sort(new FieldSortBuilder("uploadTime").order(SortOrder.DESC));
} else if ("ASC".equalsIgnoreCase(sortOrder)) {
sourceBuilder.sort(new FieldSortBuilder("uploadTime").order(SortOrder.ASC));
}
}
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
// Finalize request
request.source(sourceBuilder);
String dslString = searchExecutor.generateDsl(request);
log.debug("Generated DSL: {}", dslString);
return dslString;
}
private QueryFilterStrategy resolveStrategy(SearchModeEnum mode) {
switch (mode) {
case BASIC:
return new BasicSearchFilter();
case ADVANCED:
return new AdvancedSearchFilter();
case REFINE:
return new RefineSearchFilter();
default:
throw new IllegalArgumentException("Unsupported search mode: " + mode);
}
}
private BoolQueryBuilder initBooleanQuery(SearchCriteriaVO.SearchNode node) {
BoolQueryBuilder bq = QueryBuilders.boolQuery();
if (node == null) return bq;
// Multi-field text search
String searchText = node.getQueryText();
if (StringUtils.isNotBlank(searchText)) {
bq.must(QueryBuilders.multiMatchQuery(searchText, "title", "description")
.analyzer("ik_smart"));
}
// Element tags (scoped query)
List<String> tags = node.getTags();
if (CollectionUtils.isNotEmpty(tags)) {
BoolQueryBuilder tagQuery = QueryBuilders.boolQuery();
tagQuery.should(QueryBuilders.termsQuery("sceneTags", tags));
tagQuery.should(QueryBuilders.termsQuery("itemTags", tags));
bq.filter(tagQuery);
}
// Source filtering
List<String> sources = node.getSources();
if (CollectionUtils.isNotEmpty(sources)) {
bq.filter(QueryBuilders.termsQuery("origin", sources));
}
// Author filtering
List<String> authors = node.getAuthors();
if (CollectionUtils.isNotEmpty(authors)) {
bq.filter(QueryBuilders.termsQuery("photographer", authors));
}
// Keywords
String keyTerms = node.getKeywords();
if (StringUtils.isNotBlank(keyTerms)) {
bq.must(QueryBuilders.multiMatchQuery(keyTerms, "caption", "metaKeywords")
.analyzer("ik_smart"));
}
// Date range
if (StringUtils.isNotBlank(node.getStartDate())) {
bq.filter(QueryBuilders.rangeQuery("captureDate").gte(node.getStartDate()));
}
if (StringUtils.isNotBlank(node.getEndDate())) {
bq.filter(QueryBuilders.rangeQuery("captureDate").lte(node.getEndDate()));
}
return bq;
}
// Basic Search Implementation
private class BasicSearchFilter implements QueryFilterStrategy {
public QueryBuilder assemble() {
BoolQueryBuilder bq = QueryBuilders.boolQuery();
String genericInput = criteria.getUniversalInput();
if (StringUtils.isNotBlank(genericInput)) {
bq.must(QueryBuilders.multiMatchQuery(genericInput, "title", "tags", "summary")
.analyzer("ik_smart"));
}
QueryFilterStrategy.applyDefaultFilters(bq);
return bq;
}
}
// Advanced Search Implementation
private class AdvancedSearchFilter implements QueryFilterStrategy {
public QueryBuilder assemble() {
BoolQueryBuilder bq = initBooleanQuery(criteria.getPrimaryNode());
QueryFilterStrategy.applyDefaultFilters(bq);
return bq;
}
}
// Refine Search Implementation (Search within results)
private class RefineSearchFilter implements QueryFilterStrategy {
public QueryBuilder assemble() {
// Outer query constraints
BoolQueryBuilder outerQuery = initBooleanQuery(criteria.getPrimaryNode());
// Inner query constraints for refinement
BoolQueryBuilder innerQuery = initBooleanQuery(criteria.getRefinementNode());
// Nest the inner query as a filter
outerQuery.filter(innerQuery);
QueryFilterStrategy.applyDefaultFilters(outerQuery);
return outerQuery;
}
}
}
Execute the construction process by invoking director.construct() to generate the final query string.