Fixed
Details
Assignee
Davide D'AltoDavide D'AltoReporter
Yoann RodièreYoann RodièreSprint
NoneFix versions
Priority
Major
Details
Details
Assignee
Davide D'Alto
Davide D'AltoReporter
Yoann Rodière
Yoann RodièreSprint
None
Fix versions
Priority
Created November 16, 2018 at 9:38 AM
Updated January 31, 2019 at 3:08 PM
Resolved December 13, 2018 at 9:02 AM
Follow-up on HSEARCH-3088, where we added support for ID predicates, but only using the String type used in the backend, without taking into account the fact that users usually manipulate a different type for their ID (Long, Integer, ...) that is transparently converted by the mapper to the String required by the backend.
We already tackled a similar problem for the "match" predicate: the
org.hibernate.search.engine.search.predicate.spi.MatchPredicateBuilder#value
accepts arguments of the type used in the mapped entity (say,MyEnum
) while the index manager may internally manipulate another type (for enums, that internal type would beString
).This was solved in https://hibernate.atlassian.net/browse/HSEARCH-3221#icft=HSEARCH-3221 by requiring the mapper to pass a "converter" to the index manager at bootstrap, so that the index manager can later use that converter in its implementation of the predicate DSL, whenever the user passes a value.
So, we only have to do the same for IDs.
First step: alter the backends so that they accept such a converter from the mapper:
Introduce a
ToIndexIdValueConverter
interface, similar toToIndexFieldValueConverter
, with its ownToIndexIdValueConvertContext
. Note that there will only be one generic parameter inToIndexIdValueConverter
, since the ID is always a String in the backend.Introduce a way for the mapper to pass the converter when building the index manager. I think the best place to do this would be a method in
org.hibernate.search.engine.mapper.mapping.building.spi.IndexModelBindingContext
, next toexplicitRouting()
. Say,void idConverter(ToIndexIdValueConverter)
. As to implementations:In
NonRootIndexModelBindingContext
, as withexplicitRouting()
, you can throw an assertion failure, since it should never be called.In
RootIndexModelBindingContext
, you should delegate to theIndexSchemaRootNodeBuilder
, via a newly introduce method in that inteface (sayvoid idConverter(ToIndexIdValueConverter)
too). For now keep the implementation of that method inElasticsearchIndexSchemaRootNodeBuilder
,LuceneIndexSchemaRootNodeBuilder
andStubIndexSchemaRootNodeBuilder
very simple: just store the converter in a field, even if it's not used.Make sure the converter is stored in the data that will be used at runtime. It's a bit complex, but I think your best bet will be to:
Rename the interface
ElasticsearchRootIndexSchemaContributor
toElasticsearchIndexModelBuilder
(and similarly in other backends)Change the method
RootTypeMapping contribute(ElasticsearchIndexSchemaNodeCollector collector);
and replace it with a methodElasticsearchIndexModel build(String hibernateSearchIndexName, URLEncodedString elasticsearchIndexName, ElasticsearchIndexSettingsBuilder settingsBuilder)
(and similarly in other backends); the implementation will take care of callingnew ElasticsearchIndexModel
with all the appropriate arguments (including the map of object nodes and field nodes) and will return the model.Add a parameter to the ElasticsearchIndexModel constructor: the ID converter. Pass it as necessary where the constructor is called. Add a "idConverter" attribute in ElasticsearchIndexModel as well as a getter.
Actually use the converter at runtime
Add a getIdConverter() method to ElasticsearchSearchTargetModel. The implementation will be similar to
getSchemaNodeComponent
, but simpler: essentially you need to get the idConverter of the first index model, check that it's compatible with the converter of all other index models, and if so return it.Use that
getIdConverter
method inorg.hibernate.search.backend.elasticsearch.search.predicate.impl.SearchPredicateBuilderFactoryImpl#id
and pass it to theMatchIdPredicateBuilderImpl
constructor.That should be it. Now you can add a test next to the existing
MatchIdPredicateIT
, except in that case you will callctx.idConverter(someConverter)
in thectx -> ...
lambda in thesetup
method of your test. You can useorg.hibernate.search.integrationtest.backend.tck.util.ValueWrapper<String>
to easily simulate a type to be converted to/from String.Second step: actually implement that in the POJO mapper:
Create an implementation of
ToIndexIdValueConverter
that delegates to aIdentifierBridge
. Seeorg.hibernate.search.mapper.pojo.mapping.building.impl.ValueBridgeToIndexFieldValueConverter
, it should be fairly similar, except that instead of delegating to aValueBridge
, it will delegate to aIdentifierBridge
. You will probably need to add aisCompatibleWith(IdentifierBridge)
method toIdentifierBridge
: again, you can take inspiration fromorg.hibernate.search.mapper.pojo.bridge.ValueBridge#isCompatibleWith
Change
org.hibernate.search.mapper.pojo.mapping.building.impl.PojoIndexModelBinderImpl#addIdentifierBridge
to:Pass a
IndexModelBindingContext bindingContext
parameter, much like we do inorg.hibernate.search.mapper.pojo.mapping.building.impl.PojoIndexModelBinderImpl#addRoutingKeyBridge
Call
bindingContext.idConverter( new IdenfitierBridgeToIndexIdValueConverter( bridge ) )
, just before returning from the method.