使用 es 实现动态集合

工作中有时候需要根据条件配置的方式实现动态集合呢?本案例讲解一种通用的方案,如下:

(以下仅部分主要逻辑)

一、需要定义条件中需要的字段

        定义字段及可以被选择的关系列表

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;


@Getter
public enum FilterColumn implements EnumerationType {

    PRODUCT_TITLE("product title", false, "title", IS_EQUAL_TO, IS_NOT_EQUAL_TO, CONTAINS),
    PRODUCT_TAG("product tag", true, "tag_id", IS_EQUAL_TO, IS_NOT_EQUAL_TO),
    UPDATE_TIME("update time", true, "updated_at", IS_EQUAL_TO),
    CREATE_TIME("create time", true, "created_at", IS_EQUAL_TO),
    COST_PRICE("cost price", true, "cost_price_cent", IS_EQUAL_TO, IS_NOT_EQUAL_TO, IS_GREATER_THAN, IS_LESS_THAN),
    SELLING_PRICE("selling price", true, "selling_price_cent", IS_EQUAL_TO, IS_NOT_EQUAL_TO, IS_GREATER_THAN,IS_LESS_THAN),
    INVENTORY("inventory", true,"inventory", IS_EQUAL_TO, IS_NOT_EQUAL_TO, IS_GREATER_THAN, IS_LESS_THAN),
    CATEGORY("category", true, "product_category_id", IS_EQUAL_TO, IS_NOT_EQUAL_TO),
    BRAND_NAME("brand name", false, "id", IS_EQUAL_TO), // id
    COLLECTION("collection", true, "collection", IS_EQUAL_TO),
    ;

    private final String type;
    private final boolean isNumber;
    private final String columnName;
    private final FilterRelation[] supportedRelations;

    FilterColumn(String type, boolean isNumber, String columnName, FilterRelation... supportedRelations) {
        this.type = type;
        this.columnName = columnName;
        this.isNumber = isNumber;
        this.supportedRelations = supportedRelations;
    }

    @JsonCreator
    public static FilterColumn create (String name) {
        if(name == null) {
            throw new IllegalArgumentException();
        }
        return Enum.valueOf(FilterColumn.class, name.trim().replaceAll(" ", "_").toUpperCase());
    }

    @Override
    @JsonValue
    public String getType() {
        return type;
    }

    @Override
    public String getFriendlyType() {
        return null;
    }

    public boolean verifyCondition(String condition) {
        if (this.equals(UPDATE_TIME)) {
            return TimeCondition.findByType(condition) != null;
        } else if(this.equals(CREATE_TIME)){
            return TimeCondition.findByType(condition) != null;
        } else if (isNumber) {
            return NumberUtils.isDigits(condition);
        }
        return StringUtils.isNotBlank(condition);
    }
}

二、定义条件的执行方式

        如 等于、不等于、大于、小于等关系

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;


@Getter
public enum FilterRelation implements EnumerationType {

    /**
     * collection 筛选条件支持方式
     */
    IS_EQUAL_TO("is equal to", "="),
    IS_NOT_EQUAL_TO("is not equal to", "!="),
    CONTAINS("contains", "like"),
    IS_GREATER_THAN("is greater than", ">"),
    IS_LESS_THAN("is less than", "<");

    private final String type;
    private final String operator;

    FilterRelation(String type, String operator) {
        this.type = type;
        this.operator = operator;
    }

    @JsonCreator
    public static FilterColumn create (String name) {
        if(name == null) {
            throw new IllegalArgumentException();
        }
        return Enum.valueOf(FilterColumn.class, name.trim().replaceAll(" ", "_").toUpperCase());
    }

    @Override
    @JsonValue
    public String getType() {
        return type;
    }

    @Override
    public String getFriendlyType() {
        return null;
    }
}

三、创建集合及条件对象

/**
 * 集合对象
 */
@Data
public class Collection {
    
    private Long id;

    // 集合名
    private String title;

