How to index all entity fields into a single field in OpenSearch without adding the field to the entity class?

Hello,

I am working on a project using Hibernate Search version 7.1.0 with an OpenSearch backend. I am looking to implement a feature that allows aggregating all fields from an entity into a single indexed field to enable efficient searching using the “english” analyzer. It’s crucial that this field not be added to the class annotated with @Entity.

I implemented a custom ValueBridge that aggregates data from various entity fields. Here is the implementation example:

public class GenericOrderValueBridge implements ValueBridge<GenericOrder, String> {
    @Override
    public String toIndexedValue(GenericOrder order, ValueBridgeToIndexedValueContext context) {
        return Stream.of(
                        getFieldValues(order.getSample()),
                        getOrderFields(order),
                        getResultFields(order.getResult()),
                        getWorklistFields(order.getWorklist()),
                        getWorksheetFields(order.getWorksheet())
                )
                .flatMap(Stream::of)
                .filter(Objects::nonNull)
                .map(Object::toString)
                .collect(Collectors.joining(" "));
    }

    private Object[] getFieldValues(Sample sample) {
        return new Object[]{
                sample.getId(),
                sample.getDescription(),
                sample.getProjectName()
        };
    }

    private Object[] getOrderFields(Order order) {
        return new Object[]{
                order.getId(),
                order.getCreatedAt(),
                order.getDescription()
        };
    }

    private Object[] getResultFields(Result result) {
        return new Object[]{
                result.getId(),
                result.getStartDate(),
                result.getEndDate()
        };
    }

    private Object[] getWorklistFields(Worklist worklist) {
        return new Object[]{
                worklist.getId(),
                worklist.getCreatedAt()
        };
    }

    private Object[] getWorksheetFields(Worksheet worksheet) {
        return new Object[]{
                worksheet.getId(),
                worksheet.getCreatedAt()
        };
    }
}

I added this value bridge to the mapping configuration as follows:

public void configure(HibernateOrmMappingConfigurationContext context) {
    var mapping = context.programmaticMapping();
    var typeMapping = mapping.type(GenericOrder.class);
    typeMapping.indexed();
    typeMapping.property("allFields")
        .fullTextField()
        .valueBridge(new GenericOrderValueBridge())
        .analyzer("english");
}

However, during integration testing, I encountered a problem suggesting that Hibernate Search expects the field allFields to exist in the GenericOrder class, which is not my intention. Here is the exception:

Caused by: org.hibernate.search.util.common.SearchException: HSEARCH700078: No readable property named 'allFields' on type 'com.example.app.model.GenericOrder'.
    at org.hibernate.search.mapper.pojo.model.spi.AbstractPojoRawTypeModel.property(AbstractPojoRawTypeModel.java:113)
    at org.hibernate.search.mapper.pojo.mapping.definition.programmatic.impl.TypeMappingStepImpl.property(TypeMappingStepImpl.java:108)
    at com.example.app.config.HibernateSearchConfig.genericOrderMapping(HibernateSearchConfig.java:158)
    at com.example.app.config.HibernateSearchConfig.configure(HibernateSearchConfig.java:135)
    ... 73 more

Is there a way to define such a synthetic field in the Hibernate Search configuration that doesn’t exist directly in the database model but can be indexed and searched? What best practices or alternative approaches would you recommend?

Thank you in advance for any help.

What you’re doing is fundamentally unsafe, in particular because Hibernate Search won’t know which properties of the entity are used for indexing and thus won’t be able to trigger reindexing properly.

See [HSEARCH-3926] - Hibernate JIRA instead, in particular the comments that provide a working bridge.

Hey, thanks for the reply. Generally, we index all fields properly. We have a large table that allows for filtering/searching each column, but in addition to that, the business wants something more akin to a Google search option, which searches for anything. It’s quite time-consuming to search each field individually, so we came up with the idea of storing all the data in one column to allow for faster searches. We are simply exploring different options to determine what will work best.