Unable to add @PropertyBinding on a field, which is in includepath or other object

Unable to add @PropertyBinding on a field, which is in includepath or other object
and unable to user criteria “caseKind.translations.en”

This is my mapping as below :

@Indexed
@Table(name="dcase", schema="test")
public class Case extends VersioningEntity {

@IndexedEmbedded(includePaths = {"id", "translations"})
@IndexingDependency(reindexOnUpdate = ReindexOnUpdate.SHALLOW)
@ManyToOne
@JoinColumn(name="case_kind_type_id")
private CaseKindType caseKind;
	
}

@Entity
@Table(name="case_kind_type", schema="test")
public class CaseKindType extends Localisable implements Serializable {

 @OneToMany
 @JoinColumn(name="`key`", referencedColumnName="case_kind_name")
 @GenericField(projectable = Projectable.YES)
 @PropertyBinding(binder = @PropertyBinderRef(type = I18FieldBinder.class))
 private Set<Translation> translations;
	}

The above mapping throwing error as below:

 Hibernate ORM mapping: 
        type 'com.eurodyn.eips.core.db.entity.Case': 
            path '.caseKind<no value extractors>.translations': 
                failures: 
                  - HSEARCH000135: No default value bridge implementation for type 'com.eurodyn.eips.core.db.entity.Translation'. Use a custom bridge.

I want to search sort or search data with criteria “caseKind.translations.en” here we have translation with multiple language code like “en,nl,fr”

earlier I am using Valuebridge as below which is not working with above criteria:

@GenericField(projectable = Projectable.YES, valueBridge = @ValueBridgeRef(type = I18FieldBridge.class))

public class I18FieldBridge implements ValueBridge<Translation, String> {

	@Override
	public String toIndexedValue(Translation translation, ValueBridgeToIndexedValueContext context) {
		return translation == null ? null : translation.getDescription() + "." + translation.getLanguageCode();
	}	
}

it is storing data as
translations:
{
1: “data.en”
2: “data.nl”
3: “data.fr
}

I think I need to store data as below to use criteria “caseKind.translations.en” :
translations:
{
en: “data”
nl: “data”
fr: “data”
}

Please help me to achive it.

These two annotations are duplicates. If you’re using @PropertyBinding, you don’t need @GenericField (which in this case cannot work and is triggering the error).

Remove @GenericField, that should get you one step further.

And if things still don’t work, please provide your code, including the property brige/binder.

EDIT: We’ll need the code of Translation too.

1 Like

Thank you for your suggestion, now I am step a ahead.

My translation entity as below :

@Entity
@Table(name = "translation", schema = "translation", 
		uniqueConstraints=@UniqueConstraint(columnNames={"`key`", "lang_code"}))
@EntityListeners(EntityInterceptor.class)
public class Translation implements Translationable, Serializable {
	private static final long serialVersionUID = -7906256457718143365L;

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="id", unique=true, nullable=false, updatable=false)
	private Long id;

	@Column(name="`key`", length=100, nullable=false, updatable=false)
	private String key;
	
	@Column(name="description", columnDefinition="nvarchar", length=300, nullable=false, updatable=false)
	private String description;
	
	@Column(name="lang_code", columnDefinition="bpchar", length=2, nullable=false, updatable=false)
	private String languageCode;
}

My Binder and Bridge as below :

public class I18FieldBinder implements PropertyBinder {
	@Override
    public void bind(PropertyBindingContext context) {
        context.dependencies().useRootOnly();
        PojoModelProperty bridgedElement = context.bridgedElement();
        IndexSchemaObjectField attributeField = context.indexSchemaElement()
                .objectField( bridgedElement.name() );
        context.bridge(Set.class,new I18FieldTestBridge(attributeField.toReference()));
    }

}

public class I18FieldTestBridge implements PropertyBridge<Set> {

    private final IndexObjectFieldReference fieldReference;

    public I18FieldTestBridge(IndexObjectFieldReference fieldReference) {
        this.fieldReference = fieldReference;
    }

    @SuppressWarnings("unchecked")
	@Override
    public void write(DocumentElement target, Set bridgedElement, PropertyBridgeWriteContext context) {
		final Set<Translation> translations = (Set<Translation>) bridgedElement;
        DocumentElement indexedAttributeMetadata = target.addObject( fieldReference );
			for (Translation translation : translations) {
                        final String fieldName = translation.getLanguageCode();
                       
                                    indexedAttributeMetadata.addValue(fieldName,translation.getDescription());
                                }
            }
        }

In above code I updated proper Binder class name as below :

 @PropertyBinding(binder = @PropertyBinderRef(type = I18FieldBinder.class))

After your suggestion I run the code , now I am getting error as below in I18FieldTestBridge.write :

Caused by: org.hibernate.search.util.common.SearchException: HSEARCH400609: Unknown field 'caseKind.translations.lb'.

You declared an object field, but you didn’t declare the fields within that object field. You need one field per language, e.g. lb, en, fr, es, …

Just see the examples in the docs and how it declares the object field and then fields within it:

        IndexSchemaObjectField summaryField = context.indexSchemaElement() 
                .objectField( "summary" );

        IndexFieldType<BigDecimal> amountFieldType = context.typeFactory() 
                .asBigDecimal().decimalScale( 2 ).toIndexFieldType();

        context.bridge( 
                List.class, 
                new Bridge( 
                        summaryField.toReference(), 
                        summaryField.field( "total", amountFieldType ).toReference(), 
                        summaryField.field( "books", amountFieldType ).toReference(), 
                        summaryField.field( "shipping", amountFieldType ).toReference() 
                )
        );

