Object having id as key-name not getting populated via spring-data couchbase

Hi ,

I have a sample document as below which I am storing in couchbase DB.

{

"id": "036efcc9-20a2-4d4e-ade8-129df697e837",

"action": "User Action needed",

"actionType": "Auto",

"createdAt": 1599202767506,

"description": "Shipped",

"source": "Manual",

"user": {

    "id": "user_01",

    "email": "abc@gmail.com"

}

}

As above “id” is the document-id which we are auto-generating via SDK, also we have one more object in the document as user, which has id as its attribute.
The above document is getting saved via spring-data-couchbase successfully, but when we are trying to retrieve the same document via findById(String id),…(Here id is document-id) we are getting the below result:

{

"id": "036efcc9-20a2-4d4e-ade8-129df697e837",

"action": "User Action needed",

"actionType": "Auto",

"createdAt": 1599202767506,

"description": "Shipped",

"source": "Manual",

"user": {

    "id": null,

    "email": "abc@gmail.com"

}

}

findById when done then nested object having id as key-name is coming as null. Here user.id is coming as null when fetched from DB.

I am using java-sdk-3, spring version 2.3.3.RELEASE, and spring-data-couchbase as 4.0.3.RELEASE.

The issue is seen only with the latest update, previously it was working fine.

Please provide your inputs, it is because of the newer version that id has been marked as a reserved keyword and can only be used as part of document-id and not with other attribute key-name.

Thanks.

Adding some of our findings on the above issue.

Checked the below file

https://github.com/spring-projects/spring-data-couchbase/blob/4.0.x/src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentProperty.java

Here field which was named as id are considered as Identifiers.

Please provide your inputs.

This is a known bug in the spring-data-Couchbase.

To fix this issue you can create the following bean, till the time it’s officially fixed.

import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.mapping.id.IdPrefix;
import org.springframework.data.couchbase.core.mapping.id.IdSuffix;
import org.springframework.data.couchbase.core.query.N1qlJoin;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import java.util.Objects;

// fix for https://www.couchbase.com/forums/t/id-is-null-when-retrieving-with-spring-data/13410
public class MyMappingCouchbaseConverter extends MappingCouchbaseConverter {

  public MyMappingCouchbaseConverter(
      final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext,
      final String typeKey) {
    super(mappingContext, typeKey);
  }

  @Override
  protected <R> R read(CouchbasePersistentEntity<R> entity, CouchbaseDocument source,
      Object parent) {
    R instance = super.read(entity, source, parent);
    final ConvertingPropertyAccessor accessor = getPropertyAccessor(instance);

    entity.doWithProperties(new PropertyHandler<CouchbasePersistentProperty>() {
      @Override
      public void doWithPersistentProperty(final CouchbasePersistentProperty prop) {
        if (!doesPropertyExistInSource(prop) || entity.isConstructorArgument(prop)
            || isIdConstructionProperty(prop)
            || prop.isAnnotationPresent(N1qlJoin.class)) {
          return;
        }
        Object obj = prop.isIdProperty() && Objects.isNull(parent) ? source.getId()
            : getValueInternal(prop, source, instance);
        accessor.setProperty(prop, obj);
      }

      private boolean doesPropertyExistInSource(final CouchbasePersistentProperty property) {
        return property.isIdProperty() || source.containsKey(property.getFieldName());
      }

      private boolean isIdConstructionProperty(final CouchbasePersistentProperty property) {
        return property.isAnnotationPresent(IdPrefix.class) || property.isAnnotationPresent(
            IdSuffix.class);
      }
    });
    return instance;
  }

  private ConvertingPropertyAccessor<Object> getPropertyAccessor(Object source) {
    CouchbasePersistentEntity<?> entity = mappingContext
        .getRequiredPersistentEntity(source.getClass());
    PersistentPropertyAccessor<Object> accessor = entity.getPropertyAccessor(source);
    return new ConvertingPropertyAccessor<>(accessor, conversionService);
  }
}
  @Bean
  public MappingCouchbaseConverter mappingCouchbaseConverter(CouchbaseMappingContext couchbaseMappingContext,
      CouchbaseCustomConversions couchbaseCustomConversions){
    MyMappingCouchbaseConverter myMappingCouchbaseConverter = new MyMappingCouchbaseConverter(
        couchbaseMappingContext, super.typeKey());
    ofsMappingCouchbaseConverter.setCustomConversions(couchbaseCustomConversions);
    return ofsMappingCouchbaseConverter;
  }