    // 是否可用
    private Boolean available;

    // 是否匹配任意一个条件
    private Boolean anyMatch;

    // 集合条件
    private List<CollectionFilter> filters;

}


/**
 * 单个集合条件
 */
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class CollectionFilter implements Entity<Long> {

    private Long id;

    private FilterColumn column;

    private FilterRelation relation;

    private String condition;

    private Long collectionId;

    public static SmartCollectionFilter createFromDTO(
            SmartCollectionFilterDTO collectionFilterDTO, Long collectionId) {
        if (ObjectUtils.isEmpty(collectionFilterDTO)) {
            return null;
        }
        SmartCollectionFilter collectionFilter = new SmartCollectionFilter();
        collectionFilter.setId(IdGenerator.getUID());
        collectionFilter.setColumn(collectionFilterDTO.getColumn());
        collectionFilter.setRelation(collectionFilterDTO.getRelation());
        collectionFilter.setCondition(collectionFilterDTO.getCondition());
        collectionFilter.setCollectionId(collectionId);
        return collectionFilter;
    }

    public boolean updateFromDTO(SmartCollectionFilterDTO dto) {
        boolean isChanged = false;
        if(!ObjectUtils.isEmpty(dto.getColumn()) && !dto.getColumn().equals(getColumn())) {
            setColumn(dto.getColumn());
            isChanged = true;
        }
        if(!ObjectUtils.isEmpty(dto.getRelation()) && !dto.getRelation().equals(getRelation())) {
            setRelation(dto.getRelation());
            isChanged = true;
        }
        if(!StringUtils.isEmpty(dto.getCondition()) && !dto.getCondition().equals(getCondition())) {
            setCondition(dto.getCondition());
            isChanged = true;
        }
        return isChanged;
    }

}

四、查询实现方式

/**
 * 集合条件列表转 es 条件列表
 * @param collectionId
 */
public void buildFilterList(Long collectionId) {
    List<CollectionFilter> filters = collectionService.findByCollectionId(collectionId);
    if (CollectionUtils.isEmpty(filters)) {
        return Page.empty(pageable);
    }
    BoolQueryBuilder builder = new BoolQueryBuilder();
    queryBuilder.filter(builder);
    builder.must(QueryBuilders.termQuery("available", true));
    if (CollectionUtils.isNotEmpty(excludeIds)){
        builder.mustNot(QueryBuilders.termsQuery("_id",excludeIds));
    }
    for (SmartCollectionFilter filter : filters) {
        applyFilter(filter,builder,baseCollection.getAnyMatch());
    }
}


/**
 * 单个条件转 es 条件
 * @param collectionId
 */
