public Collection<Long> prune(int columnId, HashDistributionKey hashKey, int complex) {
        if (columnId == distributionColumns.size()) {
            // compute Hash Key
            long hashValue = hashKey.getHashValue();
            return Lists.newArrayList(bucketsList.get((int) ((hashValue & 0xffffffff) % hashMod)));
        }
        Column keyColumn = distributionColumns.get(columnId);
        PartitionColumnFilter filter = distributionColumnFilters.get(keyColumn.getName());
        if (null == filter) {
            // no filter in this column, no partition Key
            // return all subPartition
            return bucketsList;
        }

        List<LiteralExpr> inPredicateLiterals = filter.getInPredicateLiterals();
        if (null == inPredicateLiterals ||
                inPredicateLiterals.size() * complex > Config.max_distribution_pruner_recursion_depth) {
            LiteralExpr lowerBound = filter.getLowerBound();
            LiteralExpr upperBound = filter.getUpperBound();
            // equal one value
            if (filter.lowerBoundInclusive && filter.upperBoundInclusive
                    && lowerBound != null && upperBound != null
                    && 0 == lowerBound.compareLiteral(upperBound)) {
                try {
                    boolean isConvertToDate = PartitionUtil.isConvertToDate(keyColumn.getType(), lowerBound.getType());
                    hashKey.pushColumn(filter.getLowerBound(isConvertToDate), keyColumn.getType());
                    Collection<Long> result = prune(columnId + 1, hashKey, complex);
                    hashKey.popColumn();
                    return result;
                } catch (Exception e) {
                    LOG.warn("Prune distribution key {} with predicate {} failed:", keyColumn, filter, e);
                    return bucketsList;
                }
            }
            // return all SubPartition
            return bucketsList;
        }

        InPredicate inPredicate = filter.getInPredicate();
        if (null != inPredicate && !(inPredicate.getChild(0) instanceof SlotRef)) {
            // return all SubPartition
            return bucketsList;
        }

        Set<Long> resultSet = Sets.newHashSet();
        int inElementNum = inPredicateLiterals.size();
        int newComplex = inElementNum * complex;
        for (LiteralExpr expr : inPredicateLiterals) {
            hashKey.pushColumn(expr, keyColumn.getType());
            Collection<Long> subList = prune(columnId + 1, hashKey, newComplex);
            resultSet.addAll(subList);
            hashKey.popColumn();
            if (resultSet.size() >= bucketsList.size()) {
                break;
            }
        }
        return resultSet;
    }
