| Searching on a numeric field that was indexed through an @IndexedEmbedded will return each and every document (i.e. the filter won't apply) for a [value TO *] query and will return no document for a [value1 TO value2] query. This happens when using the Hibernate Search DSL to build range queries. I ran the test case in debug mode, and it seems that the problem starts here when building the query:
private Query createQuery(FieldContext fieldContext, ConversionContext conversionContext) {
final Query perFieldQuery;
final String fieldName = fieldContext.getField();
FieldDescriptor fieldDescriptor = queryContext.getFactory().getIndexedTypeDescriptor( queryContext.getEntityType() ).getIndexedField( fieldName );
if ( fieldDescriptor != null ) {
if ( FieldDescriptor.Type.NUMERIC.equals( fieldDescriptor.getType() ) ) {
perFieldQuery = createNumericRangeQuery( fieldName, rangeContext );
}
else {
perFieldQuery = createKeywordRangeQuery( fieldName, rangeContext, queryContext, conversionContext, fieldContext );
}
}
else {
if ( rangeBoundaryTypeRequiredNumericQuery( rangeContext.getFrom(), rangeContext.getTo() ) ) {
perFieldQuery = createNumericRangeQuery( fieldName, rangeContext );
}
else {
perFieldQuery = createKeywordRangeQuery( fieldName, rangeContext, queryContext, conversionContext, fieldContext );
}
}
return fieldContext.getFieldCustomizer().setWrappedQuery( perFieldQuery ).createQuery();
}
The problem is that fieldDescriptor is null for embedded fields, which makes the DSL fall back to some Infinispan-related magic, which doesn't consider Date to be a numeric indexed type. This the query becomes a keyword-range query ( ?) instead of a numeric-range query. I searched around a little, and indeed it seems that this method, which initializes the field descriptors of a type descriptor, doesn't consider embedded fields:
private Map<String, FieldDescriptor> createAllFieldDescriptors() {
Map<String, FieldDescriptor> fieldDescriptorMap = new HashMap<String, FieldDescriptor>();
for ( FieldDescriptor fieldDescriptor : classBridgeFieldDescriptors ) {
fieldDescriptorMap.put( fieldDescriptor.getName(), fieldDescriptor );
}
for ( PropertyDescriptor propertyDescriptor : propertyDescriptors ) {
for ( FieldDescriptor fieldDescriptor : propertyDescriptor.getIndexedFields() ) {
fieldDescriptorMap.put( fieldDescriptor.getName(), fieldDescriptor );
}
}
return fieldDescriptorMap;
}
The property descriptors don't include the embedded fields, since they are built that way:
private void createOrMergePropertyDescriptor(Map<String, PropertyDescriptor> propertyDescriptorsTmp,
PropertyMetadata propertyMetadata) {
String propertyName = propertyMetadata.getPropertyAccessorName();
Set<FieldDescriptor> tmpSet = new HashSet<FieldDescriptor>();
if ( propertyDescriptorsTmp.containsKey( propertyName ) ) {
tmpSet.addAll( propertyDescriptorsTmp.get( propertyName ).getIndexedFields() );
}
boolean id = false;
for ( DocumentFieldMetadata documentFieldMetadata : propertyMetadata.getFieldMetadataSet() ) {
if ( documentFieldMetadata.isId() ) {
id = true;
}
FieldDescriptor fieldDescriptor;
if ( documentFieldMetadata.isNumeric() ) {
fieldDescriptor = new NumericFieldDescriptorImpl( documentFieldMetadata );
}
else {
fieldDescriptor = new FieldDescriptorImpl( documentFieldMetadata );
}
tmpSet.add( fieldDescriptor );
}
PropertyDescriptor propertyDescriptor = new PropertyDescriptorImpl(
propertyMetadata.getPropertyAccessorName(),
id,
tmpSet
);
propertyDescriptorsTmp.put( propertyDescriptor.getName(), propertyDescriptor );
}
... which won't do since the fieldMetadataSet on propertyMetadata doesn't include embedded fields. I see two solutions: 1. Either we change the propertyMetadata so that it also includes a embeddedFieldsMetadataSet (and we initialize it, and we use it in createOrMergePropertyDescriptor()) 2. OR we change IndexedTypeDescriptorImpl so that, in some way, it uses typeMetadata.getEmbeddedTypeMetadata() in the createAllFieldDescriptors() method. Test case coming. |