in grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jAssociationQueryExecutor.groovy [84:254]
List<Object> query(Serializable primaryKey) {
QueryRunner statementRunner = session.hasTransaction() ? session.getTransaction().getNativeTransaction() : (Session)session.nativeInterface
String relType
GraphPersistentEntity parent = (GraphPersistentEntity)association.owner
GraphPersistentEntity related = (GraphPersistentEntity)indexedEntity
boolean isRelationship = related.isRelationshipEntity()
if(isRelationship) {
RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity)related
GraphPersistentEntity fromEntity = (GraphPersistentEntity) relEntity.getFrom().getAssociatedEntity()
GraphPersistentEntity toEntity = (GraphPersistentEntity) relEntity.getTo().getAssociatedEntity()
if(parent == fromEntity) {
relType = "-[rel]->"
related = toEntity
}
else {
relType = "<-[rel]-"
parent = toEntity
related = fromEntity
}
}
else {
relType = RelationshipUtils.matchForAssociation(association)
}
String relationship = CypherBuilder.buildRelationship(parent.labelsAsString, relType, related.labelsAsString)
StringBuilder cypher = new StringBuilder(CypherBuilder.buildRelationshipMatch(parent.labelsAsString, relType, related.labelsAsString))
cypher.append('( ')
.append(parent.formatId(RelationshipPersistentEntity.FROM))
.append(" = \$id )")
boolean isLazyToMany = lazy && !isRelationship && association instanceof ToMany
if(isLazyToMany) {
cypher.append(related.formatId(RelationshipPersistentEntity.TO))
.append("RETURN as id")
}
else {
if(!isRelationship) {
StringBuilder returnString = new StringBuilder("\nRETURN to as data")
Set<Association> associations = new TreeSet<Association>((Comparator<Association>){ Association a1, Association a2 -> a1.name <=> a2.name })
PersistentEntity entity = association.associatedEntity
if (entity) {
Collection<PersistentEntity> childEntities = entity.mappingContext.getChildEntities(entity)
if (!childEntities.empty) {
for (PersistentEntity childEntity : childEntities) {
associations.addAll(childEntity.associations)
}
}
associations.addAll(entity.associations)
if(associations.size() > 0) {
int i = 0
List previousAssociations = []
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
boolean isAssociationRelationshipEntity = associatedGraphEntity.isRelationshipEntity()
boolean isToMany = association instanceof ToMany
boolean isToOne = association instanceof ToOne
boolean lazy = false
boolean isNullable = false
if(isToOne && !isEager) {
Property propertyMapping = association.mapping.mappedForm
Boolean isLazy = propertyMapping.getLazy()
isNullable = propertyMapping.isNullable()
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 to, ${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 a one-to-one where the association is nullable or not lazy
// then just collect the identifiers and not the nodes
if((isToMany && lazy) || (isToOne && !isEager && (isNullable || !lazy ) )) {
withMatch += "collect(DISTINCT ${associatedGraphEntity.formatId(associationNodeRef)}) as ${associationIdsRef}"
returnString.append(", ").append(associationIdsRef)
previousAssociations << associationIdsRef
addOptionalMatch = true
}
else if(isEager) {
withMatch += "collect(DISTINCT $associationNodeRef) as $associationNodesRef"
returnString.append(", ").append(associationNodesRef)
if(isAssociationRelationshipEntity) {
withMatch += ", collect($r) as ${associationName}Rels"
returnString.append(", ").append("${associationName}Rels")
}
previousAssociations << associationNodesRef
addOptionalMatch = true
}
if(addOptionalMatch) {
String relationshipPattern = related
.formatAssociationPatternFromExisting(
association,
r,
"to",
associationNodeRef
)
cypher.append(CypherBuilder.NEW_LINE)
.append(CypherBuilder.OPTIONAL_MATCH)
.append(relationshipPattern)
.append(" ")
.append(withMatch)
}
}
}
}
cypher.append(returnString.toString())
}
else {
cypher.append('RETURN rel as rel')
}
}
cypher.append(singleResult ? 'LIMIT 1' : '')
Map<String, Object> params = Collections.singletonMap(GormProperties.IDENTITY, (Object) primaryKey)
log.debug("Lazy loading association [${association}] using relationship $relationship")
log.debug("QUERY Cypher [$cypher] for params [$params]")
Result result = statementRunner.run(cypher.toString(), params)
if(isLazyToMany) {
List<Object> results = []
while(result.hasNext()) {
def id = result.next().get(GormProperties.IDENTITY).asObject()
results.add( session.proxy(related.javaClass, id as Serializable) )
}
return results
}
else {
def resultList = new Neo4jResultList(0, result, isRelationship ? session.getEntityPersister(indexedEntity) : session.getEntityPersister(related))
if(association.isBidirectional()) {
def inverseSide = association.inverseSide
if(inverseSide instanceof ToOne) {
def parentObject = session.getCachedInstance(association.getOwner().getJavaClass(), primaryKey)
if(parentObject != null) {
resultList.setInitializedAssociations(Collections.<Association,Object>singletonMap(inverseSide, parentObject))
}
}
}
return resultList
}
}