neo4j / graphql

A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations.
https://neo4j.com/docs/graphql-manual/current/
Apache License 2.0
504 stars 149 forks source link

Cypher Builder does not connectOrCreate with the correct direction #1268

Closed litewarp closed 2 years ago

litewarp commented 2 years ago

Found this while working on the global node PR. On the dev branch which uses the cypher builder, if you connectOrCreate a relationship, it does not respect the direction.

Example TypeDefs (pared down from my schema)


type Organization {
  id: ID! @id(autogenerate: false)
  name: String!
  shortUrl: String! @unique

  investorOpportunities: [Opportunity!]! @relationship(type: "INVESTOR_IN", direction: OUT)
  companyOpportunities: [Opportunity!]! @relationship(type: "COMPANY_IN", direction: OUT)
}

type Opportunity {
  id: ID! @id(autogenerate: false)
  name: String!

  investors: [Organization!]! @relationship(type: "INVESTOR_IN", direction: IN)
  companies: [Organization!]! @relationship(type: "COMPANY_IN", direction: IN)
}

Using the OGM, run the following mutation:


const ogm = new OGM({ typeDefs, driver: getDriver() })

const input: OpportunityCreateInput = {
  id: "1219821818281",
  name: 'Twitter - IPO',
  investors: {
    connectOrCreate: [{
      where: { node: { shortUrl: "neo-capital" } },
      onCreate: { node: { id: "283828282882", name: "Neo Capital" } }
   }],
 }
}

await ogm.model('Opportunity').create({
  input: [input]
})

The cypher should have the merge statement with the correct arrows like MERGE (this_opportunity)<-[:IS_INVESTOR_IN]-(organization:Organization)

But the returned cypher has the relationship reversed:

Cypher:
CALL {
  CREATE (this0:Opportunity)
  SET this0.id = $this0_id
  SET this0.name = $this0_name
  WITH this0
    CALL {
      WITH this0
       MERGE (this0_investors_connectOrCreate_this1:`Organization` { shortUrl: $this0_investors_connectOrCreate_param0 })
       ON CREATE SET
          this0_investors_connectOrCreate_this1.id = $this0_investors_connectOrCreate_param1,
          this0_investors_connectOrCreate_this1.name = $this0_investors_connectOrCreate_param2,
        MERGE (this0)-[this0_investors_connectOrCreate_this0:`IS_INVESTOR_IN`]->(this0_investors_connectOrCreate_this1)
        RETURN COUNT (*)
      }
   [... rest of query]
}

I think it is due to the CypherBuilder Relationship module only implementing relationships in one direction:


    public getCypher(context: CypherContext) {
        const referenceId = context.getVariableId(this);
        let parametersStr = "";
        if (this.hasParameters()) {
            const parameters = serializeParameters(this.parameters, context);
            parametersStr = padLeft(parameters);
        }

        const sourceStr = `(${this.source.getReference(context)})`;
        const targetStr = `(${this.target.getReference(context)})`;
        const arrowStr = this.getRelationshipArrow();
        const relationshipStr = `${referenceId || ""}${this.getTypeString()}${parametersStr}`;

        return `${sourceStr}-[${relationshipStr}]${arrowStr}${targetStr}`;
    }

    private hasParameters(): boolean {
        return Object.keys(this.parameters).length > 0;
    }

    private getRelationshipArrow(): "-" | "->" {
        return this.directed ? "->" : "-";
    }
neo4j-team-graphql commented 2 years ago

Many thanks for raising this bug report @litewarp. :bug: We will now attempt to reproduce the bug based on the steps you have provided.

Please ensure that you've provided the necessary information for a minimal reproduction, including but not limited to:

If you have a support agreement with Neo4j, please link this GitHub issue to a new or existing Zendesk ticket.

Thanks again! :pray:

neo4j-team-graphql commented 2 years ago

We've been able to confirm this bug using the steps to reproduce that you provided - many thanks @litewarp! :pray: We will now prioritise the bug and address it appropriately.