How hiberante search creates index fields mapping?

I have one below property is created in elasticsearch using hibernate search 5

"archetype" : {
            "properties" : {
              "archetypeId" : {
                "type" : "text",
                "fields" : {
                  "keyword" : {
                    "type" : "keyword",
                    "ignore_above" : 256
                  }
                }
              }

Just wanted to know how and when this fields mapping get created?? can we make similar in HS 6?

                  "keyword" : {
                    "type" : "keyword",
                    "ignore_above" : 256
                  }

Actually we want to search fields with keyword like in my case archetype.archetypeId.keyword="some search string"
Note: This was implemented for exact match… is there any alternate solution for exact matching in hibernate search 6??

This was working fine in hibernate search 5 but not after migrating in hibernate search 6.

When I try to find out the root cause I see above index mapping difference.

How to implement hibernate search 5 behaviour for hibernate search 6 for archetype.archetypeId.keyword=“some search string”

As I already told you in another post, Hibernate Search 5 did not create this mapping. Hibernate Search did not know about this field, so it didn’t add it to the Elasticsearch schema, and Elasticsearch added it automatically the first time a document with that field was indexed:

The only way to get that exact behavior in Hibernate Search 6 would be to go full native: define your mapping as JSON, transform document data to JSON in the bridge, and define your search predicates as JSON in the Search DSL.

Alternatively, you could alter your bridge to give the keyword type to all your dynamic fields:

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

Then don’t use archetype.archetypeId.keyword in your search queries, but just archetype.archetypeId.

And, last solution, if you need both full-text search and keyword search you could define two field templates in your binder:

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

And make sure to populate both fields in your bridge:

 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;
         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 = target.addObject( entry.getKey() );
                      for (String attributeKey : attributeKeys) {
+                       String attributeKeywordFieldName = attributeKey + "_keyword";
                         final Object attributeValue = resourceAttributes.get(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);
                                     }
                                     attributesObject.addValue(attributeKey,item);
+                                    attributesObject.addValue(attributeKeywordFieldName,item);
                                 });
                             } else {
                                 if (log.isTraceEnabled()) {
                                     log.trace("Adding field {} with value {} to document.", fieldName, attributeValue.toString());
                                 }
                                 attributesObject.addValue(attributeKey,item);
+                                attributesObject.addValue(attributeKeywordFieldName,item);
                             }
                         }
                     }
                 }
             }
         }
     }
 }

Then don’t use archetype.archetypeId.keyword in your search queries, but archetype.archetypeId_keyword.

If we used this solution and tried to search like : archetype.archetypeId_keyword:spring-boot then getting same exception like

  "errorCode": "Internal",
  "errors": [
    {
      "message": "HSEARCH400611: Invalid type: 'archetype.archetypeId' is a value field, not an object field.",
      "field": null,
      "value": null
    }
  ]
}

Mapping is

"archetype" : {
          "dynamic" : "true",
          "properties" : {
            "archetypeId" : {
              "type" : "text",
              "analyzer" : "english"
            },
            "archetypeId_keyword" : {
              "type" : "text",
              "analyzer" : "english"
            }
          }
        }

is it overriding mapping??

You might need to put the *_keyword template before the existing one. I updated my example, try that?

Also, don’t forget to drop and recreate your indexes before testing.

I have done same thing and it is working. Thanks!