HSEARCH400609: Unknown field 'attributes.someattributename'

I have below code in my spring-boot application . We are not getting any exception at startup but while creating attribute I am getting below exception like
Caused by: org.hibernate.search.util.common.SearchException: HSEARCH400609: Unknown field ‘attributes.archetype.archetypeId’.

I am not sure if I miss anything else…

Resource.java(Entity class)

    @MapKeyColumn(name = "attribute_name")
    @Column(name = "attribute_value", nullable = false, length = 5000)
    @PropertyBinding(binder = @PropertyBinderRef(type = ResourceAttributesBinder.class))
    @Convert(attributeName = "value", converter = JSONObjectConverter.class)
    @EqualsAndHashCode.Exclude
    @ToString.Exclude
    @OptimisticLock(excluded = true)
    private Map<String, JSONObject> attributes;

ResourceAttributesBinder.java

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

ResourceAttributesPropertyBridge.java

public class ResourceAttributesPropertyBridge implements PropertyBridge<Map> {

    private final IndexObjectFieldReference fieldReference;

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

    @Override
    public void write(DocumentElement target, Map bridgedElement, PropertyBridgeWriteContext context) {
        System.out.println("######bridgedElement#######"+ bridgedElement);
        final Map<String, Object> indexMap = (Map<String, Object>) bridgedElement;
        DocumentElement indexedAttributeMetadata = target.addObject( fieldReference );
        if (!ObjectUtils.isEmpty(indexMap)) {
            for (Map.Entry<String, Object> entry : indexMap.entrySet()) {
                final List<String> attributeKeys = getIndexedAttributes().get(entry.getKey());
                if (!ObjectUtils.isEmpty(attributeKeys)) {
                    final Map<String, Object> resourceAttributes = (Map<String, Object>) entry.getValue();
                    for (String attributeKey : attributeKeys) {
                        final Object attributeValue = resourceAttributes.get(attributeKey);
                        final String fieldName = entry.getKey() + "." + attributeKey;
                        if (!ObjectUtils.isEmpty(attributeValue)) {
                            if (attributeValue instanceof List) {
                                ((List<String>) attributeValue).forEach(item -> {
                                    if (log.isTraceEnabled()) {
                                        log.trace("Adding field {} with value {} to document.", fieldName, item);
                                    }
                                    System.out.println("fieldName1=="+fieldName);
                                    System.out.println("attributeValue1=="+attributeValue.toString());
                                    indexedAttributeMetadata.addValue(fieldName,item);
				//   luceneOptions.addFieldToDocument(fieldName, item, document);
                                });
                            } else {
                                if (log.isTraceEnabled()) {
                                    log.trace("Adding field {} with value {} to document.", fieldName, attributeValue.toString());
                                }
                                System.out.println("fieldName1=="+fieldName);
                                System.out.println("attributeValue1=="+attributeValue.toString());
                                indexedAttributeMetadata.addValue(fieldName,attributeValue.toString());
			//       luceneOptions.addFieldToDocument(fieldName, attributeValue.toString(), document);
                            }
                        }
                    }
                }
            }
        }
    }
}

Please include the full stack trace.

Here is the full stack trace


Caused by: org.hibernate.search.util.common.SearchException: HSEARCH400609: Unknown field 'attributes.archetype.archetypeId'.
Context: index 'com.xxxx.xxxx.model.resource.0'
        at org.hibernate.search.backend.elasticsearch.document.impl.ElasticsearchDocumentObjectBuilder.addValue(ElasticsearchDocumentObjectBuilder.java:85)
        at XXXXX.ResourceAttributesPropertyBridge.write(ResourceAttributesPropertyBridge.java:52)
        at XXXX.model.ResourceAttributesPropertyBridge.write(ResourceAttributesPropertyBridge.java:13)
        at org.hibernate.search.mapper.pojo.processing.impl.PojoIndexingProcessorPropertyBridgeNode.process(PojoIndexingProcessorPropertyBridgeNode.java:45)
        at org.hibernate.search.mapper.pojo.processing.impl.PojoIndexingProcessorPropertyNode.process(PojoIndexingProcessorPropertyNode.java:50)
        at org.hibernate.search.mapper.pojo.processing.impl.PojoIndexingProcessorMultiNode.process(PojoIndexingProcessorMultiNode.java:49)
        at org.hibernate.search.mapper.pojo.processing.impl.PojoIndexingProcessorOriginalTypeNode.process(PojoIndexingProcessorOriginalTypeNode.java:55)
        at org.hibernate.search.mapper.pojo.work.impl.PojoDocumentContributor.contribute(PojoDocumentContributor.java:46)

You are missing a template for your value fields (non-object fields). As a result, when you add a value in your bridge here, Hibernate Search does not have any metadata about the field you’re attempting to add a value for, and cannot determine how to turn the value into JSON:

Define another template for your value fields. Something like this:

public class ResourceAttributesBinder implements PropertyBinder {
    @Override
    public void bind(PropertyBindingContext context) {
        context.dependencies().useRootOnly();
        PojoModelProperty bridgedElement = context.bridgedElement();
        IndexSchemaObjectField attributeField = context.indexSchemaElement()
                .objectField( bridgedElement.name() );

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

        context.bridge(Map.class,new ResourceAttributesPropertyBridge(attributeField.toReference()));
    }
}

Note this is almost exactly the code of the second example in this link that I gave you in answer to your other question.

Hi @yrodiere - As you said , I have defined another template for value. something like below but now this time diff exception like is a value field, not an object field.

        attributeField.fieldTemplate( 
                "attributeFieldValueTemplate_default",
                f -> f.asString().analyzer( "english" )
        );
Caused by: org.hibernate.search.util.common.SearchException: HSEARCH400611: Invalid type: 'attributes.archetype' is a value field, not an object field.
        at org.hibernate.search.backend.elasticsearch.document.model.impl.ElasticsearchIndexSchemaValueFieldNode.toObjectField(ElasticsearchIndexSchemaValueFieldNode.java:48)
        at org.hibernate.search.backend.elasticsearch.document.model.impl.AbstractElasticsearchIndexSchemaFieldTemplate.lambda$createNodeIfMatching$0(AbstractElasticsearchIndexSchemaFieldTemplate.java:43)
        at java.util.Optional.map(Optional.java:215)
        at org.hibernate.search.backend.elasticsearch.document.model.impl.AbstractElasticsearchIndexSchemaFieldTemplate.createNodeIfMatching(AbstractElasticsearchIndexSchemaFieldTemplate.java:42)
        at org.hibernate.search.backend.elasticsearch.document.model.impl.ElasticsearchIndexModel.fieldOrNullIgnoringInclusion(ElasticsearchIndexModel.java:142)
        at org.hibe

Can you include the full code of your bridge? Clearly something that I don’t see in your snippets is happening.

Hi @yrodiere - I have added full bridge code .

It seems you have two levels of objects. Basically your Map<String, Object> is in fact a Map<String, Map<String, Object>>, and you’re attempting to create a structure like this:

{
  "attributes": {
    "archetype": {
      "foo": "bar"
    },
    "other": {
      "foo2": "bar2"
    }
  }
}

First problem: you need to let Hibernate Search know that you expect object fields at the first and second level.

The first level is already taken care of, it’s context.indexSchemaElement().objectField( bridgedElement.name() );.
For the second level, since the object field name is not fixed, you need an object field template. And you’ll need to make sure it doesn’t conflict with your existing field template for values (at the third level):

 public class ResourceAttributesBinder implements PropertyBinder {
     @Override
     public void bind(PropertyBindingContext context) {
         context.dependencies().useRootOnly();
         PojoModelProperty bridgedElement = context.bridgedElement();
         IndexSchemaObjectField attributeField = context.indexSchemaElement()
                 .objectField( bridgedElement.name() );
 
         attributeField.fieldTemplate( 
                 "attributeFieldValueTemplate_default",
                 f -> f.asString().analyzer( "english" )
-        );
+            .matchingPathGlob( "*.*" );
+        attributeField.objectFieldTemplate( "attributeObjectFieldTemplate_default" );
 
         context.bridge(Map.class,new ResourceAttributesPropertyBridge(attributeField.toReference()));
     }
 }

