diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Direction.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Direction.groovy index bef0caa20c4..e9028aa3951 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Direction.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Direction.groovy @@ -29,6 +29,7 @@ import groovy.transform.CompileStatic */ @CompileStatic enum Direction { + INCOMING, OUTGOING, BOTH @Override @@ -53,4 +54,4 @@ enum Direction { boolean isOutgoing() { return this == OUTGOING || this == BOTH } -} \ No newline at end of file +} diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Neo4jEntity.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Neo4jEntity.groovy index 532cedb594d..00f09f79348 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Neo4jEntity.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Neo4jEntity.groovy @@ -18,10 +18,14 @@ */ package grails.neo4j +import groovy.transform.CompileStatic + +import org.neo4j.driver.QueryRunner +import org.neo4j.driver.Result + import grails.gorm.MultiTenant import grails.gorm.api.GormAllOperations import grails.gorm.multitenancy.Tenants -import groovy.transform.CompileStatic import org.grails.datastore.gorm.GormEnhancer import org.grails.datastore.gorm.GormEntity import org.grails.datastore.gorm.GormStaticApi @@ -34,8 +38,6 @@ import org.grails.datastore.gorm.schemaless.DynamicAttributes import org.grails.datastore.mapping.model.config.GormProperties import org.grails.datastore.mapping.multitenancy.MultiTenancySettings import org.grails.datastore.mapping.multitenancy.exceptions.TenantNotFoundException -import org.neo4j.driver.Result -import org.neo4j.driver.QueryRunner /** * Extends the default {@org.grails.datastore.gorm.GormEntity} trait, adding new methods specific to Neo4j @@ -67,7 +69,6 @@ trait Neo4jEntity implements GormEntity, DynamicAttributes { DynamicAttributes.super.putAt(name, val) } - /** * Obtains a dynamic attribute * @@ -77,12 +78,12 @@ trait Neo4jEntity implements GormEntity, DynamicAttributes { @Override def getAt(String name) { def val = DynamicAttributes.super.getAt(name) - if(val == null) { + if (val == null) { GormStaticApi staticApi = GormEnhancer.findStaticApi(getClass()) GraphPersistentEntity entity = (GraphPersistentEntity) staticApi.gormPersistentEntity - if(entity.hasDynamicAssociations()) { + if (entity.hasDynamicAssociations()) { def id = ident() - if(id != null) { + if (id != null) { staticApi.withSession { Neo4jSession session -> DynamicAssociationSupport.loadDynamicAssociations(session, entity, this, id) } @@ -105,10 +106,9 @@ trait Neo4jEntity implements GormEntity, DynamicAttributes { QueryRunner boltSession = getStatementRunner(session) String queryString - if(cypher instanceof GString) { + if (cypher instanceof GString) { queryString = Neo4jGormStaticApi.buildNamedParameterQueryFromGString((GString) cypher, params) - } - else { + } else { queryString = cypher.toString() } params['this'] = session.getObjectIdentifier(this) @@ -129,7 +129,7 @@ trait Neo4jEntity implements GormEntity, DynamicAttributes { QueryRunner boltSession = getStatementRunner(session) Map paramsMap = new LinkedHashMap() - paramsMap.put("this", session.getObjectIdentifier(this)) + paramsMap.put('this', session.getObjectIdentifier(this)) includeTenantIdIfNecessary(session, cypher, paramsMap) @@ -141,7 +141,6 @@ trait Neo4jEntity implements GormEntity, DynamicAttributes { } } - /** * perform a cypher query * @param queryString @@ -152,14 +151,14 @@ trait Neo4jEntity implements GormEntity, DynamicAttributes { Map arguments if (session.getDatastore().multiTenancyMode == MultiTenancySettings.MultiTenancyMode.DISCRIMINATOR) { if (!queryString.contains("\$tenantId")) { - throw new TenantNotFoundException("Query does not specify a tenant id, but multi tenant mode is DISCRIMINATOR!") + throw new TenantNotFoundException('Query does not specify a tenant id, but multi tenant mode is DISCRIMINATOR!') } else { arguments = new LinkedHashMap() arguments.put(GormProperties.TENANT_IDENTITY, Tenants.currentId(Neo4jDatastore)) - arguments.put("this", session.getObjectIdentifier(this)) + arguments.put('this', session.getObjectIdentifier(this)) } } else { - arguments = (Map) Collections.singletonMap("this", session.getObjectIdentifier(this)) + arguments = (Map) Collections.singletonMap('this', session.getObjectIdentifier(this)) } QueryRunner boltSession = getStatementRunner(session) boltSession.run(queryString, arguments) @@ -269,10 +268,10 @@ trait Neo4jEntity implements GormEntity, DynamicAttributes { private void includeTenantIdIfNecessary(Neo4jSession session, String queryString, Map paramsMap) { if ((this instanceof MultiTenant) && session.getDatastore().multiTenancyMode == MultiTenancySettings.MultiTenancyMode.DISCRIMINATOR) { if (!queryString.contains("\$tenantId")) { - throw new TenantNotFoundException("Query does not specify a tenant id, but multi tenant mode is DISCRIMINATOR!") + throw new TenantNotFoundException('Query does not specify a tenant id, but multi tenant mode is DISCRIMINATOR!') } else { paramsMap.put(GormProperties.TENANT_IDENTITY, Tenants.currentId(Neo4jDatastore)) } } } -} \ No newline at end of file +} diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Node.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Node.groovy index c39ba331ef9..c7c562e25d7 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Node.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Node.groovy @@ -20,6 +20,7 @@ package grails.neo4j import groovy.transform.CompileStatic + import org.grails.datastore.gorm.GormEnhancer import org.grails.datastore.gorm.GormEntity import org.grails.datastore.gorm.neo4j.api.Neo4jGormStaticApi @@ -33,6 +34,7 @@ import org.grails.datastore.gorm.schemaless.DynamicAttributes */ @CompileStatic trait Node implements Neo4jEntity, GormEntity, DynamicAttributes { + /** * Allows accessing to dynamic properties with the dot operator * @@ -139,4 +141,4 @@ trait Node implements Neo4jEntity, GormEntity, DynamicAttributes { static Path findPathTo(Class type, CharSequence cypher, Map params) { ((Neo4jGormStaticApi) GormEnhancer.findStaticApi(this)).findPathTo(type, cypher, params) } -} \ No newline at end of file +} diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Path.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Path.groovy index 0a8ca4da353..5ad951be189 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Path.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Path.groovy @@ -26,7 +26,7 @@ package grails.neo4j * @see org.neo4j.driver.types.Path * @since 6.1 */ -interface Path extends Iterable> { +interface Path extends Iterable> { /** * A segment @@ -34,6 +34,7 @@ interface Path extends Iterable> { * @see org.neo4j.driver.types.Path.Segment */ interface Segment { + Relationship relationship() S start() @@ -41,7 +42,6 @@ interface Path extends Iterable> { E end() } - /** @return the start node of this path */ S start() diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Relationship.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Relationship.groovy index f8b9c2260c9..bb7b05fb289 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Relationship.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/Relationship.groovy @@ -20,6 +20,7 @@ package grails.neo4j import groovy.transform.CompileStatic + import org.grails.datastore.gorm.GormEnhancer import org.grails.datastore.gorm.GormEntity import org.grails.datastore.gorm.neo4j.RelationshipPersistentEntity @@ -32,7 +33,7 @@ import org.grails.datastore.gorm.schemaless.DynamicAttributes * @since 6.1 */ @CompileStatic -trait Relationship implements DynamicAttributes, Serializable { +trait Relationship implements DynamicAttributes, Serializable { /** * The relationship type @@ -54,13 +55,12 @@ trait Relationship implements DynamicAttributes, Serializable { */ T to - /** * @return The type of the relationship */ String type() { - if(this.theType == null) { - theType = ((RelationshipPersistentEntity)GormEnhancer.findEntity(getClass())).type() + if (this.theType == null) { + theType = ((RelationshipPersistentEntity) GormEnhancer.findEntity(getClass())).type() } return theType } @@ -84,11 +84,11 @@ trait Relationship implements DynamicAttributes, Serializable { * @return True if they are equal */ boolean equals(Object object) { - if(object instanceof Relationship) { - Relationship other = (Relationship)object - GormEntity fromEntity = (GormEntity)this.from - GormEntity toEntity = (GormEntity)this.to - return fromEntity?.ident() == ((GormEntity)other.from)?.ident() && toEntity?.ident() == ((GormEntity)other.to)?.ident() + if (object instanceof Relationship) { + Relationship other = (Relationship) object + GormEntity fromEntity = (GormEntity) this.from + GormEntity toEntity = (GormEntity) this.to + return fromEntity?.ident() == ((GormEntity) other.from)?.ident() && toEntity?.ident() == ((GormEntity) other.to)?.ident() } return false } @@ -97,16 +97,16 @@ trait Relationship implements DynamicAttributes, Serializable { * @return hashCode */ int hashCode() { - GormEntity fromEntity = (GormEntity)this.from - GormEntity toEntity = (GormEntity)this.to + GormEntity fromEntity = (GormEntity) this.from + GormEntity toEntity = (GormEntity) this.to int result = type != null ? type.hashCode() : 0 def fromId = fromEntity?.ident() - if(fromId != null) { + if (fromId != null) { result = 31 * result + fromId.hashCode() } def toId = toEntity?.ident() - if(toId != null) { + if (toId != null) { result = 31 * result + toId.hashCode() } return result diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/mapping/MappingBuilder.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/mapping/MappingBuilder.groovy index f8d5a09245c..03ca2ddc1f8 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/mapping/MappingBuilder.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/mapping/MappingBuilder.groovy @@ -20,6 +20,7 @@ package grails.neo4j.mapping import groovy.transform.CompileStatic + import org.grails.datastore.gorm.neo4j.mapping.config.Attribute import org.grails.datastore.gorm.neo4j.mapping.config.NodeConfig import org.grails.datastore.gorm.neo4j.mapping.config.RelationshipConfig @@ -54,9 +55,9 @@ class MappingBuilder { new ClosureRelMappingDefinition(mappingDefinition) } - @CompileStatic private static class ClosureRelMappingDefinition implements MappingDefinition { + final Closure definition private RelationshipConfig mapping @@ -71,7 +72,7 @@ class MappingBuilder { @Override RelationshipConfig build() { - if(mapping == null) { + if (mapping == null) { RelationshipConfig nc = new RelationshipConfig() mapping = RelationshipConfig.configureExisting(nc, definition) } @@ -79,8 +80,10 @@ class MappingBuilder { } } + @CompileStatic private static class ClosureNodeMappingDefinition implements MappingDefinition { + final Closure definition private NodeConfig mapping @@ -95,7 +98,7 @@ class MappingBuilder { @Override NodeConfig build() { - if(mapping == null) { + if (mapping == null) { NodeConfig nc = new NodeConfig() mapping = NodeConfig.configureExisting(nc, definition) } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/services/Cypher.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/services/Cypher.groovy index 29bc77fa381..f15793603d9 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/services/Cypher.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/grails/neo4j/services/Cypher.groovy @@ -33,5 +33,6 @@ import java.lang.annotation.Target @Retention(RetentionPolicy.SOURCE) @Target(ElementType.METHOD) @interface Cypher { + String value() -} \ No newline at end of file +} diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/CypherBuilder.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/CypherBuilder.java index 475b938127d..f1e89ad9f66 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/CypherBuilder.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/CypherBuilder.java @@ -18,7 +18,11 @@ */ package org.grails.datastore.gorm.neo4j; -import java.util.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; /** * A builder for Cypher queries @@ -58,17 +62,16 @@ public class CypherBuilder { private static final String CYPHER_RELATIONSHIP_MATCH = "MATCH %s WHERE "; private static final String CYPHER_RELATIONSHIP = "(from%s)%s(to%s)"; - - private String forLabels; - private List matches = new ArrayList<>(); - private List relationshipMatches = new ArrayList<>(); - private List optionalMatches = new ArrayList(); + private final String forLabels; + private final List matches = new ArrayList<>(); + private final List relationshipMatches = new ArrayList<>(); + private final List optionalMatches = new ArrayList(); private String conditions; private String orderAndLimits; - private List returnColumns = new ArrayList(); - private List deleteColumns = new ArrayList(); + private final List returnColumns = new ArrayList(); + private final List deleteColumns = new ArrayList(); private Map sets = null; - private Map params = new LinkedHashMap(); + private final Map params = new LinkedHashMap(); private int setIndex; private String startNode = NODE_VAR; private String defaultReturnStatement = DEFAULT_RETURN_STATEMENT; @@ -77,20 +80,51 @@ public CypherBuilder(String forLabels) { this.forLabels = forLabels; } + /** + * Produces "MATCH (from%s)%s(to%s) WHERE " + * @param fromLabels The from node labels + * @param toLabels The to node labels + * @param relationship The relationship match + * @return The MATCH + */ + public static String buildRelationshipMatch(String fromLabels, String relationship, String toLabels) { + return String.format(CYPHER_RELATIONSHIP_MATCH, buildRelationship(fromLabels, relationship, toLabels)); + } + + /** + * Produces "MATCH %s WHERE " + * @param relationship The relationship match + * @return The MATCH + */ + public static String buildRelationshipMatch(String relationship) { + return String.format(CYPHER_RELATIONSHIP_MATCH, relationship); + } + + /** + * Produces "(from%s)%s(to%s)" + * @param fromLabels The from node labels + * @param toLabels The to node labels + * @param relationship The relationship match + * @return The MATCH + */ + public static String buildRelationship(String fromLabels, String relationship, String toLabels) { + return String.format(CypherBuilder.CYPHER_RELATIONSHIP, fromLabels, relationship, toLabels); + } + /** * Sets the node name to start matching from (defaults to 'n') * * @param startNode The start node */ public void setStartNode(String startNode) { - if(startNode != null) { + if (startNode != null) { this.startNode = startNode; this.defaultReturnStatement = RETURN + startNode + " as data\n"; } } public void addMatch(String match) { - if(!matches.contains(match)) { + if (!matches.contains(match)) { // Add empty match if the target entity is already defined final String lastNode = match.substring(match.lastIndexOf('(')); final boolean alreadyDefined = matches.stream().anyMatch(m -> m.endsWith(lastNode)); @@ -99,19 +133,19 @@ public void addMatch(String match) { } public void addRelationshipMatch(String match) { - if(!relationshipMatches.contains(match)) { + if (!relationshipMatches.contains(match)) { relationshipMatches.add(match); } } public void replaceFirstRelationshipMatch(String match) { - if(relationshipMatches.isEmpty()) { + if (relationshipMatches.isEmpty()) { relationshipMatches.add(match); - } - else { - relationshipMatches.set(0,match); + } else { + relationshipMatches.set(0, match); } } + /** * Optional matches are added to do joins for relationships * @@ -179,7 +213,7 @@ public void addDeleteColumn(String deleteColumn) { * @param sets The property to be set */ public void addPropertySet(Map sets) { - if(sets != null) { + if (sets != null) { final int index = addParam(sets); this.setIndex = index; this.sets = sets; @@ -190,7 +224,7 @@ public String build() { StringBuilder cypher = new StringBuilder(); cypher.append(START_MATCH).append(startNode).append(forLabels).append(")"); - for(String r : relationshipMatches) { + for (String r : relationshipMatches) { cypher.append(r); } @@ -200,21 +234,20 @@ public String build() { cypher.append(COMMAND_SEPARATOR).append(m); } - - if ((conditions!=null) && (!conditions.isEmpty())) { + if ((conditions != null) && (!conditions.isEmpty())) { cypher.append(WHERE).append(conditions); } - if(!optionalMatches.isEmpty()) { + if (!optionalMatches.isEmpty()) { for (String m : optionalMatches) { cypher.append(NEW_LINE) - .append(OPTIONAL_MATCH) - .append(m); + .append(OPTIONAL_MATCH) + .append(m); } } - if(!deleteColumns.isEmpty()) { + if (!deleteColumns.isEmpty()) { cypher.append(DELETE); Iterator iter = deleteColumns.iterator(); // same as Collection.join(String separator) if (iter.hasNext()) { @@ -226,13 +259,13 @@ public String build() { return cypher.toString(); } - if(sets != null) { + if (sets != null) { cypher.append("\nSET n += {").append(setIndex).append("}\n"); } if (returnColumns.isEmpty()) { cypher.append(defaultReturnStatement); - if (orderAndLimits!=null) { + if (orderAndLimits != null) { cypher.append(orderAndLimits).append(NEW_LINE); } } else { @@ -244,7 +277,7 @@ public String build() { cypher.append(COMMAND_SEPARATOR).append(iter.next()); } } - if (orderAndLimits!=null) { + if (orderAndLimits != null) { cypher.append(SPACE); cypher.append(orderAndLimits); } @@ -252,35 +285,4 @@ public String build() { return cypher.toString(); } - - /** - * Produces "MATCH (from%s)%s(to%s) WHERE " - * @param fromLabels The from node labels - * @param toLabels The to node labels - * @param relationship The relationship match - * @return The MATCH - */ - public static String buildRelationshipMatch(String fromLabels, String relationship, String toLabels) { - return String.format( CYPHER_RELATIONSHIP_MATCH, buildRelationship(fromLabels, relationship, toLabels)); - } - - /** - * Produces "MATCH %s WHERE " - * @param relationship The relationship match - * @return The MATCH - */ - public static String buildRelationshipMatch(String relationship) { - return String.format( CYPHER_RELATIONSHIP_MATCH, relationship); - } - - /** - * Produces "(from%s)%s(to%s)" - * @param fromLabels The from node labels - * @param toLabels The to node labels - * @param relationship The relationship match - * @return The MATCH - */ - public static String buildRelationship(String fromLabels, String relationship, String toLabels) { - return String.format(CypherBuilder.CYPHER_RELATIONSHIP, fromLabels, relationship, toLabels); - } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/GraphClassMapping.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/GraphClassMapping.groovy index 9907495415c..da8b7b477c3 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/GraphClassMapping.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/GraphClassMapping.groovy @@ -19,6 +19,7 @@ package org.grails.datastore.gorm.neo4j import groovy.transform.CompileStatic + import org.grails.datastore.gorm.neo4j.mapping.config.NodeConfig import org.grails.datastore.mapping.model.AbstractClassMapping import org.grails.datastore.mapping.model.MappingContext @@ -41,6 +42,6 @@ class GraphClassMapping extends AbstractClassMapping { NodeConfig getMappedForm() { // Explicit field access (.@) - entity.mappedForm would resolve to the getter, // which recurses back here via PersistentEntity#getMappedForm()'s default method. - ((GraphPersistentEntity)entity).@mappedForm + ((GraphPersistentEntity) entity).@mappedForm } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/GraphGormMappingFactory.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/GraphGormMappingFactory.groovy index 7a8d46ca503..a2671604146 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/GraphGormMappingFactory.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/GraphGormMappingFactory.groovy @@ -53,7 +53,7 @@ class GraphGormMappingFactory extends AbstractGormMappingFactory { @Override Entity createMappedForm(PersistentEntity entity) { - if(entity instanceof RelationshipPersistentEntity) { + if (entity instanceof RelationshipPersistentEntity) { Class previousType = currentNodeConfigType try { currentNodeConfigType = RelationshipConfig @@ -61,8 +61,7 @@ class GraphGormMappingFactory extends AbstractGormMappingFactory { } finally { currentNodeConfigType = previousType } - } - else { + } else { return super.createMappedForm(entity) } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/GraphPersistentEntity.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/GraphPersistentEntity.groovy index 3a17454c8ef..20780318782 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/GraphPersistentEntity.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/GraphPersistentEntity.groovy @@ -19,9 +19,15 @@ package org.grails.datastore.gorm.neo4j -import grails.neo4j.Relationship +import java.beans.Introspector + import groovy.transform.CompileStatic -import org.codehaus.groovy.runtime.HandleMetaClass + +import org.neo4j.driver.types.Entity + +import org.springframework.util.ClassUtils + +import grails.neo4j.Relationship import org.grails.datastore.gorm.neo4j.mapping.config.NodeConfig import org.grails.datastore.mapping.config.Property import org.grails.datastore.mapping.model.AbstractPersistentEntity @@ -34,11 +40,9 @@ import org.grails.datastore.mapping.model.PersistentProperty import org.grails.datastore.mapping.model.config.GormMappingConfigurationStrategy import org.grails.datastore.mapping.model.config.GormProperties import org.grails.datastore.mapping.model.types.Association -import org.neo4j.driver.types.Entity -import org.springframework.util.ClassUtils -import java.beans.Introspector -import static org.grails.datastore.gorm.neo4j.RelationshipPersistentEntity.* +import static org.grails.datastore.gorm.neo4j.RelationshipPersistentEntity.FROM +import static org.grails.datastore.gorm.neo4j.RelationshipPersistentEntity.TO /** @@ -78,10 +82,9 @@ class GraphPersistentEntity extends AbstractPersistentEntity { GraphPersistentEntity(Class javaClass, MappingContext context, boolean external) { super(javaClass, context) - if(isExternal()) { + if (isExternal()) { this.mappedForm = null - } - else { + } else { this.mappedForm = (NodeConfig) context.getMappingFactory().createMappedForm(this) } this.relationshipEntity = this instanceof RelationshipPersistentEntity @@ -96,20 +99,20 @@ class GraphPersistentEntity extends AbstractPersistentEntity { @Override void initialize() { - if(!isInitialized()) { + if (!isInitialized()) { super.initialize() - if(!isExternal()) { + if (!isExternal()) { PersistentProperty identity = getIdentity() - if(identity != null) { + if (identity != null) { String generatorType = ((Property) identity.getMapping().getMappedForm()).getGenerator() this.idGenerator = createIdGenerator(generatorType) - if(identity.name != GormProperties.IDENTITY) { + if (identity.name != GormProperties.IDENTITY) { Class clazz = getJavaClass() MetaClass metaClass = clazz.getMetaClass() MetaProperty idProp = metaClass.getMetaProperty(GormProperties.IDENTITY) - if(idProp != null && Long.class.isAssignableFrom(idProp.getType())) { + if (idProp != null && Long.class.isAssignableFrom(idProp.getType())) { MappingFactory mappingFactory = mappingContext.mappingFactory nodeId = mappingFactory.createSimple(this, context, mappingFactory.createPropertyDescriptor(javaClass, idProp)) propertiesByName.put(GormProperties.IDENTITY, nodeId) @@ -129,7 +132,7 @@ class GraphPersistentEntity extends AbstractPersistentEntity { IdGenerator.Type type = generatorType == null ? IdGenerator.Type.NATIVE : IdGenerator.Type.valueOf(generatorType.toUpperCase()) idGeneratorType = type - switch(type) { + switch (type) { case IdGenerator.Type.NATIVE: // for the native generator use null to indicate that generation requires an insert nativeId = true @@ -139,10 +142,10 @@ class GraphPersistentEntity extends AbstractPersistentEntity { ((Property) getIdentity().getMapping().getMappedForm()).setUnique(true) return null case IdGenerator.Type.SNOWFLAKE: - return ((Neo4jMappingContext)mappingContext).getSnowflakeIdGenerator() + return ((Neo4jMappingContext) mappingContext).getSnowflakeIdGenerator() default: def generator = ((Neo4jMappingContext) mappingContext).getIdGenerator() - if(generator == null) { + if (generator == null) { nativeId = true } return generator @@ -150,16 +153,15 @@ class GraphPersistentEntity extends AbstractPersistentEntity { } catch (IllegalArgumentException e) { try { def generatorClass = ClassUtils.forName(generatorType) - if(IdGenerator.isAssignableFrom(generatorClass)) { + if (IdGenerator.isAssignableFrom(generatorClass)) { idGeneratorType = IdGenerator.Type.CUSTOM - return (IdGenerator)generatorClass.newInstance() - } - else { + return (IdGenerator) generatorClass.newInstance() + } else { throw new DatastoreConfigurationException("Entity $javaClass defines an invalid id generator [$generatorClass]. The class must implement the IdGenerator interface") } } catch (Throwable e2) { def types = IdGenerator.Type.values().collect() { Enum en -> "'${en.name()}'" }.join(',') - throw new DatastoreConfigurationException("Entity $javaClass defines an invalid id generator [$generatorType]. Should be one of ${types} or a class that implements the IdGenerator interface",e2 ) + throw new DatastoreConfigurationException("Entity $javaClass defines an invalid id generator [$generatorType]. Should be one of ${types} or a class that implements the IdGenerator interface", e2) } } } @@ -201,7 +203,7 @@ class GraphPersistentEntity extends AbstractPersistentEntity { * @return The batch create statement */ String getBatchCreateStatement() { - if(this.batchCreateStatement == null) { + if (this.batchCreateStatement == null) { this.batchCreateStatement = formatBatchCreate("\$${batchId}") } return batchCreateStatement @@ -269,13 +271,11 @@ class GraphPersistentEntity extends AbstractPersistentEntity { * @return The formatted id */ String formatProperty(String variable, String property) { - if(property == identity.name) { + if (property == identity.name) { return formatId(variable) - } - else if(nodeId != null && property == nodeId.name) { + } else if (nodeId != null && property == nodeId.name) { return "ID($variable)" - } - else { + } else { return "${variable}.${property}" } } @@ -316,30 +316,29 @@ class GraphPersistentEntity extends AbstractPersistentEntity { * @return The match for the ID */ String formatMatchAndUpdate(String variable, Map props) { - StringBuilder builder = new StringBuilder( formatMatchId(variable) ) + StringBuilder builder = new StringBuilder(formatMatchId(variable)) Class clazz = Long - if(isVersioned() && hasProperty(GormProperties.VERSION, clazz)) { + if (isVersioned() && hasProperty(GormProperties.VERSION, clazz)) { builder.append(" AND ${variable}.version=\$version") } builder.append(" SET ").append(variable).append(" +=\$props") Set keysToRemove = [] - for(key in props.keySet()) { + for (key in props.keySet()) { Object v = props.get(key) - if(v == null) { + if (v == null) { builder.append(", ${variable}.").append(key).append(" = NULL") - } - else if(v instanceof Collection && ((Collection)v).isEmpty()) { + } else if (v instanceof Collection && ((Collection) v).isEmpty()) { keysToRemove.add(key) } } - if(!keysToRemove.isEmpty()) { + if (!keysToRemove.isEmpty()) { builder.append(" REMOVE ") def i = keysToRemove.iterator() - while(i.hasNext()) { + while (i.hasNext()) { String key = i.next() builder.append(variable).append(".").append(key) props.remove(key) - if(i.hasNext()) { + if (i.hasNext()) { builder.append(", ") } } @@ -397,8 +396,8 @@ ${formatAssociationMerge(association, parentVariable, variableId)})""" * @return The match */ String formatAssociationMatch(Association association, String var = CypherBuilder.REL_VAR, String start = FROM, String end = TO) { - GraphPersistentEntity parent = (GraphPersistentEntity)association.owner - GraphPersistentEntity child = (GraphPersistentEntity)association.associatedEntity + GraphPersistentEntity parent = (GraphPersistentEntity) association.owner + GraphPersistentEntity child = (GraphPersistentEntity) association.associatedEntity String associationMatch = calculateAssociationMatch(parent, child, association, var) return "MATCH ${parent.formatNode(start)}${associationMatch}${child.formatNode(end)}" @@ -427,17 +426,17 @@ ${formatAssociationMerge(association, parentVariable, variableId)})""" * @return The match */ String formatAssociationPatternFromExisting(Association association, String var = CypherBuilder.REL_VAR, String start = FROM, String end = TO) { - GraphPersistentEntity parent = (GraphPersistentEntity)association.owner - GraphPersistentEntity child = (GraphPersistentEntity)association.associatedEntity - String associationMatch = calculateAssociationMatch(parent,child, association, var) - if(child.isRelationshipEntity()) { - RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity)child + GraphPersistentEntity parent = (GraphPersistentEntity) association.owner + GraphPersistentEntity child = (GraphPersistentEntity) association.associatedEntity + String associationMatch = calculateAssociationMatch(parent, child, association, var) + if (child.isRelationshipEntity()) { + RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity) child child = (GraphPersistentEntity) (relEntity.from.associatedEntity == parent ? relEntity.to.associatedEntity : relEntity.from.associatedEntity) } return "(${start})${associationMatch}${child.formatNode(end)}" } - protected String calculateAssociationMatch(GraphPersistentEntity parent, GraphPersistentEntity child,Association association, String var) { + protected String calculateAssociationMatch(GraphPersistentEntity parent, GraphPersistentEntity child, Association association, String var) { String associationMatch if (parent.isRelationshipEntity()) { if (association.name == FROM || association.name == TO) { @@ -447,12 +446,10 @@ ${formatAssociationMerge(association, parentVariable, variableId)})""" throw new IllegalStateException("Relationship entities cannot have associations") } - } - else if(child.isRelationshipEntity()) { + } else if (child.isRelationshipEntity()) { RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity) child associationMatch = RelationshipUtils.matchForRelationshipEntity(association, relEntity, var) - } - else { + } else { associationMatch = RelationshipUtils.matchForAssociation(association, var) } return associationMatch @@ -467,31 +464,28 @@ ${formatAssociationMerge(association, parentVariable, variableId)})""" */ String formatAssociationDelete(Association association, Object entity = null) { - GraphPersistentEntity parent = (GraphPersistentEntity)association.owner - GraphPersistentEntity child = (GraphPersistentEntity)association.associatedEntity + GraphPersistentEntity parent = (GraphPersistentEntity) association.owner + GraphPersistentEntity child = (GraphPersistentEntity) association.associatedEntity String associationMatch - if(parent.isRelationshipEntity() && entity instanceof Relationship) { - if(association.name == FROM) { - RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity)parent + if (parent.isRelationshipEntity() && entity instanceof Relationship) { + if (association.name == FROM) { + RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity) parent child = relEntity.getToEntity() parent = relEntity.getFromEntity() associationMatch = RelationshipUtils.toMatch(association, (Relationship) entity) - } - else { + } else { return null } - } - else { + } else { associationMatch = RelationshipUtils.matchForAssociation(association, CypherBuilder.REL_VAR) } - if(RelationshipUtils.useReversedMappingFor(association)) { + if (RelationshipUtils.useReversedMappingFor(association)) { return """MATCH ${parent.formatNode(FROM)}${associationMatch}${child.formatNode(TO)} WHERE ${parent.formatId(FROM)} = {${GormProperties.IDENTITY}} DELETE r""" - } - else { + } else { return """MATCH ${parent.formatNode(FROM)}${associationMatch}${child.formatNode(TO)} WHERE ${parent.formatId(FROM)} = {${CypherBuilder.START}} AND ${parent.formatId(TO)} IN {${CypherBuilder.END}} DELETE r""" @@ -527,17 +521,16 @@ DELETE r""" * @return the abels */ Collection getLabels(Object domainInstance) { - if(hasDynamicLabels) { + if (hasDynamicLabels) { Collection labels = [] - for(obj in labelObjects) { + for (obj in labelObjects) { String label = getLabelFor(obj, domainInstance) - if(label) { + if (label) { labels.add(label) } } return labels - } - else { + } else { return staticLabels } } @@ -554,10 +547,9 @@ DELETE r""" * @return */ String getLabelsAsString(Object domainInstance) { - if(hasDynamicLabels) { + if (hasDynamicLabels) { return ":${getLabels(domainInstance).join(LABEL_SEPARATOR)}" - } - else { + } else { return getLabelsAsString() } } @@ -604,7 +596,7 @@ DELETE r""" } protected Collection establishLabelObjects() { - Object labels = mappedForm.getLabels(); + Object labels = mappedForm.getLabels() List objs = labels instanceof Object[] ? labels as List : [labels] @@ -620,9 +612,9 @@ DELETE r""" case null: return discriminator case CharSequence: - return ((CharSequence)obj).toString() + return ((CharSequence) obj).toString() case Closure: - Closure closure = (Closure)obj + Closure closure = (Closure) obj Object result = null switch (closure.maximumNumberOfParameters) { case 1: @@ -640,7 +632,7 @@ DELETE r""" } } - private void appendRecursive(StringBuilder sb, domainInstance){ + private void appendRecursive(StringBuilder sb, domainInstance) { sb.append(getLabelsAsString(domainInstance)) GraphPersistentEntity parentEntity = (GraphPersistentEntity) getParentEntity() diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/IdGenerator.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/IdGenerator.java index 1041ed914d3..55c73ec2002 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/IdGenerator.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/IdGenerator.java @@ -27,6 +27,12 @@ * @author Graeme Rocher */ public interface IdGenerator { + + /** + * @return Generate and return the next identifier + */ + Serializable nextId(); + /** * Default id generator types */ @@ -49,9 +55,4 @@ enum Type { CUSTOM } - /** - * @return Generate and return the next identifier - */ - Serializable nextId(); - } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jDatastore.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jDatastore.java index fe27df98b86..670af2e08da 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jDatastore.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jDatastore.java @@ -18,8 +18,31 @@ */ package org.grails.datastore.gorm.neo4j; -import grails.neo4j.Relationship; +import java.io.Closeable; +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + import groovy.lang.Closure; + +import jakarta.annotation.PreDestroy; +import jakarta.persistence.FlushModeType; + +import org.neo4j.driver.Driver; +import org.neo4j.driver.Transaction; +import org.neo4j.driver.exceptions.Neo4jException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceAware; +import org.springframework.context.support.StaticMessageSource; +import org.springframework.core.env.PropertyResolver; + +import grails.neo4j.Relationship; import org.grails.datastore.gorm.GormEnhancer; import org.grails.datastore.gorm.GormInstanceApi; import org.grails.datastore.gorm.GormStaticApi; @@ -44,7 +67,14 @@ import org.grails.datastore.mapping.core.Datastore; import org.grails.datastore.mapping.core.DatastoreUtils; import org.grails.datastore.mapping.core.StatelessDatastore; -import org.grails.datastore.mapping.core.connections.*; +import org.grails.datastore.mapping.core.connections.ConnectionSource; +import org.grails.datastore.mapping.core.connections.ConnectionSources; +import org.grails.datastore.mapping.core.connections.ConnectionSourcesInitializer; +import org.grails.datastore.mapping.core.connections.ConnectionSourcesSupport; +import org.grails.datastore.mapping.core.connections.DefaultConnectionSource; +import org.grails.datastore.mapping.core.connections.InMemoryConnectionSources; +import org.grails.datastore.mapping.core.connections.MultipleConnectionSourceCapableDatastore; +import org.grails.datastore.mapping.core.connections.SingletonConnectionSources; import org.grails.datastore.mapping.core.exceptions.ConfigurationException; import org.grails.datastore.mapping.graph.GraphDatastore; import org.grails.datastore.mapping.model.DatastoreConfigurationException; @@ -54,29 +84,9 @@ import org.grails.datastore.mapping.model.types.Simple; import org.grails.datastore.mapping.multitenancy.MultiTenancySettings; import org.grails.datastore.mapping.multitenancy.MultiTenantCapableDatastore; -import org.grails.datastore.mapping.core.connections.MultipleConnectionSourceCapableDatastore; import org.grails.datastore.mapping.multitenancy.TenantResolver; import org.grails.datastore.mapping.transactions.TransactionCapableDatastore; import org.grails.datastore.mapping.validation.ValidatorRegistry; -import org.neo4j.driver.Driver; -import org.neo4j.driver.Transaction; -import org.neo4j.driver.exceptions.Neo4jException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.MessageSource; -import org.springframework.context.MessageSourceAware; -import org.springframework.context.support.StaticMessageSource; -import org.springframework.core.env.PropertyResolver; - -import jakarta.annotation.PreDestroy; -import jakarta.persistence.FlushModeType; -import java.io.Closeable; -import java.io.IOException; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; import static org.grails.datastore.gorm.neo4j.config.Settings.DATABASE_TYPE_EMBEDDED; import static org.grails.datastore.gorm.neo4j.config.Settings.SETTING_NEO4J_EMBEDDED_EPHEMERAL; @@ -92,9 +102,7 @@ */ public class Neo4jDatastore extends AbstractDatastore implements Closeable, StatelessDatastore, GraphDatastore, Settings, MultipleConnectionSourceCapableDatastore, MultiTenantCapableDatastore, MessageSourceAware, TransactionCapableDatastore { - private static Logger log = LoggerFactory.getLogger(Neo4jDatastore.class); - - protected boolean skipIndexSetup = false; + private static final Logger log = LoggerFactory.getLogger(Neo4jDatastore.class); protected final Driver boltDriver; protected final FlushModeType defaultFlushMode; protected final ConfigurableApplicationEventPublisher eventPublisher; @@ -105,6 +113,7 @@ public class Neo4jDatastore extends AbstractDatastore implements Closeable, Stat protected final TenantResolver tenantResolver; protected final MultiTenancySettings.MultiTenancyMode multiTenancyMode; protected final AutoTimestampEventListener autoTimestampEventListener; + protected boolean skipIndexSetup = false; /** * Configures a new {@link Neo4jDatastore} for the given arguments @@ -127,22 +136,21 @@ public Neo4jDatastore(ConnectionSources c this.tenantResolver = multiTenancySettings.getTenantResolver(); this.autoTimestampEventListener = new AutoTimestampEventListener(this); - if(!skipIndexSetup) { + if (!skipIndexSetup) { setupIndexing(); } transactionManager = new Neo4jDatastoreTransactionManager(this); - if(!(connectionSources instanceof SingletonConnectionSources)) { + if (!(connectionSources instanceof SingletonConnectionSources)) { Iterable> allConnectionSources = connectionSources.getAllConnectionSources(); for (ConnectionSource connectionSource : allConnectionSources) { SingletonConnectionSources singletonConnectionSources = new SingletonConnectionSources(connectionSource, connectionSources.getBaseConfiguration()); Neo4jDatastore childDatastore; - if(ConnectionSource.DEFAULT.equals(connectionSource.getName())) { + if (ConnectionSource.DEFAULT.equals(connectionSource.getName())) { childDatastore = this; - } - else { + } else { childDatastore = new Neo4jDatastore(singletonConnectionSources, mappingContext, eventPublisher) { @Override protected GormEnhancer initialize(Neo4jConnectionSourceSettings settings) { @@ -163,8 +171,8 @@ protected GormEnhancer initialize(Neo4jConnectionSourceSettings settings) { * @param eventPublisher The Spring ApplicationContext * @param classes The persistent classes */ - public Neo4jDatastore(ConnectionSources connectionSources, ConfigurableApplicationEventPublisher eventPublisher, Class...classes) { - this(connectionSources, createMappingContext(connectionSources,classes), eventPublisher); + public Neo4jDatastore(ConnectionSources connectionSources, ConfigurableApplicationEventPublisher eventPublisher, Class... classes) { + this(connectionSources, createMappingContext(connectionSources, classes), eventPublisher); } /** @@ -172,8 +180,8 @@ public Neo4jDatastore(ConnectionSources c * * @param classes The persistent classes */ - public Neo4jDatastore(ConnectionSources connectionSources, Class...classes) { - this(connectionSources, createMappingContext(connectionSources,classes), new DefaultApplicationEventPublisher()); + public Neo4jDatastore(ConnectionSources connectionSources, Class... classes) { + this(connectionSources, createMappingContext(connectionSources, classes), new DefaultApplicationEventPublisher()); } /** @@ -184,11 +192,10 @@ public Neo4jDatastore(ConnectionSources c * @param eventPublisher The Spring ApplicationContext * @param classes The persistent classes */ - public Neo4jDatastore(Driver boltDriver, PropertyResolver configuration, ConfigurableApplicationEventPublisher eventPublisher, Class...classes) { + public Neo4jDatastore(Driver boltDriver, PropertyResolver configuration, ConfigurableApplicationEventPublisher eventPublisher, Class... classes) { this(createDefaultConnectionSources(boltDriver, configuration), eventPublisher, classes); } - /** * Configures a new {@link Neo4jDatastore} for the given arguments * @@ -196,7 +203,7 @@ public Neo4jDatastore(Driver boltDriver, PropertyResolver configuration, Configu * @param eventPublisher The Spring ApplicationContext * @param classes The persistent classes */ - public Neo4jDatastore(Driver boltDriver, ConfigurableApplicationEventPublisher eventPublisher, Class...classes) { + public Neo4jDatastore(Driver boltDriver, ConfigurableApplicationEventPublisher eventPublisher, Class... classes) { this(createDefaultConnectionSources(boltDriver, DatastoreUtils.createPropertyResolver(null)), eventPublisher, classes); } @@ -207,16 +214,17 @@ public Neo4jDatastore(Driver boltDriver, ConfigurableApplicationEventPublisher e * @param configuration The configuration for the datastore * @param classes The persistent classes */ - public Neo4jDatastore(Driver boltDriver, PropertyResolver configuration, Class...classes) { + public Neo4jDatastore(Driver boltDriver, PropertyResolver configuration, Class... classes) { this(boltDriver, configuration, new DefaultApplicationEventPublisher(), classes); } + /** * Configures a new {@link Neo4jDatastore} for the given arguments * * @param boltDriver The driver * @param classes The persistent classes */ - public Neo4jDatastore(Driver boltDriver, Class...classes) { + public Neo4jDatastore(Driver boltDriver, Class... classes) { this(boltDriver, DatastoreUtils.createPropertyResolver(null), new DefaultApplicationEventPublisher(), classes); } @@ -228,7 +236,7 @@ public Neo4jDatastore(Driver boltDriver, Class...classes) { * @param eventPublisher The Spring ApplicationContext * @param classes The persistent classes */ - public Neo4jDatastore(PropertyResolver configuration, Neo4jConnectionSourceFactory connectionSourceFactory, ConfigurableApplicationEventPublisher eventPublisher, Class...classes) { + public Neo4jDatastore(PropertyResolver configuration, Neo4jConnectionSourceFactory connectionSourceFactory, ConfigurableApplicationEventPublisher eventPublisher, Class... classes) { this(ConnectionSourcesInitializer.create(connectionSourceFactory, configuration), eventPublisher, classes); } @@ -239,8 +247,8 @@ public Neo4jDatastore(PropertyResolver configuration, Neo4jConnectionSourceFacto * @param eventPublisher The Spring ApplicationContext * @param packagesToScan The packages to scan */ - public Neo4jDatastore(PropertyResolver configuration, Neo4jConnectionSourceFactory connectionSourceFactory, ConfigurableApplicationEventPublisher eventPublisher, Package...packagesToScan) { - this(configuration,connectionSourceFactory, eventPublisher, new ClasspathEntityScanner().scan(packagesToScan)); + public Neo4jDatastore(PropertyResolver configuration, Neo4jConnectionSourceFactory connectionSourceFactory, ConfigurableApplicationEventPublisher eventPublisher, Package... packagesToScan) { + this(configuration, connectionSourceFactory, eventPublisher, new ClasspathEntityScanner().scan(packagesToScan)); } /** @@ -250,7 +258,7 @@ public Neo4jDatastore(PropertyResolver configuration, Neo4jConnectionSourceFacto * @param eventPublisher The Spring ApplicationContext * @param classes The persistent classes */ - public Neo4jDatastore(PropertyResolver configuration, ConfigurableApplicationEventPublisher eventPublisher, Class...classes) { + public Neo4jDatastore(PropertyResolver configuration, ConfigurableApplicationEventPublisher eventPublisher, Class... classes) { this(ConnectionSourcesInitializer.create(new Neo4jConnectionSourceFactory(), configuration), eventPublisher, classes); } @@ -261,18 +269,17 @@ public Neo4jDatastore(PropertyResolver configuration, ConfigurableApplicationEve * @param eventPublisher The Spring ApplicationContext * @param packagesToScan The packages to scan */ - public Neo4jDatastore(PropertyResolver configuration, ConfigurableApplicationEventPublisher eventPublisher, Package...packagesToScan) { + public Neo4jDatastore(PropertyResolver configuration, ConfigurableApplicationEventPublisher eventPublisher, Package... packagesToScan) { this(configuration, eventPublisher, new ClasspathEntityScanner().scan(packagesToScan)); } - /** * Configures a new {@link Neo4jDatastore} for the given arguments * * @param configuration The configuration for the datastore * @param classes The persistent classes */ - public Neo4jDatastore(PropertyResolver configuration, Class...classes) { + public Neo4jDatastore(PropertyResolver configuration, Class... classes) { this(configuration, new DefaultApplicationEventPublisher(), classes); } @@ -282,7 +289,7 @@ public Neo4jDatastore(PropertyResolver configuration, Class...classes) { * * @param classes The persistent classes */ - public Neo4jDatastore(Class...classes) { + public Neo4jDatastore(Class... classes) { this(resolveEmbeddedConfiguration(), classes); } @@ -292,8 +299,8 @@ public Neo4jDatastore(Class...classes) { * @param configuration The configuration * @param classes The persistent classes */ - public Neo4jDatastore(Map configuration, ConfigurableApplicationEventPublisher eventPublisher,Class...classes) { - this(mapToPropertyResolver(configuration),eventPublisher, classes); + public Neo4jDatastore(Map configuration, ConfigurableApplicationEventPublisher eventPublisher, Class... classes) { + this(mapToPropertyResolver(configuration), eventPublisher, classes); } /** @@ -302,7 +309,7 @@ public Neo4jDatastore(Map configuration, ConfigurableApplication * @param configuration The configuration * @param classes The persistent classes */ - public Neo4jDatastore(Map configuration, Class...classes) { + public Neo4jDatastore(Map configuration, Class... classes) { this(configuration, new DefaultApplicationEventPublisher(), classes); } @@ -311,7 +318,7 @@ public Neo4jDatastore(Map configuration, Class...classes) { * * @param packagesToScan The packages to scan */ - public Neo4jDatastore(Package...packagesToScan) { + public Neo4jDatastore(Package... packagesToScan) { this(new ClasspathEntityScanner().scan(packagesToScan)); } @@ -324,14 +331,13 @@ public Neo4jDatastore(Package packageToScan) { this(new ClasspathEntityScanner().scan(packageToScan)); } - /** * Construct a Mongo datastore scanning the given packages * * @param configuration The configuration * @param packagesToScan The packages to scan */ - public Neo4jDatastore(PropertyResolver configuration, Package...packagesToScan) { + public Neo4jDatastore(PropertyResolver configuration, Package... packagesToScan) { this(configuration, new ClasspathEntityScanner().scan(packagesToScan)); } @@ -341,34 +347,10 @@ public Neo4jDatastore(PropertyResolver configuration, Package...packagesToScan) * @param configuration The configuration * @param packagesToScan The packages to scan */ - public Neo4jDatastore(Map configuration, Package...packagesToScan) { + public Neo4jDatastore(Map configuration, Package... packagesToScan) { this(DatastoreUtils.createPropertyResolver(configuration), packagesToScan); } - /** - * @return The transaction manager - */ - @Override - public Neo4jDatastoreTransactionManager getTransactionManager() { - return transactionManager; - } - - - @Override - public ConfigurableApplicationEventPublisher getApplicationEventPublisher() { - return this.eventPublisher; - } - - @Override - public Datastore getDatastoreForConnection(String connectionName) { - return datastoresByConnectionSource.get(connectionName); - } - - @Override - public Neo4jMappingContext getMappingContext() { - return (Neo4jMappingContext)super.getMappingContext(); - } - /** * Creates the connection sources for an existing {@link Driver} * @@ -390,19 +372,18 @@ protected static Neo4jMappingContext createMappingContext(ConnectionSources config = new LinkedHashMap<>(); config.put(SETTING_NEO4J_TYPE, DATABASE_TYPE_EMBEDDED); - config.put(SETTING_NEO4J_EMBEDDED_EPHEMERAL, true ); + config.put(SETTING_NEO4J_EMBEDDED_EPHEMERAL, true); return mapToPropertyResolver(config); - } - else { + } else { return mapToPropertyResolver(null); } } @@ -415,22 +396,44 @@ private static ValidatorRegistry createValidatorRegistry(Neo4jConnectionSourceSe } private static void configureValidatorRegistry(Neo4jMappingContext neo4jMappingContext, MessageSource messageSource, ValidatorRegistry defaultValidatorRegistry) { - if(defaultValidatorRegistry instanceof ConstraintRegistry) { - ((ConstraintRegistry)defaultValidatorRegistry).addConstraintFactory( - new MappingContextAwareConstraintFactory(UniqueConstraint.class, messageSource, neo4jMappingContext) + if (defaultValidatorRegistry instanceof ConstraintRegistry) { + ((ConstraintRegistry) defaultValidatorRegistry).addConstraintFactory( + new MappingContextAwareConstraintFactory(UniqueConstraint.class, messageSource, neo4jMappingContext) ); } } + /** + * @return The transaction manager + */ + @Override + public Neo4jDatastoreTransactionManager getTransactionManager() { + return transactionManager; + } + + @Override + public ConfigurableApplicationEventPublisher getApplicationEventPublisher() { + return this.eventPublisher; + } + + @Override + public Datastore getDatastoreForConnection(String connectionName) { + return datastoresByConnectionSource.get(connectionName); + } + + @Override + public Neo4jMappingContext getMappingContext() { + return (Neo4jMappingContext) super.getMappingContext(); + } + protected void registerEventListeners(ConfigurableApplicationEventPublisher eventPublisher) { eventPublisher.addApplicationListener(new DomainEventListener(this)); eventPublisher.addApplicationListener(autoTimestampEventListener); - if(multiTenancyMode == MultiTenancySettings.MultiTenancyMode.DISCRIMINATOR) { + if (multiTenancyMode == MultiTenancySettings.MultiTenancyMode.DISCRIMINATOR) { eventPublisher.addApplicationListener(new MultiTenantEventListener(this)); } } - protected GormEnhancer initialize(Neo4jConnectionSourceSettings settings) { registerEventListeners(this.eventPublisher); @@ -462,7 +465,7 @@ protected GormValidationApi getValidationApi(Class cls, String qualifi @Override protected GormInstanceApi getInstanceApi(Class cls, String qualifier) { Neo4jDatastore neo4jDatastore = getDatastoreForQualifier(cls, qualifier); - GormInstanceApi instanceApi = new GormInstanceApi<>(cls,neo4jDatastore); + GormInstanceApi instanceApi = new GormInstanceApi<>(cls, neo4jDatastore); instanceApi.setFailOnError(getFailOnError()); instanceApi.setMarkDirty(getMarkDirty()); return instanceApi; @@ -471,16 +474,15 @@ protected GormInstanceApi getInstanceApi(Class cls, String qualifier) private Neo4jDatastore getDatastoreForQualifier(Class cls, String qualifier) { String defaultConnectionSourceName = ConnectionSourcesSupport.getDefaultConnectionSourceName(getMappingContext().getPersistentEntity(cls.getName())); boolean isDefaultQualifier = qualifier.equals(ConnectionSource.DEFAULT); - if(isDefaultQualifier && defaultConnectionSourceName.equals(ConnectionSource.DEFAULT)) { + if (isDefaultQualifier && defaultConnectionSourceName.equals(ConnectionSource.DEFAULT)) { return Neo4jDatastore.this; - } - else { - if(isDefaultQualifier) { + } else { + if (isDefaultQualifier) { qualifier = defaultConnectionSourceName; } ConnectionSource connectionSource = connectionSources.getConnectionSource(qualifier); - if(connectionSource == null) { - throw new ConfigurationException("Invalid connection ["+defaultConnectionSourceName+"] configured for class ["+cls+"]"); + if (connectionSource == null) { + throw new ConfigurationException("Invalid connection [" + defaultConnectionSourceName + "] configured for class [" + cls + "]"); } return Neo4jDatastore.this.datastoresByConnectionSource.get(qualifier); } @@ -499,42 +501,39 @@ protected org.grails.datastore.mapping.core.Session createSession(PropertyResolv return neo4jSession; } - public void setupIndexing() { - if(skipIndexSetup) return; + if (skipIndexSetup) return; List schemaStrings = new ArrayList(); // using set to avoid duplicate index creation - for (PersistentEntity persistentEntity: mappingContext.getPersistentEntities()) { - if(persistentEntity.isExternal() || Relationship.class.isAssignableFrom(persistentEntity.getJavaClass())) continue; + for (PersistentEntity persistentEntity : mappingContext.getPersistentEntities()) { + if (persistentEntity.isExternal() || Relationship.class.isAssignableFrom(persistentEntity.getJavaClass())) + continue; - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("Setting up indexing for entity " + persistentEntity.getName()); } final GraphPersistentEntity graphPersistentEntity = (GraphPersistentEntity) persistentEntity; - for (String label: graphPersistentEntity.getLabels()) { + for (String label : graphPersistentEntity.getLabels()) { StringBuilder sb = new StringBuilder(); IdGenerator.Type idGeneratorType = graphPersistentEntity.getIdGeneratorType(); - if(graphPersistentEntity.getIdGenerator() != null && idGeneratorType.equals(IdGenerator.Type.SNOWFLAKE)) { + if (graphPersistentEntity.getIdGenerator() != null && idGeneratorType.equals(IdGenerator.Type.SNOWFLAKE)) { sb.append("CREATE CONSTRAINT ON (n:").append(label).append(") ASSERT n.").append(CypherBuilder.IDENTIFIER).append(" IS UNIQUE"); schemaStrings.add(sb.toString()); - } - else if(!graphPersistentEntity.isNativeId()) { + } else if (!graphPersistentEntity.isNativeId()) { createUniqueConstraintOnProperty(label, graphPersistentEntity.getIdentity(), schemaStrings); } - for (PersistentProperty persistentProperty : persistentEntity.getPersistentProperties()) { Property mappedForm = persistentProperty.getMapping().getMappedForm(); - if ((persistentProperty instanceof Simple) && (mappedForm != null) ) { + if ((persistentProperty instanceof Simple) && (mappedForm != null)) { - if(mappedForm.isUnique()) { + if (mappedForm.isUnique()) { createUniqueConstraintOnProperty(label, persistentProperty, schemaStrings); - } - else if(mappedForm.isIndex()) { + } else if (mappedForm.isIndex()) { sb = new StringBuilder(); sb.append("CREATE INDEX ON :").append(label).append("(").append(persistentProperty.getName()).append(")"); schemaStrings.add(sb.toString()); - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("setting up indexing for " + label + " property " + persistentProperty.getName()); } } @@ -545,24 +544,23 @@ else if(mappedForm.isIndex()) { org.neo4j.driver.Session boltSession = boltDriver.session(); - final Transaction transaction = boltSession.beginTransaction();; + final Transaction transaction = boltSession.beginTransaction(); try { - for (String cypher: schemaStrings) { - if(log.isDebugEnabled()) { + for (String cypher : schemaStrings) { + if (log.isDebugEnabled()) { log.debug("CREATE INDEX Cypher [{}]", cypher); } transaction.run(cypher); } transaction.commit(); - } catch(Throwable e) { + } catch (Throwable e) { log.error("Error creating Neo4j index: " + e.getMessage(), e); transaction.rollback(); throw new DatastoreConfigurationException("Error creating Neo4j index: " + e.getMessage(), e); - } - finally { + } finally { boltSession.close(); } - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("done setting up indexes"); } } @@ -619,7 +617,7 @@ public TenantResolver getTenantResolver() { @Override public Neo4jDatastore getDatastoreForTenantId(Serializable tenantId) { - if(getMultiTenancyMode() == MultiTenancySettings.MultiTenancyMode.DATABASE) { + if (getMultiTenancyMode() == MultiTenancySettings.MultiTenancyMode.DATABASE) { return this.datastoresByConnectionSource.get(tenantId.toString()); } return this; @@ -632,13 +630,11 @@ public T1 withNewSession(Serializable tenantId, Closure callable) { try { DatastoreUtils.bindNewSession(session); return callable.call(session); - } - finally { + } finally { DatastoreUtils.unbindSession(session); } } - @Override public void setMessageSource(MessageSource messageSource) { if (messageSource != null) { @@ -652,4 +648,4 @@ public void setMessageSource(MessageSource messageSource) { public AutoTimestampEventListener getAutoTimestampEventListener() { return this.autoTimestampEventListener; } -} \ No newline at end of file +} diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jDatastoreTransactionManager.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jDatastoreTransactionManager.java index db6bcf15814..bef86af4c2c 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jDatastoreTransactionManager.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jDatastoreTransactionManager.java @@ -16,20 +16,21 @@ */ package org.grails.datastore.gorm.neo4j; -import org.grails.datastore.mapping.core.DatastoreUtils; -import org.grails.datastore.mapping.transactions.DatastoreTransactionManager; -import org.grails.datastore.mapping.transactions.Transaction; -import org.grails.datastore.mapping.transactions.TransactionObject; +import jakarta.persistence.FlushModeType; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.transaction.CannotCreateTransactionException; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionException; import org.springframework.transaction.support.DefaultTransactionStatus; import org.springframework.transaction.support.TransactionSynchronizationManager; -import jakarta.persistence.FlushModeType; -import java.io.IOException; +import org.grails.datastore.mapping.core.DatastoreUtils; +import org.grails.datastore.mapping.transactions.DatastoreTransactionManager; +import org.grails.datastore.mapping.transactions.Transaction; +import org.grails.datastore.mapping.transactions.TransactionObject; /** * @author Stefan Armbruster @@ -54,7 +55,7 @@ protected void doSetRollbackOnly(DefaultTransactionStatus status) throws Transac super.doSetRollbackOnly(status); TransactionObject txObject = (TransactionObject) status.getTransaction(); Neo4jTransaction neo4jTransaction = (Neo4jTransaction) txObject.getTransaction(); - if(neo4jTransaction != null) { + if (neo4jTransaction != null) { neo4jTransaction.rollbackOnly(); } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jEntityTraitProvider.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jEntityTraitProvider.groovy index 97fdd74a801..8a6c91d6644 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jEntityTraitProvider.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jEntityTraitProvider.groovy @@ -18,12 +18,12 @@ */ package org.grails.datastore.gorm.neo4j -import grails.neo4j.Neo4jEntity import groovy.transform.CompileStatic + +import grails.neo4j.Neo4jEntity import org.grails.compiler.gorm.GormEntityTraitProvider import org.grails.datastore.mapping.reflect.ClassUtils - /** * Tells GORM to use the Neo4jEntity trait * @@ -32,6 +32,7 @@ import org.grails.datastore.mapping.reflect.ClassUtils */ @CompileStatic class Neo4jEntityTraitProvider implements GormEntityTraitProvider { + final Class entityTrait = Neo4jEntity final boolean available = ClassUtils.isPresent("org.neo4j.driver.Driver") } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jMappingContext.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jMappingContext.java index 2bce596c9f0..53937e06300 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jMappingContext.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jMappingContext.java @@ -18,8 +18,20 @@ */ package org.grails.datastore.gorm.neo4j; -import grails.neo4j.Relationship; +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + import groovy.lang.Closure; + +import org.springframework.core.convert.ConversionService; + +import grails.neo4j.Relationship; import org.grails.datastore.gorm.neo4j.connections.Neo4jConnectionSourceSettings; import org.grails.datastore.gorm.neo4j.identity.SnowflakeIdGenerator; import org.grails.datastore.gorm.neo4j.proxy.HashcodeEqualsAwareProxyFactory; @@ -30,10 +42,6 @@ import org.grails.datastore.mapping.model.PersistentEntity; import org.grails.datastore.mapping.model.config.GormMappingConfigurationStrategy; import org.grails.datastore.mapping.proxy.ProxyFactory; -import org.springframework.core.convert.ConversionService; - -import java.math.BigDecimal; -import java.util.*; /** * A {@link org.grails.datastore.mapping.model.MappingContext} implementation for Neo4j @@ -43,48 +51,44 @@ * * @since 1.0 */ -public class Neo4jMappingContext extends AbstractMappingContext { +public class Neo4jMappingContext extends AbstractMappingContext { private static final Class LONG_TYPE = long.class; private static final Class DOUBLE_TYPE = double.class; private static final Class STRING_TYPE = String.class; - public static final Set BASIC_TYPES = Collections.unmodifiableSet( new HashSet( Arrays.asList( - STRING_TYPE, - Long.class, - Float.class, - Integer.class, - Double.class, - Short.class, - Boolean.class, - Byte.class, - // primitives - byte.class, - int.class, - LONG_TYPE, - float.class, - DOUBLE_TYPE, - short.class, - boolean.class, - // primitive arrays - byte[].class, - int[].class, - long[].class, - float[].class, - double[].class, - short[].class, - boolean[].class, - String[].class - ) ) ); - - GraphGormMappingFactory mappingFactory = new GraphGormMappingFactory(); - MappingConfigurationStrategy mappingSyntaxStrategy = new GormMappingConfigurationStrategy(mappingFactory); - + public static final Set BASIC_TYPES = Collections.unmodifiableSet(new HashSet(Arrays.asList( + STRING_TYPE, + Long.class, + Float.class, + Integer.class, + Double.class, + Short.class, + Boolean.class, + Byte.class, + // primitives + byte.class, + int.class, + LONG_TYPE, + float.class, + DOUBLE_TYPE, + short.class, + boolean.class, + // primitive arrays + byte[].class, + int[].class, + long[].class, + float[].class, + double[].class, + short[].class, + boolean[].class, + String[].class + ))); protected Map, GraphPersistentEntity> entitiesByLabel = new LinkedHashMap<>(); - // default id generator strategy is native protected IdGenerator idGenerator = null; - protected SnowflakeIdGenerator snowflakeIdGenerator = null; + GraphGormMappingFactory mappingFactory = new GraphGormMappingFactory(); + MappingConfigurationStrategy mappingSyntaxStrategy = new GormMappingConfigurationStrategy(mappingFactory); public Neo4jMappingContext() { super(); @@ -98,13 +102,13 @@ public Neo4jMappingContext(Closure defaultMapping) { } @Deprecated - public Neo4jMappingContext(Closure defaultMapping, Class...classes) { + public Neo4jMappingContext(Closure defaultMapping, Class... classes) { super(); mappingFactory.setDefaultMapping(defaultMapping); addPersistentEntities(classes); } - public Neo4jMappingContext(Neo4jConnectionSourceSettings settings, Class...classes) { + public Neo4jMappingContext(Neo4jConnectionSourceSettings settings, Class... classes) { super(); mappingFactory.setDefaultMapping(settings.getDefault().getMapping()); mappingFactory.setDefaultConstraints(settings.getDefault().getConstraints()); @@ -112,13 +116,12 @@ public Neo4jMappingContext(Neo4jConnectionSourceSettings settings, Class...class addPersistentEntities(classes); } - public IdGenerator getIdGenerator() { return idGenerator; } public SnowflakeIdGenerator getSnowflakeIdGenerator() { - if(snowflakeIdGenerator == null) { + if (snowflakeIdGenerator == null) { snowflakeIdGenerator = new SnowflakeIdGenerator(); } return snowflakeIdGenerator; @@ -142,8 +145,8 @@ protected PersistentEntity createPersistentEntity(Class javaClass) { @Override protected PersistentEntity createPersistentEntity(Class javaClass, boolean external) { final GraphPersistentEntity entity = Relationship.class.isAssignableFrom(javaClass) ? - new RelationshipPersistentEntity(javaClass, this, external) : - new GraphPersistentEntity(javaClass, this, external); + new RelationshipPersistentEntity(javaClass, this, external) : + new GraphPersistentEntity(javaClass, this, external); final Collection labels = entity.getLabels(); entitiesByLabel.put(labels, entity); return entity; @@ -157,13 +160,9 @@ protected PersistentEntity createPersistentEntity(Class javaClass, boolean exter */ public GraphPersistentEntity findPersistentEntityForLabels(Iterable labels) { final GraphPersistentEntity entity = entitiesByLabel.get(labels); - if(entity != null) { - return entity; - } - return null; + return entity; } - /** * Obtain the native type to use for the given value * @@ -171,36 +170,30 @@ public GraphPersistentEntity findPersistentEntityForLabels(Iterable labe * @return The value converted to a native Neo4j type */ public Object convertToNative(Object value) { - if(value != null) { + if (value != null) { final Class type = value.getClass(); - if(type.equals(byte[].class)) { + if (type.equals(byte[].class)) { // workaround for https://github.com/neo4j/neo4j-java-driver/issues/182, remove when fixed - byte[] bytes = (byte[])value; + byte[] bytes = (byte[]) value; int[] intArray = new int[bytes.length]; for (int i = 0; i < bytes.length; i++) { byte b = bytes[i]; intArray[i] = b; } return intArray; - } - else if(BASIC_TYPES.contains(type)) { + } else if (BASIC_TYPES.contains(type)) { return value; - } - else if(value instanceof CharSequence) { + } else if (value instanceof CharSequence) { return value.toString(); - } - else if(value instanceof Collection) { + } else if (value instanceof Collection) { return value; - } - else if(value instanceof BigDecimal) { - return ((BigDecimal)value).doubleValue(); - } - else { + } else if (value instanceof BigDecimal) { + return ((BigDecimal) value).doubleValue(); + } else { final ConversionService conversionService = getConversionService(); - if(byte[].class.isInstance(value)){ + if (value instanceof byte[]) { return conversionService.convert(value, String.class); - } - else { + } else { if (conversionService.canConvert(type, LONG_TYPE)) { return conversionService.convert(value, LONG_TYPE); } else if (conversionService.canConvert(type, DOUBLE_TYPE)) { @@ -212,15 +205,14 @@ else if(value instanceof BigDecimal) { } } } - } - else { + } else { return value; } } @Override public ProxyFactory getProxyFactory() { - if (!(this.proxyFactory instanceof Neo4jProxyFactory)){ + if (!(this.proxyFactory instanceof Neo4jProxyFactory)) { this.proxyFactory = new Neo4jProxyFactory(); } return proxyFactory; diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jSession.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jSession.java index 765d4152d63..0ac84053f71 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jSession.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jSession.java @@ -16,19 +16,55 @@ */ package org.grails.datastore.gorm.neo4j; -import com.github.benmanes.caffeine.cache.Cache; +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ConcurrentMap; + +import jakarta.persistence.CascadeType; + import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.RemovalCause; import com.github.benmanes.caffeine.cache.RemovalListener; +import org.neo4j.driver.Driver; +import org.neo4j.driver.Result; +import org.neo4j.driver.Session; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.dao.DataAccessResourceFailureException; +import org.springframework.transaction.NoTransactionException; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.springframework.transaction.support.TransactionSynchronizationManager; + import grails.neo4j.Relationship; -import org.grails.datastore.gorm.neo4j.engine.*; +import org.grails.datastore.gorm.neo4j.engine.Neo4jEntityPersister; +import org.grails.datastore.gorm.neo4j.engine.Neo4jQuery; +import org.grails.datastore.gorm.neo4j.engine.RelationshipPendingDelete; +import org.grails.datastore.gorm.neo4j.engine.RelationshipPendingInsert; import org.grails.datastore.gorm.neo4j.mapping.config.DynamicAssociation; import org.grails.datastore.gorm.neo4j.mapping.config.DynamicToOneAssociation; import org.grails.datastore.gorm.schemaless.DynamicAttributes; import org.grails.datastore.mapping.core.AbstractSession; import org.grails.datastore.mapping.core.Datastore; import org.grails.datastore.mapping.core.OptimisticLockingException; -import org.grails.datastore.mapping.core.impl.*; +import org.grails.datastore.mapping.core.impl.PendingDelete; +import org.grails.datastore.mapping.core.impl.PendingInsert; +import org.grails.datastore.mapping.core.impl.PendingOperation; +import org.grails.datastore.mapping.core.impl.PendingOperationAdapter; +import org.grails.datastore.mapping.core.impl.PendingUpdate; import org.grails.datastore.mapping.dirty.checking.DirtyCheckable; import org.grails.datastore.mapping.engine.EntityAccess; import org.grails.datastore.mapping.engine.Persister; @@ -37,31 +73,18 @@ import org.grails.datastore.mapping.model.PersistentEntity; import org.grails.datastore.mapping.model.PersistentProperty; import org.grails.datastore.mapping.model.config.GormProperties; -import org.grails.datastore.mapping.model.types.*; +import org.grails.datastore.mapping.model.types.Association; +import org.grails.datastore.mapping.model.types.Basic; +import org.grails.datastore.mapping.model.types.Custom; +import org.grails.datastore.mapping.model.types.ManyToMany; +import org.grails.datastore.mapping.model.types.Simple; +import org.grails.datastore.mapping.model.types.TenantId; import org.grails.datastore.mapping.query.Query; import org.grails.datastore.mapping.query.Restrictions; import org.grails.datastore.mapping.query.api.QueryableCriteria; import org.grails.datastore.mapping.reflect.EntityReflector; import org.grails.datastore.mapping.transactions.SessionHolder; import org.grails.datastore.mapping.transactions.Transaction; -import org.neo4j.driver.Driver; -import org.neo4j.driver.Session; -import org.neo4j.driver.Result; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.dao.DataAccessResourceFailureException; -import org.springframework.transaction.NoTransactionException; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.support.DefaultTransactionDefinition; -import org.springframework.transaction.support.TransactionSynchronizationManager; - -import jakarta.persistence.CascadeType; -import java.io.IOException; -import java.io.Serializable; -import java.util.*; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ConcurrentMap; /** * Represents a session for interacting with Neo4j @@ -74,34 +97,29 @@ */ public class Neo4jSession extends AbstractSession { - private static Logger log = LoggerFactory.getLogger(Neo4jSession.class); private static final RemovalListener> EXCEPTION_THROWING_INSERT_LISTENER = - (key, value, cause) -> { - if (RemovalCause.SIZE == cause) { - throw new DataAccessResourceFailureException("Maximum number (5000) of relationship update operations to flush() exceeded. Flush the session periodically to avoid this error for batch operations."); - } - - }; - - protected ConcurrentMap> pendingRelationshipInserts = - Caffeine.newBuilder() - .executor(Runnable::run) - .removalListener(EXCEPTION_THROWING_INSERT_LISTENER) - .maximumSize(5000).build().asMap(); - - protected ConcurrentMap> pendingRelationshipDeletes = - Caffeine.newBuilder() - .executor(Runnable::run) - .removalListener(EXCEPTION_THROWING_INSERT_LISTENER) - .maximumSize(5000).build().asMap(); - + (key, value, cause) -> { + if (RemovalCause.SIZE == cause) { + throw new DataAccessResourceFailureException("Maximum number (5000) of relationship update operations to flush() exceeded. Flush the session periodically to avoid this error for batch operations."); + } + }; + private static final Logger log = LoggerFactory.getLogger(Neo4jSession.class); /** * map node id to hashmap of relationship types showing startNode id and endNode id */ protected final Session boltSession; protected final Driver boltDriver; - + protected ConcurrentMap> pendingRelationshipInserts = + Caffeine.newBuilder() + .executor(Runnable::run) + .removalListener(EXCEPTION_THROWING_INSERT_LISTENER) + .maximumSize(5000).build().asMap(); + protected ConcurrentMap> pendingRelationshipDeletes = + Caffeine.newBuilder() + .executor(Runnable::run) + .removalListener(EXCEPTION_THROWING_INSERT_LISTENER) + .maximumSize(5000).build().asMap(); public Neo4jSession(Datastore datastore, MappingContext mappingContext, ApplicationEventPublisher publisher, boolean stateless, Driver boltDriver) { super(datastore, mappingContext, publisher, stateless); @@ -112,6 +130,15 @@ public Neo4jSession(Datastore datastore, MappingContext mappingContext, Applicat this.boltSession = boltDriver.session(); } + public static boolean isCollectionWithPersistentEntities(Object o, MappingContext mappingContext) { + if (!(o instanceof Collection)) { + return false; + } else { + @SuppressWarnings("unchecked") Collection c = (Collection) o; + return c.stream().anyMatch(mappingContext::isPersistentEntity); + } + } + public Driver getBoltDriver() { return boltDriver; } @@ -155,11 +182,10 @@ protected void clearPendingOperations() { super.clearPendingOperations(); } finally { pendingRelationshipInserts.clear(); - pendingRelationshipDeletes.clear();; + pendingRelationshipDeletes.clear(); } } - /** * Gets a Neo4jEntityPersister for the given object * @@ -248,7 +274,6 @@ protected void flushPendingUpdates(Map entities = updates.keySet(); final Neo4jMappingContext mappingContext = getMappingContext(); - for (PersistentEntity entity : entities) { final Collection pendingUpdates = updates.get(entity); GraphPersistentEntity graphPersistentEntity = (GraphPersistentEntity) entity; @@ -271,7 +296,6 @@ protected void flushPendingUpdates(Map simpleProps = new HashMap<>(); - final DirtyCheckable dirtyCheckable = (DirtyCheckable) object; final List dirtyPropertyNames = dirtyCheckable.listDirtyPropertyNames(); for (String dirtyPropertyName : dirtyPropertyNames) { @@ -308,7 +332,7 @@ protected void flushPendingUpdates(Map childIds = pendingRelationshipInserts.get(updateKey); - if(childIds != null) { + if (childIds != null) { pendingRelationshipInserts.remove(updateKey); Map rowData = new LinkedHashMap<>(); LinkedHashMap nodeProperties = new LinkedHashMap<>(); for (PersistentProperty pp : relEntity.getPersistentProperties()) { - if(pp instanceof Simple || pp instanceof Basic || pp instanceof TenantId) { + if (pp instanceof Simple || pp instanceof Basic || pp instanceof TenantId) { String propertyName = pp.getName(); - if(RelationshipPersistentEntity.TYPE.equals(propertyName)) { + if (RelationshipPersistentEntity.TYPE.equals(propertyName)) { continue; } Object v = reflector.getProperty(relationship, propertyName); - if(v != null) { + if (v != null) { nodeProperties.put(propertyName, mappingContext.convertToNative(v)); } } } - nodeProperties.putAll( relationship.attributes() ); + nodeProperties.putAll(relationship.attributes()); rowData.put(CypherBuilder.PROPS, nodeProperties); rowData.put(RelationshipPersistentEntity.FROM, id); @@ -512,8 +536,7 @@ private void processInsertsForEntity(org.neo4j.driver.Transaction neo4jTransacti } } - } - else { + } else { // otherwise process individual inserts processPendingRelationshipUpdates(entity, entityAccess, (Serializable) entityInsert.getNativeKey(), cascadingOperations, false); } @@ -527,7 +550,6 @@ private void processInsertsForEntity(org.neo4j.driver.Transaction neo4jTransacti data.put(CypherBuilder.PROPS, nodeProperties); rows.add(data); - for (Association association : entity.getAssociations()) { if (association.isBasic()) continue; boolean isTree = association.isCircular(); @@ -553,7 +575,7 @@ private void processInsertsForEntity(org.neo4j.driver.Transaction neo4jTransacti childIds.remove(childId); Map childProperties = readNodePropertiesForInsert(pendingInsert, associatedEntity, associatedEntity.getPersistentProperties(), pendingInsert.getEntityAccess()); - childRows.add(Collections.singletonMap(CypherBuilder.PROPS, childProperties)); + childRows.add(Collections.singletonMap(CypherBuilder.PROPS, childProperties)); cascadingOperations.addAll(pendingInsert.getCascadeOperations()); } @@ -569,12 +591,10 @@ private void processInsertsForEntity(org.neo4j.driver.Transaction neo4jTransacti } } - processDynamicAssociationsIfNecessary(entity, entityAccess, obj, entityInsert, cascadingOperations, params, dynamicRelProps); params.remove(Neo4jEntityPersister.DYNAMIC_ASSOCIATION_PARAM); } - cascadingOperations.addAll(entityInsert.getCascadeOperations()); } @@ -598,7 +618,6 @@ private void processInsertsForEntity(org.neo4j.driver.Transaction neo4jTransacti } } - } } @@ -617,7 +636,7 @@ private void buildAndExecuteCreateStatement(Collection entityInse // dynamic labels require individual create statements and are less efficient final Map createParams = new HashMap<>(entityInserts.size()); final String finalCypher = buildCypherCreateStatement(entityInserts, graphEntity, cascadingOperations, createParams); - if(graphEntity.hasDynamicAssociations()) { + if (graphEntity.hasDynamicAssociations()) { createParams.remove(Neo4jEntityPersister.DYNAMIC_ASSOCIATION_PARAM); } if (finalCypher.length() > 0) { @@ -677,7 +696,6 @@ public void buildEntityCreateOperation(StringBuilder createCypher, String index, final EntityAccess access = entityInsert.getEntityAccess(); final Map simpleProps = readNodePropertiesForInsert(entityInsert, graphEntity, persistentProperties, access); - Map> dynamicRelProps = amendMapWithUndeclaredProperties(graphEntity, simpleProps, obj, getMappingContext()); final String labels = graphEntity.getLabelsWithInheritance(obj); @@ -713,7 +731,6 @@ private Map readNodePropertiesForInsert(PendingInsert entityInse } } - // build a properties map for each CREATE statement for (PersistentProperty pp : persistentProperties) { if ((pp instanceof Simple) || (pp instanceof TenantId) || (pp instanceof Basic)) { @@ -733,7 +750,7 @@ private Map readNodePropertiesForInsert(PendingInsert entityInse private void applyCustomType(EntityAccess access, PersistentProperty property, Map simpleProps) { Custom custom = (Custom) property; final CustomTypeMarshaller, Map> customTypeMarshaller = - (CustomTypeMarshaller, Map>) custom.getCustomTypeMarshaller(); + (CustomTypeMarshaller, Map>) custom.getCustomTypeMarshaller(); Object value = access.getProperty(property.getName()); customTypeMarshaller.write(custom, value, simpleProps); } @@ -789,7 +806,7 @@ protected void flushPendingDeletes(Map> amendMapWithUndeclaredProperties(GraphPersistentEntity graphEntity, Map simpleProps, Object pojo, MappingContext mappingContext) { boolean hasDynamicAssociations = graphEntity.hasDynamicAssociations(); - Map> dynRelProps = hasDynamicAssociations ? new LinkedHashMap>() : Collections.>emptyMap(); + Map> dynRelProps = hasDynamicAssociations ? new LinkedHashMap>() : Collections.emptyMap(); if (pojo instanceof DynamicAttributes) { Map map = ((DynamicAttributes) pojo).attributes(); if (map != null) { @@ -833,17 +850,6 @@ private List getOrInit(Map> dynRelProps, String key return objects; } - - public static boolean isCollectionWithPersistentEntities(Object o, MappingContext mappingContext) { - if (!(o instanceof Collection)) { - return false; - } else { - @SuppressWarnings("unchecked") Collection c = (Collection) o; - return c.stream().anyMatch(mappingContext::isPersistentEntity); - } - } - - @Override public void flush() { if (wasTransactionTerminated()) return; @@ -930,7 +936,6 @@ private void persistDirtyButUnsavedInstances() { } } - @Override public long deleteAll(QueryableCriteria criteria) { @@ -1013,9 +1018,8 @@ public boolean equals(Object o) { RelationshipUpdateKey that = (RelationshipUpdateKey) o; - if (association != null ? !association.equals(that.association) : that.association != null) return false; - return !(id != null ? !id.equals(that.id) : that.id != null); - + if (!Objects.equals(association, that.association)) return false; + return Objects.equals(id, that.id); } @Override diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jTransaction.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jTransaction.groovy index db2f618ecde..b3d0646c771 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jTransaction.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jTransaction.groovy @@ -20,14 +20,17 @@ package org.grails.datastore.gorm.neo4j import groovy.transform.CompileStatic import groovy.util.logging.Slf4j + import org.neo4j.driver.AccessMode import org.neo4j.driver.Driver import org.neo4j.driver.Session -import org.grails.datastore.mapping.transactions.Transaction import org.neo4j.driver.SessionConfig + import org.springframework.transaction.TransactionDefinition import org.springframework.transaction.support.DefaultTransactionDefinition +import org.grails.datastore.mapping.transactions.Transaction + /** * Represents a Neo4j transaction * @@ -58,7 +61,7 @@ class Neo4jTransaction implements Transaction, Clo } void commit() { - if(isActive() && !rollbackOnly) { + if (isActive() && !rollbackOnly) { log.debug("TX COMMIT: Neo4J commit()") transaction.commit() close() @@ -66,7 +69,7 @@ class Neo4jTransaction implements Transaction, Clo } void rollback() { - if(isActive()) { + if (isActive()) { log.debug("TX ROLLBACK: Neo4J rollback()") transaction.rollback() close() @@ -74,7 +77,7 @@ class Neo4jTransaction implements Transaction, Clo } void rollbackOnly() { - if(active) { + if (active) { rollbackOnly = true log.debug("TX ROLLBACK ONLY: Neo4J rollback()") transaction.rollback() @@ -85,8 +88,8 @@ class Neo4jTransaction implements Transaction, Clo @Override void close() throws IOException { - if(active) { - log.debug("TX CLOSE: Neo4j tx.close()"); + if (active) { + log.debug("TX CLOSE: Neo4j tx.close()") transaction.close() boltSession.close() active = false diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/RelationshipPersistentEntity.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/RelationshipPersistentEntity.groovy index 94c0960812a..d6e103e860a 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/RelationshipPersistentEntity.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/RelationshipPersistentEntity.groovy @@ -19,9 +19,9 @@ package org.grails.datastore.gorm.neo4j -import grails.neo4j.Direction -import grails.neo4j.Relationship import groovy.transform.CompileStatic + +import grails.neo4j.Direction import org.grails.datastore.gorm.neo4j.mapping.config.NodeConfig import org.grails.datastore.gorm.neo4j.mapping.config.RelationshipConfig import org.grails.datastore.mapping.config.Property @@ -41,11 +41,11 @@ import org.grails.datastore.mapping.model.types.Basic class RelationshipPersistentEntity extends GraphPersistentEntity { /** * The name of the from property - */ + */ public static final String FROM = "from" /** - * The name of the to property - */ + * The name of the to property + */ public static final String TO = "to" /** @@ -66,19 +66,18 @@ class RelationshipPersistentEntity extends GraphPersistentEntity { } - @Override void initialize() { - if(!isInitialized()) { + if (!isInitialized()) { super.initialize() - for(association in associations) { - if(!isRelationshipAssociation(association) && !(association instanceof Basic)) { + for (association in associations) { + if (!isRelationshipAssociation(association) && !(association instanceof Basic)) { throw new IllegalMappingException("Invalid association $association. You cannot have associations to other nodes within a relationship entity") } } NodeConfig mappedForm = getMapping().getMappedForm() - if(mappedForm instanceof RelationshipConfig) { + if (mappedForm instanceof RelationshipConfig) { RelationshipConfig rc = (RelationshipConfig) mappedForm this.type = rc.type ?: type this.direction = rc.direction @@ -115,21 +114,19 @@ ON CREATE SET r = row.props""" @Override String formatAssociationPatternFromExisting(Association association, String var = CypherBuilder.REL_VAR, String start = FROM, String end = TO) { String associationMatch - if(association.name == FROM || association.name == TO) { - associationMatch = RelationshipUtils.matchForRelationshipEntity(association, this,var) + if (association.name == FROM || association.name == TO) { + associationMatch = RelationshipUtils.matchForRelationshipEntity(association, this, var) return "(${start})${associationMatch}${toEntity.formatNode(end)}" - } - else { + } else { throw new IllegalStateException("Relationship entities cannot have associations") } } @Override String formatProperty(String variable, String property) { - if(property.startsWith(FROM) || property.startsWith(TO)) { + if (property.startsWith(FROM) || property.startsWith(TO)) { return property - } - else { + } else { return super.formatProperty(variable, property) } } @@ -155,19 +152,19 @@ ON CREATE SET r = row.props""" } Association getFrom() { - (Association)getPropertyByName(FROM) + (Association) getPropertyByName(FROM) } Association getTo() { - (Association)getPropertyByName(TO) + (Association) getPropertyByName(TO) } GraphPersistentEntity getFromEntity() { - (GraphPersistentEntity)getFrom().getAssociatedEntity() + (GraphPersistentEntity) getFrom().getAssociatedEntity() } GraphPersistentEntity getToEntity() { - (GraphPersistentEntity)getTo().getAssociatedEntity() + (GraphPersistentEntity) getTo().getAssociatedEntity() } @@ -186,16 +183,15 @@ ON CREATE SET r = row.props""" } String buildRelationshipMatchTo(String type, String var = "r") { - buildRelationshipMatch(type ,var) + toEntity.formatNode(TO) + buildRelationshipMatch(type, var) + toEntity.formatNode(TO) } String buildRelationshipMatch(String type, String var = "r") { boolean incoming = direction.isIncoming() boolean outgoing = direction.isOutgoing() - if(type != null) { + if (type != null) { return "${incoming ? RelationshipUtils.INCOMING_CHAR : ''}-[$var:${type}]-${outgoing ? RelationshipUtils.OUTGOING_CHAR : ''}" - } - else { + } else { return "${incoming ? RelationshipUtils.INCOMING_CHAR : ''}-[$var]-${outgoing ? RelationshipUtils.OUTGOING_CHAR : ''}" } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/RelationshipUtils.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/RelationshipUtils.groovy index 9c2a4aa0b7b..5c29a1aedee 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/RelationshipUtils.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/RelationshipUtils.groovy @@ -18,16 +18,16 @@ */ package org.grails.datastore.gorm.neo4j +import groovy.transform.CompileStatic + import grails.neo4j.Direction import grails.neo4j.Relationship -import groovy.transform.CompileStatic import org.grails.datastore.gorm.neo4j.mapping.config.Attribute import org.grails.datastore.gorm.neo4j.mapping.config.DynamicAssociation import org.grails.datastore.mapping.model.PersistentEntity import org.grails.datastore.mapping.model.types.Association import org.grails.datastore.mapping.model.types.ManyToMany import org.grails.datastore.mapping.model.types.ManyToOne -import org.grails.datastore.mapping.model.types.OneToMany /** * Utility methods for manipulating relationships @@ -37,6 +37,7 @@ import org.grails.datastore.mapping.model.types.OneToMany */ @CompileStatic class RelationshipUtils { + static final char INCOMING_CHAR = '<' static final char OUTGOING_CHAR = '>' private static final char OPEN_BRACE = '{' @@ -54,12 +55,11 @@ class RelationshipUtils { * @return True if it is */ static boolean useReversedMappingFor(Association association) { - GraphPersistentEntity entity = (GraphPersistentEntity)association.getOwner() - if(entity.isRelationshipEntity()) { + GraphPersistentEntity entity = (GraphPersistentEntity) association.getOwner() + if (entity.isRelationshipEntity()) { return false - } - else { - Attribute attr = (Attribute)association.getMapping()?.getMappedForm() + } else { + Attribute attr = (Attribute) association.getMapping()?.getMappedForm() return isIncomingRelationship(association, attr) } } @@ -71,7 +71,7 @@ class RelationshipUtils { */ static String relationshipTypeUsedFor(Association association) { Attribute mappedForm = (Attribute) association.getMapping()?.getMappedForm() - return getRelationshipType( association, mappedForm ) + return getRelationshipType(association, mappedForm) } /** @@ -91,17 +91,17 @@ class RelationshipUtils { sb.append(INCOMING_CHAR) } sb.append(START_RELATIONSHIP).append(var).append(COLON).append(relationshipType) - if(!attributes.isEmpty()) { + if (!attributes.isEmpty()) { sb.append(OPEN_BRACE) def i = attributes.entrySet().iterator() - while(i.hasNext()) { + while (i.hasNext()) { def entry = i.next() sb.append(entry.key) .append(COLON).append(SINGLE_QUOTE) .append(entry.value) .append(SINGLE_QUOTE) - if(i.hasNext()) { + if (i.hasNext()) { sb.append(COMMA) } } @@ -136,7 +136,7 @@ class RelationshipUtils { } sb.append(START_RELATIONSHIP).append(var).append(COLON).append(relationshipType) sb.append(END_RELATIONSHIP) - if (direction == Direction.OUTGOING || direction == Direction.BOTH ) { + if (direction == Direction.OUTGOING || direction == Direction.BOTH) { sb.append(OUTGOING_CHAR) } sb.toString() @@ -160,9 +160,9 @@ class RelationshipUtils { sb.append(INCOMING_CHAR) } sb.append(START_RELATIONSHIP) - .append(var) - .append(END_RELATIONSHIP) - if (direction == Direction.OUTGOING || direction == Direction.BOTH ) { + .append(var) + .append(END_RELATIONSHIP) + if (direction == Direction.OUTGOING || direction == Direction.BOTH) { sb.append(OUTGOING_CHAR) } sb.toString() @@ -183,19 +183,16 @@ class RelationshipUtils { String relationshipType = mappedForm?.getType() if (relationshipType != null) { return relationshipType - } - else if (association instanceof DynamicAssociation) { + } else if (association instanceof DynamicAssociation) { return association.getName() - } - else { + } else { boolean reversed = useReversedMappingFor(association) String name = reversed ? association.getReferencedPropertyName() : association.getName() - if(name != null) { + if (name != null) { return name.toUpperCase(Locale.ENGLISH) - } - else { + } else { return association.getName().toUpperCase(Locale.ENGLISH) } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/SessionFlushedEvent.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/SessionFlushedEvent.java index ded112f698d..b73067e3d1c 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/SessionFlushedEvent.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/SessionFlushedEvent.java @@ -16,9 +16,10 @@ */ package org.grails.datastore.gorm.neo4j; -import org.grails.datastore.mapping.core.Session; import org.springframework.context.ApplicationEvent; +import org.grails.datastore.mapping.core.Session; + /** * event emitted when session is flushed */ diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/TypeDirectionPair.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/TypeDirectionPair.java index 61f3ff62230..34f688050f4 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/TypeDirectionPair.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/TypeDirectionPair.java @@ -18,7 +18,6 @@ */ package org.grails.datastore.gorm.neo4j; - /** * combination of relationship type and direction to be used a key in a map */ @@ -57,9 +56,7 @@ public boolean equals(Object o) { TypeDirectionPair that = (TypeDirectionPair) o; if (outgoing != that.outgoing) return false; - if (!type.equals(that.type)) return false; - - return true; + return type.equals(that.type); } @Override @@ -72,16 +69,16 @@ public int hashCode() { @Override public String toString() { return "TypeDirectionPair{" + - "type='" + type + '\'' + - ", outgoing=" + outgoing + - '}'; - } - - public void setTargetType(String targetType) { - this.targetType = targetType; + "type='" + type + '\'' + + ", outgoing=" + outgoing + + '}'; } public String getTargetType() { return targetType; } + + public void setTargetType(String targetType) { + this.targetType = targetType; + } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/api/Neo4jGormStaticApi.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/api/Neo4jGormStaticApi.groovy index 9847089cec0..8f416ea2f3d 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/api/Neo4jGormStaticApi.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/api/Neo4jGormStaticApi.groovy @@ -19,15 +19,29 @@ package org.grails.datastore.gorm.neo4j.api +import java.util.function.Function + +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j + +import org.neo4j.driver.QueryRunner +import org.neo4j.driver.Record +import org.neo4j.driver.Result +import org.neo4j.driver.types.Node + +import org.springframework.transaction.PlatformTransactionManager + import grails.gorm.multitenancy.Tenants import grails.neo4j.Path import grails.neo4j.Relationship -import groovy.transform.CompileStatic -import groovy.util.logging.Slf4j import org.grails.datastore.gorm.GormEntity import org.grails.datastore.gorm.GormStaticApi import org.grails.datastore.gorm.finders.FinderMethod -import org.grails.datastore.gorm.neo4j.* +import org.grails.datastore.gorm.neo4j.CypherBuilder +import org.grails.datastore.gorm.neo4j.GraphPersistentEntity +import org.grails.datastore.gorm.neo4j.Neo4jDatastore +import org.grails.datastore.gorm.neo4j.Neo4jSession +import org.grails.datastore.gorm.neo4j.RelationshipPersistentEntity import org.grails.datastore.gorm.neo4j.collection.Neo4jPath import org.grails.datastore.gorm.neo4j.collection.Neo4jRelationship import org.grails.datastore.gorm.neo4j.collection.Neo4jResultList @@ -41,13 +55,6 @@ import org.grails.datastore.mapping.model.config.GormProperties import org.grails.datastore.mapping.multitenancy.MultiTenancySettings import org.grails.datastore.mapping.multitenancy.exceptions.TenantNotFoundException import org.grails.datastore.mapping.query.QueryException -import org.neo4j.driver.QueryRunner -import org.neo4j.driver.Record -import org.neo4j.driver.Result -import org.neo4j.driver.types.Node -import org.springframework.transaction.PlatformTransactionManager - -import java.util.function.Function /** * Static API implementation for Neo4j @@ -93,25 +100,23 @@ class Neo4jGormStaticApi extends GormStaticApi { QueryRunner boltSession = getStatementRunner(neo4jSession) params = new LinkedHashMap(params) String queryString - if(query instanceof GString) { + if (query instanceof GString) { queryString = buildNamedParameterQueryFromGString((GString) query, params) - } - else { + } else { queryString = query.toString() } includeTenantIdIfNecessary(neo4jSession, queryString, params) - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("QUERY Cypher [$queryString] for params [$params]") } - Result result = boltSession.run( queryString, (Map)params) + Result result = boltSession.run(queryString, (Map) params) def persister = neo4jSession .getEntityPersister(persistentEntity) - if(result.hasNext()) { + if (result.hasNext()) { return new Neo4jResultList(0, result, persister) - } - else { + } else { return Collections.emptyList() } } as SessionCallback>) @@ -120,24 +125,23 @@ class Neo4jGormStaticApi extends GormStaticApi { @Override List findAll(CharSequence query, Collection params, Map args) { - if(query instanceof GString) { - throw new QueryException("Unsafe query [$query]. GORM cannot automatically escape a GString value when combined with ordinal parameters, so this query is potentially vulnerable to HQL injection attacks. Please embed the parameters within the GString so they can be safely escaped."); + if (query instanceof GString) { + throw new QueryException("Unsafe query [$query]. GORM cannot automatically escape a GString value when combined with ordinal parameters, so this query is potentially vulnerable to HQL injection attacks. Please embed the parameters within the GString so they can be safely escaped.") } execute({ Session session -> Neo4jSession neo4jSession = (Neo4jSession) session QueryRunner boltSession = getStatementRunner(neo4jSession) - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("QUERY Cypher [$query] for params [$params]") } - Result result = Neo4jExtensions.execute(boltSession, query.toString(), (List)params.toList()) + Result result = Neo4jExtensions.execute(boltSession, query.toString(), (List) params.toList()) def persister = neo4jSession .getEntityPersister(persistentEntity) - if(result.hasNext()) { + if (result.hasNext()) { return new Neo4jResultList(0, result, persister) - } - else { + } else { return Collections.emptyList() } } as SessionCallback>) @@ -150,49 +154,48 @@ class Neo4jGormStaticApi extends GormStaticApi { QueryRunner boltSession = getStatementRunner(neo4jSession) params = new LinkedHashMap(params) String queryString - if(query instanceof GString) { + if (query instanceof GString) { queryString = buildNamedParameterQueryFromGString((GString) query, params) - } - else { + } else { queryString = query.toString() } includeTenantIdIfNecessary(neo4jSession, queryString, params) - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("QUERY Cypher [$queryString] for params [$params]") } - Result result = boltSession.run( queryString, (Map)params) + Result result = boltSession.run(queryString, (Map) params) def persister = neo4jSession .getEntityPersister(persistentEntity) def resultList = new Neo4jResultList(0, 1, (Iterator) result, persister) - if( !resultList.isEmpty() ) { - return (D)resultList.get(0) + if (!resultList.isEmpty()) { + return (D) resultList.get(0) } } as SessionCallback) } @Override D find(CharSequence query, Collection params, Map args) { - if(query instanceof GString) { - throw new QueryException("Unsafe query [$query]. GORM cannot automatically escape a GString value when combined with ordinal parameters, so this query is potentially vulnerable to HQL injection attacks. Please embed the parameters within the GString so they can be safely escaped."); + if (query instanceof GString) { + throw new QueryException("Unsafe query [$query]. GORM cannot automatically escape a GString value when combined with ordinal parameters, so this query is potentially vulnerable to HQL injection attacks. Please embed the parameters within the GString so they can be safely escaped.") } execute({ Session session -> Neo4jSession neo4jSession = (Neo4jSession) session QueryRunner boltSession = getStatementRunner(neo4jSession) - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("QUERY Cypher [$query] for params [$params]") } - Result result = Neo4jExtensions.execute(boltSession, query.toString(), (List)params.toList()) + Result result = Neo4jExtensions.execute(boltSession, query.toString(), (List) params.toList()) def persister = neo4jSession .getEntityPersister(persistentEntity) def resultList = new Neo4jResultList(0, 1, (Iterator) result, persister) - if( !resultList.isEmpty() ) { - return (D)resultList.get(0) + if (!resultList.isEmpty()) { + return (D) resultList.get(0) } return null @@ -229,14 +232,13 @@ class Neo4jGormStaticApi extends GormStaticApi { QueryRunner boltSession = getStatementRunner(neo4jSession) params = new LinkedHashMap(params) String queryString - if(query instanceof GString) { + if (query instanceof GString) { queryString = buildNamedParameterQueryFromGString((GString) query, params) - } - else { + } else { queryString = query.toString() } includeTenantIdIfNecessary(neo4jSession, queryString, params) - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("UPDATE Cypher [$queryString] for params [$params]") } @@ -246,33 +248,32 @@ class Neo4jGormStaticApi extends GormStaticApi { } - @Override Integer executeUpdate(CharSequence query, Collection params, Map args) { - return (int) Neo4jEntityPersister.countUpdates(cypherStatic(query, params.toList()) ) + return (int) Neo4jEntityPersister.countUpdates(cypherStatic(query, params.toList())) } Path findPath(CharSequence query, Map params) { Result result = cypherStatic(query, params) - if(result.hasNext()) { + if (result.hasNext()) { Record record = result.next() GraphPersistentEntity graphEntity = (GraphPersistentEntity) persistentEntity - return new Neo4jPath((Neo4jDatastore)datastore, record.values().first().asPath(), graphEntity, graphEntity) + return new Neo4jPath((Neo4jDatastore) datastore, record.values().first().asPath(), graphEntity, graphEntity) } return null } Path findPathTo(Class type, CharSequence query, Map params) { Result result = cypherStatic(query, params) - if(result.hasNext()) { + if (result.hasNext()) { Record record = result.next() GraphPersistentEntity graphEntity = (GraphPersistentEntity) persistentEntity - GraphPersistentEntity toEntity = (GraphPersistentEntity)persistentEntity.mappingContext.getPersistentEntity(type.name) - if(toEntity == null) { + GraphPersistentEntity toEntity = (GraphPersistentEntity) persistentEntity.mappingContext.getPersistentEntity(type.name) + if (toEntity == null) { throw new QueryException("Target type [$type] is not a persistent entity") } - return new Neo4jPath((Neo4jDatastore)datastore, record.values().first().asPath(), graphEntity, toEntity) + return new Neo4jPath((Neo4jDatastore) datastore, record.values().first().asPath(), graphEntity, toEntity) } return null } @@ -291,14 +292,14 @@ class Neo4jGormStaticApi extends GormStaticApi { throw new QueryException("Target type [$to] is not a persistent entity") } Relationship relationship = null - if(from != null && to != null) { + if (from != null && to != null) { String query = """MATCH (from)-[r]-(to) WHERE ${fromEntity.formatId(RelationshipPersistentEntity.FROM)} = \$start AND ${toEntity.formatId(RelationshipPersistentEntity.TO)} = \$end RETURN r LIMIT 1""" - List results = executeQuery(query, [start:from.ident(), end:to.ident()]) + List results = executeQuery(query, [start: from.ident(), end: to.ident()]) - if(!results.isEmpty()) { + if (!results.isEmpty()) { org.neo4j.driver.types.Relationship neoRel = (org.neo4j.driver.types.Relationship) results.first() relationship = new Neo4jRelationship<>(from, to, neoRel) relationship.attributes(neoRel.asMap()) @@ -309,7 +310,7 @@ LIMIT 1""" } as SessionCallback>) } - List> findRelationships(F from, T to, Map params = Collections.emptyMap()) { + List> findRelationships(F from, T to, Map params = Collections.emptyMap()) { execute({ Session session -> Neo4jSession neo4jSession = (Neo4jSession) session EntityPersister fromPersister = (EntityPersister) neo4jSession.getPersister(from) @@ -323,16 +324,16 @@ LIMIT 1""" throw new QueryException("Target type [$to] is not a persistent entity") } List rels = [] - if(from != null && to != null) { + if (from != null && to != null) { String skip = params.offset ? " SKIP ${Integer.valueOf(params.offset.toString())}" : '' String limit = params.max ? " LIMIT ${Integer.valueOf(params.max.toString())}" : '' String query = """MATCH (from)-[r]-(to) WHERE ${fromEntity.formatId(RelationshipPersistentEntity.FROM)} = \$start AND ${toEntity.formatId(RelationshipPersistentEntity.TO)} = \$end RETURN DISTINCT(r)$skip$limit""" - List results = (List) executeQuery(query, [start:from.ident(), end:to.ident()]) + List results = (List) executeQuery(query, [start: from.ident(), end: to.ident()]) - for(org.neo4j.driver.types.Relationship neoRel in results) { - def relationship = new Neo4jRelationship(from, to, neoRel) + for (org.neo4j.driver.types.Relationship neoRel in results) { + def relationship = new Neo4jRelationship(from, to, neoRel) relationship.attributes(neoRel.asMap()) rels.add(relationship) } @@ -342,7 +343,7 @@ RETURN DISTINCT(r)$skip$limit""" } as SessionCallback>>) } - List> findRelationships(Class from, Class to, Map params = Collections.emptyMap()) { + List> findRelationships(Class from, Class to, Map params = Collections.emptyMap()) { execute({ Session session -> Neo4jEntityPersister fromPersister = (Neo4jEntityPersister) session.getPersister(from) Neo4jEntityPersister toPersister = (Neo4jEntityPersister) session.getPersister(to) @@ -355,21 +356,21 @@ RETURN DISTINCT(r)$skip$limit""" throw new QueryException("Target type [$to] is not a persistent entity") } List rels = [] - if(from != null && to != null) { + if (from != null && to != null) { String skip = params.offset ? " SKIP ${Integer.valueOf(params.offset.toString())}" : '' String limit = params.max ? " LIMIT ${Integer.valueOf(params.max.toString())}" : '' String query = """MATCH ${fromEntity.formatNode(RelationshipPersistentEntity.FROM)}-[r]-${toEntity.formatNode(RelationshipPersistentEntity.TO)} RETURN DISTINCT(r), from, to$skip$limit""" Result results = cypherStatic(query) - while(results.hasNext()) { + while (results.hasNext()) { Record record = results.next() org.neo4j.driver.types.Relationship neoRel = record.get(CypherBuilder.REL_VAR).asRelationship() Node fromNode = record.get(RelationshipPersistentEntity.FROM).asNode() Node toNode = record.get(RelationshipPersistentEntity.TO).asNode() F fromObject = (F) fromPersister.unmarshallOrFromCache(fromEntity, fromNode) T toObject = (T) fromPersister.unmarshallOrFromCache(toEntity, toNode) - def relationship = new Neo4jRelationship(fromObject, toObject, neoRel) + def relationship = new Neo4jRelationship(fromObject, toObject, neoRel) relationship.attributes(neoRel.asMap()) rels.add(relationship) } @@ -385,19 +386,19 @@ RETURN DISTINCT(r), from, to$skip$limit""" EntityPersister toPersister = (EntityPersister) session.getPersister(to) GraphPersistentEntity fromEntity = (GraphPersistentEntity) fromPersister?.getPersistentEntity() GraphPersistentEntity toEntity = (GraphPersistentEntity) toPersister?.getPersistentEntity() - if(fromEntity == null) { + if (fromEntity == null) { throw new QueryException("From type [$from] is not a persistent entity") } - if(toEntity == null) { + if (toEntity == null) { throw new QueryException("Target type [$to] is not a persistent entity") } Serializable fromId = fromPersister.getObjectIdentifier(from) Serializable toId = toPersister.getObjectIdentifier(to) - if(fromId == null) { + if (fromId == null) { throw new QueryException("From type [$from] has not been persisted (null id)") } - if(toId == null) { + if (toId == null) { throw new QueryException("Target type [$to] has not been persisted (null id)") } String query = """MATCH ${ @@ -408,9 +409,9 @@ RETURN DISTINCT(r), from, to$skip$limit""" fromEntity.formatId(RelationshipPersistentEntity.FROM) } = \$start AND ${toEntity.formatId(RelationshipPersistentEntity.TO)} = \$end RETURN p""" Result result = cypherStatic(query, [start: fromId, end: toId]) - if(result.hasNext()) { + if (result.hasNext()) { Record record = result.next() - return new Neo4jPath((Neo4jDatastore)datastore, record.values().first().asPath(), (GraphPersistentEntity)fromEntity, (GraphPersistentEntity)toEntity) + return new Neo4jPath((Neo4jDatastore) datastore, record.values().first().asPath(), (GraphPersistentEntity) fromEntity, (GraphPersistentEntity) toEntity) } return null @@ -423,23 +424,23 @@ RETURN DISTINCT(r), from, to$skip$limit""" * @return */ Result cypherStatic(CharSequence query, List params) { - if(query instanceof GString) { - throw new QueryException("Unsafe query [$query]. GORM cannot automatically escape a GString value when combined with ordinal parameters, so this query is potentially vulnerable to HQL injection attacks. Please embed the parameters within the GString so they can be safely escaped."); + if (query instanceof GString) { + throw new QueryException("Unsafe query [$query]. GORM cannot automatically escape a GString value when combined with ordinal parameters, so this query is potentially vulnerable to HQL injection attacks. Please embed the parameters within the GString so they can be safely escaped.") } execute({ Session session -> Neo4jSession neo4jSession = (Neo4jSession) session Map paramsMap = new LinkedHashMap() int i = 0 - for(p in params) { + for (p in params) { paramsMap.put(String.valueOf(++i), p) } String queryString = query.toString() - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("QUERY Cypher [$queryString] for params [$params]") } - includeTenantIdIfNecessary(neo4jSession, queryString, (Map)paramsMap) + includeTenantIdIfNecessary(neo4jSession, queryString, (Map) paramsMap) QueryRunner boltSession = getStatementRunner(neo4jSession) boltSession.run(queryString, paramsMap) } as SessionCallback) @@ -451,20 +452,19 @@ RETURN DISTINCT(r), from, to$skip$limit""" * @param query * @return */ - Result cypherStatic(CharSequence query, Map params ) { + Result cypherStatic(CharSequence query, Map params) { execute({ Session session -> Neo4jSession neo4jSession = (Neo4jSession) session QueryRunner boltSession = getStatementRunner(neo4jSession) params = new LinkedHashMap(params) String queryString - if(query instanceof GString) { + if (query instanceof GString) { queryString = buildNamedParameterQueryFromGString((GString) query, params) - } - else { + } else { queryString = query.toString() } includeTenantIdIfNecessary(neo4jSession, queryString, params) - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("QUERY Cypher [$queryString] for params [$params]") } @@ -484,36 +484,33 @@ RETURN DISTINCT(r), from, to$skip$limit""" Neo4jSession neo4jSession = (Neo4jSession) session Map params = [:] String queryString - if(query instanceof GString) { + if (query instanceof GString) { queryString = buildNamedParameterQueryFromGString((GString) query, params) - } - else { + } else { queryString = query.toString() } if (persistentEntity.isMultiTenant() && neo4jSession.getDatastore().multiTenancyMode == MultiTenancySettings.MultiTenancyMode.DISCRIMINATOR) { if (!queryString.contains("\$tenantId")) { throw new TenantNotFoundException("Query does not specify a tenant id, but multi tenant mode is DISCRIMINATOR!") } else { - Map paramsMap = new LinkedHashMap<>() + Map paramsMap = new LinkedHashMap<>() paramsMap.put(GormProperties.TENANT_IDENTITY, Tenants.currentId(Neo4jDatastore)) QueryRunner boltSession = getStatementRunner(neo4jSession) - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("QUERY Cypher [$queryString] for params [$paramsMap]") } return boltSession.run(queryString, paramsMap) } - } - else { + } else { QueryRunner boltSession = getStatementRunner(neo4jSession) - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("QUERY Cypher [$queryString]") } - if(params.isEmpty()) { + if (params.isEmpty()) { return boltSession.run(queryString) - } - else { + } else { return boltSession.run(queryString, params) } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/GraphAdapter.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/GraphAdapter.groovy index e25d12e7fbf..a24768d9ce1 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/GraphAdapter.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/GraphAdapter.groovy @@ -20,18 +20,15 @@ package org.grails.datastore.gorm.neo4j.collection import groovy.transform.CompileStatic import groovy.transform.PackageScope -import org.grails.datastore.gorm.GormEnhancer -import org.grails.datastore.gorm.GormEntity + import org.grails.datastore.gorm.neo4j.Neo4jSession import org.grails.datastore.gorm.neo4j.RelationshipUtils import org.grails.datastore.mapping.engine.EntityAccess import org.grails.datastore.mapping.model.types.Association import org.grails.datastore.mapping.model.types.ManyToMany import org.grails.datastore.mapping.model.types.ToOne -import org.grails.datastore.mapping.proxy.ProxyFactory import org.grails.datastore.mapping.reflect.EntityReflector - /** * Helps to Adapt a collection to the Neo4j graph * @@ -59,66 +56,63 @@ class GraphAdapter { } void adaptGraphUponRemove(Object o, boolean currentlyInitializing = false) { - if(currentlyInitializing) return + if (currentlyInitializing) return - Neo4jSession session = (Neo4jSession)this.session + Neo4jSession session = (Neo4jSession) this.session def childAccess = session.mappingContext.getEntityReflector(association.getAssociatedEntity()) def proxyFactory = session.getMappingContext().getProxyFactory() Serializable id if (proxyFactory.isProxy(o)) { id = proxyFactory.getIdentifier(o) - } - else { + } else { id = (Serializable) childAccess.getIdentifier(o) } - if(association.isOrphanRemoval()) { + if (association.isOrphanRemoval()) { session.delete(o) - } - else if (!reversed && id != null) { - session.addPendingRelationshipDelete((Serializable)parentAccess.getIdentifier(), association, id) + } else if (!reversed && id != null) { + session.addPendingRelationshipDelete((Serializable) parentAccess.getIdentifier(), association, id) } } void adaptGraphUponAdd(Object t, boolean currentlyInitializing = false) { def proxyFactory = session.getMappingContext().getProxyFactory() - Neo4jSession session = (Neo4jSession)this.session - if(currentlyInitializing) { + Neo4jSession session = (Neo4jSession) this.session + if (currentlyInitializing) { // if the association is initializing then replace parent entities with non proxied version to prevent N+1 problem if (association.isBidirectional() && !proxyFactory.isProxy(t)) { def inverseSide = association.inverseSide - if(inverseSide instanceof ToOne) { + if (inverseSide instanceof ToOne) { EntityReflector target = session.mappingContext.getEntityReflector(association.getAssociatedEntity()) - target.setProperty( t, inverseSide.name, parentAccess.entity ) + target.setProperty(t, inverseSide.name, parentAccess.entity) } } - } - else { + } else { if (proxyFactory.isProxy(t)) { - if ( !proxyFactory.isInitialized(t) ) return - if ( !childType.isInstance(t) ) return + if (!proxyFactory.isInitialized(t)) return + if (!childType.isInstance(t)) return } EntityReflector target = session.mappingContext.getEntityReflector(association.getAssociatedEntity()) if (association.isBidirectional()) { if (association instanceof ManyToMany) { - Collection coll = (Collection) target.getProperty(t, association.getReferencedPropertyName()); - coll.add(parentAccess.entity); + Collection coll = (Collection) target.getProperty(t, association.getReferencedPropertyName()) + coll.add(parentAccess.entity) } else { - target.setProperty(t, association.getReferencedPropertyName(), parentAccess.entity); + target.setProperty(t, association.getReferencedPropertyName(), parentAccess.entity) } } def identifier = target.getIdentifier(t) if (identifier == null) { // non-persistent instance - identifier = session.persist(t); + identifier = session.persist(t) } if (!reversed && identifier != null) { // prevent duplicated rels - session.addPendingRelationshipInsert((Serializable)parentAccess.getIdentifier(), association, identifier) + session.addPendingRelationshipInsert((Serializable) parentAccess.getIdentifier(), association, identifier) } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jAssociationResultList.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jAssociationResultList.groovy index 647415183dc..4d36727f1c2 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jAssociationResultList.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jAssociationResultList.groovy @@ -19,11 +19,11 @@ package org.grails.datastore.gorm.neo4j.collection import groovy.transform.CompileStatic + import org.grails.datastore.gorm.neo4j.Neo4jSession import org.grails.datastore.mapping.engine.EntityAccess import org.grails.datastore.mapping.model.types.Association - /** * Extended result list aware of the parent entity and association to prevent N+1 queries * @@ -42,8 +42,8 @@ class Neo4jAssociationResultList extends Neo4jResultList { this.parent = parent this.association = association this.session = session - if(association.isBidirectional()) { - setInitializedAssociations(Collections.singletonMap(association.inverseSide, parent.entity)) + if (association.isBidirectional()) { + setInitializedAssociations(Collections. singletonMap(association.inverseSide, parent.entity)) } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jList.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jList.groovy index eddd1dfcba3..2919439d64e 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jList.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jList.groovy @@ -19,13 +19,13 @@ package org.grails.datastore.gorm.neo4j.collection import groovy.transform.CompileStatic + import org.grails.datastore.gorm.neo4j.Neo4jSession import org.grails.datastore.mapping.dirty.checking.DirtyCheckable import org.grails.datastore.mapping.dirty.checking.DirtyCheckingList import org.grails.datastore.mapping.engine.EntityAccess import org.grails.datastore.mapping.model.types.Association - /** * * A {@link org.grails.datastore.mapping.dirty.checking.DirtyCheckingList} for Neo4j @@ -36,13 +36,15 @@ import org.grails.datastore.mapping.model.types.Association @CompileStatic class Neo4jList extends DirtyCheckingList { + final transient Association association final transient Neo4jSession session - protected final @Delegate GraphAdapter graphAdapter + protected final @Delegate + GraphAdapter graphAdapter Neo4jList(EntityAccess parentAccess, Association association, List delegate, Neo4jSession session) { - super(delegate, (DirtyCheckable)parentAccess.getEntity(), association.getName()) + super(delegate, (DirtyCheckable) parentAccess.getEntity(), association.getName()) this.association = association this.session = session this.graphAdapter = new GraphAdapter(session, parentAccess, association) @@ -60,7 +62,7 @@ class Neo4jList extends DirtyCheckingList { boolean add(Object o) { def added = super.add(o) - if(added) { + if (added) { adaptGraphUponAdd(o) } return added @@ -69,8 +71,8 @@ class Neo4jList extends DirtyCheckingList { @Override boolean addAll(Collection c) { def added = super.addAll(c) - if(added) { - for( o in c ) { + if (added) { + for (o in c) { adaptGraphUponAdd(o) } } @@ -81,8 +83,8 @@ class Neo4jList extends DirtyCheckingList { @Override boolean removeAll(Collection c) { def removed = super.removeAll(c) - if(removed) { - for(o in c) { + if (removed) { + for (o in c) { adaptGraphUponRemove(o) } } @@ -92,7 +94,7 @@ class Neo4jList extends DirtyCheckingList { @Override boolean remove(Object o) { def removed = super.remove(o) - if(removed) { + if (removed) { adaptGraphUponRemove(o) } return removed diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPath.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPath.groovy index b5a1e3fd21e..5686d80dddb 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPath.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPath.groovy @@ -19,17 +19,18 @@ package org.grails.datastore.gorm.neo4j.collection +import groovy.transform.CompileStatic + +import org.neo4j.driver.types.Node + import grails.neo4j.Neo4jEntity import grails.neo4j.Path import grails.neo4j.Relationship -import groovy.transform.CompileStatic import org.grails.datastore.gorm.GormEnhancer import org.grails.datastore.gorm.neo4j.GraphPersistentEntity import org.grails.datastore.gorm.neo4j.Neo4jDatastore -import org.grails.datastore.gorm.neo4j.Neo4jMappingContext import org.grails.datastore.gorm.neo4j.engine.Neo4jEntityPersister import org.grails.datastore.mapping.query.QueryException -import org.neo4j.driver.types.Node /** * A neo4j {@link org.neo4j.driver.types.Path} adapter @@ -39,6 +40,7 @@ import org.neo4j.driver.types.Node */ @CompileStatic class Neo4jPath, E extends Neo4jEntity> implements Path { + final Neo4jDatastore datastore final org.neo4j.driver.types.Path neo4jPath final GraphPersistentEntity from @@ -51,16 +53,16 @@ class Neo4jPath, E extends Neo4jEntity> implements P Neo4jPath(Neo4jDatastore datastore, org.neo4j.driver.types.Path neo4jPath, GraphPersistentEntity from, GraphPersistentEntity to) { this.datastore = datastore this.neo4jPath = neo4jPath - if(from == null) { + if (from == null) { from = datastore.mappingContext.findPersistentEntityForLabels(neo4jPath.start().labels()) } - if(to == null) { + if (to == null) { to = datastore.mappingContext.findPersistentEntityForLabels(neo4jPath.end().labels()) } - if(from == null) { + if (from == null) { throw new IllegalArgumentException("From domain class type cannot be established for path [$neo4jPath]") } - if(to == null) { + if (to == null) { throw new IllegalArgumentException("From domain class type cannot be established for path [$neo4jPath]") } this.from = from @@ -69,33 +71,32 @@ class Neo4jPath, E extends Neo4jEntity> implements P Neo4jPath(Neo4jDatastore datastore, org.neo4j.driver.types.Path neo4jPath, Class from, Class to) { this(datastore, neo4jPath, - (GraphPersistentEntity)datastore.mappingContext.getPersistentEntity(from.name), - (GraphPersistentEntity)datastore.mappingContext.getPersistentEntity(to.name)) + (GraphPersistentEntity) datastore.mappingContext.getPersistentEntity(from.name), + (GraphPersistentEntity) datastore.mappingContext.getPersistentEntity(to.name)) } Neo4jPath(Neo4jDatastore datastore, org.neo4j.driver.types.Path neo4jPath) { - this(datastore, neo4jPath,(GraphPersistentEntity)null,null) + this(datastore, neo4jPath, (GraphPersistentEntity) null, null) } - @Override S start() { - if(start == null) { + if (start == null) { Class clazz = from.javaClass - Neo4jEntityPersister persister = (Neo4jEntityPersister )GormEnhancer.findDatastore(clazz).currentSession.getPersister(clazz) - start = (S)persister.unmarshallOrFromCache(from, neo4jPath.start()) + Neo4jEntityPersister persister = (Neo4jEntityPersister) GormEnhancer.findDatastore(clazz).currentSession.getPersister(clazz) + start = (S) persister.unmarshallOrFromCache(from, neo4jPath.start()) } return start } @Override E end() { - if(end == null) { + if (end == null) { Class clazz = to.javaClass - Neo4jEntityPersister persister = (Neo4jEntityPersister )GormEnhancer.findDatastore(clazz).currentSession.getPersister(clazz) + Neo4jEntityPersister persister = (Neo4jEntityPersister) GormEnhancer.findDatastore(clazz).currentSession.getPersister(clazz) - end = (E)persister.unmarshallOrFromCache(to, neo4jPath.end()) + end = (E) persister.unmarshallOrFromCache(to, neo4jPath.end()) } return end } @@ -107,9 +108,9 @@ class Neo4jPath, E extends Neo4jEntity> implements P @Override Iterable nodes() { - if(nodes == null) { + if (nodes == null) { List nodeList = [] - for(Node n in neo4jPath.nodes()) { + for (Node n in neo4jPath.nodes()) { nodeList.add(unmarshallNode(datastore, n)) } nodes = nodeList @@ -129,6 +130,7 @@ class Neo4jPath, E extends Neo4jEntity> implements P } static class Neo4jPathIterator implements Iterator { + final Neo4jDatastore datastore final Iterator iterator @@ -145,11 +147,12 @@ class Neo4jPath, E extends Neo4jEntity> implements P @Override Path.Segment next() { org.neo4j.driver.types.Path.Segment neoSegment = iterator.next() - return new Neo4jPathSegment(datastore,neoSegment) + return new Neo4jPathSegment(datastore, neoSegment) } } - static class Neo4jPathSegment implements Path.Segment { + static class Neo4jPathSegment implements Path.Segment { + final Neo4jDatastore datastore final org.neo4j.driver.types.Path.Segment neoSegment @@ -168,14 +171,14 @@ class Neo4jPath, E extends Neo4jEntity> implements P @Override Neo4jEntity start() { - if(start == null) + if (start == null) start = unmarshallNode(datastore, neoSegment.start()) return start } @Override Neo4jEntity end() { - if(end == null) { + if (end == null) { end = unmarshallNode(datastore, neoSegment.end()) } return end diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPersistentList.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPersistentList.groovy index b8789f548a6..949b6d247c6 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPersistentList.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPersistentList.groovy @@ -1,4 +1,3 @@ - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -20,6 +19,7 @@ package org.grails.datastore.gorm.neo4j.collection import groovy.transform.CompileStatic + import org.grails.datastore.gorm.neo4j.Neo4jSession import org.grails.datastore.gorm.neo4j.engine.Neo4jAssociationQueryExecutor import org.grails.datastore.mapping.collection.PersistentList @@ -27,6 +27,7 @@ import org.grails.datastore.mapping.dirty.checking.DirtyCheckable import org.grails.datastore.mapping.engine.EntityAccess import org.grails.datastore.mapping.model.types.Association import org.grails.datastore.mapping.model.types.ToMany + /** * Neo4j version of the {@link PersistentList} class * @@ -38,7 +39,8 @@ class Neo4jPersistentList extends PersistentList { protected transient final EntityAccess parentAccess protected transient final Association association - protected transient final @Delegate GraphAdapter graphAdapter + protected transient final @Delegate + GraphAdapter graphAdapter Neo4jPersistentList(Collection keys, Neo4jSession session, EntityAccess parentAccess, ToMany association) { super(keys, association.associatedEntity.javaClass, session) @@ -71,7 +73,7 @@ class Neo4jPersistentList extends PersistentList { @Override boolean add(Object o) { def added = super.add(o) - if(added) { + if (added) { adaptGraphUponAdd(o, currentlyInitializing()) } @@ -81,8 +83,8 @@ class Neo4jPersistentList extends PersistentList { @Override boolean removeAll(Collection c) { def removed = super.removeAll(c) - if(removed) { - for(o in c) { + if (removed) { + for (o in c) { adaptGraphUponRemove(o, currentlyInitializing()) } } @@ -92,7 +94,7 @@ class Neo4jPersistentList extends PersistentList { @Override boolean remove(Object o) { def removed = super.remove(o) - if(removed) { + if (removed) { adaptGraphUponRemove(o, currentlyInitializing()) } return removed diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPersistentSet.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPersistentSet.groovy index 2970d1354d8..02c12402efb 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPersistentSet.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPersistentSet.groovy @@ -19,6 +19,7 @@ package org.grails.datastore.gorm.neo4j.collection import groovy.transform.CompileStatic + import org.grails.datastore.gorm.neo4j.Neo4jSession import org.grails.datastore.gorm.neo4j.engine.Neo4jAssociationQueryExecutor import org.grails.datastore.mapping.collection.PersistentSet @@ -26,6 +27,7 @@ import org.grails.datastore.mapping.dirty.checking.DirtyCheckable import org.grails.datastore.mapping.engine.EntityAccess import org.grails.datastore.mapping.model.types.Association import org.grails.datastore.mapping.model.types.ToMany + /** * Neo4j version of the {@link PersistentSet} class * @@ -34,9 +36,11 @@ import org.grails.datastore.mapping.model.types.ToMany */ @CompileStatic class Neo4jPersistentSet extends PersistentSet { + protected final EntityAccess parentAccess protected final Association association - protected final @Delegate GraphAdapter graphAdapter + protected final @Delegate + GraphAdapter graphAdapter Neo4jPersistentSet(Collection keys, Neo4jSession session, EntityAccess parentAccess, ToMany association) { super(keys, association.associatedEntity.javaClass, session) @@ -58,8 +62,8 @@ class Neo4jPersistentSet extends PersistentSet { @Override boolean addAll(Collection c) { def added = super.addAll(c) - if(added) { - for( o in c ) { + if (added) { + for (o in c) { adaptGraphUponAdd(o, currentlyInitializing()) } } @@ -70,7 +74,7 @@ class Neo4jPersistentSet extends PersistentSet { @Override boolean add(Object o) { def added = super.add(o) - if(added) { + if (added) { adaptGraphUponAdd(o, currentlyInitializing()) } @@ -80,8 +84,8 @@ class Neo4jPersistentSet extends PersistentSet { @Override boolean removeAll(Collection c) { def removed = super.removeAll(c) - if(removed) { - for(o in c) { + if (removed) { + for (o in c) { adaptGraphUponRemove(o) } } @@ -91,7 +95,7 @@ class Neo4jPersistentSet extends PersistentSet { @Override boolean remove(Object o) { def removed = super.remove(o) - if(removed) { + if (removed) { adaptGraphUponRemove(o, currentlyInitializing()) } return removed @@ -114,8 +118,8 @@ class Neo4jPersistentSet extends PersistentSet { @Override void markDirty() { - if(!currentlyInitializing()) { - ((DirtyCheckable)parentAccess.entity).markDirty(association.getName()) + if (!currentlyInitializing()) { + ((DirtyCheckable) parentAccess.entity).markDirty(association.getName()) super.markDirty() } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPersistentSortedSet.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPersistentSortedSet.groovy index e8f3ca468fe..07021e31538 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPersistentSortedSet.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jPersistentSortedSet.groovy @@ -19,6 +19,7 @@ package org.grails.datastore.gorm.neo4j.collection import groovy.transform.CompileStatic + import org.grails.datastore.gorm.neo4j.Neo4jSession import org.grails.datastore.gorm.neo4j.engine.Neo4jAssociationQueryExecutor import org.grails.datastore.mapping.collection.PersistentSet @@ -27,6 +28,7 @@ import org.grails.datastore.mapping.dirty.checking.DirtyCheckable import org.grails.datastore.mapping.engine.EntityAccess import org.grails.datastore.mapping.model.types.Association import org.grails.datastore.mapping.model.types.ToMany + /** * Neo4j version of the {@link PersistentSet} class * @@ -35,9 +37,11 @@ import org.grails.datastore.mapping.model.types.ToMany */ @CompileStatic class Neo4jPersistentSortedSet extends PersistentSortedSet { + protected final EntityAccess parentAccess protected final Association association - protected final @Delegate GraphAdapter graphAdapter + protected final @Delegate + GraphAdapter graphAdapter Neo4jPersistentSortedSet(Collection keys, Neo4jSession session, EntityAccess parentAccess, ToMany association) { super(keys, association.associatedEntity.javaClass, session) @@ -58,8 +62,8 @@ class Neo4jPersistentSortedSet extends PersistentSortedSet { @Override boolean addAll(Collection c) { def added = super.addAll(c) - if(added) { - for( o in c ) { + if (added) { + for (o in c) { adaptGraphUponAdd(o, currentlyInitializing()) } } @@ -70,7 +74,7 @@ class Neo4jPersistentSortedSet extends PersistentSortedSet { @Override boolean add(Object o) { def added = super.add(o) - if(added) { + if (added) { adaptGraphUponAdd(o, currentlyInitializing()) } @@ -80,8 +84,8 @@ class Neo4jPersistentSortedSet extends PersistentSortedSet { @Override boolean removeAll(Collection c) { def removed = super.removeAll(c) - if(removed) { - for(o in c) { + if (removed) { + for (o in c) { adaptGraphUponRemove(o, currentlyInitializing()) } } @@ -91,7 +95,7 @@ class Neo4jPersistentSortedSet extends PersistentSortedSet { @Override boolean remove(Object o) { def removed = super.remove(o) - if(removed) { + if (removed) { adaptGraphUponRemove(o, currentlyInitializing()) } return removed diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jRelationship.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jRelationship.groovy index c144dbdc024..324b1e61264 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jRelationship.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jRelationship.groovy @@ -19,9 +19,9 @@ package org.grails.datastore.gorm.neo4j.collection -import grails.neo4j.Relationship import groovy.transform.CompileStatic -import org.grails.datastore.gorm.schemaless.DynamicAttributes + +import grails.neo4j.Relationship /** * Default implementation of the {@link Relationship} trait @@ -31,6 +31,7 @@ import org.grails.datastore.gorm.schemaless.DynamicAttributes */ @CompileStatic class Neo4jRelationship implements Relationship { + String type Neo4jRelationship(F from, T to, String type) { diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jResultList.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jResultList.groovy index a34b81a811f..c7a2a0a047b 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jResultList.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jResultList.groovy @@ -19,23 +19,22 @@ package org.grails.datastore.gorm.neo4j.collection import groovy.transform.CompileStatic + +import jakarta.persistence.LockModeType + +import org.neo4j.driver.Record +import org.neo4j.driver.Result +import org.neo4j.driver.Value +import org.neo4j.driver.types.Node +import org.neo4j.driver.types.Relationship + import org.grails.datastore.gorm.neo4j.CypherBuilder import org.grails.datastore.gorm.neo4j.RelationshipPersistentEntity import org.grails.datastore.gorm.neo4j.engine.Neo4jEntityPersister import org.grails.datastore.gorm.query.AbstractResultList -import org.grails.datastore.mapping.engine.EntityPersister import org.grails.datastore.mapping.model.PersistentEntity import org.grails.datastore.mapping.model.types.Association import org.grails.datastore.mapping.query.QueryException -import org.neo4j.driver.Record -import org.neo4j.driver.Result -import org.neo4j.driver.Value -import org.neo4j.driver.types.Entity -import org.neo4j.driver.types.Node -import org.neo4j.driver.types.Relationship - -import jakarta.persistence.LockModeType - /** * A Neo4j result list for decoding objects from the {@link Result} interface @@ -49,7 +48,7 @@ class Neo4jResultList extends AbstractResultList { private static final Map EMPTY_ASSOCIATIONS = Collections. emptyMap() private static final Map EMPTY_RESULT_DATA = Collections. emptyMap() - final protected transient Neo4jEntityPersister entityPersister + final protected transient Neo4jEntityPersister entityPersister protected transient Map initializedAssociations = EMPTY_ASSOCIATIONS protected transient Map initializedNodes = [:] @@ -57,7 +56,7 @@ class Neo4jResultList extends AbstractResultList { protected final LockModeType lockMode Neo4jResultList(int offset, Result cursor, Neo4jEntityPersister entityPersister, LockModeType lockMode = LockModeType.NONE) { - super(offset, (Iterator)cursor) + super(offset, (Iterator) cursor) this.entityPersister = entityPersister this.lockMode = lockMode } @@ -102,55 +101,49 @@ class Neo4jResultList extends AbstractResultList { if (next instanceof Node) { Node node = (Node) next return entityPersister.unmarshallOrFromCache(entityPersister.getPersistentEntity(), node, EMPTY_RESULT_DATA, initializedAssociations, lockMode) - } - else if(next instanceof Relationship) { + } else if (next instanceof Relationship) { PersistentEntity persistentEntity = entityPersister.getPersistentEntity() - if(persistentEntity instanceof RelationshipPersistentEntity) { + if (persistentEntity instanceof RelationshipPersistentEntity) { Relationship data = (Relationship) next return entityPersister.unmarshallOrFromCache(entityPersister.getPersistentEntity(), data, initializedAssociations, initializedNodes) - } - else { + } else { throw new QueryException("Query must return a node as the first column of the RETURN statement") } - } - else { + } else { Record record = (Record) next if (record.containsKey(CypherBuilder.NODE_DATA)) { Node data = (Node) record.get(CypherBuilder.NODE_DATA).asNode() return entityPersister.unmarshallOrFromCache(entityPersister.getPersistentEntity(), data, record.asMap(), initializedAssociations, lockMode) - } - else if(record.containsKey(CypherBuilder.REL_DATA)) { + } else if (record.containsKey(CypherBuilder.REL_DATA)) { PersistentEntity persistentEntity = entityPersister.getPersistentEntity() - if(persistentEntity instanceof RelationshipPersistentEntity) { + if (persistentEntity instanceof RelationshipPersistentEntity) { def recordMap = record.asMap() - RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity)persistentEntity + RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity) persistentEntity Relationship data = (Relationship) record.get(CypherBuilder.REL_DATA).asRelationship() this.initializedAssociations = new LinkedHashMap<>(initializedAssociations) - if(record.containsKey(RelationshipPersistentEntity.FROM)) { + if (record.containsKey(RelationshipPersistentEntity.FROM)) { Association fromAssociation = relEntity.getFrom() PersistentEntity fromEntity = fromAssociation.getAssociatedEntity() Neo4jEntityPersister fromPersister = entityPersister.getSession().getEntityPersister(fromEntity) this.initializedAssociations.put(fromAssociation, - fromPersister.unmarshallOrFromCache( fromEntity, record.get(RelationshipPersistentEntity.FROM).asNode(),recordMap, initializedAssociations) + fromPersister.unmarshallOrFromCache(fromEntity, record.get(RelationshipPersistentEntity.FROM).asNode(), recordMap, initializedAssociations) ) } - if(record.containsKey(RelationshipPersistentEntity.TO)) { + if (record.containsKey(RelationshipPersistentEntity.TO)) { Association toAssociation = relEntity.getTo() PersistentEntity toEntity = toAssociation.getAssociatedEntity() Neo4jEntityPersister fromPersister = entityPersister.getSession().getEntityPersister(toEntity) this.initializedAssociations.put(toAssociation, - fromPersister.unmarshallOrFromCache( toEntity, record.get(RelationshipPersistentEntity.TO).asNode() ,recordMap, initializedAssociations) + fromPersister.unmarshallOrFromCache(toEntity, record.get(RelationshipPersistentEntity.TO).asNode(), recordMap, initializedAssociations) ) } return entityPersister.unmarshallOrFromCache(entityPersister.getPersistentEntity(), data, initializedAssociations, initializedNodes) - } - else { + } else { throw new QueryException("Query must return a node as the first column of the RETURN statement") } - } - else { + } else { - Node node = record.values().find() { Value v -> + Node node = record.values().find() { Value v -> v.type() == entityPersister.getSession().boltDriver.defaultTypeSystem().NODE() }?.asNode() diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jSet.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jSet.groovy index e46852abcbc..d1bb0116eaa 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jSet.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jSet.groovy @@ -19,11 +19,13 @@ package org.grails.datastore.gorm.neo4j.collection import groovy.transform.CompileStatic + import org.grails.datastore.gorm.neo4j.Neo4jSession import org.grails.datastore.mapping.dirty.checking.DirtyCheckable import org.grails.datastore.mapping.dirty.checking.DirtyCheckingSet import org.grails.datastore.mapping.engine.EntityAccess import org.grails.datastore.mapping.model.types.Association + /** * A {@link DirtyCheckingSet} for Neo4j * @@ -32,13 +34,15 @@ import org.grails.datastore.mapping.model.types.Association */ @CompileStatic class Neo4jSet extends DirtyCheckingSet { + final transient Association association final transient Neo4jSession session - protected final transient @Delegate GraphAdapter graphAdapter + protected final transient @Delegate + GraphAdapter graphAdapter Neo4jSet(EntityAccess parentAccess, Association association, Set delegate, Neo4jSession session) { - super(delegate, (DirtyCheckable)parentAccess.getEntity(), association.getName()) + super(delegate, (DirtyCheckable) parentAccess.getEntity(), association.getName()) this.association = association this.session = session this.graphAdapter = new GraphAdapter(session, parentAccess, association) @@ -48,7 +52,7 @@ class Neo4jSet extends DirtyCheckingSet { boolean add(Object o) { def added = super.add(o) - if(added) { + if (added) { adaptGraphUponAdd(o) } return added @@ -57,8 +61,8 @@ class Neo4jSet extends DirtyCheckingSet { @Override boolean addAll(Collection c) { def added = super.addAll(c) - if(added) { - for( o in c ) { + if (added) { + for (o in c) { adaptGraphUponAdd(o) } } @@ -69,8 +73,8 @@ class Neo4jSet extends DirtyCheckingSet { @Override boolean removeAll(Collection c) { def removed = super.removeAll(c) - if(removed) { - for(o in c) { + if (removed) { + for (o in c) { adaptGraphUponRemove(o) } } @@ -80,7 +84,7 @@ class Neo4jSet extends DirtyCheckingSet { @Override boolean remove(Object o) { def removed = super.remove(o) - if(removed) { + if (removed) { adaptGraphUponRemove(o) } return removed diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jSortedSet.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jSortedSet.groovy index c52dccd1179..c0a6b79c178 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jSortedSet.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/collection/Neo4jSortedSet.groovy @@ -32,13 +32,15 @@ import org.grails.datastore.mapping.model.types.Association * @since 5.0 */ class Neo4jSortedSet extends DirtyCheckingSortedSet { + final transient Association association final transient Neo4jSession session - protected final transient @Delegate GraphAdapter graphAdapter + protected final transient @Delegate + GraphAdapter graphAdapter Neo4jSortedSet(EntityAccess parentAccess, Association association, SortedSet delegate, Neo4jSession session) { - super(delegate, (DirtyCheckable)parentAccess.getEntity(), association.getName()) + super(delegate, (DirtyCheckable) parentAccess.getEntity(), association.getName()) this.association = association this.session = session this.graphAdapter = new GraphAdapter(session, parentAccess, association) @@ -48,7 +50,7 @@ class Neo4jSortedSet extends DirtyCheckingSortedSet { boolean add(Object o) { def added = super.add(o) - if(added) { + if (added) { adaptGraphUponAdd(o) } return added @@ -57,8 +59,8 @@ class Neo4jSortedSet extends DirtyCheckingSortedSet { @Override boolean addAll(Collection c) { def added = super.addAll(c) - if(added) { - for( o in c ) { + if (added) { + for (o in c) { adaptGraphUponAdd(o) } } @@ -69,8 +71,8 @@ class Neo4jSortedSet extends DirtyCheckingSortedSet { @Override boolean removeAll(Collection c) { def removed = super.removeAll(c) - if(removed) { - for(o in c) { + if (removed) { + for (o in c) { adaptGraphUponRemove(o) } } @@ -80,7 +82,7 @@ class Neo4jSortedSet extends DirtyCheckingSortedSet { @Override boolean remove(Object o) { def removed = super.remove(o) - if(removed) { + if (removed) { adaptGraphUponRemove(o) } return removed diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/config/Neo4jDriverConfigBuilder.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/config/Neo4jDriverConfigBuilder.groovy index 813cf99deed..ecf909edfc5 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/config/Neo4jDriverConfigBuilder.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/config/Neo4jDriverConfigBuilder.groovy @@ -18,15 +18,18 @@ */ package org.grails.datastore.gorm.neo4j.config +import java.lang.reflect.Method +import java.lang.reflect.Modifier + import groovy.transform.CompileStatic import groovy.util.logging.Slf4j -import org.grails.datastore.mapping.config.ConfigurationBuilder + import org.neo4j.driver.Config + import org.springframework.core.env.PropertyResolver import org.springframework.util.ReflectionUtils -import java.lang.reflect.Method -import java.lang.reflect.Modifier +import org.grails.datastore.mapping.config.ConfigurationBuilder /** * Constructs the Neo4j driver configuration @@ -62,13 +65,12 @@ class Neo4jDriverConfigBuilder extends ConfigurationBuilder { + /** + * @return Whether the embedded server capability is available or not + */ + public static boolean isEmbeddedAvailable() { + return ClassUtils.isPresent("org.neo4j.harness.ServerControls", Neo4jConnectionSourceFactory.class.getClassLoader()); + } + @Override protected Neo4jConnectionSourceSettings buildSettings(String name, PropertyResolver configuration, F fallbackSettings, boolean isDefaultDataSource) { - String prefix = isDefaultDataSource? Settings.PREFIX : Settings.SETTING_CONNECTIONS + "." + name; + String prefix = isDefaultDataSource ? Settings.PREFIX : Settings.SETTING_CONNECTIONS + "." + name; Neo4jConnectionSourceSettingsBuilder settingsBuilder = new Neo4jConnectionSourceSettingsBuilder(configuration, prefix, fallbackSettings); Neo4jConnectionSourceSettings settings = settingsBuilder.build(); return settings; @@ -63,29 +76,27 @@ public ConnectionSource create(String nam final String username = settings.getUsername(); final String password = settings.getPassword(); final Neo4jConnectionSourceSettings.ConnectionType type = settings.getType(); - if(type == Neo4jConnectionSourceSettings.ConnectionType.embedded && ConnectionSource.DEFAULT.equals(name)) { - if(isEmbeddedAvailable()) { + if (type == Neo4jConnectionSourceSettings.ConnectionType.embedded && ConnectionSource.DEFAULT.equals(name)) { + if (isEmbeddedAvailable()) { Neo4jConnectionSourceSettings.EmbeddedSettings embeddedSettings = settings.getEmbedded(); final String location = settings.getLocation() != null ? settings.getLocation() : embeddedSettings.getDirectory(); final Map options = embeddedSettings.getOptions(); final File dataDir; try { - if(location != null) { + if (location != null) { dataDir = new File(location); - } - else if(embeddedSettings.isEphemeral()) { + } else if (embeddedSettings.isEphemeral()) { dataDir = File.createTempFile("neo4j-temporary-data", "-tempdir"); - } - else { + } else { dataDir = new File(Settings.DEFAULT_LOCATION); } } catch (IOException e) { throw new DatastoreConfigurationException("Unable to create temporary data directory: " + e.getMessage(), e); } - if(embeddedSettings.isDropData() || embeddedSettings.isEphemeral()) { + if (embeddedSettings.isDropData() || embeddedSettings.isEphemeral()) { dataDir.delete(); } - if(embeddedSettings.isEphemeral()) { + if (embeddedSettings.isEphemeral()) { dataDir.deleteOnExit(); } ServerControls serverControls; @@ -93,24 +104,22 @@ else if(embeddedSettings.isEphemeral()) { serverControls = url != null ? EmbeddedNeo4jServer.start(url, dataDir, options) : EmbeddedNeo4jServer.start(dataDir, options); Config config = Config.builder().withoutEncryption().build(); URI boltURI = serverControls.boltURI(); - Driver driver = GraphDatabase.driver(boltURI, config); + Driver driver = GraphDatabase.driver(boltURI, config); return new Neo4jEmbeddedConnectionSource(name, driver, settings, serverControls); } catch (Throwable e) { throw new DatastoreConfigurationException("Unable to start embedded Neo4j server: " + e.getMessage(), e); } - } - else { + } else { throw new DatastoreConfigurationException("Embedded Neo4j server was configured but 'neo4j-harness' classes not found on classpath."); } } AuthToken authToken = null; - if(username != null && password != null) { + if (username != null && password != null) { authToken = AuthTokens.basic(username, password); } - Config driverConfig = settings.getOptions().build(); Driver driver = GraphDatabase.driver(url != null ? url : Settings.DEFAULT_URL, authToken, driverConfig); return new DefaultConnectionSource<>(name, driver, settings); @@ -121,11 +130,4 @@ public Serializable getConnectionSourcesConfigurationKey() { return Settings.SETTING_CONNECTIONS; } - /** - * @return Whether the embedded server capability is available or not - */ - public static boolean isEmbeddedAvailable() { - return ClassUtils.isPresent("org.neo4j.harness.ServerControls", Neo4jConnectionSourceFactory.class.getClassLoader()); - } - } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/connections/Neo4jConnectionSourceSettings.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/connections/Neo4jConnectionSourceSettings.groovy index b34f63096f4..a481ac1acc6 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/connections/Neo4jConnectionSourceSettings.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/connections/Neo4jConnectionSourceSettings.groovy @@ -21,6 +21,7 @@ package org.grails.datastore.gorm.neo4j.connections import groovy.transform.AutoClone import groovy.transform.builder.Builder import groovy.transform.builder.SimpleStrategy + import org.grails.datastore.gorm.neo4j.config.Neo4jDriverConfigBuilder import org.grails.datastore.gorm.neo4j.config.Settings import org.grails.datastore.mapping.core.connections.ConnectionSourceSettings @@ -99,6 +100,7 @@ class Neo4jConnectionSourceSettings extends ConnectionSourceSettings implements } static enum ConnectionType { + remote, embedded } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/connections/Neo4jConnectionSourceSettingsBuilder.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/connections/Neo4jConnectionSourceSettingsBuilder.groovy index 8f339f12ceb..432040f63f2 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/connections/Neo4jConnectionSourceSettingsBuilder.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/connections/Neo4jConnectionSourceSettingsBuilder.groovy @@ -19,10 +19,12 @@ package org.grails.datastore.gorm.neo4j.connections import groovy.transform.CompileStatic + +import org.springframework.core.env.PropertyResolver + import org.grails.datastore.gorm.neo4j.config.Settings import org.grails.datastore.mapping.config.ConfigurationBuilder import org.grails.datastore.mapping.core.connections.ConnectionSourceSettings -import org.springframework.core.env.PropertyResolver /** * A builder for creating {@link Neo4jConnectionSourceSettings} objects diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/connections/Neo4jEmbeddedConnectionSource.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/connections/Neo4jEmbeddedConnectionSource.java index b6b9a509bb7..b976ba49e3e 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/connections/Neo4jEmbeddedConnectionSource.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/connections/Neo4jEmbeddedConnectionSource.java @@ -18,11 +18,12 @@ */ package org.grails.datastore.gorm.neo4j.connections; -import org.grails.datastore.mapping.core.connections.DefaultConnectionSource; +import java.io.IOException; + import org.neo4j.driver.Driver; import org.neo4j.harness.ServerControls; -import java.io.IOException; +import org.grails.datastore.mapping.core.connections.DefaultConnectionSource; /** * A {@link org.grails.datastore.mapping.core.connections.ConnectionSource} for embedded Neo4j @@ -31,6 +32,7 @@ * @since 6.0 */ public class Neo4jEmbeddedConnectionSource extends DefaultConnectionSource { + protected final ServerControls serverControls; Neo4jEmbeddedConnectionSource(String name, Driver source, Neo4jConnectionSourceSettings settings, ServerControls serverControls) { diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/DynamicAssociationSupport.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/DynamicAssociationSupport.java index 24695931ed4..ef02d1a2f22 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/DynamicAssociationSupport.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/DynamicAssociationSupport.java @@ -18,6 +18,21 @@ */ package org.grails.datastore.gorm.neo4j.engine; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.neo4j.driver.QueryRunner; +import org.neo4j.driver.Record; +import org.neo4j.driver.Result; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.grails.datastore.gorm.neo4j.GraphPersistentEntity; import org.grails.datastore.gorm.neo4j.Neo4jMappingContext; import org.grails.datastore.gorm.neo4j.Neo4jSession; @@ -28,16 +43,7 @@ import org.grails.datastore.gorm.neo4j.util.IteratorUtil; import org.grails.datastore.gorm.schemaless.DynamicAttributes; import org.grails.datastore.mapping.engine.EntityAccess; -import org.grails.datastore.mapping.engine.NonPersistentTypeException; import org.grails.datastore.mapping.model.config.GormProperties; -import org.neo4j.driver.Record; -import org.neo4j.driver.Result; -import org.neo4j.driver.QueryRunner; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.Serializable; -import java.util.*; /** * Support class for dynamic associations @@ -49,32 +55,32 @@ public class DynamicAssociationSupport { private static final Logger log = LoggerFactory.getLogger(DynamicAssociationSupport.class); - public static Map> loadDynamicAssociations(Neo4jSession session, GraphPersistentEntity graphPersistentEntity, DynamicAttributes object, Serializable id) { + public static Map> loadDynamicAssociations(Neo4jSession session, GraphPersistentEntity graphPersistentEntity, DynamicAttributes object, Serializable id) { Map> relationshipsMap = new HashMap<>(); final boolean hasDynamicAssociations = graphPersistentEntity.hasDynamicAssociations(); Object alreadyLoaded = session.getAttribute(object, Neo4jEntityPersister.DYNAMIC_ASSOCIATION_PARAM); - if(alreadyLoaded == null && hasDynamicAssociations) { + if (alreadyLoaded == null && hasDynamicAssociations) { session.setAttribute(object, Neo4jEntityPersister.DYNAMIC_ASSOCIATION_PARAM, Boolean.TRUE); final String cypher = graphPersistentEntity.formatDynamicAssociationQuery(); - final Map isMap = Collections.singletonMap(GormProperties.IDENTITY, id); + final Map isMap = Collections.singletonMap(GormProperties.IDENTITY, id); final QueryRunner boltSession = session.hasTransaction() ? session.getTransaction().getNativeTransaction() : session.getNativeInterface(); - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("QUERY Cypher [{}] for parameters [{}]", cypher, isMap); } final Result relationships = boltSession.run(cypher, isMap); - while(relationships.hasNext()) { + while (relationships.hasNext()) { final Record row = relationships.next(); String relType = row.get("relType").asString(); Boolean outGoing = row.get("out").asBoolean(); Map values = row.get("values").asMap(); TypeDirectionPair key = new TypeDirectionPair(relType, outGoing); - if(row.containsKey(RelationshipPendingInsert.TARGET_TYPE) && !row.get(RelationshipPendingInsert.TARGET_TYPE).isNull()) { + if (row.containsKey(RelationshipPendingInsert.TARGET_TYPE) && !row.get(RelationshipPendingInsert.TARGET_TYPE).isNull()) { key.setTargetType( - row.get(RelationshipPendingInsert.TARGET_TYPE).asString() + row.get(RelationshipPendingInsert.TARGET_TYPE).asString() ); relationshipsMap.put(key, values); } @@ -82,14 +88,14 @@ public static Map> loadDynamicAssociation // if the relationship map is not empty as this point there are dynamic relationships that need to be loaded as undeclared if (!relationshipsMap.isEmpty()) { Neo4jMappingContext mappingContext = session.getMappingContext(); - for (Map.Entry> entry: relationshipsMap.entrySet()) { + for (Map.Entry> entry : relationshipsMap.entrySet()) { EntityAccess entityAccess = session.createEntityAccess(graphPersistentEntity, object); TypeDirectionPair key = entry.getKey(); if (key.isOutgoing()) { Map relationshipData = entry.getValue(); Object idsObject = relationshipData.get("ids"); Object labelsObject = relationshipData.get("labels"); - if((idsObject instanceof Iterable) && (labelsObject instanceof Iterable)) { + if ((idsObject instanceof Iterable) && (labelsObject instanceof Iterable)) { Iterator idIter = ((Iterable) idsObject).iterator(); String targetType = key.getTargetType(); @@ -102,25 +108,24 @@ public static Map> loadDynamicAssociation Collection nextLabels = labelIter.next(); Collection labels = nextLabels.isEmpty() ? Collections.singletonList(targetType) : nextLabels; associatedEntity = mappingContext.findPersistentEntityForLabels(labels); - if(associatedEntity == null) { + if (associatedEntity == null) { associatedEntity = mappingContext.findPersistentEntityForLabels(Collections.singletonList(targetType)); } - if(associatedEntity == null) { + if (associatedEntity == null) { continue; } Object proxy = mappingContext.getProxyFactory().createProxy( - session, - associatedEntity.getJavaClass(), - targetId + session, + associatedEntity.getJavaClass(), + targetId ); values.add(proxy); } // for single instances and singular property name do not use an array Object value; - if(values.size() == 1 && isSingular(key.getType())) { + if (values.size() == 1 && isSingular(key.getType())) { value = IteratorUtil.singleOrNull(values); - } - else { + } else { DynamicToManyAssociation dynamicAssociation = new DynamicToManyAssociation(graphPersistentEntity, graphPersistentEntity.getMappingContext(), key.getType(), associatedEntity); value = new Neo4jList(entityAccess, dynamicAssociation, values, session); } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jAssociationQueryExecutor.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jAssociationQueryExecutor.groovy index 455b0bf5646..3d6158aef57 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jAssociationQueryExecutor.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jAssociationQueryExecutor.groovy @@ -20,6 +20,11 @@ package org.grails.datastore.gorm.neo4j.engine import groovy.transform.CompileStatic import groovy.util.logging.Slf4j + +import org.neo4j.driver.QueryRunner +import org.neo4j.driver.Result +import org.neo4j.driver.Session + import org.grails.datastore.gorm.neo4j.CypherBuilder import org.grails.datastore.gorm.neo4j.GraphPersistentEntity import org.grails.datastore.gorm.neo4j.Neo4jSession @@ -35,12 +40,6 @@ import org.grails.datastore.mapping.model.types.Basic import org.grails.datastore.mapping.model.types.ManyToOne import org.grails.datastore.mapping.model.types.ToMany import org.grails.datastore.mapping.model.types.ToOne -import org.neo4j.driver.Session -import org.neo4j.driver.Result -import org.neo4j.driver.QueryRunner - -import jakarta.persistence.FetchType - /** * Responsible for lazy loading associations @@ -61,13 +60,12 @@ class Neo4jAssociationQueryExecutor implements AssociationQueryExecutor query(Serializable primaryKey) { - QueryRunner statementRunner = session.hasTransaction() ? session.getTransaction().getNativeTransaction() : (Session)session.nativeInterface + QueryRunner statementRunner = session.hasTransaction() ? session.getTransaction().getNativeTransaction() : (Session) session.nativeInterface String relType - GraphPersistentEntity parent = (GraphPersistentEntity)association.owner - GraphPersistentEntity related = (GraphPersistentEntity)indexedEntity + GraphPersistentEntity parent = (GraphPersistentEntity) association.owner + GraphPersistentEntity related = (GraphPersistentEntity) indexedEntity boolean isRelationship = related.isRelationshipEntity() - if(isRelationship) { - RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity)related + if (isRelationship) { + RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity) related GraphPersistentEntity fromEntity = (GraphPersistentEntity) relEntity.getFrom().getAssociatedEntity() GraphPersistentEntity toEntity = (GraphPersistentEntity) relEntity.getTo().getAssociatedEntity() - if(parent == fromEntity) { + if (parent == fromEntity) { relType = "-[rel]->" related = toEntity - } - else { + } else { relType = "<-[rel]-" parent = toEntity related = fromEntity } - } - else { + } else { relType = RelationshipUtils.matchForAssociation(association) } @@ -113,20 +109,19 @@ class Neo4jAssociationQueryExecutor implements AssociationQueryExecutor associations = new TreeSet((Comparator){ Association a1, Association a2 -> a1.name <=> a2.name }) + Set associations = new TreeSet((Comparator) { Association a1, Association a2 -> a1.name <=> a2.name }) PersistentEntity entity = association.associatedEntity if (entity) { Collection childEntities = entity.mappingContext.getChildEntities(entity) @@ -137,32 +132,31 @@ class Neo4jAssociationQueryExecutor implements AssociationQueryExecutor 0) { + if (associations.size() > 0) { int i = 0 List previousAssociations = [] - for(Association association in associations) { - if(association.isBasic()) continue + for (Association association in associations) { + if (association.isBasic()) continue boolean isEager = ((Property) association.mapping.mappedForm).isLazy() String r = "r${i++}" String associationName = association.name - GraphPersistentEntity associatedGraphEntity = (GraphPersistentEntity)association.associatedEntity + GraphPersistentEntity associatedGraphEntity = (GraphPersistentEntity) association.associatedEntity boolean isAssociationRelationshipEntity = associatedGraphEntity.isRelationshipEntity() boolean isToMany = association instanceof ToMany boolean isToOne = association instanceof ToOne - boolean lazy = false - if(isToOne && !isEager) { + boolean lazy = false + if (isToOne && !isEager) { Property propertyMapping = association.mapping.mappedForm Boolean isLazy = propertyMapping.getLazy() lazy = (isLazy != null ? isLazy : (association instanceof ManyToOne ? !association.isCircular() : true)) - } - else if(isToMany) { - lazy = ((ToMany)association).lazy + } else if (isToMany) { + lazy = ((ToMany) association).lazy } // if there are associations, add a join to get them @@ -178,16 +172,15 @@ class Neo4jAssociationQueryExecutor implements AssociationQueryExecutorId lookups. - if((isToMany && lazy) || (isToOne && !isEager)) { + if ((isToMany && lazy) || (isToOne && !isEager)) { withMatch += "collect(DISTINCT ${associatedGraphEntity.formatId(associationNodeRef)}) as ${associationIdsRef}" returnString.append(", ").append(associationIdsRef) previousAssociations << associationIdsRef addOptionalMatch = true - } - else if(isEager) { + } else if (isEager) { withMatch += "collect(DISTINCT $associationNodeRef) as $associationNodesRef" returnString.append(", ").append(associationNodesRef) - if(isAssociationRelationshipEntity) { + if (isAssociationRelationshipEntity) { withMatch += ", collect($r) as ${associationName}Rels" returnString.append(", ").append("${associationName}Rels") } @@ -195,15 +188,15 @@ class Neo4jAssociationQueryExecutor implements AssociationQueryExecutor results = [] - while(result.hasNext()) { + while (result.hasNext()) { def id = result.next().get(GormProperties.IDENTITY).asObject() - results.add( session.proxy(related.javaClass, id as Serializable) ) + results.add(session.proxy(related.javaClass, id as Serializable)) } return results - } - else { + } else { def resultList = new Neo4jResultList(0, result, isRelationship ? session.getEntityPersister(indexedEntity) : session.getEntityPersister(related)) - if(association.isBidirectional()) { + if (association.isBidirectional()) { def inverseSide = association.inverseSide - if(inverseSide instanceof ToOne) { + if (inverseSide instanceof ToOne) { def parentObject = session.getCachedInstance(association.getOwner().getJavaClass(), primaryKey) - if(parentObject != null) { - resultList.setInitializedAssociations(Collections.singletonMap(inverseSide, parentObject)) + if (parentObject != null) { + resultList.setInitializedAssociations(Collections. singletonMap(inverseSide, parentObject)) } } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jEntityPersister.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jEntityPersister.java index 1b30bd09388..299d39168b0 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jEntityPersister.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jEntityPersister.java @@ -18,18 +18,68 @@ */ package org.grails.datastore.gorm.neo4j.engine; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + import groovy.lang.GroovyObject; import org.codehaus.groovy.runtime.DefaultGroovyMethods; + +import jakarta.persistence.FetchType; +import jakarta.persistence.LockModeType; + +import org.neo4j.driver.QueryRunner; +import org.neo4j.driver.Record; +import org.neo4j.driver.Result; +import org.neo4j.driver.summary.ResultSummary; +import org.neo4j.driver.summary.SummaryCounters; +import org.neo4j.driver.types.Entity; +import org.neo4j.driver.types.Node; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.util.Assert; + import org.grails.datastore.gorm.GormEntity; -import org.grails.datastore.gorm.neo4j.*; -import org.grails.datastore.gorm.neo4j.collection.*; +import org.grails.datastore.gorm.neo4j.CypherBuilder; +import org.grails.datastore.gorm.neo4j.GraphPersistentEntity; +import org.grails.datastore.gorm.neo4j.IdGenerator; +import org.grails.datastore.gorm.neo4j.Neo4jMappingContext; +import org.grails.datastore.gorm.neo4j.Neo4jSession; +import org.grails.datastore.gorm.neo4j.RelationshipPersistentEntity; +import org.grails.datastore.gorm.neo4j.RelationshipUtils; +import org.grails.datastore.gorm.neo4j.collection.Neo4jList; +import org.grails.datastore.gorm.neo4j.collection.Neo4jPersistentList; +import org.grails.datastore.gorm.neo4j.collection.Neo4jPersistentSet; +import org.grails.datastore.gorm.neo4j.collection.Neo4jPersistentSortedSet; +import org.grails.datastore.gorm.neo4j.collection.Neo4jResultList; +import org.grails.datastore.gorm.neo4j.collection.Neo4jSet; +import org.grails.datastore.gorm.neo4j.collection.Neo4jSortedSet; import org.grails.datastore.gorm.neo4j.mapping.config.DynamicToOneAssociation; import org.grails.datastore.gorm.neo4j.util.IteratorUtil; import org.grails.datastore.gorm.schemaless.DynamicAttributes; import org.grails.datastore.mapping.collection.PersistentCollection; import org.grails.datastore.mapping.core.IdentityGenerationException; import org.grails.datastore.mapping.core.Session; -import org.grails.datastore.mapping.core.impl.*; +import org.grails.datastore.mapping.core.impl.PendingDeleteAdapter; +import org.grails.datastore.mapping.core.impl.PendingInsertAdapter; +import org.grails.datastore.mapping.core.impl.PendingOperation; +import org.grails.datastore.mapping.core.impl.PendingOperationAdapter; +import org.grails.datastore.mapping.core.impl.PendingUpdateAdapter; import org.grails.datastore.mapping.dirty.checking.DirtyCheckable; import org.grails.datastore.mapping.dirty.checking.DirtyCheckableCollection; import org.grails.datastore.mapping.engine.EntityAccess; @@ -37,31 +87,24 @@ import org.grails.datastore.mapping.model.MappingContext; import org.grails.datastore.mapping.model.PersistentEntity; import org.grails.datastore.mapping.model.PersistentProperty; -import org.grails.datastore.mapping.model.config.GormMappingConfigurationStrategy; import org.grails.datastore.mapping.model.config.GormProperties; -import org.grails.datastore.mapping.model.types.*; +import org.grails.datastore.mapping.model.types.Association; +import org.grails.datastore.mapping.model.types.Basic; +import org.grails.datastore.mapping.model.types.ManyToMany; +import org.grails.datastore.mapping.model.types.OneToMany; +import org.grails.datastore.mapping.model.types.OneToOne; +import org.grails.datastore.mapping.model.types.Simple; +import org.grails.datastore.mapping.model.types.TenantId; +import org.grails.datastore.mapping.model.types.ToMany; +import org.grails.datastore.mapping.model.types.ToOne; import org.grails.datastore.mapping.proxy.EntityProxy; import org.grails.datastore.mapping.query.Query; import org.grails.datastore.mapping.reflect.EntityReflector; -import org.neo4j.driver.Record; -import org.neo4j.driver.Result; -import org.neo4j.driver.QueryRunner; -import org.neo4j.driver.summary.ResultSummary; -import org.neo4j.driver.summary.SummaryCounters; -import org.neo4j.driver.types.Entity; -import org.neo4j.driver.types.Node; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.util.Assert; - -import jakarta.persistence.FetchType; -import jakarta.persistence.LockModeType; -import java.io.Serializable; -import java.util.*; -import static org.grails.datastore.mapping.query.Query.*; +import static org.grails.datastore.mapping.query.Query.Conjunction; +import static org.grails.datastore.mapping.query.Query.Criterion; +import static org.grails.datastore.mapping.query.Query.In; +import static org.grails.datastore.mapping.query.Query.Junction; /** * Core {@link EntityPersister} implementation responsible for CRUD operations against the Graph. @@ -72,16 +115,23 @@ */ public class Neo4jEntityPersister extends EntityPersister { - public static final String DYNAMIC_ASSOCIATION_PARAM = "org.grails.neo4j.DYNAMIC_ASSOCIATIONS"; - private static Logger log = LoggerFactory.getLogger(Neo4jEntityPersister.class); - + private static final Logger log = LoggerFactory.getLogger(Neo4jEntityPersister.class); public Neo4jEntityPersister(MappingContext mappingContext, PersistentEntity entity, Session session, ApplicationEventPublisher publisher) { super(mappingContext, entity, session, publisher); } + public static long countUpdates(Result execute) { + ResultSummary resultSummary = execute.consume(); + SummaryCounters counters = resultSummary.counters(); + if (counters.containsUpdates()) { + return counters.nodesCreated() + counters.nodesDeleted() + counters.propertiesSet() + counters.relationshipsCreated() + counters.relationshipsDeleted(); + } else { + return 0; + } + } @Override public Neo4jSession getSession() { @@ -107,23 +157,22 @@ protected List persistEntities(final PersistentEntity pe, @Suppres GraphPersistentEntity graphPersistentEntity = (GraphPersistentEntity) pe; List idList = new ArrayList<>(); - if(graphPersistentEntity.hasDynamicAssociations()) { + if (graphPersistentEntity.hasDynamicAssociations()) { // dynamic association entities cannot be batch inserted for (Object obj : objs) { Serializable id = persistEntity(pe, obj); - if(id != null) { + if (id != null) { idList.add(id); } } - } - else if(graphPersistentEntity.isNativeId() && !graphPersistentEntity.isRelationshipEntity()) { + } else if (graphPersistentEntity.isNativeId() && !graphPersistentEntity.isRelationshipEntity()) { List entityAccesses = new ArrayList<>(); // optimize batch inserts for multiple entities with native id final Neo4jSession session = getSession(); StringBuilder createCypher = new StringBuilder(CypherBuilder.CYPHER_CREATE); int listIndex = 0; - List> cascadingOperations = new ArrayList<>(); + List> cascadingOperations = new ArrayList<>(); final Map params = new HashMap<>(1); final Map indexMap = new HashMap<>(); int insertIndex = 0; @@ -153,8 +202,7 @@ else if(graphPersistentEntity.isNativeId() && !graphPersistentEntity.isRelations registerPendingUpdate(session, persistentEntity, entityAccess, obj, identifier); idList.add(identifier); - } - else { + } else { final PendingInsertAdapter pendingInsert = new PendingInsertAdapter(persistentEntity, identifier, obj, entityAccess) { @Override public void run() { @@ -187,7 +235,7 @@ public void run() { // temporarily add null so it is replaced later idList.add(null); - if(pendingInsert.isVetoed()) { + if (pendingInsert.isVetoed()) { continue; } @@ -195,33 +243,33 @@ public void run() { indexMap.put(insertIndex++, listIndex - 1); entityAccesses.add(entityAccess); - if(previous) { + if (previous) { createCypher.append(CypherBuilder.COMMAND_SEPARATOR); } session.buildEntityCreateOperation(createCypher, String.valueOf(insertIndex), entityAccess.getPersistentEntity(), pendingInsert, params, cascadingOperations); - if(iterator.hasNext()) { + if (iterator.hasNext()) { previous = true; } } } - if(insertIndex > 0) { + if (insertIndex > 0) { QueryRunner statementRunner = session.hasTransaction() ? session.getTransaction().getNativeTransaction() : session.getNativeInterface(); - final String finalCypher = createCypher.toString() + " RETURN *"; - if(log.isDebugEnabled()) { + final String finalCypher = createCypher + " RETURN *"; + if (log.isDebugEnabled()) { log.debug("CREATE Cypher [{}] for parameters [{}]", finalCypher, params); } - if(graphPersistentEntity.hasDynamicAssociations()) { + if (graphPersistentEntity.hasDynamicAssociations()) { params.remove(DYNAMIC_ASSOCIATION_PARAM); } final Result result = statementRunner.run(finalCypher, params); - if(!result.hasNext()) { + if (!result.hasNext()) { throw new IdentityGenerationException("CREATE operation did not generate an identifier for entity " + pe.getJavaClass()); } @@ -231,7 +279,7 @@ public void run() { Assert.notNull(targetIndex, "Should never be null. Please file an issue"); final Node node = record.get("n" + (j + 1)).asNode(); - if(node == null) { + if (node == null) { throw new IdentityGenerationException("CREATE operation did not generate an identifier for entity " + pe.getJavaClass()); } final long identifier = node.id(); @@ -242,13 +290,12 @@ public void run() { } } - } - else { + } else { for (Object obj : objs) { final EntityAccess entityAccess = createEntityAccess(pe, obj); Serializable id = persistEntity(entityAccess.getPersistentEntity(), obj); - if(id != null) { + if (id != null) { idList.add(id); } } @@ -259,7 +306,7 @@ public void run() { @Override protected Object retrieveEntity(PersistentEntity pe, Serializable key) { - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("Retrieving entity [{}] by node id [{}]", pe.getJavaClass(), key); } @@ -270,25 +317,26 @@ protected Object retrieveEntity(PersistentEntity pe, Serializable key) { public Object unmarshallOrFromCache(PersistentEntity defaultPersistentEntity, Map resultData) { - Node data = (Node)resultData.get(CypherBuilder.NODE_DATA); + Node data = (Node) resultData.get(CypherBuilder.NODE_DATA); return unmarshallOrFromCache(defaultPersistentEntity, data, resultData); } public Object unmarshallOrFromCache(PersistentEntity defaultPersistentEntity, Node data) { - return unmarshallOrFromCache(defaultPersistentEntity, data, Collections.emptyMap()); + return unmarshallOrFromCache(defaultPersistentEntity, data, Collections.emptyMap()); } public Object unmarshallOrFromCache(PersistentEntity defaultPersistentEntity, Node data, Map resultData) { - return unmarshallOrFromCache(defaultPersistentEntity, data, resultData, Collections.emptyMap()); + return unmarshallOrFromCache(defaultPersistentEntity, data, resultData, Collections.emptyMap()); } - public Object unmarshallOrFromCache(PersistentEntity defaultPersistentEntity, Node data, Map resultData, Map initializedAssociations ) { + public Object unmarshallOrFromCache(PersistentEntity defaultPersistentEntity, Node data, Map resultData, Map initializedAssociations) { return unmarshallOrFromCache(defaultPersistentEntity, data, resultData, initializedAssociations, LockModeType.NONE); } + public Object unmarshallOrFromCache(PersistentEntity defaultPersistentEntity, Node data, Map resultData, Map initializedAssociations, LockModeType lockModeType) { final Neo4jSession session = getSession(); if (LockModeType.PESSIMISTIC_WRITE.equals(lockModeType)) { - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("Locking entity [{}] node [{}] for pessimistic write", defaultPersistentEntity.getName(), data.id()); } throw new UnsupportedOperationException("Write locks are not supported by the Bolt Java Driver."); @@ -306,48 +354,45 @@ public Object unmarshallOrFromCache(PersistentEntity defaultPersistentEntity, No return instance; } - public Object unmarshallOrFromCache(PersistentEntity entity, org.neo4j.driver.types.Relationship data, Map initializedAssociations, Map initializedNodes) { Object object = unmarshallOrFromCache(entity, data, initializedAssociations); RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity) entity; - if(object != null) { + if (object != null) { EntityReflector reflector = entity.getReflector(); Object from = reflector.getProperty(object, RelationshipPersistentEntity.FROM); - if(from == null || from instanceof EntityProxy) { + if (from == null || from instanceof EntityProxy) { long nodeId = data.startNodeId(); - if(initializedNodes.containsKey(nodeId)) { + if (initializedNodes.containsKey(nodeId)) { reflector.setProperty( - object, - RelationshipPersistentEntity.FROM, unmarshallOrFromCache(relEntity.getFromEntity(), initializedNodes.get(nodeId)) + object, + RelationshipPersistentEntity.FROM, unmarshallOrFromCache(relEntity.getFromEntity(), initializedNodes.get(nodeId)) ); - } - else { + } else { reflector.setProperty( - object, - RelationshipPersistentEntity.FROM, session.proxy( relEntity.getFrom().getType(), nodeId) + object, + RelationshipPersistentEntity.FROM, session.proxy(relEntity.getFrom().getType(), nodeId) ); } } Object to = reflector.getProperty(object, RelationshipPersistentEntity.TO); - if(to == null || to instanceof EntityProxy) { + if (to == null || to instanceof EntityProxy) { long nodeId = data.endNodeId(); - if(initializedNodes.containsKey(nodeId)) { + if (initializedNodes.containsKey(nodeId)) { reflector.setProperty( - object, - RelationshipPersistentEntity.TO, unmarshallOrFromCache(relEntity.getToEntity(), initializedNodes.get(nodeId)) + object, + RelationshipPersistentEntity.TO, unmarshallOrFromCache(relEntity.getToEntity(), initializedNodes.get(nodeId)) ); - } - else { + } else { reflector.setProperty( - object, - RelationshipPersistentEntity.TO, session.proxy( relEntity.getTo().getType(), nodeId) + object, + RelationshipPersistentEntity.TO, session.proxy(relEntity.getTo().getType(), nodeId) ); } } reflector.setProperty(object, - RelationshipPersistentEntity.TYPE, - data.type()); + RelationshipPersistentEntity.TYPE, + data.type()); } return object; } @@ -359,7 +404,7 @@ public Object unmarshallOrFromCache(PersistentEntity entity, Entity data, MapemptyMap(), initializedAssociations); + instance = unmarshall(entity, id, data, Collections.emptyMap(), initializedAssociations); } return instance; } @@ -368,9 +413,9 @@ private PersistentEntity mostSpecificPersistentEntity(PersistentEntity pe, Itera PersistentEntity result = null; int longestInheritenceChain = -1; - for (String l: labels) { + for (String l : labels) { PersistentEntity persistentEntity = findDerivedPersistentEntityWithLabel(pe, l); - if (persistentEntity!=null) { + if (persistentEntity != null) { int inheritenceChain = calcInheritenceChain(persistentEntity); if (inheritenceChain > longestInheritenceChain) { longestInheritenceChain = inheritenceChain; @@ -378,18 +423,17 @@ private PersistentEntity mostSpecificPersistentEntity(PersistentEntity pe, Itera } } } - if(result != null) { + if (result != null) { return result; - } - else { + } else { return pe; } } private PersistentEntity findDerivedPersistentEntityWithLabel(PersistentEntity parent, String label) { - for (PersistentEntity pe: getMappingContext().getPersistentEntities()) { + for (PersistentEntity pe : getMappingContext().getPersistentEntities()) { if (isInParentsChain(parent, pe)) { - if (((GraphPersistentEntity)pe).getLabels().contains(label)) { + if (((GraphPersistentEntity) pe).getLabels().contains(label)) { return pe; } } @@ -398,7 +442,7 @@ private PersistentEntity findDerivedPersistentEntityWithLabel(PersistentEntity p } private boolean isInParentsChain(PersistentEntity parent, PersistentEntity it) { - if (it==null) { + if (it == null) { return false; } else if (it.equals(parent)) { return true; @@ -413,11 +457,10 @@ private int calcInheritenceChain(PersistentEntity current) { } } - protected Object unmarshall(PersistentEntity persistentEntity, Serializable id, Entity node, Map resultData, Map initializedAssociations) { - if(log.isDebugEnabled()) { - log.debug( "unmarshalling entity [{}] with id [{}], props {}, {}", persistentEntity.getName(), id, node); + if (log.isDebugEnabled()) { + log.debug("unmarshalling entity [{}] with id [{}], props {}, {}", persistentEntity.getName(), id, node); } final Neo4jSession session = getSession(); GraphPersistentEntity graphPersistentEntity = (GraphPersistentEntity) persistentEntity; @@ -426,8 +469,8 @@ protected Object unmarshall(PersistentEntity persistentEntity, Serializable id, entityAccess.setIdentifierNoConversion(id); PersistentProperty nodeId = graphPersistentEntity.getNodeId(); - if( nodeId != null ) { - ((GroovyObject)entityAccess.getEntity()).setProperty(nodeId.getName(), node.id()); + if (nodeId != null) { + ((GroovyObject) entityAccess.getEntity()).setProperty(nodeId.getName(), node.id()); } final Object entity = entityAccess.getEntity(); @@ -435,24 +478,22 @@ protected Object unmarshall(PersistentEntity persistentEntity, Serializable id, final List nodeProperties = DefaultGroovyMethods.toList(node.keys()); - for (PersistentProperty property: entityAccess.getPersistentEntity().getPersistentProperties()) { + for (PersistentProperty property : entityAccess.getPersistentEntity().getPersistentProperties()) { String propertyName = property.getName(); - if ( (property instanceof Simple) || (property instanceof TenantId) || (property instanceof Basic)) { + if ((property instanceof Simple) || (property instanceof TenantId) || (property instanceof Basic)) { // implicitly sets version property as well - if(node.containsKey(propertyName)) { + if (node.containsKey(propertyName)) { entityAccess.setProperty(propertyName, node.get(propertyName).asObject()); nodeProperties.remove(propertyName); } - } else if (property instanceof Association) { - - Association association = (Association) property; + } else if (property instanceof Association association) { final String associationName = association.getName(); - if(initializedAssociations.containsKey(association)) { + if (initializedAssociations.containsKey(association)) { entityAccess.setPropertyNoConversion(associationName, initializedAssociations.get(association)); continue; } @@ -462,68 +503,61 @@ protected Object unmarshall(PersistentEntity persistentEntity, Serializable id, final String associationIdsKey = associationName + "Ids"; // if the node key is present we have an eager fetch, so initialise the association - if(resultData.containsKey(associationNodesKey)) { + if (resultData.containsKey(associationNodesKey)) { final PersistentEntity associatedEntity = association.getAssociatedEntity(); if (association instanceof ToOne) { final Neo4jEntityPersister associationPersister = session.getEntityPersister(associatedEntity.getJavaClass()); final Iterable associationNodes = (Iterable) resultData.get(associationNodesKey); final Node associationNode = IteratorUtil.singleOrNull(associationNodes); - if(associationNode != null) { + if (associationNode != null) { entityAccess.setPropertyNoConversion( - associationName, - associationPersister.unmarshallOrFromCache(associatedEntity, associationNode) + associationName, + associationPersister.unmarshallOrFromCache(associatedEntity, associationNode) ); } - } - else if(association instanceof ToMany) { + } else if (association instanceof ToMany) { Collection values; final Class type = association.getType(); final Collection associationNodes; boolean isRelationshipEntity = associatedEntity instanceof RelationshipPersistentEntity; - if(isRelationshipEntity && resultData.containsKey(associationRelKey)) { + if (isRelationshipEntity && resultData.containsKey(associationRelKey)) { associationNodes = (Collection) resultData.get(associationRelKey); - } - else { + } else { associationNodes = (Collection) resultData.get(associationNodesKey); } final Neo4jResultList resultSet = new Neo4jResultList(0, associationNodes.size(), associationNodes.iterator(), session.getEntityPersister(associatedEntity)); - if(isRelationshipEntity) { + if (isRelationshipEntity) { RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity) associatedEntity; Association from = relEntity.getFrom(); Association to = relEntity.getTo(); handleRelationshipSide(persistentEntity, resultData, entity, associationNodesKey, resultSet, from); handleRelationshipSide(persistentEntity, resultData, entity, associationNodesKey, resultSet, to); - } - else if(association.isBidirectional()) { + } else if (association.isBidirectional()) { final Association inverseSide = association.getInverseSide(); - if(inverseSide instanceof ToOne) { + if (inverseSide instanceof ToOne) { resultSet.setInitializedAssociations(Collections.singletonMap( - inverseSide, entity + inverseSide, entity )); } } - if(List.class.isAssignableFrom(type)) { + if (List.class.isAssignableFrom(type)) { values = new Neo4jList(entityAccess, association, resultSet, session); - } - else if(SortedSet.class.isAssignableFrom(type)) { + } else if (SortedSet.class.isAssignableFrom(type)) { values = new Neo4jSortedSet(entityAccess, association, new TreeSet<>(resultSet), session); - } - else { + } else { values = new Neo4jSet(entityAccess, association, new HashSet<>(resultSet), session); } entityAccess.setPropertyNoConversion(propertyName, values); } - } - else if(resultData.containsKey(associationIdsKey)) { + } else if (resultData.containsKey(associationIdsKey)) { final Object associationValues = resultData.get(associationIdsKey); List targetIds = Collections.emptyList(); - if(associationValues instanceof Collection) { + if (associationValues instanceof Collection) { targetIds = (List) associationValues; } - if (association instanceof ToOne) { - ToOne toOne = (ToOne) association; + if (association instanceof ToOne toOne) { if (!targetIds.isEmpty()) { Serializable targetId; try { @@ -532,93 +566,85 @@ else if(resultData.containsKey(associationIdsKey)) { throw new DataIntegrityViolationException("Single-ended association has more than one associated identifier: " + association); } entityAccess.setPropertyNoConversion(propertyName, - getMappingContext().getProxyFactory().createProxy( - this.session, - toOne.getAssociatedEntity().getJavaClass(), - targetId - ) + getMappingContext().getProxyFactory().createProxy( + this.session, + toOne.getAssociatedEntity().getJavaClass(), + targetId + ) ); } } else if (association instanceof ToMany) { Collection values; final Class type = association.getType(); - if(List.class.isAssignableFrom(type)) { + if (List.class.isAssignableFrom(type)) { values = new Neo4jPersistentList(targetIds, session, entityAccess, (ToMany) association); - } - else if(SortedSet.class.isAssignableFrom(type)) { + } else if (SortedSet.class.isAssignableFrom(type)) { values = new Neo4jPersistentSortedSet(targetIds, session, entityAccess, (ToMany) association); - } - else { + } else { values = new Neo4jPersistentSet(targetIds, session, entityAccess, (ToMany) association); } entityAccess.setPropertyNoConversion(propertyName, values); } else { throw new IllegalArgumentException("association " + associationName + " is of type " + association.getClass().getSuperclass().getName()); } - } - else { + } else { // No OPTIONAL MATCH specified so the association queries are lazily executed - if(association instanceof ToOne) { + if (association instanceof ToOne) { // first check whether the object has already been loaded from the cache // if a lazy proxy should be created for this association then create it, // note that this strategy does not allow for null checks final Neo4jAssociationQueryExecutor associationQueryExecutor = new Neo4jAssociationQueryExecutor(session, association); - if(association.getMapping().getMappedForm().getFetchStrategy() == FetchType.LAZY) { + if (association.getMapping().getMappedForm().getFetchStrategy() == FetchType.LAZY) { final Object proxy = getMappingContext().getProxyFactory().createProxy( - this.session, - associationQueryExecutor, - id + this.session, + associationQueryExecutor, + id ); entityAccess.setPropertyNoConversion(propertyName, - proxy + proxy ); - } - else { + } else { final List results = associationQueryExecutor.query(id); - if(!results.isEmpty()) { + if (!results.isEmpty()) { entityAccess.setPropertyNoConversion(propertyName, results.get(0)); } } - } - else if(association instanceof ToMany) { + } else if (association instanceof ToMany) { Collection values; final Class type = association.getType(); - if(List.class.isAssignableFrom(type)) { + if (List.class.isAssignableFrom(type)) { values = new Neo4jPersistentList(id, session, entityAccess, (ToMany) association); - } - else if(SortedSet.class.isAssignableFrom(type)) { + } else if (SortedSet.class.isAssignableFrom(type)) { values = new Neo4jPersistentSortedSet(id, session, entityAccess, (ToMany) association); - } - else { + } else { values = new Neo4jPersistentSet(id, session, entityAccess, (ToMany) association); } entityAccess.setPropertyNoConversion(propertyName, values); } } - } else { throw new IllegalArgumentException("property " + property.getName() + " is of type " + property.getClass().getSuperclass().getName()); } } - Map undeclared = new LinkedHashMap<>(); + Map undeclared = new LinkedHashMap<>(); if (!nodeProperties.isEmpty()) { for (String nodeProperty : nodeProperties) { - if(!nodeProperty.equals(CypherBuilder.IDENTIFIER)) { + if (!nodeProperty.equals(CypherBuilder.IDENTIFIER)) { undeclared.put(nodeProperty, node.get(nodeProperty).asObject()); } } } final Object obj = entity; - if(!undeclared.isEmpty()) { - if(obj instanceof DynamicAttributes) { - ((DynamicAttributes)obj).attributes(undeclared); + if (!undeclared.isEmpty()) { + if (obj instanceof DynamicAttributes) { + ((DynamicAttributes) obj).attributes(undeclared); } } @@ -629,10 +655,10 @@ else if(SortedSet.class.isAssignableFrom(type)) { private void handleRelationshipSide(PersistentEntity persistentEntity, Map resultData, Object entity, String associationNodesKey, Neo4jResultList resultSet, Association association) { if (persistentEntity.equals(association.getAssociatedEntity())) { resultSet.setInitializedAssociations(Collections.singletonMap( - association, entity + association, entity )); // the associated nodes are the inverse nodes, pass those through to avoid additional queries - if(resultData.containsKey(associationNodesKey)) { + if (resultData.containsKey(associationNodesKey)) { Collection nodes = (Collection) resultData.get(associationNodesKey); for (Node n : nodes) { resultSet.addInitializedNode(n); @@ -646,42 +672,39 @@ private Collection createCollection(Association association) { } private Collection createDirtyCheckableAwareCollection(EntityAccess entityAccess, Association association, Collection delegate) { - if (delegate==null) { + if (delegate == null) { delegate = createCollection(association); } - if( !(delegate instanceof DirtyCheckableCollection)) { + if (!(delegate instanceof DirtyCheckableCollection dirtyCheckableCollection)) { final Object entity = entityAccess.getEntity(); - if(entity instanceof DirtyCheckable) { + if (entity instanceof DirtyCheckable) { final Neo4jSession session = getSession(); - for( Object o : delegate ) { + for (Object o : delegate) { final EntityAccess associationAccess = getSession().createEntityAccess(association.getAssociatedEntity(), o); Serializable associationId = (Serializable) associationAccess.getIdentifier(); - if(associationId != null) { + if (associationId != null) { session.addPendingRelationshipInsert((Serializable) entityAccess.getIdentifier(), association, associationId); } } delegate = association.isList() ? - new Neo4jList(entityAccess, association, (List)delegate, session) : - new Neo4jSet(entityAccess, association, (Set)delegate, session); + new Neo4jList(entityAccess, association, (List) delegate, session) : + new Neo4jSet(entityAccess, association, (Set) delegate, session); } - } - else { - final DirtyCheckableCollection dirtyCheckableCollection = (DirtyCheckableCollection) delegate; + } else { final Neo4jSession session = getSession(); - if(dirtyCheckableCollection.hasChanged()) { - for (Object o : ((Iterable)dirtyCheckableCollection)) { + if (dirtyCheckableCollection.hasChanged()) { + for (Object o : ((Iterable) dirtyCheckableCollection)) { final EntityAccess associationAccess = getSession().createEntityAccess(association.getAssociatedEntity(), o); - session.addPendingRelationshipInsert((Serializable) entityAccess.getIdentifier(),association, (Serializable) associationAccess.getIdentifier()); + session.addPendingRelationshipInsert((Serializable) entityAccess.getIdentifier(), association, (Serializable) associationAccess.getIdentifier()); } } } return delegate; } - @Override protected Serializable persistEntity(final PersistentEntity entity, Object obj, boolean isInsert) { if (obj == null) { @@ -701,14 +724,13 @@ protected Serializable persistEntity(final PersistentEntity entity, Object obj, final EntityAccess entityAccess = createEntityAccess(entity, obj); Object identifier = entityAccess.getIdentifier(); - session.registerPending(obj); // cancel operation if vetoed boolean isUpdate = identifier != null && !isInsert; GraphPersistentEntity graphPersistentEntity = (GraphPersistentEntity) entity; boolean assignedId = graphPersistentEntity.isAssignedId(); - if(assignedId && !session.contains(obj)) { + if (assignedId && !session.contains(obj)) { isUpdate = false; } @@ -717,11 +739,11 @@ protected Serializable persistEntity(final PersistentEntity entity, Object obj, } else { boolean isNativeId = false; - if(identifier == null) { + if (identifier == null) { final IdGenerator idGenerator = graphPersistentEntity.getIdGenerator(); isNativeId = idGenerator == null; - if(!isNativeId) { + if (!isNativeId) { identifier = idGenerator.nextId(); entityAccess.setIdentifier(identifier); @@ -748,7 +770,7 @@ public void run() { } }); - if(isNativeId && !graphPersistentEntity.isRelationshipEntity()) { + if (isNativeId && !graphPersistentEntity.isRelationshipEntity()) { // if we have a native identifier then we have to perform an insert to obtain the id final List> preOperations = pendingInsert.getPreOperations(); for (PendingOperation preOperation : preOperations) { @@ -758,7 +780,7 @@ public void run() { pendingInsert.run(); pendingInsert.setExecuted(true); - if(pendingInsert.isVetoed()) { + if (pendingInsert.isVetoed()) { return null; } @@ -766,41 +788,39 @@ public void run() { final String cypher = session.buildEntityCreateOperation(entity, pendingInsert, params, pendingInsert.getCascadeOperations()); Map> dynamicAssociations = null; - if(graphPersistentEntity.hasDynamicAssociations()) { + if (graphPersistentEntity.hasDynamicAssociations()) { dynamicAssociations = (Map>) params.remove(DYNAMIC_ASSOCIATION_PARAM); } final QueryRunner boltSession = session.hasTransaction() ? session.getTransaction().getNativeTransaction() : session.getNativeInterface(); final String finalCypher = cypher + graphPersistentEntity.formatReturnId(); - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("CREATE Cypher [{}] for parameters [{}]", finalCypher, params); } final Result result = boltSession.run(finalCypher, params); - if(!result.hasNext()) { + if (!result.hasNext()) { throw new IdentityGenerationException("CREATE operation did not generate an identifier for entity " + entityAccess.getEntity()); } Record idMap = result.next(); - if(idMap != null) { + if (idMap != null) { identifier = idMap.get(GormProperties.IDENTITY).asObject(); - if(identifier == null) { + if (identifier == null) { throw new IdentityGenerationException("CREATE operation did not generate an identifier for entity " + entityAccess.getEntity()); } entityAccess.setIdentifier(identifier); - if(obj instanceof DirtyCheckable) { - ((DirtyCheckable)obj).trackChanges(); + if (obj instanceof DirtyCheckable) { + ((DirtyCheckable) obj).trackChanges(); } persistAssociationsOfEntity(graphPersistentEntity, entityAccess, false); - if(dynamicAssociations != null) { - processDynamicAssociations(graphPersistentEntity, entityAccess, (Neo4jMappingContext) getMappingContext(),dynamicAssociations, pendingInsert.getCascadeOperations(), false); + if (dynamicAssociations != null) { + processDynamicAssociations(graphPersistentEntity, entityAccess, (Neo4jMappingContext) getMappingContext(), dynamicAssociations, pendingInsert.getCascadeOperations(), false); } - } - else { + } else { throw new IdentityGenerationException("CREATE operation did not generate an identifier for entity " + entityAccess.getEntity()); } session.addPendingInsert(pendingInsert); - } - else { + } else { session.addPendingInsert(pendingInsert); persistAssociationsOfEntity((GraphPersistentEntity) entity, entityAccess, false); } @@ -815,7 +835,6 @@ protected Serializable persistEntity(final PersistentEntity pe, Object obj) { return persistEntity(pe, obj, false); } - private void registerPendingUpdate(Neo4jSession session, final PersistentEntity pe, final EntityAccess ea, final Object obj, final Serializable identifier) { final Neo4jModificationTrackingEntityAccess entityAccess = new Neo4jModificationTrackingEntityAccess(ea); final Object notEqualMarker = new Object(); @@ -826,9 +845,8 @@ public void run() { setVetoed(true); } else { Object entity = entityAccess.getEntity(); - if (entity instanceof DirtyCheckable) { - DirtyCheckable dirtyCheckable = (DirtyCheckable) entity; - for (Map.Entry entry: entityAccess.getModifiedProperties().entrySet()) { + if (entity instanceof DirtyCheckable dirtyCheckable) { + for (Map.Entry entry : entityAccess.getModifiedProperties().entrySet()) { dirtyCheckable.markDirty(entry.getKey(), notEqualMarker, entry.getValue()); } } @@ -847,27 +865,27 @@ public void run() { } private boolean shouldIgnore(Neo4jSession session, Object obj) { - boolean isDirty = obj instanceof DirtyCheckable ? ((DirtyCheckable)obj).hasChanged() : true; + boolean isDirty = !(obj instanceof DirtyCheckable) || ((DirtyCheckable) obj).hasChanged(); return session.isPendingAlready(obj) || (!isDirty); } public void processDynamicAssociations(GraphPersistentEntity graphEntity, EntityAccess access, Neo4jMappingContext mappingContext, Map> dynamicRelProps, List> cascadingOperations, boolean isUpdate) { - if(graphEntity.hasDynamicAssociations()) { + if (graphEntity.hasDynamicAssociations()) { Serializable parentId = (Serializable) access.getIdentifier(); - for (final Map.Entry> e: dynamicRelProps.entrySet()) { - for (final Object o : e.getValue()) { + for (final Map.Entry> e : dynamicRelProps.entrySet()) { + for (final Object o : e.getValue()) { - if (((DirtyCheckable)access.getEntity()).hasChanged(e.getKey()) || !isUpdate) { + if (((DirtyCheckable) access.getEntity()).hasChanged(e.getKey()) || !isUpdate) { final GraphPersistentEntity associated = (GraphPersistentEntity) mappingContext.getPersistentEntity(o.getClass().getName()); if (associated != null) { Object childId = getSession().getEntityPersister(o).getObjectIdentifier(o); if (childId == null) { childId = persist(o); } - getSession(). - addPendingRelationshipInsert(parentId, - new DynamicToOneAssociation(graphEntity, mappingContext, e.getKey(), associated), - (Serializable) childId); + getSession() + .addPendingRelationshipInsert(parentId, + new DynamicToOneAssociation(graphEntity, mappingContext, e.getKey(), associated), + (Serializable) childId); } } } @@ -880,45 +898,41 @@ private void persistAssociationsOfEntity(GraphPersistentEntity graphEntity, Enti Object obj = entityAccess.getEntity(); DirtyCheckable dirtyCheckable = null; if (obj instanceof DirtyCheckable) { - dirtyCheckable = (DirtyCheckable)obj; + dirtyCheckable = (DirtyCheckable) obj; } Neo4jSession neo4jSession = getSession(); - if(graphEntity.hasDynamicAssociations()) { + if (graphEntity.hasDynamicAssociations()) { MappingContext mappingContext = graphEntity.getMappingContext(); DynamicAttributes dynamicAttributes = (DynamicAttributes) obj; Map attributes = dynamicAttributes.attributes(); for (Map.Entry entry : attributes.entrySet()) { Object value = entry.getValue(); - if(value == null) { + if (value == null) { String associationName = entry.getKey(); Object originalValue = ((DirtyCheckable) obj).getOriginalValue(associationName); - if(originalValue != null && mappingContext.isPersistentEntity(originalValue)) { + if (originalValue != null && mappingContext.isPersistentEntity(originalValue)) { processDynamicAssociationRemoval(graphEntity, entityAccess, neo4jSession, mappingContext, associationName, originalValue); - } - else if(originalValue instanceof Iterable) { - Iterable i = (Iterable) originalValue; + } else if (originalValue instanceof Iterable i) { for (Object o : i) { processDynamicAssociationRemoval(graphEntity, entityAccess, neo4jSession, mappingContext, associationName, o); } } - } - else if(mappingContext.isPersistentEntity(value)) { - if( ((DirtyCheckable)value).hasChanged() ) { + } else if (mappingContext.isPersistentEntity(value)) { + if (((DirtyCheckable) value).hasChanged()) { neo4jSession.persist(value); } - } - else if(value instanceof Iterable) { + } else if (value instanceof Iterable) { boolean collectionChanged = false; - for(Object o : ((Iterable)value)) { - if(mappingContext.isPersistentEntity(o)) { - collectionChanged = ((DirtyCheckable)o).hasChanged(); - if(collectionChanged) break; + for (Object o : ((Iterable) value)) { + if (mappingContext.isPersistentEntity(o)) { + collectionChanged = ((DirtyCheckable) o).hasChanged(); + if (collectionChanged) break; } } - if(collectionChanged) { - neo4jSession.persist((Iterable)value); + if (collectionChanged) { + neo4jSession.persist((Iterable) value); } } } @@ -926,35 +940,33 @@ else if(value instanceof Iterable) { for (PersistentProperty persistentProperty : graphEntity.getAssociations()) { String propertyName = persistentProperty.getName(); - if(persistentProperty instanceof Basic) { + if (persistentProperty instanceof Basic) { continue; } - if ((!isUpdate) || ((dirtyCheckable!=null) && dirtyCheckable.hasChanged(propertyName))) { + if ((!isUpdate) || ((dirtyCheckable != null) && dirtyCheckable.hasChanged(propertyName))) { Object propertyValue = entityAccess.getProperty(propertyName); if ((persistentProperty instanceof OneToMany) || (persistentProperty instanceof ManyToMany)) { Association association = (Association) persistentProperty; - if (propertyValue!= null) { + if (propertyValue != null) { - if(propertyValue instanceof PersistentCollection) { - PersistentCollection pc = (PersistentCollection) propertyValue; - if(!pc.isInitialized()) continue; + if (propertyValue instanceof PersistentCollection pc) { + if (!pc.isInitialized()) continue; } if (association.isBidirectional()) { // Populate other side of bidi - for (Object associatedObject: (Iterable)propertyValue) { + for (Object associatedObject : (Iterable) propertyValue) { EntityAccess assocEntityAccess = createEntityAccess(association.getAssociatedEntity(), associatedObject); String referencedPropertyName = association.getReferencedPropertyName(); - if(association instanceof ManyToMany) { - ((GormEntity)associatedObject).addTo(referencedPropertyName, obj); - } - else { + if (association instanceof ManyToMany) { + ((GormEntity) associatedObject).addTo(referencedPropertyName, obj); + } else { assocEntityAccess.setPropertyNoConversion(referencedPropertyName, obj); - ((DirtyCheckable)associatedObject).markDirty(referencedPropertyName); + ((DirtyCheckable) associatedObject).markDirty(referencedPropertyName); } } } @@ -979,22 +991,22 @@ else if(value instanceof Iterable) { assocEntityAccess.setProperty(to.getReferencedPropertyName(), obj); } else { Collection collection = (Collection) assocEntityAccess.getProperty(to.getReferencedPropertyName()); - if (collection == null ) { + if (collection == null) { collection = new ArrayList(); assocEntityAccess.setProperty(to.getReferencedPropertyName(), collection); } - if(proxyFactory.isInitialized(collection) && !collection.isEmpty()) { + if (proxyFactory.isInitialized(collection) && !collection.isEmpty()) { boolean found = false; for (Object o : collection) { - if(o.equals(obj)) { - found = true; break; + if (o.equals(obj)) { + found = true; + break; } } if (!found) { collection.add(obj); } - } - else { + } else { collection.add(obj); } } @@ -1002,41 +1014,37 @@ else if(value instanceof Iterable) { persistEntity(to.getAssociatedEntity(), propertyValue); - Serializable thisId = (Serializable) entityAccess.getIdentifier(); final EntityAccess associationAccess = neo4jSession.createEntityAccess(to.getAssociatedEntity(), propertyValue); Serializable associationId = (Serializable) associationAccess.getIdentifier(); - if(graphEntity.isRelationshipEntity() && propertyName.equals(RelationshipPersistentEntity.FROM)) { + if (graphEntity.isRelationshipEntity() && propertyName.equals(RelationshipPersistentEntity.FROM)) { RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity) graphEntity; // reverse the ids to correctly align from and to thisId = associationId; Object toValue = entityAccess.getProperty(RelationshipPersistentEntity.TO); - if(toValue != null) { + if (toValue != null) { GraphPersistentEntity toEntity = relEntity.getToEntity(); associationId = toEntity.getReflector().getIdentifier(toValue); - if(associationId == null) { - associationId = persistEntity(toEntity,toValue); + if (associationId == null) { + associationId = persistEntity(toEntity, toValue); } } } - if(thisId != null && associationId != null) { + if (thisId != null && associationId != null) { boolean reversed = RelationshipUtils.useReversedMappingFor(to); if (reversed) { Association inverseSide = to.getInverseSide(); - if(inverseSide != null) { + if (inverseSide != null) { neo4jSession.addPendingRelationshipInsert(associationId, inverseSide, thisId); } - } - else { + } else { neo4jSession.addPendingRelationshipInsert(thisId, to, associationId); } } - - } - else if(isUpdate) { + } else if (isUpdate) { Object previousValue = dirtyCheckable.getOriginalValue(propertyName); if (previousValue != null) { ToOne to = (ToOne) persistentProperty; @@ -1047,11 +1055,10 @@ else if(isUpdate) { } } } else { - throw new IllegalArgumentException("GORM for Neo4j doesn't support properties of the given type " + persistentProperty + "(" + persistentProperty.getClass().getSuperclass() +")" ); + throw new IllegalArgumentException("GORM for Neo4j doesn't support properties of the given type " + persistentProperty + "(" + persistentProperty.getClass().getSuperclass() + ")"); } } - } } @@ -1060,9 +1067,9 @@ private void processDynamicAssociationRemoval(GraphPersistentEntity graphEntity, if (associated != null) { Object childId = getSession().getEntityPersister(originalValue).getObjectIdentifier(originalValue); - if(childId != null) { + if (childId != null) { Serializable parentId = (Serializable) entityAccess.getIdentifier(); - neo4jSession.addPendingRelationshipDelete(parentId,new DynamicToOneAssociation(graphEntity, mappingContext, associationName, associated), (Serializable)childId ); + neo4jSession.addPendingRelationshipDelete(parentId, new DynamicToOneAssociation(graphEntity, mappingContext, associationName, associated), (Serializable) childId); } } } @@ -1071,7 +1078,6 @@ private void processDynamicAssociationRemoval(GraphPersistentEntity graphEntity, protected void deleteEntity(final PersistentEntity pe, final Object obj) { final EntityAccess entityAccess = createEntityAccess(pe, obj); - final Neo4jSession session = getSession(); session.clear(obj); final PendingDeleteAdapter pendingDelete = createPendingDeleteOne(session, pe, entityAccess, obj); @@ -1097,8 +1103,6 @@ public void run() { }; } - - @Override protected void deleteEntities(PersistentEntity pe, @SuppressWarnings("rawtypes") Iterable objects) { for (Object object : objects) { @@ -1115,14 +1119,4 @@ public Query createQuery() { public Serializable refresh(Object o) { throw new UnsupportedOperationException(); } - - public static long countUpdates(Result execute) { - ResultSummary resultSummary = execute.consume(); - SummaryCounters counters = resultSummary.counters(); - if (counters.containsUpdates()) { - return counters.nodesCreated() + counters.nodesDeleted() + counters.propertiesSet() + counters.relationshipsCreated() + counters.relationshipsDeleted(); - } else { - return 0; - } - } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jModificationTrackingEntityAccess.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jModificationTrackingEntityAccess.groovy index 5acd12c55fb..ee0a97d46e2 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jModificationTrackingEntityAccess.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jModificationTrackingEntityAccess.groovy @@ -19,6 +19,7 @@ package org.grails.datastore.gorm.neo4j.engine import groovy.transform.CompileStatic + import org.grails.datastore.mapping.engine.EntityAccess import org.grails.datastore.mapping.engine.ModificationTrackingEntityAccess diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jQuery.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jQuery.groovy index fe4806db628..1f745a0274c 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jQuery.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jQuery.groovy @@ -21,12 +21,25 @@ package org.grails.datastore.gorm.neo4j.engine import groovy.transform.CompileStatic import groovy.transform.EqualsAndHashCode import groovy.util.logging.Slf4j -import org.grails.datastore.gorm.neo4j.* + +import jakarta.persistence.FetchType + +import org.neo4j.driver.QueryRunner +import org.neo4j.driver.Record +import org.neo4j.driver.Result +import org.neo4j.driver.Value +import org.neo4j.driver.types.Node + +import org.grails.datastore.gorm.neo4j.CypherBuilder +import org.grails.datastore.gorm.neo4j.GraphPersistentEntity +import org.grails.datastore.gorm.neo4j.Neo4jMappingContext +import org.grails.datastore.gorm.neo4j.Neo4jSession +import org.grails.datastore.gorm.neo4j.RelationshipPersistentEntity +import org.grails.datastore.gorm.neo4j.RelationshipUtils import org.grails.datastore.gorm.neo4j.collection.Neo4jResultList import org.grails.datastore.mapping.config.Property import org.grails.datastore.mapping.model.PersistentEntity import org.grails.datastore.mapping.model.PersistentProperty -import org.grails.datastore.mapping.model.config.GormProperties import org.grails.datastore.mapping.model.types.Association import org.grails.datastore.mapping.model.types.Basic import org.grails.datastore.mapping.model.types.ManyToOne @@ -35,14 +48,6 @@ import org.grails.datastore.mapping.model.types.ToOne import org.grails.datastore.mapping.query.AssociationQuery import org.grails.datastore.mapping.query.Query import org.grails.datastore.mapping.query.QueryException -import org.grails.datastore.mapping.reflect.EntityReflector -import org.neo4j.driver.Record -import org.neo4j.driver.Result -import org.neo4j.driver.QueryRunner -import org.neo4j.driver.Value -import org.neo4j.driver.types.Node - -import jakarta.persistence.FetchType /** * perform criteria queries on a Neo4j backend @@ -69,36 +74,38 @@ class Neo4jQuery extends Query { private static Map, String> COMPARISON_OPERATORS = [ (Query.GreaterThanEqualsProperty): CriterionHandler.OPERATOR_GREATER_THAN_EQUALS, - (Query.EqualsProperty): CriterionHandler.OPERATOR_EQUALS, - (Query.NotEqualsProperty): CriterionHandler.OPERATOR_NOT_EQUALS, - (Query.LessThanEqualsProperty): CriterionHandler.OPERATOR_LESS_THAN_EQUALS, - (Query.LessThanProperty): CriterionHandler.OPERATOR_LESS_THAN, - (Query.GreaterThanProperty): CriterionHandler.OPERATOR_GREATER_THAN + (Query.EqualsProperty) : CriterionHandler.OPERATOR_EQUALS, + (Query.NotEqualsProperty) : CriterionHandler.OPERATOR_NOT_EQUALS, + (Query.LessThanEqualsProperty) : CriterionHandler.OPERATOR_LESS_THAN_EQUALS, + (Query.LessThanProperty) : CriterionHandler.OPERATOR_LESS_THAN, + (Query.GreaterThanProperty) : CriterionHandler.OPERATOR_GREATER_THAN ] protected static Map, ProjectionHandler> PROJECT_HANDLERS = [ - (Query.CountProjection): new ProjectionHandler() { + (Query.CountProjection) : new ProjectionHandler() { + @Override @CompileStatic String handle(PersistentEntity entity, Query.CountProjection projection, CypherBuilder builder) { return ProjectionHandler.COUNT } }, - (Query.IdProjection): new ProjectionHandler() { + (Query.IdProjection) : new ProjectionHandler() { + @Override @CompileStatic String handle(PersistentEntity entity, Query.IdProjection projection, CypherBuilder builder) { GraphPersistentEntity graphEntity = (GraphPersistentEntity) entity - if(graphEntity.isRelationshipEntity()) { + if (graphEntity.isRelationshipEntity()) { return graphEntity.formatId(CypherBuilder.REL_VAR) - } - else { + } else { return graphEntity.formatId(CypherBuilder.NODE_VAR) } } }, (Query.CountDistinctProjection): new ProjectionHandler() { + @Override @CompileStatic String handle(PersistentEntity entity, Query.CountDistinctProjection projection, CypherBuilder builder) { @@ -107,7 +114,8 @@ class Neo4jQuery extends Query { return "count( distinct ${var} )" } }, - (Query.MinProjection): new ProjectionHandler() { + (Query.MinProjection) : new ProjectionHandler() { + @Override @CompileStatic String handle(PersistentEntity entity, Query.MinProjection projection, CypherBuilder builder) { @@ -117,7 +125,8 @@ class Neo4jQuery extends Query { return "min(${var})" } }, - (Query.MaxProjection): new ProjectionHandler() { + (Query.MaxProjection) : new ProjectionHandler() { + @Override @CompileStatic String handle(PersistentEntity entity, Query.MaxProjection projection, CypherBuilder builder) { @@ -127,7 +136,8 @@ class Neo4jQuery extends Query { return "max(${var})" } }, - (Query.SumProjection): new ProjectionHandler() { + (Query.SumProjection) : new ProjectionHandler() { + @Override @CompileStatic String handle(PersistentEntity entity, Query.SumProjection projection, CypherBuilder builder) { @@ -136,7 +146,8 @@ class Neo4jQuery extends Query { return "sum(${var})" } }, - (Query.AvgProjection): new ProjectionHandler() { + (Query.AvgProjection) : new ProjectionHandler() { + @Override @CompileStatic String handle(PersistentEntity entity, Query.AvgProjection projection, CypherBuilder builder) { @@ -145,7 +156,8 @@ class Neo4jQuery extends Query { return "avg($var)" } }, - (Query.PropertyProjection): new ProjectionHandler() { + (Query.PropertyProjection) : new ProjectionHandler() { + @Override @CompileStatic String handle(PersistentEntity entity, Query.PropertyProjection projection, CypherBuilder builder) { @@ -154,11 +166,11 @@ class Neo4jQuery extends Query { GraphPersistentEntity graphEntity = (GraphPersistentEntity) entity String var = graphEntity.variableName if (association instanceof Association && !(association instanceof Basic)) { - if(entity instanceof RelationshipPersistentEntity) { + if (entity instanceof RelationshipPersistentEntity) { throw new QueryException("Cannot apply projection on property [$propertyName] of class [$entity.name]. Associations on relationships are not allowed") } def targetNodeName = "${association.name}_${builder.getNextMatchNumber()}" - builder.addMatch("(n)${RelationshipUtils.matchForAssociation((Association)association)}(${targetNodeName})") + builder.addMatch("(n)${RelationshipUtils.matchForAssociation((Association) association)}(${targetNodeName})") return targetNodeName } else { return graphEntity.formatProperty(var, propertyName) @@ -170,27 +182,30 @@ class Neo4jQuery extends Query { ] as Map, ProjectionHandler> public static Map, CriterionHandler> CRITERION_HANDLERS = [ - (Query.Conjunction): new CriterionHandler() { + (Query.Conjunction) : new CriterionHandler() { + @Override @CompileStatic CypherExpression handle(GraphPersistentEntity entity, Query.Conjunction criterion, CypherBuilder builder, String prefix) { - def inner = ((Query.Junction)criterion).criteria + def inner = ((Query.Junction) criterion).criteria .collect { Query.Criterion it -> dispatchCriterion(it, entity, builder, prefix).toString() } - .join( CriterionHandler.OPERATOR_AND ) + .join(CriterionHandler.OPERATOR_AND) return new CypherExpression(inner ? "( $inner )" : inner) } }, - (Query.Disjunction): new CriterionHandler() { + (Query.Disjunction) : new CriterionHandler() { + @Override @CompileStatic CypherExpression handle(GraphPersistentEntity entity, Query.Disjunction criterion, CypherBuilder builder, String prefix) { - def inner = ((Query.Junction)criterion).criteria + def inner = ((Query.Junction) criterion).criteria .collect { Query.Criterion it -> dispatchCriterion(it, entity, builder, prefix).toString() } - .join( CriterionHandler.OPERATOR_OR ) + .join(CriterionHandler.OPERATOR_OR) return new CypherExpression(inner ? "( $inner )" : inner) } }, - (Query.Negation): new CriterionHandler() { + (Query.Negation) : new CriterionHandler() { + @Override @CompileStatic CypherExpression handle(GraphPersistentEntity entity, Query.Negation criterion, CypherBuilder builder, String prefix) { @@ -199,35 +214,34 @@ class Neo4jQuery extends Query { new CypherExpression("NOT (${dispatchCriterion(disjunction, entity, builder, prefix)})") } }, - (Query.Equals): new CriterionHandler() { + (Query.Equals) : new CriterionHandler() { + @Override @CompileStatic CypherExpression handle(GraphPersistentEntity graphEntity, Query.Equals criterion, CypherBuilder builder, String prefix) { - Neo4jMappingContext mappingContext = (Neo4jMappingContext)graphEntity.mappingContext - int paramNumber = builder.addParam( mappingContext.convertToNative(criterion.value) ) + Neo4jMappingContext mappingContext = (Neo4jMappingContext) graphEntity.mappingContext + int paramNumber = builder.addParam(mappingContext.convertToNative(criterion.value)) PersistentProperty association = graphEntity.getPropertyByName(criterion.property) boolean isRelationshipEntity = graphEntity.isRelationshipEntity() String lhs if (association instanceof Association && !(association instanceof Basic)) { - if(isRelationshipEntity && RelationshipPersistentEntity.isRelationshipAssociation((Association)association)) { + if (isRelationshipEntity && RelationshipPersistentEntity.isRelationshipAssociation((Association) association)) { lhs = graphEntity.formatId(association.name) - } - else { + } else { String targetNodeName = "m_${builder.getNextMatchNumber()}" builder.addMatch( - graphEntity.formatAssociationPatternFromExisting((Association)association, "", prefix, targetNodeName) + graphEntity.formatAssociationPatternFromExisting((Association) association, "", prefix, targetNodeName) ) lhs = graphEntity.formatId(targetNodeName) } } else { - if(isRelationshipEntity && criterion.property == RelationshipPersistentEntity.TYPE) { - builder.replaceFirstRelationshipMatch( ((RelationshipPersistentEntity)graphEntity).buildRelationshipMatchTo(null, CypherBuilder.REL_VAR)) + if (isRelationshipEntity && criterion.property == RelationshipPersistentEntity.TYPE) { + builder.replaceFirstRelationshipMatch(((RelationshipPersistentEntity) graphEntity).buildRelationshipMatchTo(null, CypherBuilder.REL_VAR)) lhs = "TYPE($CypherBuilder.REL_VAR)" - } - else { + } else { lhs = graphEntity.formatProperty(prefix, criterion.property) } } @@ -236,7 +250,8 @@ class Neo4jQuery extends Query { } }, - (Query.IdEquals): new CriterionHandler() { + (Query.IdEquals) : new CriterionHandler() { + @Override @CompileStatic CypherExpression handle(GraphPersistentEntity entity, Query.IdEquals criterion, CypherBuilder builder, String prefix) { @@ -244,7 +259,8 @@ class Neo4jQuery extends Query { return new CypherExpression(entity.formatId(prefix), "\$$paramNumber", CriterionHandler.OPERATOR_EQUALS) } }, - (Query.Like): new CriterionHandler() { + (Query.Like) : new CriterionHandler() { + @Override @CompileStatic CypherExpression handle(GraphPersistentEntity entity, Query.Like criterion, CypherBuilder builder, String prefix) { @@ -253,7 +269,8 @@ class Neo4jQuery extends Query { return new CypherExpression(entity.formatProperty(prefix, criterion.property), "\$$paramNumber", operator) } }, - (Query.ILike): new CriterionHandler() { + (Query.ILike) : new CriterionHandler() { + @Override @CompileStatic CypherExpression handle(GraphPersistentEntity entity, Query.ILike criterion, CypherBuilder builder, String prefix) { @@ -261,14 +278,15 @@ class Neo4jQuery extends Query { String operator = handleLike(criterion, builder, paramNumber, true) String propertyRef = entity.formatProperty(prefix, criterion.property) String parameterRef = "\$$paramNumber" - if(operator != CriterionHandler.OPERATOR_LIKE) { + if (operator != CriterionHandler.OPERATOR_LIKE) { propertyRef = "lower($propertyRef)" parameterRef = "lower($parameterRef)" } return new CypherExpression(propertyRef, parameterRef, operator) } }, - (Query.RLike): new CriterionHandler() { + (Query.RLike) : new CriterionHandler() { + @Override @CompileStatic CypherExpression handle(GraphPersistentEntity entity, Query.RLike criterion, CypherBuilder builder, String prefix) { @@ -276,94 +294,98 @@ class Neo4jQuery extends Query { return new CypherExpression(entity.formatProperty(prefix, criterion.property), "\$$paramNumber", CriterionHandler.OPERATOR_LIKE) } }, - (Query.In): new CriterionHandler() { + (Query.In) : new CriterionHandler() { + @Override @CompileStatic CypherExpression handle(GraphPersistentEntity entity, Query.In criterion, CypherBuilder builder, String prefix) { int paramNumber = addBuildParameterForCriterion(builder, entity, criterion) - GraphPersistentEntity graphPersistentEntity = (GraphPersistentEntity)entity + GraphPersistentEntity graphPersistentEntity = (GraphPersistentEntity) entity String lhs Collection values = ((Query.In) criterion).values - if(graphPersistentEntity.isRelationshipEntity()) { + if (graphPersistentEntity.isRelationshipEntity()) { PersistentProperty persistentProperty = entity.getPropertyByName(criterion.property) - if(RelationshipPersistentEntity.isRelationshipAssociation(persistentProperty)) { - GraphPersistentEntity associatedEntity = (GraphPersistentEntity)((Association) persistentProperty).associatedEntity + if (RelationshipPersistentEntity.isRelationshipAssociation(persistentProperty)) { + GraphPersistentEntity associatedEntity = (GraphPersistentEntity) ((Association) persistentProperty).associatedEntity lhs = associatedEntity.formatId(criterion.property) def associatedReflector = associatedEntity.reflector values = values?.collect { associatedReflector.getIdentifier(it) } - } - else { + } else { lhs = graphPersistentEntity.formatProperty(prefix, criterion.property) } - } - else { + } else { lhs = graphPersistentEntity.formatProperty(prefix, criterion.property) } builder.replaceParamAt(paramNumber, convertEnumsInList(values)) return new CypherExpression(lhs, "\$$paramNumber", CriterionHandler.OPERATOR_IN) } }, - (Query.IsNull): new CriterionHandler() { + (Query.IsNull) : new CriterionHandler() { + @Override @CompileStatic CypherExpression handle(GraphPersistentEntity entity, Query.IsNull criterion, CypherBuilder builder, String prefix) { return new CypherExpression("${entity.formatProperty(prefix, criterion.property)} IS NULL") } }, - (Query.IsEmpty): new CriterionHandler() { + (Query.IsEmpty) : new CriterionHandler() { + @Override @CompileStatic CypherExpression handle(GraphPersistentEntity entity, Query.IsEmpty criterion, CypherBuilder builder, String prefix) { return new CypherExpression("length(${entity.formatProperty(prefix, criterion.property)}) = 0") } }, - (Query.IsNotEmpty): new CriterionHandler() { + (Query.IsNotEmpty) : new CriterionHandler() { + @Override @CompileStatic CypherExpression handle(GraphPersistentEntity entity, Query.IsNotEmpty criterion, CypherBuilder builder, String prefix) { return new CypherExpression("length(${entity.formatProperty(prefix, criterion.property)}) > 0") } }, - (Query.IsNotNull): new CriterionHandler() { + (Query.IsNotNull) : new CriterionHandler() { + @Override @CompileStatic CypherExpression handle(GraphPersistentEntity entity, Query.IsNotNull criterion, CypherBuilder builder, String prefix) { return new CypherExpression("${entity.formatProperty(prefix, criterion.property)} IS NOT NULL") } }, - (AssociationQuery): new AssociationQueryHandler(), - (Query.GreaterThan): ComparisonCriterionHandler.GREATER_THAN, - (Query.GreaterThanEquals): ComparisonCriterionHandler.GREATER_THAN_EQUALS, - (Query.LessThan): ComparisonCriterionHandler.LESS_THAN, - (Query.LessThanEquals): ComparisonCriterionHandler.LESS_THAN_EQUALS, - (Query.NotEquals): ComparisonCriterionHandler.NOT_EQUALS, - - (Query.GreaterThanProperty): PropertyComparisonCriterionHandler.GREATER_THAN, + (AssociationQuery) : new AssociationQueryHandler(), + (Query.GreaterThan) : ComparisonCriterionHandler.GREATER_THAN, + (Query.GreaterThanEquals) : ComparisonCriterionHandler.GREATER_THAN_EQUALS, + (Query.LessThan) : ComparisonCriterionHandler.LESS_THAN, + (Query.LessThanEquals) : ComparisonCriterionHandler.LESS_THAN_EQUALS, + (Query.NotEquals) : ComparisonCriterionHandler.NOT_EQUALS, + + (Query.GreaterThanProperty) : PropertyComparisonCriterionHandler.GREATER_THAN, (Query.GreaterThanEqualsProperty): PropertyComparisonCriterionHandler.GREATER_THAN_EQUALS, - (Query.LessThanProperty): PropertyComparisonCriterionHandler.LESS_THAN, - (Query.LessThanEqualsProperty): PropertyComparisonCriterionHandler.LESS_THAN_EQUALS, - (Query.NotEqualsProperty): PropertyComparisonCriterionHandler.NOT_EQUALS, - (Query.EqualsProperty): PropertyComparisonCriterionHandler.EQUALS, + (Query.LessThanProperty) : PropertyComparisonCriterionHandler.LESS_THAN, + (Query.LessThanEqualsProperty) : PropertyComparisonCriterionHandler.LESS_THAN_EQUALS, + (Query.NotEqualsProperty) : PropertyComparisonCriterionHandler.NOT_EQUALS, + (Query.EqualsProperty) : PropertyComparisonCriterionHandler.EQUALS, + + (Query.Between) : new CriterionHandler() { - (Query.Between): new CriterionHandler() { @Override @CompileStatic CypherExpression handle(GraphPersistentEntity entity, Query.Between criterion, CypherBuilder builder, String prefix) { int paramNumber = addBuildParameterForCriterion(builder, entity, criterion) - Neo4jMappingContext mappingContext = (Neo4jMappingContext)entity.mappingContext - int paramNumberFrom = builder.addParam( mappingContext.convertToNative(criterion.from) ) - int parmaNumberTo = builder.addParam( mappingContext.convertToNative(criterion.to) ) - new CypherExpression( "\$$paramNumberFrom<=${prefix}.$criterion.property and ${prefix}.$criterion.property<=\$$parmaNumberTo") + Neo4jMappingContext mappingContext = (Neo4jMappingContext) entity.mappingContext + int paramNumberFrom = builder.addParam(mappingContext.convertToNative(criterion.from)) + int parmaNumberTo = builder.addParam(mappingContext.convertToNative(criterion.to)) + new CypherExpression("\$$paramNumberFrom<=${prefix}.$criterion.property and ${prefix}.$criterion.property<=\$$parmaNumberTo") } }, - (Query.SizeLessThanEquals): SizeCriterionHandler.LESS_THAN_EQUALS, - (Query.SizeLessThan): SizeCriterionHandler.LESS_THAN, - (Query.SizeEquals): SizeCriterionHandler.EQUALS, - (Query.SizeNotEquals): SizeCriterionHandler.NOT_EQUALS, - (Query.SizeGreaterThan): SizeCriterionHandler.GREATER_THAN, - (Query.SizeGreaterThanEquals): SizeCriterionHandler.GREATER_THAN_EQUALS + (Query.SizeLessThanEquals) : SizeCriterionHandler.LESS_THAN_EQUALS, + (Query.SizeLessThan) : SizeCriterionHandler.LESS_THAN, + (Query.SizeEquals) : SizeCriterionHandler.EQUALS, + (Query.SizeNotEquals) : SizeCriterionHandler.NOT_EQUALS, + (Query.SizeGreaterThan) : SizeCriterionHandler.GREATER_THAN, + (Query.SizeGreaterThanEquals) : SizeCriterionHandler.GREATER_THAN_EQUALS ] as Map, CriterionHandler> @@ -415,105 +437,101 @@ class Neo4jQuery extends Query { CypherBuilder cypherBuilder = buildBaseQuery(persistentEntity, criteria) cypherBuilder.setOrderAndLimits(applyOrderAndLimits(cypherBuilder)) - GraphPersistentEntity graphEntity = (GraphPersistentEntity)persistentEntity + GraphPersistentEntity graphEntity = (GraphPersistentEntity) persistentEntity def projectionList = projections.projectionList - if(projectionList.isEmpty()) { - if(isRelationshipEntity) { - cypherBuilder.addReturnColumn(CypherBuilder.DEFAULT_REL_RETURN_STATEMENT) - } - else { - Set associations = new TreeSet((Comparator){ Association a1, Association a2 -> a1.name <=> a2.name }) - Collection childEntities = persistentEntity.mappingContext.getChildEntities(persistentEntity) - if (!childEntities.empty) { - for (PersistentEntity childEntity : childEntities) { - associations.addAll(childEntity.associations) - } - } - associations.addAll(persistentEntity.associations) - - if(associations.size() > 0) { - int i = 0 - List previousAssociations = [] - cypherBuilder.addReturnColumn(CypherBuilder.DEFAULT_RETURN_TYPES) - - for(Association association in associations) { - if(association.isBasic()) continue - - FetchType fetchType = fetchStrategy(association.name) - boolean isEager = fetchType.is(fetchType.EAGER) - - String r = "r${i++}" - - String associationName = association.name - GraphPersistentEntity associatedGraphEntity = (GraphPersistentEntity)association.associatedEntity - boolean isAssociationRelationshipEntity = associatedGraphEntity.isRelationshipEntity() - boolean isToMany = association instanceof ToMany - boolean isToOne = association instanceof ToOne - - boolean lazy = false - if(isToOne && !isEager) { - Property propertyMapping = association.mapping.mappedForm - Boolean isLazy = propertyMapping.getLazy() - lazy = (isLazy != null ? isLazy : (association instanceof ManyToOne ? !association.isCircular() : true)) - - } - else if(isToMany) { - lazy = ((ToMany)association).lazy - } - - // if there are associations, add a join to get them - String withMatch = "WITH n, ${previousAssociations.size() > 0 ? previousAssociations.join(", ") + ", " : ""}" - String associationIdsRef = "${associationName}Ids" - String associationNodeRef = "${associationName}Node" - String associationNodesRef = "${associationName}Nodes" - - boolean addOptionalMatch = false - // If it is a one-to-many and lazy=true - // Or it is any non-eager to-one (regardless of nullability/laziness) - // then just collect the identifiers and not the nodes. - // A mandatory, lazy to-one (e.g. a required hasOne) must still have its id - // collected here - otherwise the persister has no way to know the associated - // entity's real identifier and falls back to an association-query-executor - // proxy whose "key" is the *parent's* id, not the target's (see - // AssociationQueryProxyHandler.getProxyKey()), corrupting things like - // Id lookups performed before the association is initialized. - if((isToMany && lazy) || (isToOne && !isEager)) { - withMatch += "collect(DISTINCT ${associatedGraphEntity.formatId(associationNodeRef)}) as ${associationIdsRef}" - cypherBuilder.addReturnColumn(associationIdsRef) - previousAssociations << associationIdsRef - addOptionalMatch = true - } - else if(isEager) { - withMatch += "collect(DISTINCT $associationNodeRef) as $associationNodesRef" - cypherBuilder.addReturnColumn(associationNodesRef) - if(isAssociationRelationshipEntity) { - withMatch += ", collect($r) as ${associationName}Rels" - cypherBuilder.addReturnColumn("${associationName}Rels") - } - previousAssociations << associationNodesRef - addOptionalMatch = true - } - - if(addOptionalMatch) { - - String relationshipPattern = graphEntity - .formatAssociationPatternFromExisting( - association, - r, - CypherBuilder.NODE_VAR, - associationNodeRef - ) - cypherBuilder.addOptionalMatch( - "$relationshipPattern $withMatch" - ) - } - - } - } - } - } - else { + if (projectionList.isEmpty()) { + if (isRelationshipEntity) { + cypherBuilder.addReturnColumn(CypherBuilder.DEFAULT_REL_RETURN_STATEMENT) + } else { + Set associations = new TreeSet((Comparator) { Association a1, Association a2 -> a1.name <=> a2.name }) + Collection childEntities = persistentEntity.mappingContext.getChildEntities(persistentEntity) + if (!childEntities.empty) { + for (PersistentEntity childEntity : childEntities) { + associations.addAll(childEntity.associations) + } + } + associations.addAll(persistentEntity.associations) + + if (associations.size() > 0) { + int i = 0 + List previousAssociations = [] + cypherBuilder.addReturnColumn(CypherBuilder.DEFAULT_RETURN_TYPES) + + for (Association association in associations) { + if (association.isBasic()) continue + + FetchType fetchType = fetchStrategy(association.name) + boolean isEager = fetchType.is(fetchType.EAGER) + + String r = "r${i++}" + + String associationName = association.name + GraphPersistentEntity associatedGraphEntity = (GraphPersistentEntity) association.associatedEntity + boolean isAssociationRelationshipEntity = associatedGraphEntity.isRelationshipEntity() + boolean isToMany = association instanceof ToMany + boolean isToOne = association instanceof ToOne + + boolean lazy = false + if (isToOne && !isEager) { + Property propertyMapping = association.mapping.mappedForm + Boolean isLazy = propertyMapping.getLazy() + lazy = (isLazy != null ? isLazy : (association instanceof ManyToOne ? !association.isCircular() : true)) + + } else if (isToMany) { + lazy = ((ToMany) association).lazy + } + + // if there are associations, add a join to get them + String withMatch = "WITH n, ${previousAssociations.size() > 0 ? previousAssociations.join(", ") + ", " : ""}" + String associationIdsRef = "${associationName}Ids" + String associationNodeRef = "${associationName}Node" + String associationNodesRef = "${associationName}Nodes" + + boolean addOptionalMatch = false + // If it is a one-to-many and lazy=true + // Or it is any non-eager to-one (regardless of nullability/laziness) + // then just collect the identifiers and not the nodes. + // A mandatory, lazy to-one (e.g. a required hasOne) must still have its id + // collected here - otherwise the persister has no way to know the associated + // entity's real identifier and falls back to an association-query-executor + // proxy whose "key" is the *parent's* id, not the target's (see + // AssociationQueryProxyHandler.getProxyKey()), corrupting things like + // Id lookups performed before the association is initialized. + if ((isToMany && lazy) || (isToOne && !isEager)) { + withMatch += "collect(DISTINCT ${associatedGraphEntity.formatId(associationNodeRef)}) as ${associationIdsRef}" + cypherBuilder.addReturnColumn(associationIdsRef) + previousAssociations << associationIdsRef + addOptionalMatch = true + } else if (isEager) { + withMatch += "collect(DISTINCT $associationNodeRef) as $associationNodesRef" + cypherBuilder.addReturnColumn(associationNodesRef) + if (isAssociationRelationshipEntity) { + withMatch += ", collect($r) as ${associationName}Rels" + cypherBuilder.addReturnColumn("${associationName}Rels") + } + previousAssociations << associationNodesRef + addOptionalMatch = true + } + + if (addOptionalMatch) { + + String relationshipPattern = graphEntity + .formatAssociationPatternFromExisting( + association, + r, + CypherBuilder.NODE_VAR, + associationNodeRef + ) + cypherBuilder.addOptionalMatch( + "$relationshipPattern $withMatch" + ) + } + + } + } + } + } else { for (projection in projectionList) { cypherBuilder.addReturnColumn(buildProjection(projection, cypherBuilder)) } @@ -532,23 +550,23 @@ class Neo4jQuery extends Query { } else { List projectedResults = [] - while( executionResult.hasNext() ) { + while (executionResult.hasNext()) { Record record = executionResult.next() def columnNames = executionResult.keys() projectedResults.add columnNames.collect { String columnName -> Value value = record.get(columnName) - if(value.type() == session.boltDriver.defaultTypeSystem().NODE()) { + if (value.type() == session.boltDriver.defaultTypeSystem().NODE()) { // if a Node has been project then this is an association def propName = columnName.substring(0, columnName.lastIndexOf('_')) def prop = persistentEntity.getPropertyByName(propName) - if(prop instanceof ToOne) { - Association association = (Association)prop + if (prop instanceof ToOne) { + Association association = (Association) prop Node childNode = value.asNode() def persister = getSession().getEntityPersister(association.type) - def data = Collections.singletonMap( CypherBuilder.NODE_DATA, childNode) + def data = Collections. singletonMap(CypherBuilder.NODE_DATA, childNode) return persister.unmarshallOrFromCache( association.associatedEntity, data) } @@ -557,10 +575,9 @@ class Neo4jQuery extends Query { } } - if(projectionList.size() == 1 || projectedResults.size() == 1) { + if (projectionList.size() == 1 || projectedResults.size() == 1) { return projectedResults.flatten() - } - else { + } else { return projectedResults } } @@ -579,14 +596,13 @@ class Neo4jQuery extends Query { GraphPersistentEntity graphEntity = (GraphPersistentEntity) persistentEntity CypherBuilder cypherBuilder - if(isRelationshipEntity) { - RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity)graphEntity + if (isRelationshipEntity) { + RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity) graphEntity GraphPersistentEntity fromEntity = relEntity.getFromEntity() cypherBuilder = new CypherBuilder(fromEntity.labelsAsString) cypherBuilder.setStartNode(RelationshipPersistentEntity.FROM) cypherBuilder.addRelationshipMatch(relEntity.buildToMatch()) - } - else { + } else { cypherBuilder = new CypherBuilder(graphEntity.labelsAsString) } @@ -615,7 +631,7 @@ class Neo4jQuery extends Query { } String buildConditions(Query.Criterion criterion, CypherBuilder builder, String prefix) { - return dispatchCriterion(criterion, (GraphPersistentEntity)entity, builder, prefix).toString() + return dispatchCriterion(criterion, (GraphPersistentEntity) entity, builder, prefix).toString() } private static Collection convertEnumsInList(Collection collection) { @@ -630,17 +646,17 @@ class Neo4jQuery extends Query { } private static int addBuildParameterForCriterion(CypherBuilder builder, PersistentEntity entity, Query.PropertyCriterion criterion) { - Neo4jMappingContext mappingContext = (Neo4jMappingContext)entity.mappingContext - return builder.addParam( mappingContext.convertToNative(criterion.value) ) + Neo4jMappingContext mappingContext = (Neo4jMappingContext) entity.mappingContext + return builder.addParam(mappingContext.convertToNative(criterion.value)) } @Override Neo4jSession getSession() { - return (Neo4jSession)super.getSession() + return (Neo4jSession) super.getSession() } org.neo4j.driver.Session getBoltSession() { - return (org.neo4j.driver.Session)getSession().getNativeInterface() + return (org.neo4j.driver.Session) getSession().getNativeInterface() } @@ -660,18 +676,16 @@ class Neo4jQuery extends Query { } else if (left) { operator = CriterionHandler.OPERATOR_ENDS_WITH builder.replaceParamAt(paramNumber, value.substring(1, length)) - } else if(value.indexOf((int)CriterionHandler.PATTERN_CHAR) > -1) { + } else if (value.indexOf((int) CriterionHandler.PATTERN_CHAR) > -1) { operator = CriterionHandler.OPERATOR_LIKE String pattern = Query.patternToRegex(value) - if(caseSensitive) { + if (caseSensitive) { builder.replaceParamAt(paramNumber, "(?i)${pattern}".toString()) - } - else { + } else { builder.replaceParamAt(paramNumber, pattern) } - } - else { + } else { operator = CriterionHandler.OPERATOR_EQUALS } } else { @@ -685,7 +699,9 @@ class Neo4jQuery extends Query { * @param < T > The projection type */ static interface ProjectionHandler { + String COUNT = "count(*)" + String handle(PersistentEntity entity, T projection, CypherBuilder builder) } @@ -695,6 +711,7 @@ class Neo4jQuery extends Query { * @param < T > The criterion type */ static interface CriterionHandler { + char PATTERN_CHAR = '%' String COUNT = "count" String OPERATOR_EQUALS = '=' @@ -719,24 +736,24 @@ class Neo4jQuery extends Query { */ @CompileStatic static class AssociationQueryHandler implements CriterionHandler { + @Override CypherExpression handle(GraphPersistentEntity entity, AssociationQuery criterion, CypherBuilder builder, String prefix) { - AssociationQuery aq = (AssociationQuery)criterion - if(entity.isRelationshipEntity()) { + AssociationQuery aq = (AssociationQuery) criterion + if (entity.isRelationshipEntity()) { return dispatchCriterion(aq.criteria, entity, builder, aq.association.name) - } - else { + } else { String targetNodeName = "m_${builder.getNextMatchNumber()}" String nextPrefix = targetNodeName String relationship = "" - Association association = (Association)aq.association + Association association = (Association) aq.association if (criterion.entity instanceof RelationshipPersistentEntity) { String type = (criterion.entity as RelationshipPersistentEntity).type() relationship = "$nextPrefix:$type" targetNodeName = "m_${builder.getNextMatchNumber() + 1}" } builder.addMatch( - entity.formatAssociationPatternFromExisting(association, relationship, prefix, targetNodeName) + entity.formatAssociationPatternFromExisting(association, relationship, prefix, targetNodeName) ) return dispatchCriterion(aq.criteria, entity, builder, nextPrefix) } @@ -751,6 +768,7 @@ class Neo4jQuery extends Query { */ @CompileStatic static class ComparisonCriterionHandler implements CriterionHandler { + public static final ComparisonCriterionHandler GREATER_THAN_EQUALS = new ComparisonCriterionHandler(CriterionHandler.OPERATOR_GREATER_THAN_EQUALS) public static final ComparisonCriterionHandler GREATER_THAN = new ComparisonCriterionHandler(CriterionHandler.OPERATOR_GREATER_THAN) public static final ComparisonCriterionHandler LESS_THAN = new ComparisonCriterionHandler(CriterionHandler.OPERATOR_LESS_THAN) @@ -771,24 +789,21 @@ class Neo4jQuery extends Query { PersistentProperty association = graphEntity.getPropertyByName(criterion.property) if (association instanceof Association && !(association instanceof Basic)) { GraphPersistentEntity associatedGraphEntity = (GraphPersistentEntity) ((Association) association).associatedEntity - if(graphEntity.isRelationshipEntity() && RelationshipPersistentEntity.isRelationshipAssociation((Association)association)) { + if (graphEntity.isRelationshipEntity() && RelationshipPersistentEntity.isRelationshipAssociation((Association) association)) { lhs = associatedGraphEntity.formatId(association.name) - } - else { + } else { String targetNodeName = "m_${builder.getNextMatchNumber()}" builder.addMatch( - graphEntity.formatAssociationPatternFromExisting((Association)association, "", prefix, targetNodeName) + graphEntity.formatAssociationPatternFromExisting((Association) association, "", prefix, targetNodeName) ) lhs = graphEntity.formatId(targetNodeName) } - } - else { - if(graphEntity.isRelationshipEntity() && criterion.property == RelationshipPersistentEntity.TYPE) { - builder.replaceFirstRelationshipMatch( ((RelationshipPersistentEntity)graphEntity).buildRelationshipMatchTo(null, CypherBuilder.REL_VAR)) + } else { + if (graphEntity.isRelationshipEntity() && criterion.property == RelationshipPersistentEntity.TYPE) { + builder.replaceFirstRelationshipMatch(((RelationshipPersistentEntity) graphEntity).buildRelationshipMatchTo(null, CypherBuilder.REL_VAR)) lhs = "TYPE(r)" - } - else { + } else { lhs = graphEntity.formatProperty(prefix, criterion.property) } @@ -818,6 +833,7 @@ class Neo4jQuery extends Query { */ @CompileStatic static class PropertyComparisonCriterionHandler implements CriterionHandler { + public static final PropertyComparisonCriterionHandler GREATER_THAN_EQUALS = new PropertyComparisonCriterionHandler(CriterionHandler.OPERATOR_GREATER_THAN_EQUALS) public static final PropertyComparisonCriterionHandler GREATER_THAN = new PropertyComparisonCriterionHandler(CriterionHandler.OPERATOR_GREATER_THAN) public static final PropertyComparisonCriterionHandler LESS_THAN = new PropertyComparisonCriterionHandler(CriterionHandler.OPERATOR_LESS_THAN) @@ -834,7 +850,7 @@ class Neo4jQuery extends Query { @Override CypherExpression handle(GraphPersistentEntity entity, T criterion, CypherBuilder builder, String prefix) { def operator = COMPARISON_OPERATORS.get(criterion.getClass()) - if(operator == null) { + if (operator == null) { throw new UnsupportedOperationException("Unsupported Neo4j property comparison: ${criterion}") } return new CypherExpression("$prefix.${criterion.property}${operator}n.${criterion.otherProperty}") @@ -850,7 +866,7 @@ class Neo4jQuery extends Query { public static final SizeCriterionHandler EQUALS = new SizeCriterionHandler(CriterionHandler.OPERATOR_EQUALS) public static final SizeCriterionHandler NOT_EQUALS = new SizeCriterionHandler(CriterionHandler.OPERATOR_NOT_EQUALS) - public static final SizeCriterionHandler GREATER_THAN= new SizeCriterionHandler(CriterionHandler.OPERATOR_GREATER_THAN) + public static final SizeCriterionHandler GREATER_THAN = new SizeCriterionHandler(CriterionHandler.OPERATOR_GREATER_THAN) public static final SizeCriterionHandler GREATER_THAN_EQUALS = new SizeCriterionHandler(CriterionHandler.OPERATOR_GREATER_THAN_EQUALS) public static final SizeCriterionHandler LESS_THAN = new SizeCriterionHandler(CriterionHandler.OPERATOR_LESS_THAN) public static final SizeCriterionHandler LESS_THAN_EQUALS = new SizeCriterionHandler(CriterionHandler.OPERATOR_LESS_THAN_EQUALS) @@ -874,7 +890,8 @@ class Neo4jQuery extends Query { @EqualsAndHashCode static class CypherExpression { - @Delegate final CharSequence expression + @Delegate + final CharSequence expression CypherExpression(String lhs, String rhs, String operator) { this.expression = "$lhs$operator$rhs".toString() diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/RelationshipPendingDelete.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/RelationshipPendingDelete.java index c0d0478a847..39652b69d87 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/RelationshipPendingDelete.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/RelationshipPendingDelete.java @@ -18,6 +18,15 @@ */ package org.grails.datastore.gorm.neo4j.engine; +import java.io.Serializable; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.neo4j.driver.Transaction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.grails.datastore.gorm.neo4j.CypherBuilder; import org.grails.datastore.gorm.neo4j.GraphPersistentEntity; import org.grails.datastore.gorm.neo4j.RelationshipPersistentEntity; @@ -26,14 +35,6 @@ import org.grails.datastore.mapping.engine.EntityAccess; import org.grails.datastore.mapping.model.config.GormProperties; import org.grails.datastore.mapping.model.types.Association; -import org.neo4j.driver.Transaction; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.Serializable; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; import static org.grails.datastore.gorm.neo4j.engine.RelationshipPendingInsert.FROM; import static org.grails.datastore.gorm.neo4j.engine.RelationshipPendingInsert.TO; @@ -46,14 +47,13 @@ */ public class RelationshipPendingDelete extends PendingOperationAdapter { - private static Logger log = LoggerFactory.getLogger(RelationshipPendingDelete.class); + private static final Logger log = LoggerFactory.getLogger(RelationshipPendingDelete.class); private final Transaction boltTransaction; private final Association association; private final Collection targetIdentifiers; private final EntityAccess entityAccess; - public RelationshipPendingDelete(EntityAccess parent, Association association, Collection pendingInserts, Transaction boltTransaction) { super(parent.getPersistentEntity(), (Serializable) parent.getIdentifier(), parent.getEntity()); this.targetIdentifiers = pendingInserts; @@ -68,8 +68,8 @@ public void run() { Serializable parentId = getNativeKey(); final boolean isRelationshipAssociation = graphParent.isRelationshipEntity(); - if(isRelationshipAssociation) { - if(association.getName().equals(FROM)) { + if (isRelationshipAssociation) { + if (association.getName().equals(FROM)) { RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity) graphParent; GraphPersistentEntity graphChild = relEntity.getToEntity(); graphParent = relEntity.getFromEntity(); @@ -78,9 +78,8 @@ public void run() { Object startEntity = entityAccess.getProperty(FROM); parentId = graphParent.getReflector().getIdentifier(startEntity); this.targetIdentifiers.clear(); - this.targetIdentifiers.add( graphChild.getReflector().getIdentifier(endEntity) ); - } - else { + this.targetIdentifiers.add(graphChild.getReflector().getIdentifier(endEntity)); + } else { // don't do anything for the 'to' end return; } @@ -88,17 +87,15 @@ public void run() { final Map params = new LinkedHashMap<>(2); - if(RelationshipUtils.useReversedMappingFor(association)) { + if (RelationshipUtils.useReversedMappingFor(association)) { params.put(GormProperties.IDENTITY, parentId); - } - else { + } else { params.put(CypherBuilder.START, parentId); params.put(CypherBuilder.END, targetIdentifiers); } - String cypher = graphParent.formatAssociationDelete(association, entityAccess.getEntity()); - if(cypher != null) { + if (cypher != null) { if (log.isDebugEnabled()) { log.debug("DELETE Cypher [{}] for parameters [{}]", cypher, params); } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/RelationshipPendingInsert.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/RelationshipPendingInsert.java index bea372782cb..7099c4e1c31 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/RelationshipPendingInsert.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/RelationshipPendingInsert.java @@ -18,22 +18,35 @@ */ package org.grails.datastore.gorm.neo4j.engine; +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.neo4j.driver.Transaction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import grails.neo4j.Relationship; -import org.grails.datastore.gorm.neo4j.*; +import org.grails.datastore.gorm.neo4j.CypherBuilder; +import org.grails.datastore.gorm.neo4j.GraphPersistentEntity; +import org.grails.datastore.gorm.neo4j.Neo4jMappingContext; +import org.grails.datastore.gorm.neo4j.Neo4jSession; +import org.grails.datastore.gorm.neo4j.RelationshipPersistentEntity; +import org.grails.datastore.gorm.neo4j.RelationshipUtils; import org.grails.datastore.gorm.neo4j.mapping.config.DynamicAssociation; import org.grails.datastore.gorm.neo4j.mapping.config.DynamicToOneAssociation; import org.grails.datastore.mapping.core.impl.PendingInsertAdapter; import org.grails.datastore.mapping.engine.EntityAccess; import org.grails.datastore.mapping.model.PersistentProperty; -import org.grails.datastore.mapping.model.types.*; +import org.grails.datastore.mapping.model.types.Association; +import org.grails.datastore.mapping.model.types.Basic; +import org.grails.datastore.mapping.model.types.OneToMany; +import org.grails.datastore.mapping.model.types.OneToOne; +import org.grails.datastore.mapping.model.types.Simple; +import org.grails.datastore.mapping.model.types.TenantId; import org.grails.datastore.mapping.reflect.EntityReflector; -import org.neo4j.driver.Session; -import org.neo4j.driver.Transaction; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.Serializable; -import java.util.*; /** * Represents a pending relationship insert @@ -43,6 +56,7 @@ * */ public class RelationshipPendingInsert extends PendingInsertAdapter { + /** * The name of the from property */ @@ -54,7 +68,7 @@ public class RelationshipPendingInsert extends PendingInsertAdapter targetIdentifiers; @@ -74,16 +88,15 @@ public RelationshipPendingInsert(EntityAccess parent, Association association, C @Override public void run() { - GraphPersistentEntity graphParent = (GraphPersistentEntity) getEntity(); GraphPersistentEntity graphChild = (GraphPersistentEntity) association.getAssociatedEntity(); - Map params = new LinkedHashMap<>(2); + Map params = new LinkedHashMap<>(2); EntityAccess entityAccess = getEntityAccess(); Object parentId = entityAccess.getIdentifier(); final boolean isRelationshipAssociation = Relationship.class.isAssignableFrom(graphParent.getJavaClass()); - if(isRelationshipAssociation) { - if(association.getName().equals(FROM)) { + if (isRelationshipAssociation) { + if (association.getName().equals(FROM)) { Association endProperty = (Association) graphParent.getPropertyByName(TO); Association startProperty = (Association) graphParent.getPropertyByName(FROM); @@ -94,9 +107,8 @@ public void run() { Object startEntity = entityAccess.getProperty(FROM); parentId = graphParent.getReflector().getIdentifier(startEntity); this.targetIdentifiers.clear(); - this.targetIdentifiers.add( graphChild.getReflector().getIdentifier(endEntity) ); - } - else { + this.targetIdentifiers.add(graphChild.getReflector().getIdentifier(endEntity)); + } else { // don't do anything for the 'to' end return; } @@ -110,65 +122,62 @@ public void run() { final String relMatch; - if(association instanceof DynamicToOneAssociation) { + if (association instanceof DynamicToOneAssociation) { LinkedHashMap attrs = new LinkedHashMap(); attrs.put(SOURCE_TYPE, graphParent.getJavaClass().getSimpleName()); attrs.put(TARGET_TYPE, graphChild.getJavaClass().getSimpleName()); relMatch = RelationshipUtils.matchForAssociation(association, "r", attrs); - } - else if(isRelationshipAssociation) { + } else if (isRelationshipAssociation) { LinkedHashMap attrs = new LinkedHashMap<>(); GraphPersistentEntity relEntity = (GraphPersistentEntity) getEntity(); EntityReflector reflector = relEntity.getReflector(); - Neo4jMappingContext mappingContext = (Neo4jMappingContext)relEntity.getMappingContext(); + Neo4jMappingContext mappingContext = (Neo4jMappingContext) relEntity.getMappingContext(); Neo4jEntityPersister entityPersister = session.getEntityPersister(getEntityAccess().getEntity()); - if(entityPersister.cancelInsert(entity, getEntityAccess())) { + if (entityPersister.cancelInsert(entity, getEntityAccess())) { // operation cancelled, return return; } for (PersistentProperty pp : relEntity.getPersistentProperties()) { - if(pp instanceof Simple || pp instanceof Basic || pp instanceof TenantId) { + if (pp instanceof Simple || pp instanceof Basic || pp instanceof TenantId) { String propertyName = pp.getName(); - if(RelationshipPersistentEntity.TYPE.equals(propertyName)) { + if (RelationshipPersistentEntity.TYPE.equals(propertyName)) { continue; } Object v = reflector.getProperty(getNativeEntry(), propertyName); - if(v != null) { + if (v != null) { attrs.put(propertyName, mappingContext.convertToNative(v)); } } } Relationship relationship = (Relationship) getEntityAccess().getEntity(); - attrs.putAll( relationship.attributes() ); + attrs.putAll(relationship.attributes()); params.put("rProps", attrs); relMatch = RelationshipUtils.toMatch(association, relationship); - } - else { + } else { relMatch = RelationshipUtils.matchForAssociation(association, "r"); } - if(isUpdate && - (association instanceof DynamicAssociation || - ((association.isBidirectional() && (association instanceof OneToMany)) || (association instanceof OneToOne)) && - !RelationshipUtils.useReversedMappingFor(association))) { + if (isUpdate && + (association instanceof DynamicAssociation || + ((association.isBidirectional() && (association instanceof OneToMany)) || (association instanceof OneToOne)) && + !RelationshipUtils.useReversedMappingFor(association))) { // delete any previous StringBuilder cypher = new StringBuilder(CypherBuilder.buildRelationshipMatch(labelsFrom, relMatch, labelsTo)); Map deleteParams; - if(association instanceof DynamicToOneAssociation || association instanceof OneToOne) { + if (association instanceof DynamicToOneAssociation || association instanceof OneToOne) { cypher.append(graphChild.formatId(FROM)); - deleteParams = Collections.singletonMap(CypherBuilder.START, Collections.singletonList(parentId)); - } - else { + deleteParams = Collections.singletonMap(CypherBuilder.START, Collections.singletonList(parentId)); + } else { cypher.append(graphChild.formatId(TO)); - deleteParams = Collections.singletonMap(CypherBuilder.START, targetIdentifiers); + deleteParams = Collections.singletonMap(CypherBuilder.START, targetIdentifiers); } cypher.append(" IN $").append(CypherBuilder.START).append(" DELETE r"); - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("DELETE Cypher [{}] for parameters [{}]", cypher, deleteParams); } boltTransaction.run(cypher.toString(), deleteParams); @@ -177,16 +186,16 @@ else if(isRelationshipAssociation) { StringBuilder cypherQuery = new StringBuilder(String.format(CypherBuilder.CYPHER_FROM_TO_NODES_MATCH, labelsFrom, labelsTo)); cypherQuery.append(graphParent.formatId(RelationshipPersistentEntity.FROM)) - .append(" = $") - .append(CypherBuilder.START) - .append(" AND ") - .append(graphChild.formatId(RelationshipPersistentEntity.TO)) - .append(" IN $") - .append(CypherBuilder.END) - .append(" MERGE (from)") - .append(relMatch) - .append("(to)"); - if(isRelationshipAssociation) { + .append(" = $") + .append(CypherBuilder.START) + .append(" AND ") + .append(graphChild.formatId(RelationshipPersistentEntity.TO)) + .append(" IN $") + .append(CypherBuilder.END) + .append(" MERGE (from)") + .append(relMatch) + .append("(to)"); + if (isRelationshipAssociation) { cypherQuery.append(" ON CREATE SET r=$rProps"); } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/extensions/Neo4jExtensions.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/extensions/Neo4jExtensions.groovy index fdb281e69e2..83ba1116b3d 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/extensions/Neo4jExtensions.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/extensions/Neo4jExtensions.groovy @@ -20,18 +20,20 @@ package org.grails.datastore.gorm.neo4j.extensions import groovy.transform.CompileStatic import org.codehaus.groovy.runtime.typehandling.GroovyCastException -import org.grails.datastore.gorm.neo4j.Neo4jDatastore -import org.grails.datastore.gorm.neo4j.Neo4jSession -import org.grails.datastore.gorm.neo4j.collection.Neo4jPath -import org.grails.datastore.gorm.neo4j.collection.Neo4jResultList -import org.grails.datastore.mapping.core.AbstractDatastore -import org.neo4j.driver.Result + import org.neo4j.driver.QueryRunner +import org.neo4j.driver.Result import org.neo4j.driver.types.MapAccessor import org.neo4j.driver.types.Node import org.neo4j.driver.types.Path import org.neo4j.driver.types.Relationship +import org.grails.datastore.gorm.neo4j.Neo4jDatastore +import org.grails.datastore.gorm.neo4j.Neo4jSession +import org.grails.datastore.gorm.neo4j.collection.Neo4jPath +import org.grails.datastore.gorm.neo4j.collection.Neo4jResultList +import org.grails.datastore.mapping.core.AbstractDatastore + /** * Extension methods to improve the Neo4j experience in Groovy. * @@ -72,16 +74,14 @@ class Neo4jExtensions { */ static N asType(Node node, Class c) { //TODO: Remove explicit typecast to Class once GROOVY-9460 is resolved - if(Map.isAssignableFrom((Class) c)) { - return (N)node.asMap() - } - else { - Neo4jSession session = (Neo4jSession)AbstractDatastore.retrieveSession(Neo4jDatastore) + if (Map.isAssignableFrom((Class) c)) { + return (N) node.asMap() + } else { + Neo4jSession session = (Neo4jSession) AbstractDatastore.retrieveSession(Neo4jDatastore) def entityPersister = session.getEntityPersister(c) - if(entityPersister != null) { - return (N)entityPersister.unmarshallOrFromCache(entityPersister.getPersistentEntity(), node) - } - else { + if (entityPersister != null) { + return (N) entityPersister.unmarshallOrFromCache(entityPersister.getPersistentEntity(), node) + } else { throw new ClassCastException("Class [$c.name] is not a GORM entity") } } @@ -96,12 +96,11 @@ class Neo4jExtensions { * @return The domain instance */ static N asType(Relationship rel, Class c) { - Neo4jSession session = (Neo4jSession)AbstractDatastore.retrieveSession(Neo4jDatastore) + Neo4jSession session = (Neo4jSession) AbstractDatastore.retrieveSession(Neo4jDatastore) def entityPersister = session.getEntityPersister(c) - if(entityPersister != null) { - return (N)entityPersister.unmarshallOrFromCache(entityPersister.getPersistentEntity(), rel, Collections.emptyMap(), Collections.emptyMap()) - } - else { + if (entityPersister != null) { + return (N) entityPersister.unmarshallOrFromCache(entityPersister.getPersistentEntity(), rel, Collections.emptyMap(), Collections.emptyMap()) + } else { throw new ClassCastException("Class [$c.name] is not a GORM relationship entity") } } @@ -115,11 +114,10 @@ class Neo4jExtensions { * @return The domain instance */ static grails.neo4j.Path asType(Path path, Class c) { - if(grails.neo4j.Path.isAssignableFrom(c)) { - Neo4jSession session = (Neo4jSession)AbstractDatastore.retrieveSession(Neo4jDatastore) + if (grails.neo4j.Path.isAssignableFrom(c)) { + Neo4jSession session = (Neo4jSession) AbstractDatastore.retrieveSession(Neo4jDatastore) return new Neo4jPath<>(session.datastore, path) - } - else { + } else { throw new GroovyCastException(path, c) } } @@ -133,18 +131,16 @@ class Neo4jExtensions { * @return The domain instance */ static N asType(Result result, Class c) { - Neo4jSession session = (Neo4jSession)AbstractDatastore.retrieveSession(Neo4jDatastore) + Neo4jSession session = (Neo4jSession) AbstractDatastore.retrieveSession(Neo4jDatastore) def entityPersister = session.getEntityPersister(c) - if(entityPersister != null) { + if (entityPersister != null) { def resultList = new Neo4jResultList(0, result, entityPersister) - if(!resultList.isEmpty()) { - return (N)resultList.get(0) - } - else { + if (!resultList.isEmpty()) { + return (N) resultList.get(0) + } else { return null } - } - else { + } else { throw new ClassCastException("Class [$c.name] is not a GORM entity") } } @@ -158,12 +154,11 @@ class Neo4jExtensions { * @return The domain instance */ static List toList(Result result, Class c) { - Neo4jSession session = (Neo4jSession)AbstractDatastore.retrieveSession(Neo4jDatastore) + Neo4jSession session = (Neo4jSession) AbstractDatastore.retrieveSession(Neo4jDatastore) def entityPersister = session.getEntityPersister(c) - if(entityPersister != null) { + if (entityPersister != null) { return new Neo4jResultList(0, result, entityPersister) - } - else { + } else { throw new ClassCastException("Class [$c.name] is not a GORM entity") } } @@ -177,9 +172,9 @@ class Neo4jExtensions { * @return The query result */ static Result execute(QueryRunner session, String cypher, List positionalParameters) { - Map params = new LinkedHashMap<>() - int i = 0; - for(p in positionalParameters) { + Map params = new LinkedHashMap<>() + int i = 0 + for (p in positionalParameters) { params.put(String.valueOf(++i), p) } session.run(cypher, params) diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/identity/SnowflakeIdGenerator.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/identity/SnowflakeIdGenerator.java index 14c65270960..54ff48929d7 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/identity/SnowflakeIdGenerator.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/identity/SnowflakeIdGenerator.java @@ -18,16 +18,17 @@ */ package org.grails.datastore.gorm.neo4j.identity; -import org.grails.datastore.gorm.neo4j.IdGenerator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.Serializable; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Enumeration; import java.util.Random; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.grails.datastore.gorm.neo4j.IdGenerator; + /** * * Generate unique IDs using the Twitter Snowflake algorithm (see https://github.com/twitter/snowflake). Snowflake IDs are 64 bit positive longs composed of: - 41 bits time stamp - 10 bits machine id - 12 bits sequence number @@ -37,7 +38,7 @@ public class SnowflakeIdGenerator implements IdGenerator { - private static Logger log = LoggerFactory.getLogger(SnowflakeIdGenerator.class); + private static final Logger log = LoggerFactory.getLogger(SnowflakeIdGenerator.class); private final long datacenterIdBits = 10L; private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); @@ -64,7 +65,7 @@ public synchronized Serializable nextId() { long timestamp = System.currentTimeMillis(); if (timestamp < lastTimestamp) { throw new IllegalArgumentException("Clock moved backwards. Refusing to generate id for " + ( - lastTimestamp - timestamp) + " milliseconds."); + lastTimestamp - timestamp) + " milliseconds."); } if (lastTimestamp == timestamp) { sequence = (sequence + 1) % sequenceMax; @@ -76,8 +77,8 @@ public synchronized Serializable nextId() { } lastTimestamp = timestamp; return ((timestamp - twepoch) << timestampLeftShift) | - (datacenterId << datacenterIdShift) | - sequence; + (datacenterId << datacenterIdShift) | + sequence; } protected long tilNextMillis(long lastTimestamp) { @@ -110,7 +111,7 @@ protected long getDatacenterId() { // TODO: unit test with empty array for nic.g } private boolean isInvalidHardwareAddress(byte[] hardwareAddress) { - return ((hardwareAddress == null) || (hardwareAddress.length<6)); + return ((hardwareAddress == null) || (hardwareAddress.length < 6)); } -} \ No newline at end of file +} diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/Attribute.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/Attribute.groovy index 9b0626e68b0..f0e107834bc 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/Attribute.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/Attribute.groovy @@ -19,10 +19,11 @@ package org.grails.datastore.gorm.neo4j.mapping.config -import grails.neo4j.Direction import groovy.transform.CompileStatic import groovy.transform.builder.Builder import groovy.transform.builder.SimpleStrategy + +import grails.neo4j.Direction import org.grails.datastore.mapping.config.Property /** diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/DynamicAssociation.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/DynamicAssociation.java index 374757cb33b..dc590eb2eb9 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/DynamicAssociation.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/DynamicAssociation.java @@ -28,4 +28,5 @@ * @since 5.0 */ public interface DynamicAssociation extends PersistentProperty { + } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/DynamicToManyAssociation.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/DynamicToManyAssociation.java index 1c1ba2aeb6b..fc450f8f4c8 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/DynamicToManyAssociation.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/DynamicToManyAssociation.java @@ -29,7 +29,7 @@ * @author Graeme Rocher * @since 5.0 */ -public class DynamicToManyAssociation extends ToMany implements DynamicAssociation{ +public class DynamicToManyAssociation extends ToMany implements DynamicAssociation { public DynamicToManyAssociation(PersistentEntity owner, MappingContext context, String name, PersistentEntity associatedType) { super(owner, context, name, associatedType.getJavaClass()); diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/DynamicToOneAssociation.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/DynamicToOneAssociation.java index 670592080a0..fe63d08bb1e 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/DynamicToOneAssociation.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/DynamicToOneAssociation.java @@ -29,7 +29,7 @@ * @author Graeme Rocher * @since 5.0 */ -public class DynamicToOneAssociation extends ToOne implements DynamicAssociation{ +public class DynamicToOneAssociation extends ToOne implements DynamicAssociation { public DynamicToOneAssociation(PersistentEntity owner, MappingContext context, String name, PersistentEntity associatedType) { super(owner, context, name, associatedType.getJavaClass()); @@ -44,8 +44,7 @@ public PropertyMapping getMapping() { @Override public boolean equals(Object obj) { - if(obj instanceof DynamicToOneAssociation) { - DynamicToOneAssociation other = (DynamicToOneAssociation) obj; + if (obj instanceof DynamicToOneAssociation other) { return other.getName().equals(name) && other.getOwner().getJavaClass().equals(getOwner().getJavaClass()); } return false; diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/NodeConfig.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/NodeConfig.groovy index 84f7a50c211..ee2aa3004ad 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/NodeConfig.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/NodeConfig.groovy @@ -20,8 +20,9 @@ package org.grails.datastore.gorm.neo4j.mapping.config import groovy.transform.CompileStatic import groovy.transform.builder.Builder -import groovy.transform.builder.SimpleStrategy; -import org.grails.datastore.mapping.config.Entity; +import groovy.transform.builder.SimpleStrategy + +import org.grails.datastore.mapping.config.Entity /** * Extends the default {@link Entity} configuration adding the ability to assign labels diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/RelationshipConfig.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/RelationshipConfig.groovy index 571bb77c7f5..46562c5521d 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/RelationshipConfig.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/config/RelationshipConfig.groovy @@ -19,11 +19,11 @@ package org.grails.datastore.gorm.neo4j.mapping.config -import grails.neo4j.Direction import groovy.transform.CompileStatic import groovy.transform.builder.Builder import groovy.transform.builder.SimpleStrategy -import org.grails.datastore.mapping.config.Entity + +import grails.neo4j.Direction /** * Config for a relationship diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/reflect/Neo4jNameUtils.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/reflect/Neo4jNameUtils.groovy index 8268cee5f4b..5f9ae81c647 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/reflect/Neo4jNameUtils.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/mapping/reflect/Neo4jNameUtils.groovy @@ -19,10 +19,10 @@ package org.grails.datastore.gorm.neo4j.mapping.reflect import groovy.transform.CompileStatic + import org.grails.datastore.gorm.neo4j.parsers.PlingStemmer import org.grails.datastore.mapping.reflect.NameUtils - /** * Utilities for Neo4j naming conventions * @@ -36,7 +36,7 @@ class Neo4jNameUtils extends NameUtils { * @param word The word * @return Whether the given word is plural */ - static boolean isPlural( String word ) { + static boolean isPlural(String word) { PlingStemmer.isPlural(word) } @@ -44,7 +44,7 @@ class Neo4jNameUtils extends NameUtils { * @param word The word * @return Whether the given word is singular */ - static boolean isSingular( String word ) { - PlingStemmer.isSingular( word ) + static boolean isSingular(String word) { + PlingStemmer.isSingular(word) } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/parsers/PlingStemmer.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/parsers/PlingStemmer.java index 3f88b17e104..1554810f197 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/parsers/PlingStemmer.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/parsers/PlingStemmer.java @@ -18,8 +18,12 @@ */ package org.grails.datastore.gorm.neo4j.parsers; -import java.util.*; - +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; /** *

@@ -69,6 +73,717 @@ */ public class PlingStemmer { + /** + * Words that end in "-se" in their plural forms (like "nurse" etc.) + */ + private static final Set categorySE_SES = Collections.unmodifiableSet(new HashSet(Arrays.asList( + "nurses", + "cruises", + "premises", + "houses", + "courses", + "cases" + ))); + /** + * Words that do not have a distinct plural form (like "atlas" etc.) + */ + private static final Set category00 = Collections.unmodifiableSet(new HashSet(Arrays.asList( + "alias", + "asbestos", + "atlas", + "barracks", + "bathos", + "bias", + "breeches", + "britches", + "canvas", + "chaos", + "clippers", + "contretemps", + "corps", + "cosmos", + "crossroads", + "diabetes", + "ethos", + "gallows", + "gas", + "graffiti", + "headquarters", + "herpes", + "high-jinks", + "innings", + "jackanapes", + "lens", + "means", + "measles", + "mews", + "mumps", + "news", + "pathos", + "pincers", + "pliers", + "proceedings", + "rabies", + "rhinoceros", + "sassafras", + "scissors", + "series", + "shears", + "species", + "tuna" + ))); + /** + * Words that change from "-um" to "-a" (like "curriculum" etc.), listed in their plural forms + */ + private static final Set categoryUM_A = Collections.unmodifiableSet(new HashSet(Arrays.asList( + "addenda", + "agenda", + "aquaria", + "bacteria", + "candelabra", + "compendia", + "consortia", + "crania", + "curricula", + "data", + "desiderata", + "dicta", + "emporia", + "enconia", + "errata", + "extrema", + "gymnasia", + "honoraria", + "interregna", + "lustra", + "maxima", + "media", + "memoranda", + "millenia", + "minima", + "momenta", + "optima", + "ova", + "phyla", + "quanta", + "rostra", + "spectra", + "specula", + "stadia", + "strata", + "symposia", + "trapezia", + "ultimata", + "vacua", + "vela" + ))); + /** + * Words that change from "-on" to "-a" (like "phenomenon" etc.), listed in their plural forms + */ + private static final Set categoryON_A = Collections.unmodifiableSet(new HashSet(Arrays.asList( + "aphelia", + "asyndeta", + "automata", + "criteria", + "hyperbata", + "noumena", + "organa", + "perihelia", + "phenomena", + "prolegomena" + ))); + /** + * Words that change from "-o" to "-i" (like "libretto" etc.), listed in their plural forms + */ + private static final Set categoryO_I = Collections.unmodifiableSet(new HashSet(Arrays.asList( + "alti", + "bassi", + "canti", + "contralti", + "crescendi", + "libretti", + "soli", + "soprani", + "tempi", + "virtuosi" + ))); + /** + * Words that change from "-us" to "-i" (like "fungus" etc.), listed in their plural forms + */ + private static final Set categoryUS_I = Collections.unmodifiableSet(new HashSet(Arrays.asList( + "alumni", + "bacilli", + "cacti", + "foci", + "fungi", + "genii", + "hippopotami", + "incubi", + "nimbi", + "nuclei", + "nucleoli", + "octopi", + "radii", + "stimuli", + "styli", + "succubi", + "syllabi", + "termini", + "tori", + "umbilici", + "uteri" + ))); + /** + * Words that change from "-ix" to "-ices" (like "appendix" etc.), listed in their plural forms + */ + private static final Set categoryIX_ICES = Collections.unmodifiableSet(new HashSet(Arrays.asList( + "appendices", + "cervices" + ))); + /** + * Words that change from "-is" to "-es" (like "axis" etc.), listed in their plural forms + */ + private static final Set categoryIS_ES = Collections.unmodifiableSet(new HashSet(Arrays.asList( + // plus everybody ending in theses + "analyses", + "axes", + "bases", + "crises", + "diagnoses", + "ellipses", + "emphases", + "neuroses", + "oases", + "paralyses", + "synopses" + ))); + /** + * Words that change from "-oe" to "-oes" (like "toe" etc.), listed in their plural forms + */ + private static final Set categoryOE_OES = Collections.unmodifiableSet(new HashSet(Arrays.asList( + "aloes", + "backhoes", + "beroes", + "canoes", + "chigoes", + "cohoes", + "does", + "felloes", + "floes", + "foes", + "gumshoes", + "hammertoes", + "hoes", + "hoopoes", + "horseshoes", + "leucothoes", + "mahoes", + "mistletoes", + "oboes", + "overshoes", + "pahoehoes", + "pekoes", + "roes", + "shoes", + "sloes", + "snowshoes", + "throes", + "tic-tac-toes", + "tick-tack-toes", + "ticktacktoes", + "tiptoes", + "tit-tat-toes", + "toes", + "toetoes", + "tuckahoes", + "woes" + ))); + /** + * Words that change from "-ex" to "-ices" (like "index" etc.), listed in their plural forms + */ + private static final Set categoryEX_ICES = Collections.unmodifiableSet(new HashSet(Arrays.asList( + "apices", + "codices", + "cortices", + "indices", + "latices", + "murices", + "pontifices", + "silices", + "simplices", + "vertices", + "vortices" + ))); + /** + * Words that change from "-u" to "-us" (like "emu" etc.), listed in their plural forms + */ + private static final Set categoryU_US = Collections.unmodifiableSet(new HashSet(Arrays.asList( + "apercus", + "barbus", + "cornus", + "ecrus", + "emus", + "fondus", + "gnus", + "iglus", + "mus", + "nandus", + "napus", + "poilus", + "quipus", + "snafus", + "tabus", + "tamandus", + "tatus", + "timucus", + "tiramisus", + "tofus", + "tutus" + ))); + /** + * Words that change from "-sse" to "-sses" (like "finesse" etc.), listed in their plural forms + */ + private static final Set categorySSE_SSES = Collections.unmodifiableSet(new HashSet(Arrays.asList( + //plus those ending in mousse + "bouillabaisses", + "coulisses", + "crevasses", + "crosses", + "cuisses", + "demitasses", + "ecrevisses", + "fesses", + "finesses", + "fosses", + "impasses", + "lacrosses", + "largesses", + "masses", + "noblesses", + "palliasses", + "pelisses", + "politesses", + "posses", + "tasses", + "wrasses" + ))); + /** + * Words that change from "-che" to "-ches" (like "brioche" etc.), listed in their plural forms + */ + private static final Set categoryCHE_CHES = Collections.unmodifiableSet(new HashSet(Arrays.asList( + "adrenarches", + "attaches", + "avalanches", + "barouches", + "brioches", + "caches", + "caleches", + "caroches", + "cartouches", + "cliches", + "cloches", + "creches", + "demarches", + "douches", + "gouaches", + "guilloches", + "headaches", + "heartaches", + "huaraches", + "menarches", + "microfiches", + "moustaches", + "mustaches", + "niches", + "panaches", + "panoches", + "pastiches", + "penuches", + "pinches", + "postiches", + "psyches", + "quiches", + "schottisches", + "seiches", + "soutaches", + "synecdoches", + "thelarches", + "troches" + ))); + /** + * Words that end with "-ics" and do not exist as nouns without the 's' (like "aerobics" etc.) + */ + private static final Set categoryICS = Collections.unmodifiableSet(new HashSet(Arrays.asList( + "aerobatics", + "aerobics", + "aerodynamics", + "aeromechanics", + "aeronautics", + "alphanumerics", + "animatronics", + "apologetics", + "architectonics", + "astrodynamics", + "astronautics", + "astrophysics", + "athletics", + "atmospherics", + "autogenics", + "avionics", + "ballistics", + "bibliotics", + "bioethics", + "biometrics", + "bionics", + "bionomics", + "biophysics", + "biosystematics", + "cacogenics", + "calisthenics", + "callisthenics", + "catoptrics", + "civics", + "cladistics", + "cryogenics", + "cryonics", + "cryptanalytics", + "cybernetics", + "cytoarchitectonics", + "cytogenetics", + "diagnostics", + "dietetics", + "dramatics", + "dysgenics", + "econometrics", + "economics", + "electromagnetics", + "electronics", + "electrostatics", + "endodontics", + "enterics", + "ergonomics", + "eugenics", + "eurhythmics", + "eurythmics", + "exodontics", + "fibreoptics", + "futuristics", + "genetics", + "genomics", + "geographics", + "geophysics", + "geopolitics", + "geriatrics", + "glyptics", + "graphics", + "gymnastics", + "hermeneutics", + "histrionics", + "homiletics", + "hydraulics", + "hydrodynamics", + "hydrokinetics", + "hydroponics", + "hydrostatics", + "hygienics", + "informatics", + "kinematics", + "kinesthetics", + "kinetics", + "lexicostatistics", + "linguistics", + "lithoglyptics", + "liturgics", + "logistics", + "macrobiotics", + "macroeconomics", + "magnetics", + "magnetohydrodynamics", + "mathematics", + "metamathematics", + "metaphysics", + "microeconomics", + "microelectronics", + "mnemonics", + "morphophonemics", + "neuroethics", + "neurolinguistics", + "nucleonics", + "numismatics", + "obstetrics", + "onomastics", + "orthodontics", + "orthopaedics", + "orthopedics", + "orthoptics", + "paediatrics", + "patristics", + "patristics", + "pedagogics", + "pediatrics", + "periodontics", + "pharmaceutics", + "pharmacogenetics", + "pharmacokinetics", + "phonemics", + "phonetics", + "phonics", + "photomechanics", + "physiatrics", + "pneumatics", + "poetics", + "politics", + "pragmatics", + "prosthetics", + "prosthodontics", + "proteomics", + "proxemics", + "psycholinguistics", + "psychometrics", + "psychonomics", + "psychophysics", + "psychotherapeutics", + "robotics", + "semantics", + "semiotics", + "semitropics", + "sociolinguistics", + "stemmatics", + "strategics", + "subtropics", + "systematics", + "tectonics", + "telerobotics", + "therapeutics", + "thermionics", + "thermodynamics", + "thermostatics" + ))); + /** + * Words that change from "-ie" to "-ies" (like "auntie" etc.), listed in their plural forms + */ + private static final Set categoryIE_IES = Collections.unmodifiableSet(new HashSet(Arrays.asList( + "aeries", + "anomies", + "aunties", + "baddies", + "beanies", + "birdies", + "boccies", + "bogies", + "bolshies", + "bombies", + "bonhomies", + "bonxies", + "booboisies", + "boogies", + "boogie-woogies", + "bookies", + "booties", + "bosies", + "bourgeoisies", + "brasseries", + "brassies", + "brownies", + "budgies", + "byrnies", + "caddies", + "calories", + "camaraderies", + "capercaillies", + "capercailzies", + "cassies", + "catties", + "causeries", + "charcuteries", + "chinoiseries", + "collies", + "commies", + "cookies", + "coolies", + "coonties", + "cooties", + "corries", + "coteries", + "cowpies", + "cowries", + "cozies", + "crappies", + "crossties", + "curies", + "dachsies", + "darkies", + "dassies", + "dearies", + "dickies", + "dies", + "dixies", + "doggies", + "dogies", + "dominies", + "dovekies", + "eyries", + "faeries", + "falsies", + "floozies", + "folies", + "foodies", + "freebies", + "gaucheries", + "gendarmeries", + "genies", + "ghillies", + "gillies", + "goalies", + "goonies", + "grannies", + "grotesqueries", + "groupies", + "hankies", + "hippies", + "hoagies", + "honkies", + "hymies", + "indies", + "junkies", + "kelpies", + "kilocalories", + "knobkerries", + "koppies", + "kylies", + "laddies", + "lassies", + "lies", + "lingeries", + "magpies", + "magpies", + "marqueteries", + "mashies", + "mealies", + "meanies", + "menageries", + "millicuries", + "mollies", + "facts1", + "moxies", + "neckties", + "newbies", + "nighties", + "nookies", + "oldies", + "organdies", + "panties", + "parqueteries", + "passementeries", + "patisseries", + "pies", + "pinkies", + "pixies", + "porkpies", + "potpies", + "prairies", + "preemies", + "premies", + "punkies", + "pyxies", + "quickies", + "ramies", + "reveries", + "rookies", + "rotisseries", + "scrapies", + "sharpies", + "smoothies", + "softies", + "stoolies", + "stymies", + "swaggies", + "sweeties", + "talkies", + "techies", + "ties", + "tooshies", + "toughies", + "townies", + "veggies", + "walkie-talkies", + "wedgies", + "weenies", + "weirdies", + "yardies", + "yuppies", + "zombies" + ))); + /** + * Maps irregular Germanic English plural nouns to their singular form + */ + private static final Map irregular = Collections.unmodifiableMap(new HashMap() { + { + put("beefs", "beef"); + put("beeves", "beef"); + put("brethren", "brother"); + put("busses", "bus"); + put("cattle", "cattlebeast"); + put("children", "child"); + put("corpora", "corpus"); + put("ephemerides", "ephemeris"); + put("firemen", "fireman"); + put("genera", "genus"); + put("genies", "genie"); + put("genii", "genie"); + put("kine", "cow"); + put("lice", "louse"); + put("men", "man"); + put("mice", "mouse"); + put("mongooses", "mongoose"); + put("monies", "money"); + put("mythoi", "mythos"); + put("octopodes", "octopus"); + put("octopuses", "octopus"); + put("oxen", "ox"); + put("people", "person"); + put("soliloquies", "soliloquy"); + put("throes", "throes"); + put("trilbys", "trilby"); + put("women", "woman"); + } + }); + /** + * Contains word forms that can either be plural or singular + */ + private static final Set singAndPlur = Collections.unmodifiableSet(new HashSet(Arrays.asList( + "acoustics", + "aestetics", + "aquatics", + "basics", + "ceramics", + "classics", + "cosmetics", + "dermatoglyphics", + "dialectics", + "dynamics", + "esthetics", + "ethics", + "harmonics", + "heroics", + "isometrics", + "mechanics", + "metrics", + "statistics", + "optic", + "people", + "physics", + "polemics", + "premises", + "propaedeutics", + "pyrotechnics", + "quadratics", + "quarters", + "statistics", + "tactics", + "tropics" + ))); + /** * Tells whether a word form is plural. This method just checks whether the * stem method alters the word @@ -124,7 +839,7 @@ public static String stem(String s) { if (categoryUS_I.contains(s)) return (stem = cut(s, "i") + "us"); //Wrong plural if (s.endsWith("uses") && (categoryUS_I.contains(cut(s, "uses") + "i") || - s.equals("genuses") || s.equals("corpuses"))) return (stem = cut(s, "es")); + s.equals("genuses") || s.equals("corpuses"))) return (stem = cut(s, "es")); // -ex to -ices if (categoryEX_ICES.contains(s)) return (stem = cut(s, "ices") + "ex"); @@ -152,8 +867,8 @@ public static String stem(String s) { // -us to -us //No other common word ends in -us, except for false plurals of French words //Catch words that are not latin or known to end in -u - if (s.endsWith("us") && !s.endsWith("eaus") && !s.endsWith("ieus") && !noLatin(s) - && !categoryU_US.contains(s)) return (stem = s); + if (s.endsWith("us") && !s.endsWith("eaus") && !s.endsWith("ieus") && !noLatin(s) && + !categoryU_US.contains(s)) return (stem = s); // -tooth to -teeth // -goose to -geese @@ -195,15 +910,15 @@ public static String stem(String s) { // -[nlw]ife to -[nlw]ives //No other common word ends with "[nlw]ive(s)" except for olive if (s.endsWith("nives") || s.endsWith("lives") && !s.endsWith("olives") || - s.endsWith("wives")) return (stem = cut(s, "ves") + "fe"); + s.endsWith("wives")) return (stem = cut(s, "ves") + "fe"); // -[aeo]lf to -ves exceptions: valve, solve // -[^d]eaf to -ves exceptions: heave, weave // -arf to -ves no exception if (s.endsWith("alves") && !s.endsWith("valves") || - s.endsWith("olves") && !s.endsWith("solves") || - s.endsWith("eaves") && !s.endsWith("heaves") && !s.endsWith("weaves") || - s.endsWith("arves")) return (stem = cut(s, "ves") + "f"); + s.endsWith("olves") && !s.endsWith("solves") || + s.endsWith("eaves") && !s.endsWith("heaves") && !s.endsWith("weaves") || + s.endsWith("arves")) return (stem = cut(s, "ves") + "f"); // -y to -ies // -ies is very uncommon as a singular suffix @@ -244,736 +959,8 @@ private static boolean greek(String s) { */ private static boolean noLatin(String s) { return (s.indexOf('h') > 0 || s.indexOf('j') > 0 || s.indexOf('k') > 0 || - s.indexOf('w') > 0 || s.indexOf('y') > 0 || s.indexOf('z') > 0 || - s.indexOf("ou") > 0 || s.indexOf("sh") > 0 || s.indexOf("ch") > 0 || - s.endsWith("aus")); + s.indexOf('w') > 0 || s.indexOf('y') > 0 || s.indexOf('z') > 0 || + s.indexOf("ou") > 0 || s.indexOf("sh") > 0 || s.indexOf("ch") > 0 || + s.endsWith("aus")); } - - /** - * Words that end in "-se" in their plural forms (like "nurse" etc.) - */ - private static Set categorySE_SES = Collections.unmodifiableSet(new HashSet(Arrays.asList( - "nurses", - "cruises", - "premises", - "houses", - "courses", - "cases" - ))); - - /** - * Words that do not have a distinct plural form (like "atlas" etc.) - */ - private static Set category00 = Collections.unmodifiableSet(new HashSet(Arrays.asList( - "alias", - "asbestos", - "atlas", - "barracks", - "bathos", - "bias", - "breeches", - "britches", - "canvas", - "chaos", - "clippers", - "contretemps", - "corps", - "cosmos", - "crossroads", - "diabetes", - "ethos", - "gallows", - "gas", - "graffiti", - "headquarters", - "herpes", - "high-jinks", - "innings", - "jackanapes", - "lens", - "means", - "measles", - "mews", - "mumps", - "news", - "pathos", - "pincers", - "pliers", - "proceedings", - "rabies", - "rhinoceros", - "sassafras", - "scissors", - "series", - "shears", - "species", - "tuna" - ))); - - /** - * Words that change from "-um" to "-a" (like "curriculum" etc.), listed in their plural forms - */ - private static Set categoryUM_A = Collections.unmodifiableSet(new HashSet(Arrays.asList( - "addenda", - "agenda", - "aquaria", - "bacteria", - "candelabra", - "compendia", - "consortia", - "crania", - "curricula", - "data", - "desiderata", - "dicta", - "emporia", - "enconia", - "errata", - "extrema", - "gymnasia", - "honoraria", - "interregna", - "lustra", - "maxima", - "media", - "memoranda", - "millenia", - "minima", - "momenta", - "optima", - "ova", - "phyla", - "quanta", - "rostra", - "spectra", - "specula", - "stadia", - "strata", - "symposia", - "trapezia", - "ultimata", - "vacua", - "vela" - ))); - - /** - * Words that change from "-on" to "-a" (like "phenomenon" etc.), listed in their plural forms - */ - private static Set categoryON_A = Collections.unmodifiableSet(new HashSet(Arrays.asList( - "aphelia", - "asyndeta", - "automata", - "criteria", - "hyperbata", - "noumena", - "organa", - "perihelia", - "phenomena", - "prolegomena" - ))); - - /** - * Words that change from "-o" to "-i" (like "libretto" etc.), listed in their plural forms - */ - private static Set categoryO_I = Collections.unmodifiableSet(new HashSet(Arrays.asList( - "alti", - "bassi", - "canti", - "contralti", - "crescendi", - "libretti", - "soli", - "soprani", - "tempi", - "virtuosi" - ))); - - /** - * Words that change from "-us" to "-i" (like "fungus" etc.), listed in their plural forms - */ - private static Set categoryUS_I = Collections.unmodifiableSet(new HashSet(Arrays.asList( - "alumni", - "bacilli", - "cacti", - "foci", - "fungi", - "genii", - "hippopotami", - "incubi", - "nimbi", - "nuclei", - "nucleoli", - "octopi", - "radii", - "stimuli", - "styli", - "succubi", - "syllabi", - "termini", - "tori", - "umbilici", - "uteri" - ))); - - /** - * Words that change from "-ix" to "-ices" (like "appendix" etc.), listed in their plural forms - */ - private static Set categoryIX_ICES = Collections.unmodifiableSet(new HashSet(Arrays.asList( - "appendices", - "cervices" - ))); - - /** - * Words that change from "-is" to "-es" (like "axis" etc.), listed in their plural forms - */ - private static Set categoryIS_ES = Collections.unmodifiableSet(new HashSet(Arrays.asList( - // plus everybody ending in theses - "analyses", - "axes", - "bases", - "crises", - "diagnoses", - "ellipses", - "emphases", - "neuroses", - "oases", - "paralyses", - "synopses" - ))); - - /** - * Words that change from "-oe" to "-oes" (like "toe" etc.), listed in their plural forms - */ - private static Set categoryOE_OES = Collections.unmodifiableSet(new HashSet(Arrays.asList( - "aloes", - "backhoes", - "beroes", - "canoes", - "chigoes", - "cohoes", - "does", - "felloes", - "floes", - "foes", - "gumshoes", - "hammertoes", - "hoes", - "hoopoes", - "horseshoes", - "leucothoes", - "mahoes", - "mistletoes", - "oboes", - "overshoes", - "pahoehoes", - "pekoes", - "roes", - "shoes", - "sloes", - "snowshoes", - "throes", - "tic-tac-toes", - "tick-tack-toes", - "ticktacktoes", - "tiptoes", - "tit-tat-toes", - "toes", - "toetoes", - "tuckahoes", - "woes" - ))); - - /** - * Words that change from "-ex" to "-ices" (like "index" etc.), listed in their plural forms - */ - private static Set categoryEX_ICES = Collections.unmodifiableSet(new HashSet(Arrays.asList( - "apices", - "codices", - "cortices", - "indices", - "latices", - "murices", - "pontifices", - "silices", - "simplices", - "vertices", - "vortices" - ))); - - /** - * Words that change from "-u" to "-us" (like "emu" etc.), listed in their plural forms - */ - private static Set categoryU_US = Collections.unmodifiableSet(new HashSet(Arrays.asList( - "apercus", - "barbus", - "cornus", - "ecrus", - "emus", - "fondus", - "gnus", - "iglus", - "mus", - "nandus", - "napus", - "poilus", - "quipus", - "snafus", - "tabus", - "tamandus", - "tatus", - "timucus", - "tiramisus", - "tofus", - "tutus" - ))); - - /** - * Words that change from "-sse" to "-sses" (like "finesse" etc.), listed in their plural forms - */ - private static Set categorySSE_SSES = Collections.unmodifiableSet(new HashSet(Arrays.asList( - //plus those ending in mousse - "bouillabaisses", - "coulisses", - "crevasses", - "crosses", - "cuisses", - "demitasses", - "ecrevisses", - "fesses", - "finesses", - "fosses", - "impasses", - "lacrosses", - "largesses", - "masses", - "noblesses", - "palliasses", - "pelisses", - "politesses", - "posses", - "tasses", - "wrasses" - ))); - - /** - * Words that change from "-che" to "-ches" (like "brioche" etc.), listed in their plural forms - */ - private static Set categoryCHE_CHES = Collections.unmodifiableSet(new HashSet(Arrays.asList( - "adrenarches", - "attaches", - "avalanches", - "barouches", - "brioches", - "caches", - "caleches", - "caroches", - "cartouches", - "cliches", - "cloches", - "creches", - "demarches", - "douches", - "gouaches", - "guilloches", - "headaches", - "heartaches", - "huaraches", - "menarches", - "microfiches", - "moustaches", - "mustaches", - "niches", - "panaches", - "panoches", - "pastiches", - "penuches", - "pinches", - "postiches", - "psyches", - "quiches", - "schottisches", - "seiches", - "soutaches", - "synecdoches", - "thelarches", - "troches" - ))); - - /** - * Words that end with "-ics" and do not exist as nouns without the 's' (like "aerobics" etc.) - */ - private static Set categoryICS = Collections.unmodifiableSet(new HashSet(Arrays.asList( - "aerobatics", - "aerobics", - "aerodynamics", - "aeromechanics", - "aeronautics", - "alphanumerics", - "animatronics", - "apologetics", - "architectonics", - "astrodynamics", - "astronautics", - "astrophysics", - "athletics", - "atmospherics", - "autogenics", - "avionics", - "ballistics", - "bibliotics", - "bioethics", - "biometrics", - "bionics", - "bionomics", - "biophysics", - "biosystematics", - "cacogenics", - "calisthenics", - "callisthenics", - "catoptrics", - "civics", - "cladistics", - "cryogenics", - "cryonics", - "cryptanalytics", - "cybernetics", - "cytoarchitectonics", - "cytogenetics", - "diagnostics", - "dietetics", - "dramatics", - "dysgenics", - "econometrics", - "economics", - "electromagnetics", - "electronics", - "electrostatics", - "endodontics", - "enterics", - "ergonomics", - "eugenics", - "eurhythmics", - "eurythmics", - "exodontics", - "fibreoptics", - "futuristics", - "genetics", - "genomics", - "geographics", - "geophysics", - "geopolitics", - "geriatrics", - "glyptics", - "graphics", - "gymnastics", - "hermeneutics", - "histrionics", - "homiletics", - "hydraulics", - "hydrodynamics", - "hydrokinetics", - "hydroponics", - "hydrostatics", - "hygienics", - "informatics", - "kinematics", - "kinesthetics", - "kinetics", - "lexicostatistics", - "linguistics", - "lithoglyptics", - "liturgics", - "logistics", - "macrobiotics", - "macroeconomics", - "magnetics", - "magnetohydrodynamics", - "mathematics", - "metamathematics", - "metaphysics", - "microeconomics", - "microelectronics", - "mnemonics", - "morphophonemics", - "neuroethics", - "neurolinguistics", - "nucleonics", - "numismatics", - "obstetrics", - "onomastics", - "orthodontics", - "orthopaedics", - "orthopedics", - "orthoptics", - "paediatrics", - "patristics", - "patristics", - "pedagogics", - "pediatrics", - "periodontics", - "pharmaceutics", - "pharmacogenetics", - "pharmacokinetics", - "phonemics", - "phonetics", - "phonics", - "photomechanics", - "physiatrics", - "pneumatics", - "poetics", - "politics", - "pragmatics", - "prosthetics", - "prosthodontics", - "proteomics", - "proxemics", - "psycholinguistics", - "psychometrics", - "psychonomics", - "psychophysics", - "psychotherapeutics", - "robotics", - "semantics", - "semiotics", - "semitropics", - "sociolinguistics", - "stemmatics", - "strategics", - "subtropics", - "systematics", - "tectonics", - "telerobotics", - "therapeutics", - "thermionics", - "thermodynamics", - "thermostatics" - ))); - - /** - * Words that change from "-ie" to "-ies" (like "auntie" etc.), listed in their plural forms - */ - private static Set categoryIE_IES = Collections.unmodifiableSet(new HashSet(Arrays.asList( - "aeries", - "anomies", - "aunties", - "baddies", - "beanies", - "birdies", - "boccies", - "bogies", - "bolshies", - "bombies", - "bonhomies", - "bonxies", - "booboisies", - "boogies", - "boogie-woogies", - "bookies", - "booties", - "bosies", - "bourgeoisies", - "brasseries", - "brassies", - "brownies", - "budgies", - "byrnies", - "caddies", - "calories", - "camaraderies", - "capercaillies", - "capercailzies", - "cassies", - "catties", - "causeries", - "charcuteries", - "chinoiseries", - "collies", - "commies", - "cookies", - "coolies", - "coonties", - "cooties", - "corries", - "coteries", - "cowpies", - "cowries", - "cozies", - "crappies", - "crossties", - "curies", - "dachsies", - "darkies", - "dassies", - "dearies", - "dickies", - "dies", - "dixies", - "doggies", - "dogies", - "dominies", - "dovekies", - "eyries", - "faeries", - "falsies", - "floozies", - "folies", - "foodies", - "freebies", - "gaucheries", - "gendarmeries", - "genies", - "ghillies", - "gillies", - "goalies", - "goonies", - "grannies", - "grotesqueries", - "groupies", - "hankies", - "hippies", - "hoagies", - "honkies", - "hymies", - "indies", - "junkies", - "kelpies", - "kilocalories", - "knobkerries", - "koppies", - "kylies", - "laddies", - "lassies", - "lies", - "lingeries", - "magpies", - "magpies", - "marqueteries", - "mashies", - "mealies", - "meanies", - "menageries", - "millicuries", - "mollies", - "facts1", - "moxies", - "neckties", - "newbies", - "nighties", - "nookies", - "oldies", - "organdies", - "panties", - "parqueteries", - "passementeries", - "patisseries", - "pies", - "pinkies", - "pixies", - "porkpies", - "potpies", - "prairies", - "preemies", - "premies", - "punkies", - "pyxies", - "quickies", - "ramies", - "reveries", - "rookies", - "rotisseries", - "scrapies", - "sharpies", - "smoothies", - "softies", - "stoolies", - "stymies", - "swaggies", - "sweeties", - "talkies", - "techies", - "ties", - "tooshies", - "toughies", - "townies", - "veggies", - "walkie-talkies", - "wedgies", - "weenies", - "weirdies", - "yardies", - "yuppies", - "zombies" - ))); - - /** - * Maps irregular Germanic English plural nouns to their singular form - */ - private static Map irregular = Collections.unmodifiableMap(new HashMap() { - { - put("beefs", "beef"); - put("beeves", "beef"); - put("brethren", "brother"); - put("busses", "bus"); - put("cattle", "cattlebeast"); - put("children", "child"); - put("corpora", "corpus"); - put("ephemerides", "ephemeris"); - put("firemen", "fireman"); - put("genera", "genus"); - put("genies", "genie"); - put("genii", "genie"); - put("kine", "cow"); - put("lice", "louse"); - put("men", "man"); - put("mice", "mouse"); - put("mongooses", "mongoose"); - put("monies", "money"); - put("mythoi", "mythos"); - put("octopodes", "octopus"); - put("octopuses", "octopus"); - put("oxen", "ox"); - put("people", "person"); - put("soliloquies", "soliloquy"); - put("throes", "throes"); - put("trilbys", "trilby"); - put("women", "woman"); - } - } - ); - - /** - * Contains word forms that can either be plural or singular - */ - private static Set singAndPlur = Collections.unmodifiableSet(new HashSet(Arrays.asList( - "acoustics", - "aestetics", - "aquatics", - "basics", - "ceramics", - "classics", - "cosmetics", - "dermatoglyphics", - "dialectics", - "dynamics", - "esthetics", - "ethics", - "harmonics", - "heroics", - "isometrics", - "mechanics", - "metrics", - "statistics", - "optic", - "people", - "physics", - "polemics", - "premises", - "propaedeutics", - "pyrotechnics", - "quadratics", - "quarters", - "statistics", - "tactics", - "tropics" - ))); } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/proxy/HashcodeEqualsAwareProxyFactory.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/proxy/HashcodeEqualsAwareProxyFactory.java index ec7f5f54ad4..f444a602450 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/proxy/HashcodeEqualsAwareProxyFactory.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/proxy/HashcodeEqualsAwareProxyFactory.java @@ -18,18 +18,19 @@ */ package org.grails.datastore.gorm.neo4j.proxy; +import java.io.Serializable; +import java.lang.reflect.Method; + import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyObject; + +import org.springframework.beans.BeanUtils; +import org.springframework.util.ReflectionUtils; + import org.grails.datastore.mapping.core.Session; import org.grails.datastore.mapping.proxy.EntityProxy; import org.grails.datastore.mapping.proxy.GroovyObjectMethodHandler; import org.grails.datastore.mapping.proxy.JavassistProxyFactory; -import org.grails.datastore.mapping.proxy.SessionEntityProxyMethodHandler; -import org.springframework.beans.BeanUtils; -import org.springframework.util.ReflectionUtils; - -import java.io.Serializable; -import java.lang.reflect.Method; /** * extends {@link org.grails.datastore.mapping.proxy.JavassistProxyFactory} to capture method calls `hashCode` and `equals` @@ -40,79 +41,78 @@ public class HashcodeEqualsAwareProxyFactory extends JavassistProxyFactory { @Override protected Object createProxiedInstance(final Session session, final Class cls, Class proxyClass, final Serializable id) { - MethodHandler mi = new GroovyObjectMethodHandler(proxyClass) { - private Object target; + MethodHandler mi = new GroovyObjectMethodHandler(proxyClass) { + private Object target; - @Override - protected Object resolveDelegate(Object self) { - return target; - } + @Override + protected Object resolveDelegate(Object self) { + return target; + } - public Object invoke(Object proxy, Method method, Method proceed, Object[] args) throws Throwable { - final String methodName = method.getName(); - if (args.length == 0) { - if (methodName.equals("getId") || methodName.equals("getProxyKey")) { - return id; - } - if (methodName.equals("initialize")) { - initialize(); - return null; - } - if (methodName.equals("isInitialized")) { - return target != null; - } - if (methodName.equals("getTarget")) { - initialize(); - return target; - } - if (methodName.equals("hashCode")) { - return ((Long)id).intValue(); - } + public Object invoke(Object proxy, Method method, Method proceed, Object[] args) throws Throwable { + final String methodName = method.getName(); + if (args.length == 0) { + if (methodName.equals("getId") || methodName.equals("getProxyKey")) { + return id; } - else if(args.length == 1) { - if (methodName.equals("equals")) { - final Method getIdMethod = ReflectionUtils.findMethod(cls, "getId"); - Object other = args[0]; - if(other == null) { - return false; - } - if(other == proxy) { - return true; - } - if(other.getClass() == proxyClass && other instanceof EntityProxy && id.equals(((EntityProxy)other).getProxyKey())) { - return true; - } - if(other.getClass() == cls && id.equals(ReflectionUtils.invokeMethod(getIdMethod, other))) { - return true; - } - return false; - } + if (methodName.equals("initialize")) { + initialize(); + return null; } - if (target == null) { + if (methodName.equals("isInitialized")) { + return target != null; + } + if (methodName.equals("getTarget")) { initialize(); - - // This tends to happen during unit testing if the proxy class is not properly mocked - // and therefore can't be found in the session. - if( target == null ) { - throw new IllegalStateException("Proxy for ["+cls.getName()+":"+id+"] could not be initialized"); + return target; + } + if (methodName.equals("hashCode")) { + return ((Long) id).intValue(); + } + } else if (args.length == 1) { + if (methodName.equals("equals")) { + final Method getIdMethod = ReflectionUtils.findMethod(cls, "getId"); + Object other = args[0]; + if (other == null) { + return false; + } + if (other == proxy) { + return true; + } + if (other.getClass() == proxyClass && other instanceof EntityProxy && id.equals(((EntityProxy) other).getProxyKey())) { + return true; } + if (other.getClass() == cls && id.equals(ReflectionUtils.invokeMethod(getIdMethod, other))) { + return true; + } + return false; } + } + if (target == null) { + initialize(); - Object result = handleInvocation(target, method, args); - if(!wasHandled(result)) { - return org.springframework.util.ReflectionUtils.invokeMethod(method, target, args); - } else { - return result; + // This tends to happen during unit testing if the proxy class is not properly mocked + // and therefore can't be found in the session. + if (target == null) { + throw new IllegalStateException("Proxy for [" + cls.getName() + ":" + id + "] could not be initialized"); } } - public void initialize() { - target = session.retrieve(cls, id); + Object result = handleInvocation(target, method, args); + if (!wasHandled(result)) { + return org.springframework.util.ReflectionUtils.invokeMethod(method, target, args); + } else { + return result; } - }; - Object proxy = BeanUtils.instantiateClass(proxyClass); - ((ProxyObject)proxy).setHandler(mi); - return proxy; - } + } + + public void initialize() { + target = session.retrieve(cls, id); + } + }; + Object proxy = BeanUtils.instantiateClass(proxyClass); + ((ProxyObject) proxy).setHandler(mi); + return proxy; + } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/proxy/Neo4jAssociationQueryProxyHandler.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/proxy/Neo4jAssociationQueryProxyHandler.java index 5655faa96ea..d919849eec7 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/proxy/Neo4jAssociationQueryProxyHandler.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/proxy/Neo4jAssociationQueryProxyHandler.java @@ -18,13 +18,13 @@ */ package org.grails.datastore.gorm.neo4j.proxy; +import java.io.Serializable; + import org.grails.datastore.mapping.core.Session; import org.grails.datastore.mapping.engine.AssociationQueryExecutor; import org.grails.datastore.mapping.model.config.GormProperties; import org.grails.datastore.mapping.proxy.AssociationQueryProxyHandler; -import java.io.Serializable; - public class Neo4jAssociationQueryProxyHandler extends AssociationQueryProxyHandler { public Neo4jAssociationQueryProxyHandler(Session session, AssociationQueryExecutor executor, Serializable associationKey) { @@ -40,7 +40,6 @@ protected Object invokeEntityProxyMethods(Object self, String methodName, Object } } - @Override protected Object getPropertyBeforeResolving(Object self, String property) { if (property.equals(GormProperties.IDENTITY)) { diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/proxy/Neo4jProxyFactory.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/proxy/Neo4jProxyFactory.java index dce9570daa9..00498fb33b3 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/proxy/Neo4jProxyFactory.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/proxy/Neo4jProxyFactory.java @@ -18,13 +18,14 @@ */ package org.grails.datastore.gorm.neo4j.proxy; +import java.io.Serializable; + import javassist.util.proxy.MethodHandler; + import org.grails.datastore.mapping.core.Session; import org.grails.datastore.mapping.engine.AssociationQueryExecutor; import org.grails.datastore.mapping.proxy.JavassistProxyFactory; -import java.io.Serializable; - /** * extends {@link org.grails.datastore.mapping.proxy.JavassistProxyFactory} to capture method calls `hashCode` and `equals` * without expanding the proxy diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindAllCypherQueryImplementer.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindAllCypherQueryImplementer.groovy index bdf45683b98..0aa280906a9 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindAllCypherQueryImplementer.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindAllCypherQueryImplementer.groovy @@ -19,18 +19,19 @@ package org.grails.datastore.gorm.neo4j.services.implementers -import grails.neo4j.services.Cypher +import java.lang.annotation.Annotation + import groovy.transform.CompileStatic import org.codehaus.groovy.ast.ClassNode import org.codehaus.groovy.ast.MethodNode import org.codehaus.groovy.ast.VariableScope import org.codehaus.groovy.control.SourceUnit + +import grails.neo4j.services.Cypher import org.grails.datastore.gorm.neo4j.services.transform.CypherQueryStringTransformer import org.grails.datastore.gorm.services.implementers.FindAllStringQueryImplementer import org.grails.datastore.gorm.services.transform.QueryStringTransformer -import java.lang.annotation.Annotation - /** * A cypher query implementer * diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindOneCypherQueryImplementer.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindOneCypherQueryImplementer.groovy index d910638ecec..6256aeb20c0 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindOneCypherQueryImplementer.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindOneCypherQueryImplementer.groovy @@ -19,16 +19,17 @@ package org.grails.datastore.gorm.neo4j.services.implementers -import grails.neo4j.services.Cypher +import java.lang.annotation.Annotation + import groovy.transform.CompileStatic import org.codehaus.groovy.ast.VariableScope import org.codehaus.groovy.control.SourceUnit + +import grails.neo4j.services.Cypher import org.grails.datastore.gorm.neo4j.services.transform.CypherQueryStringTransformer import org.grails.datastore.gorm.services.implementers.FindOneStringQueryImplementer import org.grails.datastore.gorm.services.transform.QueryStringTransformer -import java.lang.annotation.Annotation - /** * A cypher query implementer * diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindPathCypherQueryImplementer.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindPathCypherQueryImplementer.groovy index cbd46f072d2..8a90004176d 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindPathCypherQueryImplementer.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindPathCypherQueryImplementer.groovy @@ -19,13 +19,13 @@ package org.grails.datastore.gorm.neo4j.services.implementers -import grails.neo4j.Path import groovy.transform.CompileStatic import org.codehaus.groovy.ast.ClassNode import org.codehaus.groovy.ast.MethodNode import org.codehaus.groovy.ast.expr.Expression import org.codehaus.groovy.ast.stmt.Statement -import org.grails.datastore.mapping.core.Ordered + +import grails.neo4j.Path import org.grails.datastore.mapping.reflect.AstUtils import static org.codehaus.groovy.ast.tools.GeneralUtils.callX diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindShortestPathImplementer.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindShortestPathImplementer.groovy index a94cde37195..88c6705c981 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindShortestPathImplementer.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/FindShortestPathImplementer.groovy @@ -19,19 +19,23 @@ package org.grails.datastore.gorm.neo4j.services.implementers -import grails.neo4j.Path import groovy.transform.CompileStatic import org.codehaus.groovy.ast.ClassNode import org.codehaus.groovy.ast.MethodNode import org.codehaus.groovy.ast.Parameter import org.codehaus.groovy.ast.expr.Expression import org.codehaus.groovy.ast.stmt.BlockStatement + +import grails.neo4j.Path import org.grails.datastore.gorm.services.implementers.AbstractReadOperationImplementer import org.grails.datastore.gorm.services.implementers.FindOneByImplementer import org.grails.datastore.gorm.services.implementers.SingleResultServiceImplementer import org.grails.datastore.mapping.reflect.AstUtils -import static org.codehaus.groovy.ast.tools.GeneralUtils.* +import static org.codehaus.groovy.ast.tools.GeneralUtils.args +import static org.codehaus.groovy.ast.tools.GeneralUtils.callX +import static org.codehaus.groovy.ast.tools.GeneralUtils.castX +import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS /** * Service implementer for findShortestPath @@ -41,6 +45,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.* */ @CompileStatic class FindShortestPathImplementer extends AbstractReadOperationImplementer implements SingleResultServiceImplementer { + @Override int getOrder() { return FindOneByImplementer.POSITION - 100 @@ -52,9 +57,9 @@ class FindShortestPathImplementer extends AbstractReadOperationImplementer imple Expression methodCall = callX(domainClassNode, "findShortestPath", args(newMethodNode.parameters)) methodCall = castX(returnType.plainNodeReference, methodCall) - BlockStatement bs = (BlockStatement)newMethodNode.code + BlockStatement bs = (BlockStatement) newMethodNode.code bs.addStatement( - returnS(methodCall) + returnS(methodCall) ) } @@ -63,14 +68,13 @@ class FindShortestPathImplementer extends AbstractReadOperationImplementer imple def alreadyImplemented = methodNode.getNodeMetaData(IMPLEMENTED) Parameter[] parameters = methodNode.parameters int paramCount = parameters.length - if(!alreadyImplemented && AstUtils.implementsInterface(methodNode.returnType, Path.name) && paramCount > 1 && paramCount < 4) { - if(AstUtils.isDomainClass(parameters[0].type) && AstUtils.isDomainClass(parameters[1].type)) { - if(paramCount == 3) { - if(AstUtils.implementsInterface(parameters[2].type, "java.lang.Number")) { + if (!alreadyImplemented && AstUtils.implementsInterface(methodNode.returnType, Path.name) && paramCount > 1 && paramCount < 4) { + if (AstUtils.isDomainClass(parameters[0].type) && AstUtils.isDomainClass(parameters[1].type)) { + if (paramCount == 3) { + if (AstUtils.implementsInterface(parameters[2].type, "java.lang.Number")) { return true } - } - else { + } else { return true } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/StatementResultCypherQueryImplementer.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/StatementResultCypherQueryImplementer.groovy index 1cb59171f7f..fdee190d5c7 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/StatementResultCypherQueryImplementer.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/StatementResultCypherQueryImplementer.groovy @@ -24,9 +24,11 @@ import org.codehaus.groovy.ast.ClassNode import org.codehaus.groovy.ast.MethodNode import org.codehaus.groovy.ast.expr.Expression import org.codehaus.groovy.ast.stmt.Statement -import org.grails.datastore.mapping.reflect.AstUtils + import org.neo4j.driver.Result +import org.grails.datastore.mapping.reflect.AstUtils + import static org.codehaus.groovy.ast.tools.GeneralUtils.callX import static org.codehaus.groovy.ast.tools.GeneralUtils.castX import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/UpdateCypherQueryImplementer.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/UpdateCypherQueryImplementer.groovy index 2efb984242c..e6eec44d657 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/UpdateCypherQueryImplementer.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/implementers/UpdateCypherQueryImplementer.groovy @@ -19,8 +19,6 @@ package org.grails.datastore.gorm.neo4j.services.implementers -import grails.gorm.services.Query -import grails.neo4j.services.Cypher import groovy.transform.CompileStatic import org.codehaus.groovy.ast.AnnotationNode import org.codehaus.groovy.ast.ClassHelper @@ -30,6 +28,8 @@ import org.codehaus.groovy.ast.expr.ConstantExpression import org.codehaus.groovy.ast.expr.Expression import org.codehaus.groovy.ast.expr.GStringExpression import org.codehaus.groovy.ast.stmt.Statement + +import grails.neo4j.services.Cypher import org.grails.datastore.gorm.transactions.transform.TransactionalTransform import org.grails.datastore.mapping.reflect.AstUtils @@ -55,21 +55,20 @@ class UpdateCypherQueryImplementer extends FindAllCypherQueryImplementer { @Override boolean doesImplement(ClassNode domainClass, MethodNode methodNode) { AnnotationNode ann = AstUtils.findAnnotation(methodNode, Cypher) - if( ann != null) { + if (ann != null) { Expression expr = ann.getMember("value") - if(expr instanceof GStringExpression) { - GStringExpression gstring = (GStringExpression)expr - for(ConstantExpression ce in gstring.strings) { + if (expr instanceof GStringExpression) { + GStringExpression gstring = (GStringExpression) expr + for (ConstantExpression ce in gstring.strings) { String queryStem = ce.text - if(isWriteOperation(queryStem)) { + if (isWriteOperation(queryStem)) { return isCompatibleReturnType(domainClass, methodNode, methodNode.returnType, methodNode.name) } } - } - else if(expr instanceof ConstantExpression) { - String queryStem = ((ConstantExpression)expr).text - if(isWriteOperation(queryStem)) { + } else if (expr instanceof ConstantExpression) { + String queryStem = ((ConstantExpression) expr).text + if (isWriteOperation(queryStem)) { return isCompatibleReturnType(domainClass, methodNode, methodNode.returnType, methodNode.name) } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/transform/CypherQueryStringTransformer.groovy b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/transform/CypherQueryStringTransformer.groovy index 832a8f98ac1..c61cfff43e1 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/transform/CypherQueryStringTransformer.groovy +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/services/transform/CypherQueryStringTransformer.groovy @@ -19,11 +19,12 @@ package org.grails.datastore.gorm.neo4j.services.transform -import grails.neo4j.Relationship import groovy.transform.CompileStatic import org.codehaus.groovy.ast.ClassNode import org.codehaus.groovy.ast.VariableScope import org.codehaus.groovy.control.SourceUnit + +import grails.neo4j.Relationship import org.grails.datastore.gorm.neo4j.RelationshipPersistentEntity import org.grails.datastore.gorm.services.transform.QueryStringTransformer import org.grails.datastore.mapping.reflect.AstUtils @@ -36,21 +37,21 @@ import org.grails.datastore.mapping.reflect.AstUtils */ @CompileStatic class CypherQueryStringTransformer extends QueryStringTransformer { + CypherQueryStringTransformer(SourceUnit sourceUnit, VariableScope variableScope) { super(sourceUnit, variableScope) } @Override protected String formatDomainClassVariable(ClassNode domainType, String variableName) { - if(AstUtils.implementsInterface(domainType, Relationship.name)) { + if (AstUtils.implementsInterface(domainType, Relationship.name)) { ClassNode relInterface = domainType.getInterfaces().find() { ClassNode ifce -> ifce.name == Relationship.name } ClassNode from = relInterface.genericsTypes[0].type.plainNodeReference ClassNode to = relInterface.genericsTypes[1].type.plainNodeReference declaredQueryTargets.put(RelationshipPersistentEntity.FROM, from) declaredQueryTargets.put(RelationshipPersistentEntity.TO, to) return "${formatNodeVariable(RelationshipPersistentEntity.FROM, from)}-[$variableName]->${formatNodeVariable(RelationshipPersistentEntity.TO, to)}" - } - else { + } else { return formatNodeVariable(variableName, domainType) } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/util/EmbeddedNeo4jServer.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/util/EmbeddedNeo4jServer.java index ffa096fe0d8..ced9c00943a 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/util/EmbeddedNeo4jServer.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/util/EmbeddedNeo4jServer.java @@ -18,8 +18,13 @@ */ package org.grails.datastore.gorm.neo4j.util; -import org.grails.datastore.mapping.reflect.ClassUtils; -import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import java.io.File; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.URI; +import java.util.Collections; +import java.util.Map; + import org.neo4j.harness.ServerControls; import org.neo4j.harness.TestServerBuilder; import org.neo4j.harness.TestServerBuilders; @@ -27,15 +32,10 @@ import org.neo4j.kernel.configuration.Connector; import org.neo4j.server.ServerStartupException; -import java.io.File; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.URI; -import java.util.Collections; -import java.util.Map; +import org.grails.datastore.mapping.reflect.ClassUtils; -import static org.neo4j.kernel.configuration.BoltConnector.EncryptionLevel.DISABLED; import static org.neo4j.graphdb.factory.GraphDatabaseSettings.data_directory; +import static org.neo4j.kernel.configuration.BoltConnector.EncryptionLevel.DISABLED; /** * Helper class for starting a Neo4j 3.x embedded server @@ -59,7 +59,7 @@ public static boolean isAvailable() { * @param dataLocation The data location */ public static ServerControls start(File dataLocation) throws IOException { - return attemptStartServer(0, dataLocation, Collections.emptyMap()); + return attemptStartServer(0, dataLocation, Collections.emptyMap()); } /** @@ -72,8 +72,6 @@ public static ServerControls start(File dataLocation, Map option return attemptStartServer(0, dataLocation, options); } - - /** * Start a server on the given address * @@ -82,7 +80,7 @@ public static ServerControls start(File dataLocation, Map option * @return The {@link ServerControls} */ public static ServerControls start(InetSocketAddress inetAddr) { - return start(inetAddr.getHostName(),inetAddr.getPort(), null); + return start(inetAddr.getHostName(), inetAddr.getPort(), null); } /** @@ -93,7 +91,7 @@ public static ServerControls start(InetSocketAddress inetAddr) { * @return The {@link ServerControls} */ public static ServerControls start(InetSocketAddress inetAddr, File dataLocation) { - return start(inetAddr.getHostName(),inetAddr.getPort(), dataLocation); + return start(inetAddr.getHostName(), inetAddr.getPort(), dataLocation); } /** @@ -104,7 +102,7 @@ public static ServerControls start(InetSocketAddress inetAddr, File dataLocation * @return The {@link ServerControls} */ public static ServerControls start(InetSocketAddress inetAddr, File dataLocation, Map options) { - return start(inetAddr.getHostName(),inetAddr.getPort(), dataLocation,options); + return start(inetAddr.getHostName(), inetAddr.getPort(), dataLocation, options); } /** @@ -131,7 +129,6 @@ public static ServerControls start(String address, File dataLocation) { return start(uri.getHost(), uri.getPort(), dataLocation); } - /** * Start a server on the given address * @@ -139,7 +136,7 @@ public static ServerControls start(String address, File dataLocation) { * * @return The {@link ServerControls} */ - public static ServerControls start(String address, File dataLocation,Map options) { + public static ServerControls start(String address, File dataLocation, Map options) { URI uri = URI.create(address); return start(uri.getHost(), uri.getPort(), dataLocation, options); } @@ -153,8 +150,9 @@ public static ServerControls start(String address, File dataLocation,MapemptyMap()); + return start(host, port, dataLocation, Collections.emptyMap()); } /** @@ -179,12 +177,12 @@ public static ServerControls start(String host, int port, File dataLocation, Map String myBoltAddress = String.format("%s:%d", host, port); TestServerBuilder serverBuilder = TestServerBuilders.newInProcessBuilder() - .withConfig(new BoltConnector("0").enabled, "true") - .withConfig(new BoltConnector("0").type, Connector.ConnectorType.BOLT.name()) - .withConfig(new BoltConnector("0").encryption_level, DISABLED.name()) - .withConfig(new BoltConnector("0").listen_address, myBoltAddress); - if(dataLocation != null) { - serverBuilder = serverBuilder.withConfig(data_directory, dataLocation.getPath()); + .withConfig(new BoltConnector("0").enabled, "true") + .withConfig(new BoltConnector("0").type, Connector.ConnectorType.BOLT.name()) + .withConfig(new BoltConnector("0").encryption_level, DISABLED.name()) + .withConfig(new BoltConnector("0").listen_address, myBoltAddress); + if (dataLocation != null) { + serverBuilder = serverBuilder.withConfig(data_directory, dataLocation.getPath()); } for (String name : options.keySet()) { @@ -192,7 +190,7 @@ public static ServerControls start(String host, int port, File dataLocation, Map } return serverBuilder - .newServer(); + .newServer(); } private static ServerControls attemptStartServer(int retryCount, File dataLocation, Map options) throws IOException { @@ -201,10 +199,9 @@ private static ServerControls attemptStartServer(int retryCount, File dataLocati //In the new driver 0 implicitly means a random port return start("localhost", 0, dataLocation, options); } catch (ServerStartupException sse) { - if(retryCount < 4) { + if (retryCount < 4) { return attemptStartServer(++retryCount, dataLocation, options); - } - else { + } else { throw sse; } } diff --git a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/util/IteratorUtil.java b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/util/IteratorUtil.java index eefe2337b71..a043353e83c 100644 --- a/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/util/IteratorUtil.java +++ b/grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/util/IteratorUtil.java @@ -28,7 +28,6 @@ */ public class IteratorUtil { - /** * A single item or null * @@ -38,7 +37,7 @@ public class IteratorUtil { */ public static T singleOrNull(Iterable iterable) { Iterator i = iterable.iterator(); - if(i.hasNext()) { + if (i.hasNext()) { return i.next(); } return null; @@ -57,7 +56,7 @@ public static T single(Iterable iterable) { } public static T single(Iterator i) { - if(i.hasNext()) { + if (i.hasNext()) { return i.next(); } return null; @@ -73,7 +72,7 @@ public static int count(Iterable iterable) { public static int count(Iterator i) { int count = 0; - while(i.hasNext()) { + while (i.hasNext()) { count++; i.next(); }