Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple Instances created during nested Create Mutation #235

Closed
ghost opened this issue Jun 4, 2021 · 2 comments · Fixed by #341
Closed

Multiple Instances created during nested Create Mutation #235

ghost opened this issue Jun 4, 2021 · 2 comments · Fixed by #341
Labels
bug Something isn't working

Comments

@ghost
Copy link

ghost commented Jun 4, 2021

Describe the bug
When during a create call:

  createAS(input:[{
    name: "test_A",
    rel_b: {connect:{where:{name_IN:["test_B1", "test_B2"]}}},
    rel_c: {create:{name: "onlyOneShouldBeCreated"}}
  }]){as{name, rel_b{name}, rel_c{name}}}
}

one has a where query that returns two or more nodes (in the connect statement), this leads to the equal number of nodes being created by the create statement. Expected behavior would be to create only one instance.

Type definitions

type A {
   ID: ID! @id
   name: String!
   rel_b: [B] @relationship(type: "REL_B", direction: OUT)
   rel_c: [C] @relationship(type: "REL_C", direction: OUT)
}

type B {
   ID: ID! @id
   name: String!
}

type C {
   ID: ID! @id
   name: String!
}

To Reproduce
Steps to reproduce the behavior:

  1. Run a server with the following code:
const { Neo4jGraphQL } = require('@neo4j/graphql');
const { ApolloServer } = require("apollo-server-express");
const neo4j = require("neo4j-driver");
const dotenv = require("dotenv");
const path = require("path");
const fs = require("fs");
const express = require('express');

dotenv.config();
const app = express();

const { NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD} = process.env

const typeDefs = fs.readFileSync(path.join(__dirname, "schema.graphql")).toString("utf-8");

const driver = neo4j.driver(
  NEO4J_URI,
  neo4j.auth.basic(NEO4J_USER, NEO4J_PASSWORD)
)

const neoSchema = new Neo4jGraphQL({ typeDefs, driver })

const server = new ApolloServer({
  context: {
    driver,
    driverConfig: { database: process.env.NEO4J_DATABASE || 'neo4j' },
  },
  schema: neoSchema.schema,
  introspection: true,
  playground: true,
})

const port = process.env.GRAPHQL_SERVER_PORT || 4002
const servpath = process.env.GRAPHQL_SERVER_PATH || '/graphql'
const host = process.env.GRAPHQL_SERVER_HOST || '0.0.0.0'

server.applyMiddleware({ app, servpath });

app.listen({ host, port, servpath }, () => {
  console.log(`GraphQL server ready at http://${host}:${port}${servpath}`)
})
  1. Execute the following Mutations:
mutation{
  createBS(input:[{
    name: "test_B1",
  },{
    name: "test_B2",
  }]){
    bs{name}
  }
}

and

mutation{
  createAS(input:[{
    name: "test_A",
    rel_b: {connect:{where:{name_IN:["test_B1", "test_B2"]}}},
    rel_c: {create:{name: "onlyOneShouldBeCreated"}}
  }]){as{name, rel_b{name}, rel_c{name}}}
}
  1. Then run the following Query:
    {as{name, rel_b{name}, rel_c{name, ID}}}
  2. See error:
    multiple nodes of type C have been created.

Expected behavior
Only a single instance is created

System (please complete the following information):

Possible Fix
The created Cypher looks as follows:

Cypher:
CALL {
CREATE (this0:A)
SET this0.ID = randomUUID()
SET this0.name = $this0_name
WITH this0
OPTIONAL MATCH (this0_rel_b_connect0:B)
WHERE this0_rel_b_connect0.name IN $this0_rel_b_connect0_name_IN
FOREACH(_ IN CASE this0_rel_b_connect0 WHEN NULL THEN [] ELSE [1] END | 
MERGE (this0)-[:REL_B]->(this0_rel_b_connect0)
)

WITH this0
CREATE (this0_rel_c0:C)
SET this0_rel_c0.ID = randomUUID()
SET this0_rel_c0.name = $this0_rel_c0_name
MERGE (this0)-[:REL_C]->(this0_rel_c0)
RETURN this0
}

RETURN 
this0 { .name, rel_b: [ (this0)-[:REL_B]->(this0_rel_b:B)   | this0_rel_b { .name } ], rel_c: [ (this0)-[:REL_C]->(this0_rel_c:C)   | this0_rel_c { .name } ] } AS this0
Params:
{
  "this0_name": "test_A",
  "this0_rel_b_connect0_name_IN": [
    "test_B1",
    "test_B2"
  ],
  "this0_rel_c0_name": "onlyOneShouldBeCreated"
}

I noticed, that adding in a DISTINCT after the WITH fixes the problem.
This can be achieved by changing line 47 of @neo4j/graphql/dist/translate/create-create-and-params.js. (@neo4j/graphql:1.0.0)

@ghost ghost added bug Something isn't working inbox labels Jun 4, 2021
@danstarns
Copy link
Contributor

Thanks, @JDFischbach for submitting this issue, with your instructions I can replicate it. I'll add this to our working board and we will look into a fix soon.

@darrellwarde
Copy link
Contributor

Sorry, it's taken way too long to get around to this one, but this is now addressed in #341.

Adding DISTINCT to the WITH would have also worked, but this solution was guided by the Cypher team.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants