Skip to content

Commit

Permalink
HSEARCH-2397 Fix type detection with ES for embeddeds whose prefix do…
Browse files Browse the repository at this point in the history
…esn't contain a dot

The previous code was retrieving the source properties for document
fields by assuming that field names always match the property paths
exactly, which isn't true due to customizable @IndexedEmbedded
prefixes.
The solution is to augment the metamodel with back references, so that
document field metadata contains the reference to their source property,
thereby removing the need to guess the property from the field name.

I took this opportunity to also add back references from properties to
their declaring types, from embedded types to their source property, and
from document fields to their source type (useful for class bridges in
particular, where there's no source property). This part could be
removed, but it makes the metamodel more consistent.
  • Loading branch information
yrodiere authored and Sanne committed Nov 28, 2016
1 parent f0f55cb commit 2635d99
Show file tree
Hide file tree
Showing 14 changed files with 254 additions and 151 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ private boolean isBridgeDefinedField(EntityIndexBinding binding, String projecte
private Object convertFieldValue(EntityIndexBinding binding, DocumentFieldMetadata field, JsonElement value, ConversionContext conversionContext) {
FieldBridge fieldBridge = field.getFieldBridge();

ExtendedFieldType type = FieldHelper.getType( binding, field );
ExtendedFieldType type = FieldHelper.getType( field );
if ( ExtendedFieldType.BOOLEAN.equals( type ) ) {
return value.getAsBoolean();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,19 +396,19 @@ private void addFieldMapping(JsonObject payload, EntityIndexBinding descriptor,
String simpleFieldName = FieldHelper.getEmbeddedFieldPropertyName( fieldMetadata.getName() );
JsonObject field = new JsonObject();

ElasticsearchFieldType fieldType = addTypeOptions( field, descriptor, fieldMetadata );
ElasticsearchFieldType fieldType = addTypeOptions( field, fieldMetadata );

field.addProperty( "store", fieldMetadata.getStore() == Store.NO ? false : true );

addIndexOptions( field, descriptor, fieldMetadata.getName(),
addIndexOptions( field, descriptor, fieldMetadata.getSourceProperty(), fieldMetadata.getFieldName(),
fieldType, fieldMetadata.getIndex(), fieldMetadata.getAnalyzerReference() );

if ( fieldMetadata.getBoost() != null ) {
field.addProperty( "boost", fieldMetadata.getBoost() );
}

if ( fieldMetadata.indexNullAs() != null ) {
JsonElement nullValueJsonElement = getNullValue( descriptor, fieldType, fieldMetadata );
JsonElement nullValueJsonElement = getNullValue( fieldType, fieldMetadata );
field.add( "null_value", nullValueJsonElement );
}

Expand Down Expand Up @@ -439,7 +439,8 @@ private void addFieldMapping(JsonObject payload, EntityIndexBinding binding, Bri

ElasticsearchFieldType fieldType = addTypeOptions( field, bridgeDefinedField );

addIndexOptions( field, binding, fieldName, fieldType, bridgeDefinedField.getIndex(), null );
addIndexOptions( field, binding, bridgeDefinedField.getSourceField().getSourceProperty(),
fieldName, fieldType, bridgeDefinedField.getIndex(), null );

// we don't overwrite already defined fields. Typically, in the case of spatial, the geo_point field
// is defined before the double field and we want to keep the geo_point one
Expand Down Expand Up @@ -489,8 +490,8 @@ private JsonObject addFieldMapping(JsonObject payload, FacetMetadata facetMetada
/**
* Adds the main indexing-related options to the given field: "index", "doc_values", "analyzer", ...
*/
private void addIndexOptions(JsonObject field, EntityIndexBinding binding, String fieldName,
ElasticsearchFieldType fieldType, Field.Index index, AnalyzerReference analyzerReference) {
private void addIndexOptions(JsonObject field, EntityIndexBinding binding, PropertyMetadata sourceProperty,
String fieldName, ElasticsearchFieldType fieldType, Field.Index index, AnalyzerReference analyzerReference) {
String elasticsearchIndex;
switch ( index ) {
case ANALYZED:
Expand All @@ -509,7 +510,7 @@ private void addIndexOptions(JsonObject field, EntityIndexBinding binding, Strin
}
field.addProperty( "index", elasticsearchIndex );

if ( NOT_INDEXED.equals( elasticsearchIndex ) && FieldHelper.isSortableField( binding, fieldName ) ) {
if ( NOT_INDEXED.equals( elasticsearchIndex ) && FieldHelper.isSortableField( sourceProperty, fieldName ) ) {
// We must use doc values in order to enable sorting on non-indexed fields
field.addProperty( "doc_values", true );
}
Expand All @@ -527,8 +528,8 @@ private boolean canTypeBeAnalyzed(ElasticsearchFieldType fieldType) {
return ElasticsearchFieldType.STRING.equals( fieldType );
}

private ElasticsearchFieldType addTypeOptions(JsonObject field, EntityIndexBinding descriptor, DocumentFieldMetadata fieldMetadata) {
return addTypeOptions( fieldMetadata.getFieldName(), field, FieldHelper.getType( descriptor, fieldMetadata ) );
private ElasticsearchFieldType addTypeOptions(JsonObject field, DocumentFieldMetadata fieldMetadata) {
return addTypeOptions( fieldMetadata.getFieldName(), field, FieldHelper.getType( fieldMetadata ) );
}

private ElasticsearchFieldType addTypeOptions(JsonObject field, BridgeDefinedField bridgeDefinedField) {
Expand Down Expand Up @@ -671,8 +672,7 @@ private ElasticsearchFieldType addTypeOptions(String fieldName, JsonObject field
return elasticsearchType;
}

private JsonElement getNullValue(EntityIndexBinding indexBinding, ElasticsearchFieldType dataType,
DocumentFieldMetadata fieldMetadata) {
private JsonElement getNullValue(ElasticsearchFieldType dataType, DocumentFieldMetadata fieldMetadata) {
Gson gson = gsonService.getGson();
Object convertedValue = ElasticSearchIndexNullAsHelper.getNullValue(
fieldMetadata.getName(), dataType, fieldMetadata.indexNullAs()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ else if ( SpatialHelper.isSpatialFieldLongitude( jsonPropertyName ) ) {
}
}
else {
ExtendedFieldType type = FieldHelper.getType( indexBinding, documentFieldMetadata );
ExtendedFieldType type = FieldHelper.getType( documentFieldMetadata );
if ( ExtendedFieldType.BOOLEAN.equals( type ) ) {
FieldBridge fieldBridge = documentFieldMetadata.getFieldBridge();
Boolean value = (Boolean) ( (TwoWayFieldBridge) fieldBridge ).get( field.name(), document );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@
import org.hibernate.search.bridge.spi.FieldType;
import org.hibernate.search.engine.metadata.impl.BridgeDefinedField;
import org.hibernate.search.engine.metadata.impl.DocumentFieldMetadata;
import org.hibernate.search.engine.metadata.impl.EmbeddedTypeMetadata;
import org.hibernate.search.engine.metadata.impl.PropertyMetadata;
import org.hibernate.search.engine.metadata.impl.SortableFieldMetadata;
import org.hibernate.search.engine.metadata.impl.TypeMetadata;
import org.hibernate.search.engine.spi.EntityIndexBinding;
import org.hibernate.search.metadata.NumericFieldSettingsDescriptor.NumericEncodingType;

Expand Down Expand Up @@ -88,11 +86,11 @@ public boolean isNumeric() {
}

// TODO HSEARCH-2259 make it work with fields embedded types
private static NumericEncodingType getNumericEncodingType(EntityIndexBinding indexBinding, DocumentFieldMetadata field) {
private static NumericEncodingType getNumericEncodingType(DocumentFieldMetadata field) {
NumericEncodingType numericEncodingType = field.getNumericEncodingType();

if ( numericEncodingType == NumericEncodingType.UNKNOWN ) {
PropertyMetadata hostingProperty = getPropertyMetadata( indexBinding, field.getName() );
PropertyMetadata hostingProperty = field.getSourceProperty();
if ( hostingProperty != null ) {
BridgeDefinedField bridgeDefinedField = hostingProperty.getBridgeDefinedFields().get( field.getName() );
if ( bridgeDefinedField != null ) {
Expand Down Expand Up @@ -135,10 +133,9 @@ private static ExtendedFieldType toExtendedFieldType(NumericEncodingType numeric
}
}

static ExtendedFieldType getType(EntityIndexBinding indexBinding, DocumentFieldMetadata fieldMetadata) {
String fieldName = fieldMetadata.getFieldName();

Class<?> propertyClass = getPropertyClass( indexBinding, fieldName );
static ExtendedFieldType getType(DocumentFieldMetadata fieldMetadata) {
PropertyMetadata propertyMetata = fieldMetadata.getSourceProperty();
Class<?> propertyClass = propertyMetata == null ? null : propertyMetata.getPropertyClass();
if ( propertyClass == null ) {
return ExtendedFieldType.UNKNOWN;
}
Expand All @@ -147,7 +144,7 @@ static ExtendedFieldType getType(EntityIndexBinding indexBinding, DocumentFieldM
return ExtendedFieldType.BOOLEAN;
}
else if ( isNumeric(fieldMetadata) ) {
return toExtendedFieldType( getNumericEncodingType( indexBinding, fieldMetadata ) );
return toExtendedFieldType( getNumericEncodingType( fieldMetadata ) );
}
else if ( Date.class.isAssignableFrom( propertyClass ) ) {
return ExtendedFieldType.DATE;
Expand Down Expand Up @@ -236,105 +233,25 @@ static String[] getFieldNameParts(String fieldName) {
return isEmbeddedField ? DOT.split( fieldName ) : new String[]{ fieldName };
}

private static Class<?> getPropertyClass(EntityIndexBinding indexBinding, String fieldName) {
PropertyMetadata propertyMetadata = getPropertyMetadata( indexBinding, fieldName );
return propertyMetadata != null ? propertyMetadata.getPropertyClass() : null;
}

private static PropertyMetadata getPropertyMetadata(EntityIndexBinding indexBinding, String fieldName) {
TypeMetadata typeMetadata;

boolean isEmbeddedField = isEmbeddedField( fieldName );
String[] fieldNameParts = isEmbeddedField ? DOT.split( fieldName ) : new String[]{ fieldName };

if ( isEmbeddedField ) {
typeMetadata = getLeafTypeMetadata( indexBinding, fieldNameParts );
}
else {
typeMetadata = indexBinding.getDocumentBuilder().getMetadata();
}

PropertyMetadata property = getPropertyMetadata( typeMetadata, fieldName, fieldNameParts );
if ( property != null ) {
return property;
}

return null;
}

static DocumentFieldMetadata getFieldMetadata(EntityIndexBinding indexBinding, String fieldName) {
String identifierName = indexBinding.getDocumentBuilder().getIdentifierName();
// The identifier name may be null when using implicit provided IDs
if ( identifierName != null && identifierName.equals( fieldName ) ) {
return indexBinding.getDocumentBuilder()
.getTypeMetadata()
.getIdPropertyMetadata()
.getFieldMetadata( fieldName );
}

PropertyMetadata property = FieldHelper.getPropertyMetadata( indexBinding, fieldName );

if ( property != null ) {
return property.getFieldMetadata( fieldName );
}
else {
Set<DocumentFieldMetadata> classBridgeMetadata = indexBinding.getDocumentBuilder().getMetadata().getClassBridgeMetadata();
for ( DocumentFieldMetadata documentFieldMetadata : classBridgeMetadata ) {
if ( documentFieldMetadata.getFieldName().equals( fieldName ) ) {
return documentFieldMetadata;
}
}
}

return null;
}

private static TypeMetadata getLeafTypeMetadata(EntityIndexBinding indexBinding, String[] fieldNameParts) {
TypeMetadata parentMetadata = indexBinding.getDocumentBuilder().getMetadata();

for ( int i = 0; i < fieldNameParts.length - 1; i++ ) {
for ( EmbeddedTypeMetadata embeddedTypeMetadata : parentMetadata.getEmbeddedTypeMetadata() ) {
if ( embeddedTypeMetadata.getEmbeddedPropertyName().equals( fieldNameParts[i] ) ) {
parentMetadata = embeddedTypeMetadata;
break;
}
}
// This also addresses the ID case
DocumentFieldMetadata result = indexBinding.getDocumentBuilder().getMetadata().getDocumentFieldMetadataFor( fieldName );
if ( result != null ) {
return result;
}

return parentMetadata;
}

private static PropertyMetadata getPropertyMetadata(TypeMetadata type, String fieldName, String[] fieldNameParts) {
String lastParticle = fieldNameParts[fieldNameParts.length - 1];

for ( PropertyMetadata property : type.getAllPropertyMetadata() ) {
for ( DocumentFieldMetadata field : property.getFieldMetadata() ) {
if ( field.getName().equals( fieldName ) ) {
return property;
}
}
}

for ( EmbeddedTypeMetadata embeddedType : type.getEmbeddedTypeMetadata() ) {
if ( !lastParticle.startsWith( embeddedType.getEmbeddedPropertyName() ) ) {
continue;
}

for ( PropertyMetadata property : embeddedType.getAllPropertyMetadata() ) {
for ( DocumentFieldMetadata field : embeddedType.getAllDocumentFieldMetadata() ) {
if ( field.getName().equals( fieldName ) ) {
return property;
}
}
Set<DocumentFieldMetadata> classBridgeMetadata = indexBinding.getDocumentBuilder().getMetadata().getClassBridgeMetadata();
for ( DocumentFieldMetadata documentFieldMetadata : classBridgeMetadata ) {
if ( documentFieldMetadata.getFieldName().equals( fieldName ) ) {
return documentFieldMetadata;
}
}

return null;
}

public static boolean isSortableField(EntityIndexBinding indexBinding, String fieldName) {
PropertyMetadata property = getPropertyMetadata( indexBinding, fieldName );
for ( SortableFieldMetadata sortableField : property.getSortableFieldMetadata() ) {
public static boolean isSortableField(PropertyMetadata sourceProperty, String fieldName) {
for ( SortableFieldMetadata sortableField : sourceProperty.getSortableFieldMetadata() ) {
if ( fieldName.equals( sortableField.getFieldName() ) ) {
return true;
}
Expand Down
Loading

0 comments on commit 2635d99

Please sign in to comment.