Sample project for demonstrating Spring Data for Neo4j issue with unique indexes on relationships.
Catalogs contain listings of products. Products are not unique in the domain, but they are exclusive to a catalog. That is, a product can only be listed in a single catalog.
Consider a stack of catlogs that may or may not have the same products in them.
Given an existing product with a relationship to a catalog via a listing (annotated with @Indexed(unique = true))
When I save a product with the same properties in the ProductRepository
Then the existing product is updated and only one product is in the repository
Considering the domain and expectations it appears that having a unique relationship on Product.listing would give the desired behavior. If a product exists in the graph and a new product with the same properties is saved the unique index on Listing causes SDN to merge instead of creating a second product.
Using spring-data-neo4j:3.2.1.RELEASE or spring-data-neo4j:3.3.0.BUILD-SNAPSHOT the following exception is thrown when using a unique index:
org.springframework.dao.InvalidDataAccessResourceUsageException: Error executing statement MERGE (n:`Product` {`listing`: {value}}) ON CREATE SET n={props} return n; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: Error executing statement MERGE (n:`Product` {`listing`: {value}}) ON CREATE SET n={props} return n; nested exception is java.lang.IllegalArgumentException: [com.gezerk.domain.Listing@7066363:com.gezerk.domain.Listing] is not a supported property value
The shouldNotCreateDuplicateProduct() test demonstrates the desired behavior for products.
A duplicate product should be merged with the existing product.
Comment out the unique index annotations on Product and run this test to see duplicate products created(this test fails with IllegalArgumentException when unique indexes are used).
There are two methods for setting a relationship according to Good Relationships.
Using a single field:
- Failing Test -
shouldRetrieveSupplierFromProduct(): fails with IllegalArgumentException - not a supported property type.
Using a relationship entity:
- Failing Test -
shouldRetrieveCatalogFromProduct(): fails with IllegalArgumentException - not a supported property type.
There is on open issue for non-primitive property types failing when using a unique index here. The exception listed in that issue is the same, a failure to recognize the property type. It would appear that SDN is not calling the registered converter for the property type (converters are registered in the tests above). The @GraphProperty(propertyType = Supplier.class) annotation was added to try and force loading of the converters. The converters for Supplier and Catalog log when they are registered and when they are called. In both tests, the converters were registered, but not called.
Debugging Spring Data Neo4j shows that the decision to load converters is made in the EntityStateHandler.class (3.3.0-BUILD-SNAPSHOT line 170) by calling the Neo4jPersistentPropertyImpl.isSerializablePropertyField() method. The isSerializablePropertyField() method returns false if the property is a relationship. That would indicate that no conversion would ever occur for a relationship.