@Test
    public void test() {
        List<Long> tabletIds = Lists.newArrayListWithExpectedSize(300);
        for (long i = 0; i < 300; i++) {
            tabletIds.add(i);
        }

        // distribution columns
        Column dealDate = new Column("dealDate", Type.DATE, false);
        Column mainBrandId = new Column("main_brand_id", Type.CHAR, false);
        Column itemThirdCateId = new Column("item_third_cate_id", Type.CHAR, false);
        Column channel = new Column("channel", Type.CHAR, false);
        Column shopType = new Column("shop_type", Type.CHAR, false);
        List<Column> columns = Lists.newArrayList(dealDate, mainBrandId, itemThirdCateId, channel, shopType);

        // filters
        PartitionColumnFilter dealDatefilter = new PartitionColumnFilter();
        dealDatefilter.setLowerBound(new StringLiteral("2019-08-22"), true);
        dealDatefilter.setUpperBound(new StringLiteral("2019-08-22"), true);

        PartitionColumnFilter mainBrandFilter = new PartitionColumnFilter();
        List<Expr> inList = Lists.newArrayList();
        inList.add(new StringLiteral("1323"));
        inList.add(new StringLiteral("2528"));
        inList.add(new StringLiteral("9610"));
        inList.add(new StringLiteral("3893"));
        inList.add(new StringLiteral("6121"));
        mainBrandFilter.setInPredicate(new InPredicate(new SlotRef(null, "main_brand_id"), inList, false));

        PartitionColumnFilter itemThirdFilter = new PartitionColumnFilter();
        List<Expr> inList2 = Lists.newArrayList();
        inList2.add(new StringLiteral("9719"));
        inList2.add(new StringLiteral("11163"));
        itemThirdFilter.setInPredicate(new InPredicate(new SlotRef(null, "item_third_cate_id"), inList2, false));

        PartitionColumnFilter channelFilter = new PartitionColumnFilter();
        List<Expr> inList3 = Lists.newArrayList();
        inList3.add(new StringLiteral("1"));
        inList3.add(new StringLiteral("3"));
        channelFilter.setInPredicate(new InPredicate(new SlotRef(null, "channel"), inList3, false));

        PartitionColumnFilter shopTypeFilter = new PartitionColumnFilter();
        List<Expr> inList4 = Lists.newArrayList();
        inList4.add(new StringLiteral("2"));
        shopTypeFilter.setInPredicate(new InPredicate(new SlotRef(null, "shop_type"), inList4, false));

        Map<String, PartitionColumnFilter> filters = Maps.newHashMap();
        filters.put("dealDate", dealDatefilter);
        filters.put("main_brand_id", mainBrandFilter);
        filters.put("item_third_cate_id", itemThirdFilter);
        filters.put("channel", channelFilter);
        filters.put("shop_type", shopTypeFilter);

        HashDistributionPruner pruner = new HashDistributionPruner(tabletIds, columns, filters, tabletIds.size());

        Collection<Long> results = pruner.prune();
        // 20 = 1 * 5 * 2 * 2 * 1 (element num of each filter)
        Assert.assertEquals(20, results.size());

        filters.get("shop_type").getInPredicate().addChild(new StringLiteral("4"));
        filters.get("shop_type").setInPredicate(filters.get("shop_type").getInPredicate());
        results = pruner.prune();
        // 40 = 1 * 5 * 2 * 2 * 2 (element num of each filter)
        // 39 is because these is hash conflict
        Assert.assertEquals(39, results.size());

        filters.get("shop_type").getInPredicate().addChild(new StringLiteral("5"));
        filters.get("shop_type").getInPredicate().addChild(new StringLiteral("6"));
        filters.get("shop_type").getInPredicate().addChild(new StringLiteral("7"));
        filters.get("shop_type").getInPredicate().addChild(new StringLiteral("8"));
        filters.get("shop_type").setInPredicate(filters.get("shop_type").getInPredicate());
        results = pruner.prune();
        // 120 = 1 * 5 * 2 * 2 * 6 (element num of each filter) > 100
        Assert.assertEquals(300, results.size());

        // check hash conflict
        inList4.add(new StringLiteral("4"));
        HashDistributionKey hashKey = new HashDistributionKey();
        Set<Long> tablets = Sets.newHashSet();
        hashKey.pushColumn(new StringLiteral("2019-08-22"), Type.DATE);
        for (Expr inLiteral : inList) {
            hashKey.pushColumn((StringLiteral) inLiteral, Type.CHAR);
            for (Expr inLiteral2 : inList2) {
                hashKey.pushColumn((StringLiteral) inLiteral2, Type.CHAR);
                for (Expr inLiteral3 : inList3) {
                    hashKey.pushColumn((StringLiteral) inLiteral3, Type.CHAR);
                    for (Expr inLiteral4 : inList4) {
                        hashKey.pushColumn((StringLiteral) inLiteral4, Type.CHAR);
                        long hashValue = hashKey.getHashValue();
                        tablets.add(tabletIds.get((int) ((hashValue & 0xffffffff) % tabletIds.size())));
                        hashKey.popColumn();
                    }
                    hashKey.popColumn();
                }
                hashKey.popColumn();
            }
            hashKey.popColumn();
        }

        Assert.assertEquals(39, tablets.size());
    }