List query()

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
        }
    }