Can a Hibernate Search FieldBridge configure facets for dynamic fields?



Using Hibernate Search 5.11.3 with programmatic API (no annotations), is there a way to facet on dynamic fields added in a class or field bridge? I don’t see any ‘facet’ config available in FieldMetadataBuilder when using MetadataProvidingFieldBridge.

I have tried various combinations of luceneOptions.addSortedDocValuesFieldToDocument() and luceneOptions.addFieldToDocument() in the set() method. This successfully updates the index, but I cannot perform facet queries.

I am trying to do a basic attribute facet/filter where I have a generic table of attributes with id/name and attribute values associated with products. For various reasons I am using the programmatic API and especially for attributes I can’t make use of the @Facet annotation. So for a product, I added this class bridge to Product.class:

public class ProductClassTagValuesBridge implements FieldBridge
{
    @Override
    public void set(String name, Object value, Document document, LuceneOptions luceneOptions)
    {
        Product product = (Product) value;

        for (TagValue v : product.getTagValues())
        {
            Tag tag = v.getTag();
            String tagName = "tag-" + tag.getId();
            String tagValue = v.getId().toString();

            // not sure if this line is required? Have tried with and without
            luceneOptions.addFieldToDocument(tagName, tagValue, document);

            luceneOptions.addSortedDocValuesFieldToDocument(tagName, tagValue, document);
        }
    }
}

Then I build my (test) faceting request to search tag-56 (which I confirmed is in the index using Luke):

FacetParameterContext context = queryBuilder.facet()
        .name("tag-56")
        .onField("tag-56")
        .discrete();

FacetingRequest facetingRequest = context.createFacetingRequest();

Which when used in the search/FacetManager gives me the error:

org.hibernate.search.exception.SearchException: HSEARCH000268: Facet request ‘TAG_56’ tries to facet on field ‘tag-56’ which either does not exist or is not configured for faceting (via @Facet). Check your configuration.

I have also tried the custom config solution from the solution in this post: Hibernate Search: configure Facet for custom FieldBridge

For the custom field I added a field bridge to tagValues on my product. The same error occurs.

mapping.entity(Product.class).indexed()
    .property("tagValues", ElementType.FIELD).field()
        .analyze(Analyze.NO).store(Store.YES)
        .bridge(ProductTagValuesFieldBridge.class)

Answer

Short answer: Hibernate Search does not allow that… yet.

Long answer:

Hibernate Search 5 allows dynamic fields, but does not allow faceting on fields declared in custom bridges. That is to say, you can add arbitrary values to your index that don’t fit a pre-defined schema, but you cannot use faceting on those fields.

Hibernate search 6 allows faceting (now called “aggregations”) on fields declared in custom bridges (just declare them as .aggregable(Aggregable.YES)), but does not allow dynamic fields yet.

EDIT: Starting with 6.0.0.Beta7, dynamic fields are supported thanks to field templates. So the rest of my message is not useful anymore. See this section of the documentation for more information about field templates. It’s totally possible to declare an aggregable, dynamic field in your bridge.


Original message about ways to work without dynamic fields (obsolete):

That is to say, if you know the list of tags upon startup, are able to list them all, and are certain they won’t change while your application is up, you could declare the fields upfront and use faceting on them. But if you don’t know the list of tags upon startup, none of this is possible (yet).

Until dynamic fields are added to Hibernate Search 6, the only solution is to use Hibernate Search 5 and to re-implement faceting yourself. As you can expect, this will be complex and you will have to get your hands dirty with Lucene. You will have to:

  1. Add fields of type SortedSetDocValuesFacetField to your document in your custom bridge.
  2. Ensure Hibernate Search calls FacetsConfig.build on your documents after they are populated. One way to do that (through a hack) would be to declare a dummy @Facet field on your entity, even if you don’t use it.
  3. Completely ignore Hibernate Search’s query feature and perform faceting yourself from an IndexReader. You can get an IndexReader from Hibernate Search as explained here. There’s an example of how to perform faceting in org.hibernate.search.query.engine.impl.QueryHits#updateStringFacets.


Source: stackoverflow