ValueBinder for Collection<String>

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.

What am I doing wrong?


Article.java

@Entity @Indexed
class Article {

	// ... ID, name...

	@FullTextField(
		projectable = Projectable.NO,
		searchable = Searchable.YES,
		valueBinder = @ValueBinderRef(type = BarcodesBinder.class)
	)
	@Convert(converter = StringSetConverter.class)
	private Set<String> barcodes;

	// ... more properties

}

BarcodesBinder.java

// Normalize Set of barcode values by removing spaces inside each barcode
// and then join all by single space.
class BarcodesBinder implements ValueBinder {

	@Override
	public void bind( ValueBindingContext<?> context ) {
		// ERROR HERE
		context.bridge( Collection.class, new BarcodesBridge() );
	}

	private static class BarcodesBridge
			implements ValueBridge<Collection<String>, String>
	{
		@Override
		public String toIndexedValue( Collection<String> value,
			ValueBridgeToIndexedValueContext context
		) {
			if (value == null) { return ""; }
			return StringUtils.trimToNull(
				value.stream()
					.map(StringUtils::trimToNull)
					.filter(Objects::nonNull)
					.map(barcode -> barcode.replace(" ", ""))
					.collect(Collectors.joining(" ")) );
		}
	}
}

@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:

@KeywordField(normalizer = "barcode")
private Collection<String> barcodes;

Just define the normalizer in your analysis configurer:

package org.hibernate.search.documentation.analysis;

import org.hibernate.search.backend.lucene.analysis.LuceneAnalysisConfigurationContext;
import org.hibernate.search.backend.lucene.analysis.LuceneAnalysisConfigurer;

public class MyLuceneAnalysisConfigurer implements LuceneAnalysisConfigurer {
    @Override
    public void configure(LuceneAnalysisConfigurationContext context) {
        context.normalizer( "barcode" ).custom() 
                .tokenFilter( "lowercase" ) // If you need it
                .tokenFilter( "patternReplace" )
                        .param( "pattern", " " )
                        .param( "replacement", "" );
    }
}

And make sure to instruct Hibernate Search to use your analysis configurer:

hibernate.search.backend.analysis.configurer = class:org.hibernate.search.documentation.analysis.MyLuceneAnalysisConfigurer

You can do something similar for Elasticsearch, you will just have to use a different interface and pass different names to the tokenFilter/param methods: Hibernate Search 6.1.7.Final: Reference Documentation

1 Like

Thank you very much!

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. :slight_smile:

Thanks!

No problem.

Right, it would.

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.

1 Like