After your suggestion, I updated My binder and Bridge as below :

public class I18FieldBinder implements PropertyBinder {
	@Override
    public void bind(PropertyBindingContext context) {
        context.dependencies().useRootOnly();
        PojoModelProperty bridgedElement = context.bridgedElement();
        IndexFieldType<String> nameType = context.typeFactory() 
                .asString()
                .toIndexFieldType();
        IndexSchemaObjectField attributeField = context.indexSchemaElement().objectField(bridgedElement.name());
        
        context.bridge(Set.class,new I18FieldTestBridge(
        		attributeField.toReference(),
        		attributeField.field("en", nameType).toReference(),
        		attributeField.field("de",nameType).toReference(),
        		attributeField.field("fr",nameType).toReference(),
        		attributeField.field("lb",nameType).toReference()
        		));
    }

}

@SuppressWarnings("rawtypes")
public class I18FieldTestBridge implements PropertyBridge<Set> {

	private final IndexObjectFieldReference fieldReference;
	private final IndexFieldReference<String> en_Field;
	private final IndexFieldReference<String> de_Field;
	private final IndexFieldReference<String> fr_Field;
	private final IndexFieldReference<String> lb_Field;

public I18FieldTestBridge(IndexObjectFieldReference fieldReference,IndexFieldReference<String> en_Field,
			IndexFieldReference<String> de_Field,IndexFieldReference<String> fr_Field,
			IndexFieldReference<String> lb_Field) {
		this.fieldReference = fieldReference;
		this.en_Field = en_Field;
		this.de_Field = de_Field;
		this.fr_Field = fr_Field;
		this.lb_Field = lb_Field;
	}

	@SuppressWarnings("unchecked")
	@Override
	public void write(DocumentElement target, Set bridgedElement, PropertyBridgeWriteContext context) {
		Set<Translation> translations = bridgedElement;
         String english = "";
         String german = "";
         String french = "";
         String luxumberg = "";
		
		for (Translation translation : translations) {
			String description = translation.getDescription();
			switch ( translation.getLanguageCode() ) {
			 case "en":
				 english = description;
                 break;
             case "de":
            	 german = description;
             case "fr":
            	 french = description;
                 break;
             case "lb":
            	 luxumberg = description;
                 break;

			}
			
		}
		DocumentElement indexedAttributeMetadata = target.addObject( fieldReference );
		indexedAttributeMetadata.addValue(this.en_Field, english);
		indexedAttributeMetadata.addValue(this.de_Field, german);
		indexedAttributeMetadata.addValue(this.fr_Field, french);
		indexedAttributeMetadata.addValue(this.lb_Field, luxumberg);
	}
}

But unfortunately it is not returning in ES document, for your info
fieldReference coming as ‘ElasticsearchIndexObjectFieldReference[absolutePath=caseKind.translations]’
but result coming it is coming empty :

you can check my mapping too :

I want to add data in side translations: object like
en: description
nl: description

can you please guide further .

Thank you

This looks like the binder and bridge are getting ignored, which is odd.

You did leave the @PropertyBinding annotation, right?

After you made your changes to the code:

  1. Did you recreate the schema?
  2. Did you trigger reindexing?

Also, probably not the cause of your trouble, but you bypassed dependency detection completely here:

Note this means Hibernate Search will not know that an update to e.g. Translation#description requires reindexing, and as a result it will not reindex properly.

To get proper automatic reindexing, define dependencies correctly: Hibernate Search 7.2.2.Final: Reference Documentation

Hi,
I am using @PropertyBinding

After I made changes to the code:

  1. should I recreate schema what you meant ?
    I redeployed and it’s trigger binding , then I re indexed, it is triggered Bridge
    I already getting proper values but it is not generating tree as I asked and shown below
attributeField = casekind.transaltions 

as I shown you image , inside translations { } I want to create

caseKind:
id: 101
trnslations : {
en:  english
lb: luxemburg
fr: french
}
  1. Yes I did reindex but it is not writenniing in index as above.

can you please guide me how to reach casekind.translations to add new field in that array , then value

Thank you

Yes, after you change your mapping to add new fields, you should recreate your schema.
See the link I provided: Hibernate Search 7.2.2.Final: Reference Documentation

Good. You need to make sure you recreate the schema first, though; see above.

I understand this.

I’m afraid I cannot, at least not with this information. Your bridge looks good, so either I’m missing something, or the problem must be elsewhere.

For further help, I will need a minimal reproducer, i.e. a small project on GitHub showing the problem, that I can run on my computer and debug. You can start from this: hibernate-test-case-templates/search/hibernate-search-7/orm-elasticsearch at main · hibernate/hibernate-test-case-templates · GitHub

Hello ,

Finally achieved it , missed below line

 // ADD THIS
        attributeField.fieldTemplate( 
                "attributeFieldValueTemplate_default",
                f -> f.asString().analyzer( "english" )
        );

Which found here https://discourse.hibernate.org/t/hsearch400609-unknown-field-attributes-someattributename/6114/4
it is mentioned by you only . :grinning:

Thank you for your guide and support

This shouldn’t be necessary if you declare your fields statically with attributeField.field("en", nameType).toReference(), but okay. Glad you found a solution!