private void applyFilter(CollectionFilter filter, BoolQueryBuilder queryBuilder, Boolean anyMatch) {
    switch (filter.getColumn()) {
        case PRODUCT_TITLE : {
            switch (filter.getRelation()) {
                case  IS_EQUAL_TO : {
                    if (anyMatch)
                        queryBuilder.should(QueryBuilders.termQuery("title.keyword", filter.getCondition()));
                    else
                        queryBuilder.must(QueryBuilders.termQuery("title.keyword", filter.getCondition()));
                    break;
                }
                case  IS_NOT_EQUAL_TO : {
                    if (anyMatch)
                        queryBuilder.should(new BoolQueryBuilder().mustNot(QueryBuilders.termQuery("title.keyword", filter.getCondition())));
                    else
                        queryBuilder.mustNot(QueryBuilders.termQuery("title.keyword", filter.getCondition()));
                    break;
                }
                case  CONTAINS : {
                    if (anyMatch)
                        queryBuilder.should(QueryBuilders.wildcardQuery("title.keyword","*" +  filter.getCondition() + "*"));
                    else
                        queryBuilder.must(QueryBuilders.wildcardQuery("title.keyword", "*" +  filter.getCondition()+ "*"));
                    break;
                }
            }
            break;
        }
        case PRODUCT_TAG :
        {
            switch (filter.getRelation()) {
                case  IS_EQUAL_TO : {
                    if (anyMatch)
                        queryBuilder.should(QueryBuilders.termQuery("tagIds", filter.getCondition()));
                    else
                        queryBuilder.must(QueryBuilders.termQuery("tagIds", filter.getCondition()));
                    break;
                }
                case  IS_NOT_EQUAL_TO : {
                    if (anyMatch)
                        queryBuilder.should(new BoolQueryBuilder().mustNot(QueryBuilders.termQuery("tagIds", filter.getCondition())));
                    else
                        queryBuilder.mustNot(QueryBuilders.termQuery("tagIds", filter.getCondition()));
                    break;
                }
            }
            break;
        }
        case UPDATE_TIME :
        {
            if (filter.getRelation() == FilterRelation.IS_EQUAL_TO) {
                if (filter.getCondition().trim().equals("latest 30 days")) {
                    if (anyMatch)
                    queryBuilder.should(QueryBuilders.rangeQuery("updatedAt").gte(System.currentTimeMillis() - 30*24*60*60*1000l));
                    else
                    queryBuilder.must(QueryBuilders.rangeQuery("updatedAt").gte(System.currentTimeMillis() - 30*24*60*60*1000l));
                } else if (filter.getCondition().trim().equals("latest 15 days")) {
                    if (anyMatch)
                        queryBuilder.should(QueryBuilders.rangeQuery("updatedAt").gte(System.currentTimeMillis() - 15*24*60*60*1000l));
                    else
                        queryBuilder.must(QueryBuilders.rangeQuery("updatedAt").gte(System.currentTimeMillis() - 15*24*60*60*1000l));
                } else if (filter.getCondition().trim().equals("latest 7 days")) {
                    if (anyMatch)
                        queryBuilder.should(QueryBuilders.rangeQuery("updatedAt").gte(System.currentTimeMillis() - 15*24*60*60*1000l));
                    else
                        queryBuilder.must(QueryBuilders.rangeQuery("updatedAt").gte(System.currentTimeMillis() - 15*24*60*60*1000l));
                }

            } else  {
                queryBuilder.must(QueryBuilders.rangeQuery("updatedAt").lt(-100));
            }
            break;
        }
        case CREATE_TIME :
        {
            if (filter.getRelation() == FilterRelation.IS_EQUAL_TO) {
                if (filter.getCondition().trim().equals("latest 30 days")) {
                    if (anyMatch)
                        queryBuilder.should(QueryBuilders.rangeQuery("createdAt").gte(System.currentTimeMillis() - 30*24*60*60*1000l));
                    else
                        queryBuilder.must(QueryBuilders.rangeQuery("createdAt").gte(System.currentTimeMillis() - 30*24*60*60*1000l));
                } else if (filter.getCondition().trim().equals("latest 15 days")) {
                    if (anyMatch)
                        queryBuilder.should(QueryBuilders.rangeQuery("createdAt").gte(System.currentTimeMillis() - 15*24*60*60*1000l));
                    else
                        queryBuilder.must(QueryBuilders.rangeQuery("createdAt").gte(System.currentTimeMillis() - 15*24*60*60*1000l));
                } else if (filter.getCondition().trim().equals("latest 7 days")) {
                    if (anyMatch)
                        queryBuilder.should(QueryBuilders.rangeQuery("createdAt").gte(System.currentTimeMillis() - 7*24*60*60*1000l));
                    else
                        queryBuilder.must(QueryBuilders.rangeQuery("createdAt").gte(System.currentTimeMillis() - 7*24*60*60*1000l));
                }

            }
            break;
        }
        case SELLING_PRICE :
        {
            switch (filter.getRelation()) {
                case  IS_EQUAL_TO : {
                    if (anyMatch)
                        queryBuilder.should(QueryBuilders.termQuery("priceMin", filter.getCondition()));
                    else
                        queryBuilder.must(QueryBuilders.termQuery("priceMin", filter.getCondition()));
                    break;
                }
                case  IS_NOT_EQUAL_TO : {
                    if (anyMatch)
                        queryBuilder.should(new BoolQueryBuilder().mustNot(QueryBuilders.termQuery("priceMin", filter.getCondition())));
                    else
                        queryBuilder.mustNot(QueryBuilders.termQuery("priceMin", filter.getCondition()));
                    break;
                }
                case  IS_GREATER_THAN : {
                    if (anyMatch)
                        queryBuilder.should(QueryBuilders.rangeQuery("priceMin").gte(filter.getCondition()));
                    else
                        queryBuilder.must(QueryBuilders.rangeQuery("priceMin").gte(filter.getCondition()));
                    break;
                }
                case  IS_LESS_THAN : {
                    if (anyMatch)
                        queryBuilder.should(QueryBuilders.rangeQuery("priceMin").lte(filter.getCondition()));
                    else
                        queryBuilder.must(QueryBuilders.rangeQuery("priceMin").lte(filter.getCondition()));
                    break;
                }
            }
            break;
        }
        case INVENTORY :
        {
            break;
        }
        case CATEGORY :
        {
            switch (filter.getRelation()) {
                case  IS_EQUAL_TO : {
                    if (anyMatch)
                        queryBuilder.should(QueryBuilders.termQuery("categoryIds", filter.getCondition()));
                    else
                        queryBuilder.must(QueryBuilders.termQuery("categoryIds", filter.getCondition()));
                    break;
                }
                case  IS_NOT_EQUAL_TO : {
                    if (anyMatch)
                        queryBuilder.should(new BoolQueryBuilder().mustNot(QueryBuilders.termQuery("categoryIds", filter.getCondition())));
                    else
                        queryBuilder.mustNot(QueryBuilders.termQuery("categoryIds", filter.getCondition()));
                    break;
                }
            }
            break;
        }
        case COLLECTION: {
            BaseCollection collection = collectionService.findOne(Long.valueOf(filter.getCondition()));
            if (!collection.getAvailable() && collection.getType().equals(CollectionType.CUSTOM)) {
                break;
            }
            List<SmartCollectionFilter> filters = collectionService.findByCollectionId(collection.getId());
            if (CollectionUtils.isEmpty(filters)) {
                break;
            }
            BoolQueryBuilder subBuilder = new BoolQueryBuilder();
            for (SmartCollectionFilter subFilter : filters) {
                applyFilter(subFilter, subBuilder, collection.getAnyMatch());
            }
            if (anyMatch) {
                queryBuilder.should(subBuilder);
            } else {
                queryBuilder.must(subBuilder);
            }
            break;
        }
        case BRAND_NAME :
        {
            switch (filter.getRelation()) {
                case  IS_EQUAL_TO : {
                    if (anyMatch)
                        queryBuilder.should(QueryBuilders.termQuery("brandIds", filter.getCondition()));
                    else
                        queryBuilder.must(QueryBuilders.termQuery("brandIds", filter.getCondition()));
                    break;
                }
            }
            break;
        }
        case DAY_ORDER: // @Author masl - 2022/4/29 11:47 上午
        {
            List<String> spus = new ArrayList<>();
            if (filter.getRelation() == FilterRelation.IS_GREATER_THAN) {
                spus = productServiceFacade.findSpuByOrder(1, Integer.valueOf(filter.getCondition()));
            }
            if (filter.getRelation() == FilterRelation.IS_LESS_THAN) {
                spus = productServiceFacade.findSpuByOrder(2, Integer.valueOf(filter.getCondition()));
            }
            if (CollectionUtils.isNotEmpty(spus)) {
                if (anyMatch)
                    queryBuilder.should(QueryBuilders.termsQuery("spu.keyword", spus));
                else
                    queryBuilder.must(QueryBuilders.termsQuery("spu.keyword", spus));
            }
            break;
        }
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林中山野

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值