spring-data-couchbase icon indicating copy to clipboard operation
spring-data-couchbase copied to clipboard

Allow for using @TypeAlias annotations [DATACOUCH-399]

Open spring-projects-issues opened this issue 7 years ago • 2 comments

Marcelo Grossi opened DATACOUCH-399 and commented

IndexManager is not respecting the configured TypeMapper in MappingCouchbaseConverter when creating secondary indices and views. It simply uses the classes fully qualified name (as below).

String typeKey = couchbaseOperations.getConverter().getTypeKey();
final String type = metadata.getDomainType().getName(); 

If one has a TypeMapper that makes use of the @TypeAlias annotation this will make the implementation unusable as the converter will correctly use the type mapper to identify the type, but (in case of no-primary-key deployments) won't have a proper index to query on your entity.

package com.sample;

@Document
@TypeAlias("my-document")
public class MyDocument {
}

@Repository
@N1qlSecondaryIndexed(indexName = "my-document-index")
public interface MyDocumentRepository extends CouchbaseRepository<MyDocument, String> {
}

The example document/repository above expects documents created in Couchbase to have a "_class" field with the value "my-document" and a secondary index like:

CREATE INDEX `my-document-index` ON `my-bucket`(`_class`) WHERE (`_class` = "my-document")

But instead, the secondary index is created as:

CREATE INDEX `my-document-index` ON `my-bucket`(`_class`) WHERE (`_class` = "com.sample.MyDocument")

In deployments where there is no primary index available your data becomes unsearchable. Which makes the generated repository unusable.

I believe that exposing the TypeMapper on MappingCouchbaseConverter via simple getter should be enough to implement this. The code in IndexManager would look like:

CouchbaseDocument tempTypeHolder = new CouchbaseDocument();
couchbaseOperations.getConverter().getTypeMapper().writeType(metadata.getDomainType(), tempTypeHolder);
final String type = tempTypeHolder.get(typeKey).toString();

 


1 votes, 3 watchers

spring-projects-issues avatar Aug 11 '18 04:08 spring-projects-issues

Quentin Bisson commented

This issue is also present in the N1qlUtils class:

/**
 * Produces an {@link Expression} that can serve as a WHERE clause criteria to only select documents in a bucket
 * that matches a particular Spring Data entity (as given by the {@link EntityMetadata} parameter).
 *
 * @param baseWhereCriteria the other criteria of the WHERE clause, or null if none.
 * @param converter the {@link CouchbaseConverter} giving the attribute storing the type information can be extracted.
 * @param entityInformation the expected type information.
 * @return an {@link Expression} to be used as a WHERE clause, that additionally restricts on the given type.
 */
public static Expression createWhereFilterForEntity(Expression baseWhereCriteria, CouchbaseConverter converter,
                                                    EntityMetadata<?> entityInformation) {
  //add part that filters on type key
  String typeKey = converter.getTypeKey();
  String typeValue = entityInformation.getJavaType().getName();
  Expression typeSelector = i(typeKey).eq(s(typeValue));
  if (baseWhereCriteria == null) {
    baseWhereCriteria = typeSelector;
  } else {
    baseWhereCriteria = x("(" + baseWhereCriteria.toString() + ")").and(typeSelector);
  }
  return baseWhereCriteria;
}

 

Exposing the mapper as suggested  or exposing a getTypeValue(Class<T> entityClass) method with a default implementation as shown below could do the trick.

/**
 * Marker interface for the converter, identifying the types to and from that can be converted.
 *
 * @author Michael Nitschinger
 * @author Simon Baslé
 */
public interface CouchbaseConverter
    extends EntityConverter<CouchbasePersistentEntity<?>,
    CouchbasePersistentProperty, Object, CouchbaseDocument>,
    CouchbaseWriter<Object, CouchbaseDocument>,
    EntityReader<Object, CouchbaseDocument> {

  /**
   * Convert the value if necessary to the class that would actually be stored,
   * or leave it as is if no conversion needed.
   *
   * @param value the value to be converted to the class that would actually be stored.
   * @return the converted value (or the same value if no conversion necessary).
   */
  Object convertForWriteIfNeeded(Object value);

  /**
   * Return the Class that would actually be stored for a given Class.
   *
   * @param clazz the source class.
   * @return the target class that would actually be stored.
   * @see #convertForWriteIfNeeded(Object)
   */
  Class<?> getWriteClassFor(Class<?> clazz);

  /**
   * @return the name of the field that will hold type information.
   */
  String getTypeKey();

  /**
   * @param the class of the persistent entity
   * @return the value of the field that will hold type information.
   */
  default String getTypeValue(Class<T> entityClass) {
    return entityClass.getName();
  }
}

spring-projects-issues avatar Sep 18 '19 14:09 spring-projects-issues

I believe this is resolved with getTypeAlias(TypeInformation<?> info) in CouchbaseConverter

	/**
	 * @return the alias value for the type
	 */
	Alias getTypeAlias(TypeInformation<?> info);

mikereiche avatar Apr 02 '21 19:04 mikereiche