diff --git a/elasticsearch/src/main/java/org/hibernate/search/elasticsearch/bridge/builtin/impl/ElasticsearchBooleanBridge.java b/elasticsearch/src/main/java/org/hibernate/search/elasticsearch/bridge/builtin/impl/ElasticsearchBooleanBridge.java index bdd93093372..c4e6669278a 100644 --- a/elasticsearch/src/main/java/org/hibernate/search/elasticsearch/bridge/builtin/impl/ElasticsearchBooleanBridge.java +++ b/elasticsearch/src/main/java/org/hibernate/search/elasticsearch/bridge/builtin/impl/ElasticsearchBooleanBridge.java @@ -7,9 +7,12 @@ package org.hibernate.search.elasticsearch.bridge.builtin.impl; import org.hibernate.search.bridge.TwoWayStringBridge; +import org.hibernate.search.bridge.spi.EncodingBridge; import org.hibernate.search.bridge.spi.IgnoreAnalyzerBridge; -import org.hibernate.search.bridge.util.impl.EncodingStringBridge; +import org.hibernate.search.bridge.spi.NullMarker; +import org.hibernate.search.bridge.util.impl.ToStringNullMarker; import org.hibernate.search.elasticsearch.logging.impl.Log; +import org.hibernate.search.metadata.NumericFieldSettingsDescriptor.NumericEncodingType; import org.hibernate.search.util.StringHelper; import org.hibernate.search.util.logging.impl.LoggerFactory; @@ -22,7 +25,7 @@ * @author Sylvain Vieujot * @author Yoann Rodiere */ -public class ElasticsearchBooleanBridge extends EncodingStringBridge implements TwoWayStringBridge, IgnoreAnalyzerBridge { +public class ElasticsearchBooleanBridge implements EncodingBridge, TwoWayStringBridge, IgnoreAnalyzerBridge { private static final Log LOG = LoggerFactory.make( Log.class ); @@ -48,6 +51,10 @@ public String objectToString(Object object) { } @Override + public NumericEncodingType getEncodingType() { + return NumericEncodingType.UNKNOWN; + } + protected Boolean parseIndexNullAs(String indexNullAs) throws IllegalArgumentException { if ( Boolean.TRUE.toString().equals( indexNullAs ) ) { return Boolean.TRUE; @@ -59,4 +66,10 @@ else if ( Boolean.FALSE.toString().equals( indexNullAs ) ) { throw LOG.invalidNullMarkerForBoolean(); } } + + @Override + public NullMarker createNullMarker(String indexNullAs) throws IllegalArgumentException { + Boolean booleanValue = parseIndexNullAs( indexNullAs ); + return new ToStringNullMarker( booleanValue ); + } } diff --git a/elasticsearch/src/main/java/org/hibernate/search/elasticsearch/schema/impl/DefaultElasticsearchSchemaTranslator.java b/elasticsearch/src/main/java/org/hibernate/search/elasticsearch/schema/impl/DefaultElasticsearchSchemaTranslator.java index 21dd57908cc..2c315f23692 100644 --- a/elasticsearch/src/main/java/org/hibernate/search/elasticsearch/schema/impl/DefaultElasticsearchSchemaTranslator.java +++ b/elasticsearch/src/main/java/org/hibernate/search/elasticsearch/schema/impl/DefaultElasticsearchSchemaTranslator.java @@ -11,14 +11,10 @@ import java.util.List; import java.util.Set; -import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; -import org.apache.lucene.index.IndexableField; import org.hibernate.search.analyzer.impl.AnalyzerReference; import org.hibernate.search.annotations.Store; -import org.hibernate.search.bridge.LuceneOptions; -import org.hibernate.search.bridge.builtin.nullencoding.impl.NotEncodingCodec; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.bridge.spi.NullMarker; import org.hibernate.search.elasticsearch.impl.ToElasticsearch; import org.hibernate.search.elasticsearch.logging.impl.Log; import org.hibernate.search.elasticsearch.schema.impl.model.DataType; @@ -32,10 +28,12 @@ import org.hibernate.search.engine.impl.DefaultBoostStrategy; import org.hibernate.search.engine.metadata.impl.BridgeDefinedField; import org.hibernate.search.engine.metadata.impl.DocumentFieldMetadata; +import org.hibernate.search.engine.metadata.impl.DocumentFieldPath; import org.hibernate.search.engine.metadata.impl.EmbeddedTypeMetadata; import org.hibernate.search.engine.metadata.impl.FacetMetadata; import org.hibernate.search.engine.metadata.impl.PropertyMetadata; import org.hibernate.search.engine.metadata.impl.TypeMetadata; +import org.hibernate.search.engine.nulls.codec.impl.NullMarkerCodec; import org.hibernate.search.engine.spi.DocumentBuilderIndexedEntity; import org.hibernate.search.engine.spi.EntityIndexBinding; import org.hibernate.search.exception.AssertionFailure; @@ -132,7 +130,7 @@ private void addPropertyMapping(ElasticsearchMappingBuilder mappingBuilder, Docu logDynamicBoostWarning( mappingBuilder, sourceProperty.getDynamicBoostStrategy(), propertyPath ); } - addNullValue( propertyMapping, fieldMetadata ); + addNullValue( propertyMapping, mappingBuilder, fieldMetadata ); for ( FacetMetadata facetMetadata : fieldMetadata.getFacetMetadata() ) { try { @@ -404,55 +402,33 @@ private DataType addTypeOptions(String fieldName, PropertyMapping propertyMappin return elasticsearchType; } - private void addNullValue(PropertyMapping propertyMapping, DocumentFieldMetadata fieldMetadata) { + private void addNullValue(PropertyMapping propertyMapping, ElasticsearchMappingBuilder mappingBuilder, DocumentFieldMetadata fieldMetadata) { NullMarkerCodec nullMarkerCodec = fieldMetadata.getNullMarkerCodec(); - - if ( nullMarkerCodec != NotEncodingCodec.SINGLETON ) { // XXX NotEncodingCodec is not accessible... - JsonPrimitive nullTokenJson = retrieveNullTokenAsJson( propertyMapping.getType(), fieldMetadata, nullMarkerCodec ); - + NullMarker nullMarker = nullMarkerCodec.getNullMarker(); + if ( nullMarker != null ) { + JsonPrimitive nullTokenJson = convertIndexedNullTokenToJson( mappingBuilder, fieldMetadata.getPath(), nullMarker.nullEncoded() ); propertyMapping.setNullValue( nullTokenJson ); } } - private JsonPrimitive retrieveNullTokenAsJson(DataType dataType, DocumentFieldMetadata fieldMetadata, NullMarkerCodec nullMarkerCodec) { - Document dummyDocument = new Document(); - LuceneOptions luceneOptions = - fieldMetadata.getSourceType().getFieldLuceneOptions( fieldMetadata.getSourceProperty(), fieldMetadata, null, 1.0f ); - - nullMarkerCodec.encodeNullValue( fieldMetadata.getAbsoluteName(), dummyDocument, luceneOptions ); - - List fields = dummyDocument.getFields(); - - if ( fields.size() > 1 ) { - throw new AssertionFailure( "Expected only one field" ); - } - - if ( !fields.isEmpty() ) { - IndexableField field = fields.iterator().next(); - return convertNullTokenFieldToJson( dataType, field ); - } - else { + private JsonPrimitive convertIndexedNullTokenToJson(ElasticsearchMappingBuilder mappingBuilder, + DocumentFieldPath fieldPath, Object indexedNullToken) { + if ( indexedNullToken == null ) { return null; } - } - private JsonPrimitive convertNullTokenFieldToJson(DataType dataType, IndexableField field) { - Number numericValue = field.numericValue(); - String stringValue = field.stringValue(); - if ( numericValue != null ) { - return new JsonPrimitive( numericValue ); + if ( indexedNullToken instanceof String ) { + return new JsonPrimitive( (String) indexedNullToken ); } - else if ( stringValue != null ) { - if ( DataType.BOOLEAN.equals( dataType ) ) { - // Parse the string so we send the proper type to Elasticsearch - return new JsonPrimitive( Boolean.parseBoolean( stringValue ) ); - } - else { - return new JsonPrimitive( stringValue ); - } + else if ( indexedNullToken instanceof Number ) { + return new JsonPrimitive( (Number) indexedNullToken ); + } + else if ( indexedNullToken instanceof Boolean ) { + return new JsonPrimitive( (Boolean) indexedNullToken ); } else { - return null; + throw LOG.unsupportedNullTokenType( mappingBuilder.getBeanClass(), fieldPath.getAbsoluteName(), + indexedNullToken.getClass() ); } } diff --git a/elasticsearch/src/main/java/org/hibernate/search/elasticsearch/spi/ElasticsearchIndexManagerType.java b/elasticsearch/src/main/java/org/hibernate/search/elasticsearch/spi/ElasticsearchIndexManagerType.java index ee7e6fdb8a4..139ec39b0dc 100644 --- a/elasticsearch/src/main/java/org/hibernate/search/elasticsearch/spi/ElasticsearchIndexManagerType.java +++ b/elasticsearch/src/main/java/org/hibernate/search/elasticsearch/spi/ElasticsearchIndexManagerType.java @@ -8,6 +8,8 @@ import org.hibernate.search.analyzer.impl.RemoteAnalyzer; import org.hibernate.search.analyzer.impl.RemoteAnalyzerProvider; +import org.hibernate.search.engine.nulls.impl.LuceneMissingValueStrategy; +import org.hibernate.search.engine.nulls.impl.MissingValueStrategy; import org.hibernate.search.indexes.spi.AnalyzerExecutionStrategy; import org.hibernate.search.indexes.spi.IndexManagerType; @@ -24,6 +26,11 @@ public AnalyzerExecutionStrategy getAnalyzerExecutionStrategy() { return AnalyzerExecutionStrategy.REMOTE; } + @Override + public MissingValueStrategy getMissingValueStrategy() { + return LuceneMissingValueStrategy.INSTANCE; + } + @Override public RemoteAnalyzer getRemoteAnalyzer(String name) { return new RemoteAnalyzer( name ); diff --git a/engine/src/main/java/org/hibernate/search/bridge/builtin/NumericEncodingDateBridge.java b/engine/src/main/java/org/hibernate/search/bridge/builtin/NumericEncodingDateBridge.java index 8e368aaaed4..08d896f4dab 100644 --- a/engine/src/main/java/org/hibernate/search/bridge/builtin/NumericEncodingDateBridge.java +++ b/engine/src/main/java/org/hibernate/search/bridge/builtin/NumericEncodingDateBridge.java @@ -19,10 +19,10 @@ import org.hibernate.search.bridge.ParameterizedBridge; import org.hibernate.search.bridge.TwoWayFieldBridge; import org.hibernate.search.bridge.builtin.impl.DateResolutionUtil; -import org.hibernate.search.bridge.builtin.nullencoding.impl.NumericLongNullCodec; import org.hibernate.search.bridge.spi.EncodingBridge; import org.hibernate.search.bridge.spi.IgnoreAnalyzerBridge; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.bridge.spi.NullMarker; +import org.hibernate.search.bridge.util.impl.ToStringNullMarker; import org.hibernate.search.metadata.NumericFieldSettingsDescriptor.NumericEncodingType; import org.hibernate.search.util.logging.impl.Log; import org.hibernate.search.util.logging.impl.LoggerFactory; @@ -109,9 +109,9 @@ public NumericEncodingType getEncodingType() { } @Override - public NullMarkerCodec createNullMarkerCodec(String indexNullAs) throws IllegalArgumentException { + public NullMarker createNullMarker(String indexNullAs) throws IllegalArgumentException { try { - return new NumericLongNullCodec( Long.parseLong( indexNullAs ) ); + return new ToStringNullMarker( Long.parseLong( indexNullAs ) ); } catch (NumberFormatException e) { throw LOG.invalidNullMarkerForLong( e ); diff --git a/engine/src/main/java/org/hibernate/search/bridge/builtin/NumericFieldBridge.java b/engine/src/main/java/org/hibernate/search/bridge/builtin/NumericFieldBridge.java index 79c5598e3b7..c48975bbbc1 100644 --- a/engine/src/main/java/org/hibernate/search/bridge/builtin/NumericFieldBridge.java +++ b/engine/src/main/java/org/hibernate/search/bridge/builtin/NumericFieldBridge.java @@ -11,13 +11,10 @@ import org.hibernate.search.bridge.FieldBridge; import org.hibernate.search.bridge.LuceneOptions; import org.hibernate.search.bridge.TwoWayFieldBridge; -import org.hibernate.search.bridge.builtin.nullencoding.impl.NumericDoubleNullCodec; -import org.hibernate.search.bridge.builtin.nullencoding.impl.NumericFloatNullCodec; -import org.hibernate.search.bridge.builtin.nullencoding.impl.NumericIntegerNullCodec; -import org.hibernate.search.bridge.builtin.nullencoding.impl.NumericLongNullCodec; import org.hibernate.search.bridge.spi.EncodingBridge; import org.hibernate.search.bridge.spi.IgnoreAnalyzerBridge; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.bridge.spi.NullMarker; +import org.hibernate.search.bridge.util.impl.ToStringNullMarker; import org.hibernate.search.metadata.NumericFieldSettingsDescriptor.NumericEncodingType; import org.hibernate.search.util.logging.impl.Log; import org.hibernate.search.util.logging.impl.LoggerFactory; @@ -51,8 +48,8 @@ public NumericEncodingType getEncodingType() { } @Override - public NullMarkerCodec createNullMarkerCodec(String indexNullAs) throws IllegalArgumentException { - return INT_FIELD_BRIDGE.createNullMarkerCodec( indexNullAs ); + public NullMarker createNullMarker(String indexNullAs) throws IllegalArgumentException { + return INT_FIELD_BRIDGE.createNullMarker( indexNullAs ); } }, /** @@ -76,8 +73,8 @@ public NumericEncodingType getEncodingType() { } @Override - public NullMarkerCodec createNullMarkerCodec(String indexNullAs) throws IllegalArgumentException { - return INT_FIELD_BRIDGE.createNullMarkerCodec( indexNullAs ); + public NullMarker createNullMarker(String indexNullAs) throws IllegalArgumentException { + return INT_FIELD_BRIDGE.createNullMarker( indexNullAs ); } }, /** @@ -90,9 +87,9 @@ public NumericEncodingType getEncodingType() { } @Override - public NullMarkerCodec createNullMarkerCodec(String indexNullAs) throws IllegalArgumentException { + public NullMarker createNullMarker(String indexNullAs) throws IllegalArgumentException { try { - return new NumericIntegerNullCodec( Integer.parseInt( indexNullAs ) ); + return new ToStringNullMarker( Integer.parseInt( indexNullAs ) ); } catch (NumberFormatException e) { throw LOG.invalidNullMarkerForInteger( e ); @@ -109,9 +106,9 @@ public NumericEncodingType getEncodingType() { } @Override - public NullMarkerCodec createNullMarkerCodec(String indexNullAs) throws IllegalArgumentException { + public NullMarker createNullMarker(String indexNullAs) throws IllegalArgumentException { try { - return new NumericFloatNullCodec( Float.parseFloat( indexNullAs ) ); + return new ToStringNullMarker( Float.parseFloat( indexNullAs ) ); } catch (NumberFormatException e) { throw LOG.invalidNullMarkerForFloat( e ); @@ -128,9 +125,9 @@ public NumericEncodingType getEncodingType() { } @Override - public NullMarkerCodec createNullMarkerCodec(String indexNullAs) throws IllegalArgumentException { + public NullMarker createNullMarker(String indexNullAs) throws IllegalArgumentException { try { - return new NumericDoubleNullCodec( Double.parseDouble( indexNullAs ) ); + return new ToStringNullMarker( Double.parseDouble( indexNullAs ) ); } catch (NumberFormatException e) { throw LOG.invalidNullMarkerForDouble( e ); @@ -147,9 +144,9 @@ public NumericEncodingType getEncodingType() { } @Override - public NullMarkerCodec createNullMarkerCodec(String indexNullAs) throws IllegalArgumentException { + public NullMarker createNullMarker(String indexNullAs) throws IllegalArgumentException { try { - return new NumericLongNullCodec( Long.parseLong( indexNullAs ) ); + return new ToStringNullMarker( Long.parseLong( indexNullAs ) ); } catch (NumberFormatException e) { throw LOG.invalidNullMarkerForLong( e ); diff --git a/engine/src/main/java/org/hibernate/search/bridge/builtin/impl/NullEncodingTwoWayFieldBridge.java b/engine/src/main/java/org/hibernate/search/bridge/builtin/impl/NullEncodingTwoWayFieldBridge.java index cc0dddbdba3..4481807b9c3 100644 --- a/engine/src/main/java/org/hibernate/search/bridge/builtin/impl/NullEncodingTwoWayFieldBridge.java +++ b/engine/src/main/java/org/hibernate/search/bridge/builtin/impl/NullEncodingTwoWayFieldBridge.java @@ -13,9 +13,10 @@ import org.apache.lucene.search.Query; import org.hibernate.search.bridge.LuceneOptions; import org.hibernate.search.bridge.TwoWayFieldBridge; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.bridge.spi.NullMarker; import org.hibernate.search.bridge.util.impl.BridgeAdaptor; import org.hibernate.search.bridge.util.impl.BridgeAdaptorUtils; +import org.hibernate.search.engine.nulls.codec.impl.NullMarkerCodec; import org.hibernate.search.util.logging.impl.Log; import org.hibernate.search.util.logging.impl.LoggerFactory; @@ -53,7 +54,8 @@ public Object get(String name, Document document) { @Override public String objectToString(Object object) { if ( object == null ) { - return nullTokenCodec.nullRepresentedAsString(); + NullMarker marker = nullTokenCodec.getNullMarker(); + return marker == null ? null : marker.nullRepresentedAsString(); } else { return fieldBridge.objectToString( object ); diff --git a/engine/src/main/java/org/hibernate/search/bridge/builtin/time/impl/DurationBridge.java b/engine/src/main/java/org/hibernate/search/bridge/builtin/time/impl/DurationBridge.java index 20adbe427ce..3444f428d4d 100644 --- a/engine/src/main/java/org/hibernate/search/bridge/builtin/time/impl/DurationBridge.java +++ b/engine/src/main/java/org/hibernate/search/bridge/builtin/time/impl/DurationBridge.java @@ -11,10 +11,10 @@ import org.apache.lucene.document.Document; import org.hibernate.search.bridge.LuceneOptions; import org.hibernate.search.bridge.TwoWayFieldBridge; -import org.hibernate.search.bridge.builtin.nullencoding.impl.NumericLongNullCodec; import org.hibernate.search.bridge.spi.EncodingBridge; import org.hibernate.search.bridge.spi.IgnoreAnalyzerBridge; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.bridge.spi.NullMarker; +import org.hibernate.search.bridge.util.impl.ToStringNullMarker; import org.hibernate.search.metadata.NumericFieldSettingsDescriptor.NumericEncodingType; import org.hibernate.search.util.logging.impl.Log; import org.hibernate.search.util.logging.impl.LoggerFactory; @@ -75,9 +75,9 @@ public NumericEncodingType getEncodingType() { } @Override - public NullMarkerCodec createNullMarkerCodec(String indexNullAs) throws IllegalArgumentException { + public NullMarker createNullMarker(String indexNullAs) throws IllegalArgumentException { try { - return new NumericLongNullCodec( Long.parseLong( indexNullAs ) ); + return new ToStringNullMarker( Long.parseLong( indexNullAs ) ); } catch (NumberFormatException e) { throw log.invalidNullMarkerForLong( e ); diff --git a/engine/src/main/java/org/hibernate/search/bridge/builtin/time/impl/InstantBridge.java b/engine/src/main/java/org/hibernate/search/bridge/builtin/time/impl/InstantBridge.java index 5ec38f127a0..1fd065e1c00 100644 --- a/engine/src/main/java/org/hibernate/search/bridge/builtin/time/impl/InstantBridge.java +++ b/engine/src/main/java/org/hibernate/search/bridge/builtin/time/impl/InstantBridge.java @@ -11,10 +11,10 @@ import org.apache.lucene.document.Document; import org.hibernate.search.bridge.LuceneOptions; import org.hibernate.search.bridge.TwoWayFieldBridge; -import org.hibernate.search.bridge.builtin.nullencoding.impl.NumericLongNullCodec; import org.hibernate.search.bridge.spi.EncodingBridge; import org.hibernate.search.bridge.spi.IgnoreAnalyzerBridge; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.bridge.spi.NullMarker; +import org.hibernate.search.bridge.util.impl.ToStringNullMarker; import org.hibernate.search.metadata.NumericFieldSettingsDescriptor.NumericEncodingType; import org.hibernate.search.util.logging.impl.Log; import org.hibernate.search.util.logging.impl.LoggerFactory; @@ -40,9 +40,9 @@ public NumericEncodingType getEncodingType() { } @Override - public NullMarkerCodec createNullMarkerCodec(String indexNullAs) throws IllegalArgumentException { + public NullMarker createNullMarker(String indexNullAs) throws IllegalArgumentException { try { - return new NumericLongNullCodec( Long.parseLong( indexNullAs ) ); + return new ToStringNullMarker( Long.parseLong( indexNullAs ) ); } catch (NumberFormatException e) { throw log.invalidNullMarkerForLong( e ); diff --git a/engine/src/main/java/org/hibernate/search/bridge/builtin/time/impl/YearBridge.java b/engine/src/main/java/org/hibernate/search/bridge/builtin/time/impl/YearBridge.java index a75e872b83c..f126a365bb6 100644 --- a/engine/src/main/java/org/hibernate/search/bridge/builtin/time/impl/YearBridge.java +++ b/engine/src/main/java/org/hibernate/search/bridge/builtin/time/impl/YearBridge.java @@ -11,10 +11,10 @@ import org.apache.lucene.document.Document; import org.hibernate.search.bridge.LuceneOptions; import org.hibernate.search.bridge.TwoWayFieldBridge; -import org.hibernate.search.bridge.builtin.nullencoding.impl.NumericIntegerNullCodec; import org.hibernate.search.bridge.spi.EncodingBridge; import org.hibernate.search.bridge.spi.IgnoreAnalyzerBridge; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.bridge.spi.NullMarker; +import org.hibernate.search.bridge.util.impl.ToStringNullMarker; import org.hibernate.search.metadata.NumericFieldSettingsDescriptor.NumericEncodingType; import org.hibernate.search.util.logging.impl.Log; import org.hibernate.search.util.logging.impl.LoggerFactory; @@ -58,9 +58,9 @@ public NumericEncodingType getEncodingType() { } @Override - public NullMarkerCodec createNullMarkerCodec(String indexNullAs) throws NumberFormatException { + public NullMarker createNullMarker(String indexNullAs) throws NumberFormatException { try { - return new NumericIntegerNullCodec( Integer.parseInt( indexNullAs ) ); + return new ToStringNullMarker( Integer.parseInt( indexNullAs ) ); } catch (NumberFormatException e) { throw LOG.invalidNullMarkerForInteger( e ); diff --git a/engine/src/main/java/org/hibernate/search/bridge/spi/EncodingBridge.java b/engine/src/main/java/org/hibernate/search/bridge/spi/EncodingBridge.java index 0e13040864d..dc9ed33a3cf 100644 --- a/engine/src/main/java/org/hibernate/search/bridge/spi/EncodingBridge.java +++ b/engine/src/main/java/org/hibernate/search/bridge/spi/EncodingBridge.java @@ -7,7 +7,6 @@ package org.hibernate.search.bridge.spi; import org.hibernate.search.annotations.Field; -import org.hibernate.search.bridge.builtin.nullencoding.impl.KeywordBasedNullCodec; import org.hibernate.search.metadata.NumericFieldSettingsDescriptor.NumericEncodingType; /** @@ -16,11 +15,12 @@ *

Those bridges should: *

* *

Bridges that do not implement this interface will be attributed a {@code null} - * numeric encoding and fields with those bridges will use a {@link KeywordBasedNullCodec}. + * numeric encoding and fields with those bridges will use a simple null marker holding + * a string. * * @author Yoann Rodiere * @hsearch.experimental This contract is currently under active development and may be altered in future releases, @@ -38,9 +38,9 @@ public interface EncodingBridge { /** * @param indexNullAs The value of {@link Field#indexNullAs()}. - * @return The codec to use. + * @return The marker to use when a value is missing. * @throws IllegalArgumentException If {@code indexNullAs} cannot be encoded in the required format. */ - NullMarkerCodec createNullMarkerCodec(String indexNullAs) throws IllegalArgumentException; + NullMarker createNullMarker(String indexNullAs) throws IllegalArgumentException; } diff --git a/engine/src/main/java/org/hibernate/search/bridge/spi/NullMarker.java b/engine/src/main/java/org/hibernate/search/bridge/spi/NullMarker.java new file mode 100644 index 00000000000..5f0ec6a838a --- /dev/null +++ b/engine/src/main/java/org/hibernate/search/bridge/spi/NullMarker.java @@ -0,0 +1,42 @@ +/* + * Hibernate Search, full-text search for your domain model + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.search.bridge.spi; + +/** + * Representation of a missing value. + * + *

When encoding a marker the index field type shall match the other fields', + * as field types often need to be consistent across the whole index. + * + * @author Sanne Grinovero + * @hsearch.experimental This contract is currently under active development and may be altered in future releases, + * breaking existing users. + */ +public interface NullMarker { + + /** + * This is mostly a requirement for integration with other old-style + * contracts which expect a strongly String based strategy. + * + * @return a string representation of the null-marker, or null if no marker is used. + */ + String nullRepresentedAsString(); + + /** + * This returns the actual value to be indexed in place of null. + * + *

The returned value may be: + *

+ * + * @return a representation of the null-marker ready to be indexed. + */ + Object nullEncoded(); + +} diff --git a/engine/src/main/java/org/hibernate/search/bridge/util/impl/EncodingStringBridge.java b/engine/src/main/java/org/hibernate/search/bridge/util/impl/EncodingStringBridge.java index c4b02450f65..d598449340c 100644 --- a/engine/src/main/java/org/hibernate/search/bridge/util/impl/EncodingStringBridge.java +++ b/engine/src/main/java/org/hibernate/search/bridge/util/impl/EncodingStringBridge.java @@ -7,9 +7,8 @@ package org.hibernate.search.bridge.util.impl; import org.hibernate.search.bridge.StringBridge; -import org.hibernate.search.bridge.builtin.nullencoding.impl.KeywordBasedNullCodec; import org.hibernate.search.bridge.spi.EncodingBridge; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.bridge.spi.NullMarker; import org.hibernate.search.metadata.NumericFieldSettingsDescriptor.NumericEncodingType; @@ -34,12 +33,12 @@ public NumericEncodingType getEncodingType() { protected abstract T parseIndexNullAs(String indexNullAs) throws IllegalArgumentException; @Override - public NullMarkerCodec createNullMarkerCodec(String indexNullAs) throws IllegalArgumentException { + public NullMarker createNullMarker(String indexNullAs) throws IllegalArgumentException { // Parse the given "indexNullAs" string T typedValue = parseIndexNullAs( indexNullAs ); // Convert the resulting date to the format used when indexing. String encodedValue = objectToString( typedValue ); - return new KeywordBasedNullCodec( encodedValue ); + return new ToStringNullMarker( encodedValue ); } } diff --git a/engine/src/main/java/org/hibernate/search/bridge/util/impl/ToStringNullMarker.java b/engine/src/main/java/org/hibernate/search/bridge/util/impl/ToStringNullMarker.java new file mode 100644 index 00000000000..f9de9a02b70 --- /dev/null +++ b/engine/src/main/java/org/hibernate/search/bridge/util/impl/ToStringNullMarker.java @@ -0,0 +1,43 @@ +/* + * Hibernate Search, full-text search for your domain model + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.search.bridge.util.impl; + +import org.hibernate.search.bridge.spi.NullMarker; + +/** + * A null marker that uses toString() to retrieve the null string representation. + * + * @author Yoann Rodiere + */ +public class ToStringNullMarker implements NullMarker { + + private final Object indexNullAs; + + public ToStringNullMarker(final Object indexNullAs) { + if ( indexNullAs == null ) { + throw new NullPointerException( "The constructor parameter is mandatory" ); + } + this.indexNullAs = indexNullAs; + } + + @Override + public String nullRepresentedAsString() { + return indexNullAs.toString(); + } + + @Override + public Object nullEncoded() { + return indexNullAs; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + indexNullAs + "]"; + } + + +} diff --git a/engine/src/main/java/org/hibernate/search/engine/impl/LuceneOptionsImpl.java b/engine/src/main/java/org/hibernate/search/engine/impl/LuceneOptionsImpl.java index 16d7973485b..312b8e0458a 100644 --- a/engine/src/main/java/org/hibernate/search/engine/impl/LuceneOptionsImpl.java +++ b/engine/src/main/java/org/hibernate/search/engine/impl/LuceneOptionsImpl.java @@ -19,6 +19,7 @@ import org.apache.lucene.index.IndexOptions; import org.hibernate.search.annotations.Store; import org.hibernate.search.bridge.LuceneOptions; +import org.hibernate.search.bridge.spi.NullMarker; import org.hibernate.search.engine.metadata.impl.DocumentFieldMetadata; import org.hibernate.search.exception.SearchException; import org.hibernate.search.util.StringHelper; @@ -65,7 +66,8 @@ public LuceneOptionsImpl(DocumentFieldMetadata fieldMetadata, float fieldLevelBo this.storeType = fieldMetadata.getStore(); this.storeCompressed = this.storeType.equals( Store.COMPRESS ); this.storeUncompressed = this.storeType.equals( Store.YES ); - this.indexNullAs = fieldMetadata.getNullMarkerCodec().nullRepresentedAsString(); + NullMarker nullMarker = fieldMetadata.getNullMarkerCodec().getNullMarker(); + this.indexNullAs = nullMarker == null ? null : nullMarker.nullRepresentedAsString(); } public LuceneOptionsImpl(Index indexMode, TermVector termVector, Store store, String indexNullAs, float fieldLevelBoost, float inheritedBoost) { diff --git a/engine/src/main/java/org/hibernate/search/engine/metadata/impl/AnnotationMetadataProvider.java b/engine/src/main/java/org/hibernate/search/engine/metadata/impl/AnnotationMetadataProvider.java index b20f697e481..219917d605c 100644 --- a/engine/src/main/java/org/hibernate/search/engine/metadata/impl/AnnotationMetadataProvider.java +++ b/engine/src/main/java/org/hibernate/search/engine/metadata/impl/AnnotationMetadataProvider.java @@ -68,18 +68,20 @@ import org.hibernate.search.bridge.builtin.DefaultStringBridge; import org.hibernate.search.bridge.builtin.impl.NullEncodingFieldBridge; import org.hibernate.search.bridge.builtin.impl.NullEncodingTwoWayFieldBridge; -import org.hibernate.search.bridge.builtin.nullencoding.impl.KeywordBasedNullCodec; -import org.hibernate.search.bridge.builtin.nullencoding.impl.NotEncodingCodec; import org.hibernate.search.bridge.impl.BridgeFactory; import org.hibernate.search.bridge.spi.EncodingBridge; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.bridge.spi.NullMarker; import org.hibernate.search.bridge.util.impl.BridgeAdaptorUtils; import org.hibernate.search.bridge.util.impl.NumericFieldUtils; +import org.hibernate.search.bridge.util.impl.ToStringNullMarker; import org.hibernate.search.bridge.util.impl.TwoWayString2FieldBridgeAdaptor; import org.hibernate.search.engine.BoostStrategy; import org.hibernate.search.engine.impl.AnnotationProcessingHelper; import org.hibernate.search.engine.impl.ConfigContext; import org.hibernate.search.engine.impl.DefaultBoostStrategy; +import org.hibernate.search.engine.nulls.codec.impl.NotEncodingCodec; +import org.hibernate.search.engine.nulls.codec.impl.NullMarkerCodec; +import org.hibernate.search.engine.nulls.impl.MissingValueStrategy; import org.hibernate.search.exception.AssertionFailure; import org.hibernate.search.exception.SearchException; import org.hibernate.search.indexes.spi.IndexManagerType; @@ -1272,7 +1274,8 @@ private void bindFieldAnnotation( } final NumericEncodingType numericEncodingType = determineNumericFieldEncoding( fieldBridge ); - final NullMarkerCodec nullTokenCodec = determineNullMarkerCodec( fieldBridge, fieldAnnotation, configContext, fieldPath ); + final NullMarkerCodec nullTokenCodec = determineNullMarkerCodec( fieldBridge, fieldAnnotation, configContext, + parseContext, typeMetadataBuilder.getIndexedType(), fieldPath ); if ( nullTokenCodec != NotEncodingCodec.SINGLETON && fieldBridge instanceof TwoWayFieldBridge ) { fieldBridge = new NullEncodingTwoWayFieldBridge( (TwoWayFieldBridge) fieldBridge, nullTokenCodec ); } @@ -1410,7 +1413,11 @@ private NumericEncodingType determineNumericFieldEncoding(FieldBridge fieldBridg private NullMarkerCodec determineNullMarkerCodec(FieldBridge fieldBridge, org.hibernate.search.annotations.Field fieldAnnotation, ConfigContext context, - DocumentFieldPath fieldPath) { + ParseContext parseContext, Class indexedType, DocumentFieldPath fieldPath) { + if ( parseContext.skipNullMarkerCodec() ) { + return NotEncodingCodec.SINGLETON; + } + if ( fieldAnnotation == null ) { // The option of null-markers is not being used return NotEncodingCodec.SINGLETON; @@ -1421,30 +1428,38 @@ private NullMarkerCodec determineNullMarkerCodec(FieldBridge fieldBridge, // The option is explicitly disabled return NotEncodingCodec.SINGLETON; } - else if ( indexNullAs.equals( org.hibernate.search.annotations.Field.DEFAULT_NULL_TOKEN ) ) { - // Use the default null token - // This will require the global default to be an encodable value - return createNullMarkerCodec( fieldBridge, context.getDefaultNullToken(), fieldPath ); - } else { - // Use the default null token - // This will require 'indexNullAs' to be an encodable value - return createNullMarkerCodec( fieldBridge, indexNullAs, fieldPath ); + NullMarker nullMarker; + if ( indexNullAs.equals( org.hibernate.search.annotations.Field.DEFAULT_NULL_TOKEN ) ) { + // Use the default null token + // This will require the global default to be an encodable value + nullMarker = createNullMarker( fieldBridge, context.getDefaultNullToken(), fieldPath ); + } + else { + // Use the default null token + // This will require 'indexNullAs' to be an encodable value + nullMarker = createNullMarker( fieldBridge, indexNullAs, fieldPath ); + } + + IndexManagerType indexManagerType = parseContext.getIndexManagerType(); + MissingValueStrategy missingValueStrategy = indexManagerType.getMissingValueStrategy(); + + return missingValueStrategy.createNullMarkerCodec( indexedType, fieldPath, nullMarker ); } } - private NullMarkerCodec createNullMarkerCodec(FieldBridge fieldBridge, String marker, DocumentFieldPath path) { + private NullMarker createNullMarker(FieldBridge fieldBridge, String marker, DocumentFieldPath path) { EncodingBridge encodingBridge = BridgeAdaptorUtils.unwrapAdaptorOnly( fieldBridge, EncodingBridge.class ); if ( encodingBridge != null ) { try { - return encodingBridge.createNullMarkerCodec( marker ); + return encodingBridge.createNullMarker( marker ); } catch (IllegalArgumentException e) { throw log.nullMarkerInvalidFormat( marker, path.getAbsoluteName(), e.getLocalizedMessage(), e ); } } else { - return new KeywordBasedNullCodec( marker ); + return new ToStringNullMarker( marker ); } } diff --git a/engine/src/main/java/org/hibernate/search/engine/metadata/impl/DocumentFieldMetadata.java b/engine/src/main/java/org/hibernate/search/engine/metadata/impl/DocumentFieldMetadata.java index 1d99e90973e..a69b2a5a0da 100644 --- a/engine/src/main/java/org/hibernate/search/engine/metadata/impl/DocumentFieldMetadata.java +++ b/engine/src/main/java/org/hibernate/search/engine/metadata/impl/DocumentFieldMetadata.java @@ -17,8 +17,8 @@ import org.hibernate.search.annotations.NumericField; import org.hibernate.search.annotations.Store; import org.hibernate.search.bridge.FieldBridge; -import org.hibernate.search.bridge.builtin.nullencoding.impl.NotEncodingCodec; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.engine.nulls.codec.impl.NotEncodingCodec; +import org.hibernate.search.engine.nulls.codec.impl.NullMarkerCodec; import static org.hibernate.search.metadata.NumericFieldSettingsDescriptor.NumericEncodingType; @@ -187,7 +187,7 @@ public String toString() { ", analyzer=" + analyzerReference + ", isId=" + isId + ", isIdInEmbedded=" + isIdInEmbedded + - ", nullToken='" + nullMarkerCodec.nullRepresentedAsString() + '\'' + + ", nullMarkerCodec='" + nullMarkerCodec + '\'' + ", isNumeric=" + isNumeric + ", isSpatial=" + isSpatial + ", precisionStep=" + precisionStep + diff --git a/engine/src/main/java/org/hibernate/search/engine/metadata/impl/MetadataProvider.java b/engine/src/main/java/org/hibernate/search/engine/metadata/impl/MetadataProvider.java index 49254420004..9b58385e37d 100644 --- a/engine/src/main/java/org/hibernate/search/engine/metadata/impl/MetadataProvider.java +++ b/engine/src/main/java/org/hibernate/search/engine/metadata/impl/MetadataProvider.java @@ -26,11 +26,11 @@ public interface MetadataProvider { /** * Returns the {@code ContainedIn} related metadata for the specified type. * - * The metadata for {@code ContainedIn} are not comprehensive: they do not - * contain the information about the FieldBridges and the analyzers. - * We can't build these information because classes only marked with + *

The metadata for {@code ContainedIn} are not comprehensive: they do not + * contain the information about the FieldBridges, the analyzers or the NullMarkerCodecs. + *

We can't build these information because classes only marked with * {@code ContainedIn} are not tied to an {@code IndexManager}. - * It's of no use for {@code ContainedIn} resolution anyway. + *

It's of no use for {@code ContainedIn} resolution anyway. * * @param clazz The type of interest. * diff --git a/engine/src/main/java/org/hibernate/search/engine/metadata/impl/ParseContext.java b/engine/src/main/java/org/hibernate/search/engine/metadata/impl/ParseContext.java index 5358283f094..7a67e14c52a 100644 --- a/engine/src/main/java/org/hibernate/search/engine/metadata/impl/ParseContext.java +++ b/engine/src/main/java/org/hibernate/search/engine/metadata/impl/ParseContext.java @@ -50,6 +50,16 @@ boolean skipFieldBridges() { return indexManagerType == null; } + /** + * If the {@code IndexManager} type is not defined, we skip the {@code NullMarkerCodec} construction. + * + * Typically the {@code IndexManager} type is not defined when building the metadata for {@code ContainedIn} + * entities. See {@link MetadataProvider#getTypeMetadataForContainedIn(Class)} for more information. + */ + boolean skipNullMarkerCodec() { + return indexManagerType == null; + } + /** * If the {@code IndexManager} type is not defined, we skip the {@code Analyzer} construction. * diff --git a/engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/NumericFloatNullCodec.java b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneDoubleNullMarkerCodec.java similarity index 51% rename from engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/NumericFloatNullCodec.java rename to engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneDoubleNullMarkerCodec.java index 4f4a12835f5..f1154159f29 100644 --- a/engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/NumericFloatNullCodec.java +++ b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneDoubleNullMarkerCodec.java @@ -4,48 +4,39 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or . */ -package org.hibernate.search.bridge.builtin.nullencoding.impl; +package org.hibernate.search.engine.nulls.codec.impl; import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexableField; import org.apache.lucene.search.NumericRangeQuery; import org.apache.lucene.search.Query; import org.hibernate.search.bridge.LuceneOptions; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.bridge.spi.NullMarker; /** * @author Sanne Grinovero */ -public class NumericFloatNullCodec implements NullMarkerCodec { +public class LuceneDoubleNullMarkerCodec extends LuceneNullMarkerCodec { - private final Float indexNullAs; - - public NumericFloatNullCodec(final Float indexNullAs) throws NumberFormatException { - if ( indexNullAs == null ) { - throw new NullPointerException( "The constructor parameter is mandatory" ); - } - this.indexNullAs = indexNullAs; - } - - @Override - public String nullRepresentedAsString() { - return indexNullAs.toString(); + public LuceneDoubleNullMarkerCodec(NullMarker nullMarker) { + super( nullMarker ); } @Override public void encodeNullValue(String name, Document document, LuceneOptions luceneOptions) { - luceneOptions.addNumericFieldToDocument( name, indexNullAs, document ); + luceneOptions.addNumericFieldToDocument( name, nullMarker.nullEncoded(), document ); } @Override public Query createNullMatchingQuery(String fieldName) { - return NumericRangeQuery.newFloatRange( fieldName, indexNullAs, indexNullAs, true, true ); + Double nullEncoded = (Double) nullMarker.nullEncoded(); + return NumericRangeQuery.newDoubleRange( fieldName, nullEncoded, nullEncoded, true, true ); } @Override public boolean representsNullValue(IndexableField field) { Number numericValue = field.numericValue(); - return indexNullAs.equals( numericValue ); + return nullMarker.nullEncoded().equals( numericValue ); } } diff --git a/engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/NumericIntegerNullCodec.java b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneFloatNullMarkerCodec.java similarity index 51% rename from engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/NumericIntegerNullCodec.java rename to engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneFloatNullMarkerCodec.java index 01fc63c6680..2150954474a 100644 --- a/engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/NumericIntegerNullCodec.java +++ b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneFloatNullMarkerCodec.java @@ -4,48 +4,39 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or . */ -package org.hibernate.search.bridge.builtin.nullencoding.impl; +package org.hibernate.search.engine.nulls.codec.impl; import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexableField; import org.apache.lucene.search.NumericRangeQuery; import org.apache.lucene.search.Query; import org.hibernate.search.bridge.LuceneOptions; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.bridge.spi.NullMarker; /** * @author Sanne Grinovero */ -public class NumericIntegerNullCodec implements NullMarkerCodec { +public class LuceneFloatNullMarkerCodec extends LuceneNullMarkerCodec { - private final Integer indexNullAs; - - public NumericIntegerNullCodec(final Integer indexNullAs) throws NumberFormatException { - if ( indexNullAs == null ) { - throw new NullPointerException( "The constructor parameter is mandatory" ); - } - this.indexNullAs = indexNullAs; - } - - @Override - public String nullRepresentedAsString() { - return indexNullAs.toString(); + public LuceneFloatNullMarkerCodec(final NullMarker nullMarker) { + super( nullMarker ); } @Override public void encodeNullValue(String name, Document document, LuceneOptions luceneOptions) { - luceneOptions.addNumericFieldToDocument( name, indexNullAs, document ); + luceneOptions.addNumericFieldToDocument( name, nullMarker.nullEncoded(), document ); } @Override public Query createNullMatchingQuery(String fieldName) { - return NumericRangeQuery.newIntRange( fieldName, indexNullAs, indexNullAs, true, true ); + Float nullEncoded = (Float) nullMarker.nullEncoded(); + return NumericRangeQuery.newFloatRange( fieldName, nullEncoded, nullEncoded, true, true ); } @Override public boolean representsNullValue(IndexableField field) { Number numericValue = field.numericValue(); - return indexNullAs.equals( numericValue ); + return nullMarker.nullEncoded().equals( numericValue ); } } diff --git a/engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/NumericDoubleNullCodec.java b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneIntegerNullMarkerCodec.java similarity index 51% rename from engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/NumericDoubleNullCodec.java rename to engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneIntegerNullMarkerCodec.java index 52dec59efff..76bdeace8c7 100644 --- a/engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/NumericDoubleNullCodec.java +++ b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneIntegerNullMarkerCodec.java @@ -4,48 +4,39 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or . */ -package org.hibernate.search.bridge.builtin.nullencoding.impl; +package org.hibernate.search.engine.nulls.codec.impl; import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexableField; import org.apache.lucene.search.NumericRangeQuery; import org.apache.lucene.search.Query; import org.hibernate.search.bridge.LuceneOptions; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.bridge.spi.NullMarker; /** * @author Sanne Grinovero */ -public class NumericDoubleNullCodec implements NullMarkerCodec { +public class LuceneIntegerNullMarkerCodec extends LuceneNullMarkerCodec { - private final Double indexNullAs; - - public NumericDoubleNullCodec(final Double indexNullAs) throws NumberFormatException { - if ( indexNullAs == null ) { - throw new NullPointerException( "The constructor parameter is mandatory" ); - } - this.indexNullAs = indexNullAs; - } - - @Override - public String nullRepresentedAsString() { - return indexNullAs.toString(); + public LuceneIntegerNullMarkerCodec(final NullMarker nullMarker) { + super( nullMarker ); } @Override public void encodeNullValue(String name, Document document, LuceneOptions luceneOptions) { - luceneOptions.addNumericFieldToDocument( name, indexNullAs, document ); + luceneOptions.addNumericFieldToDocument( name, nullMarker.nullEncoded(), document ); } @Override public Query createNullMatchingQuery(String fieldName) { - return NumericRangeQuery.newDoubleRange( fieldName, indexNullAs, indexNullAs, true, true ); + Integer nullEncoded = (Integer) nullMarker.nullEncoded(); + return NumericRangeQuery.newIntRange( fieldName, nullEncoded, nullEncoded, true, true ); } @Override public boolean representsNullValue(IndexableField field) { Number numericValue = field.numericValue(); - return indexNullAs.equals( numericValue ); + return nullMarker.nullEncoded().equals( numericValue ); } } diff --git a/engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/NumericLongNullCodec.java b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneLongNullMarkerCodec.java similarity index 52% rename from engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/NumericLongNullCodec.java rename to engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneLongNullMarkerCodec.java index 5de05594e22..f5f2ba71e3e 100644 --- a/engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/NumericLongNullCodec.java +++ b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneLongNullMarkerCodec.java @@ -4,48 +4,39 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or . */ -package org.hibernate.search.bridge.builtin.nullencoding.impl; +package org.hibernate.search.engine.nulls.codec.impl; import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexableField; import org.apache.lucene.search.NumericRangeQuery; import org.apache.lucene.search.Query; import org.hibernate.search.bridge.LuceneOptions; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.bridge.spi.NullMarker; /** * @author Sanne Grinovero */ -public class NumericLongNullCodec implements NullMarkerCodec { +public class LuceneLongNullMarkerCodec extends LuceneNullMarkerCodec { - private final Long indexNullAs; - - public NumericLongNullCodec(final Long indexNullAs) throws NumberFormatException { - if ( indexNullAs == null ) { - throw new NullPointerException( "The constructor parameter is mandatory" ); - } - this.indexNullAs = indexNullAs; - } - - @Override - public String nullRepresentedAsString() { - return indexNullAs.toString(); + public LuceneLongNullMarkerCodec(final NullMarker nullMarker) { + super( nullMarker ); } @Override public void encodeNullValue(String name, Document document, LuceneOptions luceneOptions) { - luceneOptions.addNumericFieldToDocument( name, indexNullAs, document ); + luceneOptions.addNumericFieldToDocument( name, nullMarker.nullEncoded(), document ); } @Override public Query createNullMatchingQuery(String fieldName) { - return NumericRangeQuery.newLongRange( fieldName, indexNullAs, indexNullAs, true, true ); + Long nullEncoded = (Long) nullMarker.nullEncoded(); + return NumericRangeQuery.newLongRange( fieldName, nullEncoded, nullEncoded, true, true ); } @Override public boolean representsNullValue(IndexableField field) { Number numericValue = field.numericValue(); - return indexNullAs.equals( numericValue ); + return nullMarker.nullEncoded().equals( numericValue ); } } diff --git a/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneNullMarkerCodec.java b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneNullMarkerCodec.java new file mode 100644 index 00000000000..26e605e29f0 --- /dev/null +++ b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneNullMarkerCodec.java @@ -0,0 +1,34 @@ +/* + * Hibernate Search, full-text search for your domain model + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.search.engine.nulls.codec.impl; + +import org.hibernate.search.bridge.spi.NullMarker; + + +/** + * @author Yoann Rodiere + */ +abstract class LuceneNullMarkerCodec implements NullMarkerCodec { + + protected final NullMarker nullMarker; + + public LuceneNullMarkerCodec(NullMarker nullMarker) { + super(); + this.nullMarker = nullMarker; + } + + @Override + public NullMarker getNullMarker() { + return nullMarker; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + nullMarker + "]"; + } + +} diff --git a/engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/KeywordBasedNullCodec.java b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneStringNullMarkerCodec.java similarity index 59% rename from engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/KeywordBasedNullCodec.java rename to engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneStringNullMarkerCodec.java index 52806429d7c..e7585497fab 100644 --- a/engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/KeywordBasedNullCodec.java +++ b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/LuceneStringNullMarkerCodec.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or . */ -package org.hibernate.search.bridge.builtin.nullencoding.impl; +package org.hibernate.search.engine.nulls.codec.impl; import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexableField; @@ -13,32 +13,23 @@ import org.apache.lucene.search.TermQuery; import org.apache.lucene.util.BytesRef; import org.hibernate.search.bridge.LuceneOptions; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.bridge.spi.NullMarker; /** * @author Sanne Grinovero */ -public class KeywordBasedNullCodec implements NullMarkerCodec { +public class LuceneStringNullMarkerCodec extends LuceneNullMarkerCodec { - private final String indexNullAs; private final BytesRef encodedToken; - public KeywordBasedNullCodec(final String indexNullAs) { - if ( indexNullAs == null ) { - throw new NullPointerException( "The constructor parameter is mandatory" ); - } - this.indexNullAs = indexNullAs; - this.encodedToken = new BytesRef( indexNullAs ); - } - - @Override - public String nullRepresentedAsString() { - return indexNullAs; + public LuceneStringNullMarkerCodec(NullMarker nullMarker) { + super( nullMarker ); + this.encodedToken = new BytesRef( (String) nullMarker.nullEncoded() ); } @Override public void encodeNullValue(String name, Document document, LuceneOptions luceneOptions) { - luceneOptions.addFieldToDocument( name, indexNullAs, document ); + luceneOptions.addFieldToDocument( name, (String) nullMarker.nullEncoded(), document ); } @Override @@ -49,7 +40,7 @@ public Query createNullMatchingQuery(String fieldName) { @Override public boolean representsNullValue(IndexableField field) { String stringValue = field.stringValue(); - return indexNullAs.equals( stringValue ); + return nullMarker.nullEncoded().equals( stringValue ); } } diff --git a/engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/NotEncodingCodec.java b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/NotEncodingCodec.java similarity index 81% rename from engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/NotEncodingCodec.java rename to engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/NotEncodingCodec.java index 828356c89fe..50cf3c88040 100644 --- a/engine/src/main/java/org/hibernate/search/bridge/builtin/nullencoding/impl/NotEncodingCodec.java +++ b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/NotEncodingCodec.java @@ -4,13 +4,13 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or . */ -package org.hibernate.search.bridge.builtin.nullencoding.impl; +package org.hibernate.search.engine.nulls.codec.impl; import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexableField; import org.apache.lucene.search.Query; import org.hibernate.search.bridge.LuceneOptions; -import org.hibernate.search.bridge.spi.NullMarkerCodec; +import org.hibernate.search.bridge.spi.NullMarker; /** * Implementation of NullMarkerCodec which implements the no-op strategy @@ -20,14 +20,19 @@ */ public class NotEncodingCodec implements NullMarkerCodec { - public static final NotEncodingCodec SINGLETON = new NotEncodingCodec(); + public static final NotEncodingCodec SINGLETON = new NotEncodingCodec() { + @Override + public String toString() { + return getClass().getSimpleName() + ".SINGLETON"; + } + }; private NotEncodingCodec() { // do not instantiate, use the SINGLETON } @Override - public String nullRepresentedAsString() { + public NullMarker getNullMarker() { return null; } diff --git a/engine/src/main/java/org/hibernate/search/bridge/spi/NullMarkerCodec.java b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/NullMarkerCodec.java similarity index 79% rename from engine/src/main/java/org/hibernate/search/bridge/spi/NullMarkerCodec.java rename to engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/NullMarkerCodec.java index 70ad10f7adb..f56788e4826 100644 --- a/engine/src/main/java/org/hibernate/search/bridge/spi/NullMarkerCodec.java +++ b/engine/src/main/java/org/hibernate/search/engine/nulls/codec/impl/NullMarkerCodec.java @@ -4,12 +4,13 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or . */ -package org.hibernate.search.bridge.spi; +package org.hibernate.search.engine.nulls.codec.impl; import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexableField; import org.apache.lucene.search.Query; import org.hibernate.search.bridge.LuceneOptions; +import org.hibernate.search.bridge.spi.NullMarker; /** * Contract to manage coding and decoding (querying) of null values. @@ -20,18 +21,13 @@ * Also the strategy to find a match for such a null-encoded value might vary. * * @author Sanne Grinovero - * @hsearch.experimental This contract is currently under active development and may be altered in future releases, - * breaking existing users. */ public interface NullMarkerCodec { /** - * This is mostly a requirement for integration with other old-style - * contracts which expect a strongly String based strategy. - * - * @return a string representation of the null-marker, or null if no marker is used. + * @return The null marker used by this codec. */ - String nullRepresentedAsString(); + NullMarker getNullMarker(); /** * Store the null marker in the Document. @@ -43,14 +39,6 @@ public interface NullMarkerCodec { */ void encodeNullValue(String fieldName, Document document, LuceneOptions luceneOptions); - /** - * Create a Query to find all documents which have a 'null' value encoded in the specified field - * - * @param fieldName the field to target with the Query - * @return a new Lucene Query - */ - Query createNullMatchingQuery(String fieldName); - /** * Check if the field represents the encoding for a null element * @@ -59,4 +47,12 @@ public interface NullMarkerCodec { */ boolean representsNullValue(IndexableField field); + /** + * Create a Query to find all documents which have a 'null' value encoded in the specified field + * + * @param fieldName the field to target with the Query + * @return a new Lucene Query + */ + Query createNullMatchingQuery(String fieldName); + } diff --git a/engine/src/main/java/org/hibernate/search/engine/nulls/impl/LuceneMissingValueStrategy.java b/engine/src/main/java/org/hibernate/search/engine/nulls/impl/LuceneMissingValueStrategy.java new file mode 100644 index 00000000000..0333310673c --- /dev/null +++ b/engine/src/main/java/org/hibernate/search/engine/nulls/impl/LuceneMissingValueStrategy.java @@ -0,0 +1,56 @@ +/* + * Hibernate Search, full-text search for your domain model + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.search.engine.nulls.impl; + +import org.hibernate.search.bridge.spi.NullMarker; +import org.hibernate.search.engine.metadata.impl.DocumentFieldPath; +import org.hibernate.search.engine.nulls.codec.impl.LuceneDoubleNullMarkerCodec; +import org.hibernate.search.engine.nulls.codec.impl.LuceneFloatNullMarkerCodec; +import org.hibernate.search.engine.nulls.codec.impl.LuceneIntegerNullMarkerCodec; +import org.hibernate.search.engine.nulls.codec.impl.LuceneLongNullMarkerCodec; +import org.hibernate.search.engine.nulls.codec.impl.LuceneStringNullMarkerCodec; +import org.hibernate.search.engine.nulls.codec.impl.NullMarkerCodec; +import org.hibernate.search.util.logging.impl.Log; +import org.hibernate.search.util.logging.impl.LoggerFactory; + +/** + * @author Yoann Rodiere + */ +public class LuceneMissingValueStrategy implements MissingValueStrategy { + + private static final Log LOG = LoggerFactory.make( Log.class ); + + public static final LuceneMissingValueStrategy INSTANCE = new LuceneMissingValueStrategy(); + + private LuceneMissingValueStrategy() { + // use INSTANCE + } + + @Override + public NullMarkerCodec createNullMarkerCodec(Class entityType, DocumentFieldPath path, NullMarker nullMarker) { + Object nullEncoded = nullMarker.nullEncoded(); + if ( nullEncoded instanceof String ) { + return new LuceneStringNullMarkerCodec( nullMarker ); + } + else if ( nullEncoded instanceof Integer ) { + return new LuceneIntegerNullMarkerCodec( nullMarker ); + } + else if ( nullEncoded instanceof Long ) { + return new LuceneLongNullMarkerCodec( nullMarker ); + } + else if ( nullEncoded instanceof Float ) { + return new LuceneFloatNullMarkerCodec( nullMarker ); + } + else if ( nullEncoded instanceof Double ) { + return new LuceneDoubleNullMarkerCodec( nullMarker ); + } + else { + throw LOG.unsupportedNullTokenType( entityType, path.getAbsoluteName(), nullEncoded.getClass() ); + } + } + +} diff --git a/engine/src/main/java/org/hibernate/search/engine/nulls/impl/MissingValueStrategy.java b/engine/src/main/java/org/hibernate/search/engine/nulls/impl/MissingValueStrategy.java new file mode 100644 index 00000000000..00250c8c8fd --- /dev/null +++ b/engine/src/main/java/org/hibernate/search/engine/nulls/impl/MissingValueStrategy.java @@ -0,0 +1,31 @@ +/* + * Hibernate Search, full-text search for your domain model + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.search.engine.nulls.impl; + +import org.hibernate.search.bridge.spi.NullMarker; +import org.hibernate.search.engine.metadata.impl.DocumentFieldPath; +import org.hibernate.search.engine.nulls.codec.impl.NullMarkerCodec; + +/** + * Strategy for handling missing values. + * + *

Currently only serves as a factory for {@link NullMarkerCodec}s, but could be + * extended in the future to also handle exists/missing queries (see HSEARCH-2389). + * + * @author Yoann Rodiere + */ +public interface MissingValueStrategy { + + /** + * @param entityType The entity on which the null marker is being used. + * @param path The path of the field on which the null marker is being used. + * @param marker The null marker to use when indexing/querying null values. + * @return A codec that will index and query the given marker. + */ + NullMarkerCodec createNullMarkerCodec(Class entityType, DocumentFieldPath path, NullMarker marker); + +} diff --git a/engine/src/main/java/org/hibernate/search/engine/spi/DocumentBuilderIndexedEntity.java b/engine/src/main/java/org/hibernate/search/engine/spi/DocumentBuilderIndexedEntity.java index 949cf20bb3b..6f5469b2491 100644 --- a/engine/src/main/java/org/hibernate/search/engine/spi/DocumentBuilderIndexedEntity.java +++ b/engine/src/main/java/org/hibernate/search/engine/spi/DocumentBuilderIndexedEntity.java @@ -53,6 +53,7 @@ import org.hibernate.search.bridge.builtin.NumericFieldBridge; import org.hibernate.search.bridge.builtin.StringEncodingDateBridge; import org.hibernate.search.bridge.spi.ConversionContext; +import org.hibernate.search.bridge.spi.NullMarker; import org.hibernate.search.bridge.util.impl.TwoWayString2FieldBridgeAdaptor; import org.hibernate.search.engine.ProjectionConstants; import org.hibernate.search.engine.impl.ConfigContext; @@ -794,11 +795,12 @@ private void addSortFieldDocValues(Document document, PropertyMetadata propertyM if ( fieldMetaData.getIndex() == Index.NO && fieldMetaData.getStore() == Store.NO ) { FieldBridge fieldBridge = fieldMetaData.getFieldBridge(); + NullMarker nullMarker = fieldMetaData.getNullMarkerCodec().getNullMarker(); LuceneOptionsImpl luceneOptions = new LuceneOptionsImpl( Index.NOT_ANALYZED, fieldMetaData.getTermVector(), fieldMetaData.getStore(), - fieldMetaData.getNullMarkerCodec().nullRepresentedAsString(), + nullMarker == null ? null : nullMarker.nullRepresentedAsString(), fieldMetaData.getBoost() * propertyMetadata.getDynamicBoostStrategy().defineBoost( propertyValue ), documentBoost ); diff --git a/engine/src/main/java/org/hibernate/search/indexes/spi/IndexManagerType.java b/engine/src/main/java/org/hibernate/search/indexes/spi/IndexManagerType.java index 6540222a6e0..e4b4ea068df 100644 --- a/engine/src/main/java/org/hibernate/search/indexes/spi/IndexManagerType.java +++ b/engine/src/main/java/org/hibernate/search/indexes/spi/IndexManagerType.java @@ -6,6 +6,8 @@ */ package org.hibernate.search.indexes.spi; +import org.hibernate.search.engine.nulls.impl.MissingValueStrategy; + /** * Since Hibernate Search supports different types of indexing and query technologies, * such as embedding Apache Lucene or remote via Elasticsearch, the type of @@ -32,4 +34,9 @@ public interface IndexManagerType { * The strategy of analyzer execution employed by index managers of this family. */ AnalyzerExecutionStrategy getAnalyzerExecutionStrategy(); + + /** + * The strategy for missing values employed by index managers of this family. + */ + MissingValueStrategy getMissingValueStrategy(); } diff --git a/engine/src/main/java/org/hibernate/search/indexes/spi/LuceneEmbeddedIndexManagerType.java b/engine/src/main/java/org/hibernate/search/indexes/spi/LuceneEmbeddedIndexManagerType.java index f1f194d52a4..3708593c2cc 100644 --- a/engine/src/main/java/org/hibernate/search/indexes/spi/LuceneEmbeddedIndexManagerType.java +++ b/engine/src/main/java/org/hibernate/search/indexes/spi/LuceneEmbeddedIndexManagerType.java @@ -6,6 +6,9 @@ */ package org.hibernate.search.indexes.spi; +import org.hibernate.search.engine.nulls.impl.LuceneMissingValueStrategy; +import org.hibernate.search.engine.nulls.impl.MissingValueStrategy; + public final class LuceneEmbeddedIndexManagerType implements IndexManagerType { public static final LuceneEmbeddedIndexManagerType INSTANCE = new LuceneEmbeddedIndexManagerType(); @@ -14,6 +17,11 @@ private LuceneEmbeddedIndexManagerType() { //use the INSTANCE singleton } + @Override + public MissingValueStrategy getMissingValueStrategy() { + return LuceneMissingValueStrategy.INSTANCE; + } + @Override public AnalyzerExecutionStrategy getAnalyzerExecutionStrategy() { return AnalyzerExecutionStrategy.EMBEDDED; diff --git a/engine/src/main/java/org/hibernate/search/metadata/impl/FieldDescriptorImpl.java b/engine/src/main/java/org/hibernate/search/metadata/impl/FieldDescriptorImpl.java index e8ee3bef0fa..08e5a26bf82 100644 --- a/engine/src/main/java/org/hibernate/search/metadata/impl/FieldDescriptorImpl.java +++ b/engine/src/main/java/org/hibernate/search/metadata/impl/FieldDescriptorImpl.java @@ -15,6 +15,7 @@ import org.hibernate.search.annotations.Store; import org.hibernate.search.annotations.TermVector; import org.hibernate.search.bridge.FieldBridge; +import org.hibernate.search.bridge.spi.NullMarker; import org.hibernate.search.engine.metadata.impl.DocumentFieldMetadata; import org.hibernate.search.exception.SearchException; import org.hibernate.search.metadata.FieldDescriptor; @@ -49,7 +50,8 @@ public FieldDescriptorImpl(DocumentFieldMetadata documentFieldMetadata) { this.termVector = determineTermVectorType( documentFieldMetadata.getTermVector() ); this.norms = determineNormsType( documentFieldMetadata.getIndex() ); this.boost = documentFieldMetadata.getBoost(); - this.indexNullAs = documentFieldMetadata.getNullMarkerCodec().nullRepresentedAsString(); + NullMarker nullMarker = documentFieldMetadata.getNullMarkerCodec().getNullMarker(); + this.indexNullAs = nullMarker == null ? null : nullMarker.nullRepresentedAsString(); this.analyzer = initAnalyzer( documentFieldMetadata ); this.fieldBridge = documentFieldMetadata.getFieldBridge(); this.fieldType = determineFieldType( documentFieldMetadata ); diff --git a/engine/src/main/java/org/hibernate/search/util/logging/impl/Log.java b/engine/src/main/java/org/hibernate/search/util/logging/impl/Log.java index b38616d1a0a..ec45e87b6a5 100644 --- a/engine/src/main/java/org/hibernate/search/util/logging/impl/Log.java +++ b/engine/src/main/java/org/hibernate/search/util/logging/impl/Log.java @@ -1003,4 +1003,7 @@ public interface Log extends BasicLogger { @Message(id = 326, value = "Index names collision detected. Different index managers will point to the same actual index: %1$s") SearchException indexNamesCollisionDetected(String string); + @Message(id = 327, value = "Unsupported indexNullAs token type '%3$s' on field '%2$s' of entity '%1$s'." ) + SearchException unsupportedNullTokenType(Class entityType, String fieldName, Class tokenType); + }