Hibernate Search中BigDecimal的范围查询

时间:2019-09-04 20:54:39

标签: hibernate-search

我正在尝试对BigDecimal类型的实体字段进行范围查询,但无法使其正常工作。这是我到目前为止所做的。

这是实体类。

public class Deal {
    @Field(store = Store.YES)
    @Field(name = "budget_Sort", store = Store.YES, normalizer= @Normalizer(definition = SearchConstants.LOWER_CASE_NORMALIZER))
    @FieldBridge(impl = BigDecimalNumericFieldBridge.class)
    @SortableField(forField = "budget_Sort")
    @Column(name = "BUDGET", precision = 10, scale = 2)
    private BigDecimal budget = new BigDecimal(0);

    //other fields and methods omitted for brevity
}

BigDecimal的自定义FieldBridge如下。我选择DOUBLE类型作为转换后的类型。

public class BigDecimalNumericFieldBridge implements MetadataProvidingFieldBridge, TwoWayFieldBridge {

    private static final BigDecimal storeFactor = BigDecimal.valueOf( 100 );

    @Override
    public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
        if ( value != null ) {
            BigDecimal decimalValue = (BigDecimal) value;
            Double indexedValue = decimalValue.multiply( storeFactor ).doubleValue();
            luceneOptions.addNumericFieldToDocument( name, indexedValue, document );
            luceneOptions.addNumericDocValuesFieldToDocument(name, indexedValue, document);
        }
    }

    @Override
    public Object get(String name, Document document) {
        String fromLucene = document.get( name );
        if (Objects.nonNull(fromLucene)) {
            BigDecimal storedBigDecimal = new BigDecimal(fromLucene);
            return storedBigDecimal.divide(storeFactor);
        } else {
            return null;
        }
    }

    @Override
    public String objectToString(Object object) {
        return object.toString();
    }

    @Override
    public void configureFieldMetadata(String name, FieldMetadataBuilder builder) {
        builder.field( name, FieldType.DOUBLE );
    }
}

然后我定义了一个Range类来传递下限,上限和字段数据类型。

public class Range {
    private String lowerBound;
    private String upperBound;
    private String dataType;
}

并创建了以下对象来测试此范围查询。

Range budgetRange = new Range("9500", "10500",SearchConstants.BIGDECIMAL);

实际的查询构建逻辑如下。

    protected Query rangeFilterBuilder(String key, String field) {
        Query rangeQuery = null;
        String lowerBound = searchRequest.getRangeFilters().get(key).getLowerBound();
        String upperBound = searchRequest.getRangeFilters().get(key).getUpperBound();
        String dataType = searchRequest.getRangeFilters().get(key).getDataType();
        switch (dataType) {
            case SearchConstants.INTEGER:
                rangeQuery = queryBuilder.range().onField(field).from(Integer.valueOf(lowerBound)).to(Integer.valueOf(upperBound)).createQuery();
                break;
            case SearchConstants.LONG:
                rangeQuery = queryBuilder.range().onField(field).from(Long.valueOf(lowerBound)).to(Long.valueOf(upperBound)).createQuery();
                break;
            case SearchConstants.DOUBLE:
                rangeQuery = queryBuilder.range().onField(field).from(Double.valueOf(lowerBound)).to(Double.valueOf(upperBound)).createQuery();
                break;
            case SearchConstants.STRING:
                rangeQuery = queryBuilder.range().onField(field).from((lowerBound)).to(upperBound).createQuery();
                break;
            case SearchConstants.DATE:
                rangeQuery = queryBuilder.range().onField(field).from(LocalDateTime.parse(lowerBound)).to(LocalDateTime.parse(upperBound)).createQuery();
                break;
            case SearchConstants.BIGDECIMAL:
                rangeQuery = queryBuilder.range().onField(field).from(Double.valueOf(lowerBound)).to(Double.valueOf(upperBound)).createQuery();
        }
        return rangeQuery;
    }```

This always return 0 matching result regardless of the range. I've tried other fields of type int or long and they work as expected. Is there something that I missed for BigDecimal range query?

1 个答案:

答案 0 :(得分:0)

问题在于,在建立索引时将值乘以100,但在查询时却没有。因此,当您搜索值42时,范围[40, 45]将不匹配,但范围[4000, 4500]将匹配。

对于“ BigDecimal”,解决方案是将函数中的边界乘以100:

        switch (dataType) {
            case SearchConstants.INTEGER:
                rangeQuery = queryBuilder.range().onField(field).from(Integer.valueOf(lowerBound)).to(Integer.valueOf(upperBound)).createQuery();
                break;
            case SearchConstants.LONG:
                rangeQuery = queryBuilder.range().onField(field).from(Long.valueOf(lowerBound)).to(Long.valueOf(upperBound)).createQuery();
                break;
            case SearchConstants.DOUBLE:
                rangeQuery = queryBuilder.range().onField(field).from(Double.valueOf(lowerBound)).to(Double.valueOf(upperBound)).createQuery();
                break;
            case SearchConstants.STRING:
                rangeQuery = queryBuilder.range().onField(field).from((lowerBound)).to(upperBound).createQuery();
                break;
            case SearchConstants.DATE:
                rangeQuery = queryBuilder.range().onField(field).from(LocalDateTime.parse(lowerBound)).to(LocalDateTime.parse(upperBound)).createQuery();
                break;
            case SearchConstants.BIGDECIMAL:
                rangeQuery = queryBuilder.range().onField(field).from(Double.valueOf(lowerBound) * 100.0).to(Double.valueOf(upperBound) * 100.0).createQuery();
        }

顺便说一句,我真的认为您的函数应该在BigDecimal情况下将数字解析为Double,而不是BigDecimal。然后,您可以将其相乘,然后将其转换为Double,类似于您在函数中所做的操作。

此外,在索引(和查询)缩放后的Long时,您可能希望使用Double而不是BigDecimal:您将获得更高的性能,并且可以确保索引编号恰好是您想要的编号(不四舍五入)。无论如何,您不需要double范围内的货币金额,除非您要处理的金额高于地球上所有资产的总值:)