According to Hibernate Search 6.1.7.Final: Reference Documentation, my binder should be easy.
But it won’t compile - I get an error on context.bridge():
The method bridge(Class<V2>, ValueBridge<V2,F>) in the type ValueBindingContext<capture#4-of ?> is not applicable for the arguments (Class<Collection>, BarcodesBinder.BarcodesBridge)
So far that I understand the manual, V2 should be Collection, right? Tried also with Set (more specific interface) and HashSet (just to be sure it’s not the interface causing problem).
P.S. I guess @Converter on entity property is not the problem, at least not to cause compile error because my bridge does not know about it at design time.
@horvoje Reminder that ValueBinder is for immutable values only, so it would not work for collections. See this section of the documentation , in the table, “Supports mutable types => NO”.
The way to go is indeed to bind the type of items of the collection, and Hibernate Search will apply your bridge to each item. See that same section, in the table, “Supports container extractors” => Yes.
By the way @horvoje , @gsmet pointed out to me that the only purpose of your bridge was to index multiple String values and remove spaces within those values.
With Hibernate Search 6, you can easily do that with one annotation + a normalizer:
The solution with normalizer - I guess it would take a year for me to come to this solution without your help. I’ll use this solution because it fits my needs.
But if I wanted to do more with each item of Collection, for example call my custom util method on them, then I should use ValueBridge<String, String> which would be applied to each collection element, right? I mean, I’m pretty sure it’s the case, but just to be 100% sure.
Also I guess this will be good example for others not knowing how to handle Collection so we can assume our time is not wasted on this.
Though keep in mind that there are lots of different token filters, so one of them might be able to do other things that you currently do in your utils, too.
Hi there, it’s me again
Now I have almost the same situation but…
The idea is to have URIs indexed as Collection which should give me option to search using .match() on field uris without any Lucene analyzers. For each Locale there is one URI in collection.
Is it possible to map and object that has a List inside to Set of keywords?
class Provider {
@KeywordField(
projectable = Projectable.NO,
searchable = Searchable.YES, sortable = Sortable.NO,
valueBridge = @ValueBridgeRef(type = UrisBridge.class)
)
private Uris uris;
}
// not working because of Set
class TagsBridge implements ValueBridge<Uris, Set<String>> {
@Override
public Set<String> toIndexedValue(
Uris value, ValueBridgeToIndexedValueContext context
) {
if (value == null) {
return new HashSet<>();
}
return IndexingUtil.Uris.toSet(value);
}
}
Tried also to create ValueBinder, but no success:
// not working
public class UrisValueBinder implements ValueBinder {
private final Locale language;
public UrisValueBinder( Locale language ) {
this.language = language;
}
@Override
public void bind( ValueBindingContext<?> context ) {
context.bridge( Uris.class, new Bridge( this.language ) );
}
public static class Bridge implements ValueBridge<Uris, String> {
private final Locale language;
private Bridge( Locale language ) {
this.language = language;
}
@Override
public String toIndexedValue(
Uris value,
ValueBridgeToIndexedValueContext context
) {
if (value == null) {
return null;
}
return UrisSupport.normalizeForLucene(value, this.language);
}
}
}
Tried also to create an annotation, but no success.
Thank you very much for your reply!
To be honest, here is a simplified code to point out my problem. Real class that I wrote has the other name (TextId) and much more inside - a Map where Locale is a key and a value of List<String> (for example, first element can be manufactuter URI, the next one can be product URI; or in case with article providers it can be consisted of parent provider URI, child provider URI and product URI).
Also this class has some additional methods, e.g. to create URI from its own elements depending on Locale used. I wouldn’t be happy to replace this class with simple Set<String> because it’s deeply integrated with other project code and I would have to write util classes to handle it all over the project. Now I just use private TextId textId and I get all those as methods of textId.
Otherwise I’d use a Set for sure.
Pretty much every URL you can find on thevegcat.com is handled by this class because it supports localization more or less out of the box.
P.S. Currently I have a brige over a FullTextField that converts all possible URIs from this class into a single Setand then I use join method with space as delimiter to convert it into plain String and later I use simple query to find appropriate document. But I thing having it as KeywodField would be better because I don’t need analyzers on this, just basic plain match and no substrings and no any other options rather than match.
I don’t know if it makes sense, but if your Uris/TextId classes are as complex as you imply, maybe you want to use @IndexedEmbedded on the Uris uris field and deal with the complexity in the Uris type (or in a type bridge on the Uris type).
Inside a bridge there is call to convert to a Set which then contains URIs for any Locale I support (en_us__food__semi_prepared_meals_and_mixtures, hr_hr__hrana__polugotova_jela_i_smjese, sr_rs__hrana__polugotova_jela_i_mesavine - where dobule underscore means a slash from path).
And then simply those values are written inside overriden method write() inside bridge:
target.addValue(this.valueFieldReference, uri);
I didn’t like to use @IndexedEmbedded as I use e.g. for Manufacturer of products or Provider inside a price structure because it looks to me natural to search over field textId as a set, not to use any textId.locale or something similar.
If you look at this as a tag list, that’s pretty much the same thing - you have few tags and you want to find an item over one specific tag.