Allow for using @TypeAlias annotations [DATACOUCH-399]
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
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();
}
}
I believe this is resolved with getTypeAlias(TypeInformation<?> info) in CouchbaseConverter
/**
* @return the alias value for the type
*/
Alias getTypeAlias(TypeInformation<?> info);