我正在尝试对 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] 删除。
我来说两句