Hibernate Search 中 BigDecimal 的范围查询
Range query on BigDecimal in Hibernate Search
我正在尝试对 BigDecimal 类型的实体字段进行范围查询,但无法使其工作。这是我到目前为止所做的。
这是实体 class。
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 );
}
}
然后我定义了一个class范围来传递下限、上限和字段数据类型。
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,但在查询时却没有。因此,当您搜索值 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
的情况下,我真的认为您的函数应该将数字解析为 BigDecimal
,而不是 Double
。然后你可以将它相乘,然后将它转换为 Double
,类似于你在你的函数中所做的。
此外,在索引(和查询)缩放的 BigDecimal
时,您可能希望使用 Long
而不是 Double
:您将获得更高的性能并且肯定索引数字正是您想要的数字(没有四舍五入)。无论如何,您不需要 double
的货币金额范围,除非您处理的金额高于地球上所有资产的总价值:)
我正在尝试对 BigDecimal 类型的实体字段进行范围查询,但无法使其工作。这是我到目前为止所做的。
这是实体 class。
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 );
}
}
然后我定义了一个class范围来传递下限、上限和字段数据类型。
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,但在查询时却没有。因此,当您搜索值 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
的情况下,我真的认为您的函数应该将数字解析为 BigDecimal
,而不是 Double
。然后你可以将它相乘,然后将它转换为 Double
,类似于你在你的函数中所做的。
此外,在索引(和查询)缩放的 BigDecimal
时,您可能希望使用 Long
而不是 Double
:您将获得更高的性能并且肯定索引数字正是您想要的数字(没有四舍五入)。无论如何,您不需要 double
的货币金额范围,除非您处理的金额高于地球上所有资产的总价值:)