Second problem: you need to follow a proper structure when adding fields to the document: passing foo.bar is not allowed, you need to create an object field named foo, then within that object create another field called bar.

So you’ll need to change your bridge that way:

 public class ResourceAttributesPropertyBridge implements PropertyBridge<Map> {
 
     private final IndexObjectFieldReference fieldReference;
 
     public ResourceAttributesPropertyBridge(IndexObjectFieldReference fieldReference) {
         this.fieldReference = fieldReference;
     }
 
     @Override
     public void write(DocumentElement target, Map bridgedElement, PropertyBridgeWriteContext context) {
         System.out.println("######bridgedElement#######"+ bridgedElement);
         final Map<String, Object> indexMap = (Map<String, Object>) bridgedElement;
         DocumentElement indexedAttributeMetadata = target.addObject( fieldReference );
         if (!ObjectUtils.isEmpty(indexMap)) {
             for (Map.Entry<String, Object> entry : indexMap.entrySet()) {
                 final List<String> attributeKeys = getIndexedAttributes().get(entry.getKey());
                 if (!ObjectUtils.isEmpty(attributeKeys)) {
                     final Map<String, Object> resourceAttributes = (Map<String, Object>) 
+                    DocumentElement attributesObject = indexedAttributeMetadata.addObject( entry.getKey() );
                      for (String attributeKey : attributeKeys) {
                         final Object attributeValue = resourceAttributes.get(attributeKey);
-                        final String fieldName = entry.getKey() + "." + attributeKey;
                         if (!ObjectUtils.isEmpty(attributeValue)) {
                             if (attributeValue instanceof List) {
                                 ((List<String>) attributeValue).forEach(item -> {
                                     if (log.isTraceEnabled()) {
                                         log.trace("Adding field {} with value {} to document.", fieldName, item);
                                     }
                                     System.out.println("fieldName1=="+fieldName);
                                     System.out.println("attributeValue1=="+attributeValue.toString());
-                                    indexedAttributeMetadata.addValue(fieldName,item);
+                                    attributesObject.addValue(attributeKey,item);
 				//   luceneOptions.addFieldToDocument(fieldName, item, document);
                                 });
                             } else {
                                 if (log.isTraceEnabled()) {
                                     log.trace("Adding field {} with value {} to document.", fieldName, attributeValue.toString());
                                 }
                                 System.out.println("fieldName1=="+fieldName);
                                 System.out.println("attributeValue1=="+attributeValue.toString());
-                                indexedAttributeMetadata.addValue(fieldName,attributeValue.toString());
+                                attributesObject.addValue(attributeKey,item);
 			//       luceneOptions.addFieldToDocument(fieldName, attributeValue.toString(), document);
                             }
                         }
                     }
                 }
             }
         }
     }
 }

If you have any more problems, please include the full stack trace, including all lines.

1 Like

Thanks for the detailed information. I am creating attribute by passing archetype object like
{
“archetypeId”: “value”
}

Now I have made changes as per your suggestions but still no luck
Full stack trace of exception:

Caused by: org.hibernate.search.util.common.SearchException: HSEARCH400611: Invalid type: 'attributes.archetype.archetype' is a value field, not an object field.
        at org.hibernate.search.backend.elasticsearch.document.model.impl.ElasticsearchIndexSchemaValueFieldNode.toObjectField(ElasticsearchIndexSchemaValueFieldNode.java:48)
        at org.hibernate.search.backend.elasticsearch.document.model.impl.AbstractElasticsearchIndexSchemaFieldTemplate.lambda$createNodeIfMatching$0(AbstractElasticsearchIndexSchemaFieldTemplate.java:43)
        at java.util.Optional.map(Optional.java:215)
        at org.hibernate.search.backend.elasticsearch.document.model.impl.AbstractElasticsearchIndexSchemaFieldTemplate.createNodeIfMatching(AbstractElasticsearchIndexSchemaFieldTemplate.java:42)
        at org.hibernate.search.backend.elasticsearch.document.model.impl.ElasticsearchIndexModel.fieldOrNullIgnoringInclusion(ElasticsearchIndexModel.java:142)
        at org.hibernate.search.backend.elasticsearch.document.model.impl.ElasticsearchIndexModel.fieldOrNull(ElasticsearchIndexModel.java:104)
        at org.hibernate.search.backend.elasticsearch.document.impl.ElasticsearchDocumentObjectBuilder.addValue(ElasticsearchDocumentObjectBuilder.java:82)
        at com.xxx.xxx.model.ResourceAttributesPropertyBridge.write(ResourceAttributesPropertyBridge.java:54)
        at com.xxx.model.ResourceAttributesPropertyBridge.write(ResourceAttributesPropertyBridge.java:13)
        at org.hibernate.search.mapper.pojo.processing.impl.PojoIndexingProcessorPropertyBridgeNode.process(PojoIndexingProcessorPropertyBridgeNode.java:45)
        at org.hibernate.search.mapper.pojo.processing.impl.PojoIndexingProcessorPropertyNode.process(PojoIndexingProcessorPropertyNode.java:50)
        at org.hibernate.search.mapper.pojo.processing.impl.PojoIndexingProcessorMultiNode.process(PojoIndexingProcessorMultiNode.java:49)
        at org.hibernate.search.mapper.pojo.processing.impl.PojoIndexingProcessorOriginalTypeNode.process(PojoIndexingProcessorOriginalTypeNode.java:55)
        at org.hibernate.search.mapper.pojo.work.impl.PojoDocumentContributor.contribute(PojoDocumentContributor.java:46)
        ... 156 more


It would seem you didn’t remove this line as I was suggesting?

I edited my post to clarify that you should use attributeKey as the field name.

Thanks @yrodiere ! It is working fine now and index gets created.
While searching same record I am getting ERROR:
However I will take a look first.

This code causing the issue

predicates.add(pf.simpleQueryString()
.field(filter.getKey())
.matching(String.join(" | ", filter.getValue())).toPredicate());

caused by: org.hibernate.search.util.common.SearchException: HSEARCH400504: Unknown field 'archetype.archetypeId'.
Context: indexes [com.xxx.xxx.model.resource.0]
        at org.hibernate.search.backend.elasticsearch.scope.model.impl.ElasticsearchScopeSearchIndexesContext.field(ElasticsearchScopeSearchIndexesContext.java:99)
        at org.hibernate.search.backend.elasticsearch.search.predicate.impl.ElasticsearchSimpleQueryStringPredicate$Builder.field(ElasticsearchSimpleQueryStringPredicate.java:181)
        at org.hibernate.search.engine.search.predicate.dsl.impl.SimpleQueryStringPredicateFieldMoreStepImpl$CommonState.field(SimpleQueryStringPredicateFieldMoreStepImpl.java:74)
        at org.hibernate.search.engine.search.predicate.dsl.impl.SimpleQueryStringPredicateFieldMoreStepImpl.<init>(SimpleQueryStringPredicateFieldMoreStepImpl.java:38)
        at org.hibernate.search.engine.search.predicate.dsl.impl.SimpleQueryStringPredicateFieldStepImpl.fields(SimpleQueryStringPredicateFieldStepImpl.java:27)
        at org.hibernate.search.engine.search.predicate.dsl.SimpleQueryStringPredicateFieldStep.field(SimpleQueryStringPredicateFieldStep.java:31)

Here also we need to do something like two levels of objects while searching?

As far as I can tell, your field is named attributes.archetype.archetypeId. You passed archetype.archetypeId, which is incorrect.

If this is the thing then actually we want to store attribute with name archetype.archetypeId instead attributes.archetype.archetypeId

Is there any way to exlcude attributes from the name while index creating??

Yes. Remove this:

And use context.indexSchemaElement() wherever you were using attributeField.

Okay , I will try this solution. I think we also need to replace
attributeField.toReference() to context.indexSchemaElement().toReference()

but toReference() on ``context.indexSchemaElement()` is not available.

You don’t. context.indexSchemaElement() represents the target passed to your bridge’s write method. You don’t need to create new instances of that element, one is passed to your bridge directly.

Thank you very much @yrodiere . It’s working now as expected.

1 Like