Hibernate Search 中 BigDecimal 的范围查询

我正在尝试对 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?
罗迪埃

问题是您在索引时将值乘以 100,而不是在查询时。因此,当您搜索 value 时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情况下,我真的认为您的函数应该将数字解析为 a ,而不是 a 然后你可以将它相乘,然后将它转换为Double,类似于你在函数中所做的。

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

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章