Implementing the Builder Pattern for Elasticsearch Query Construction

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.

Tags: Design Patterns Builder Pattern elasticsearch java Query Construction

Posted on Sat, 29 Aug 2026 16:26:43 +0000 by Mr.Fab