I want to test auto-generated CRUD operations created by calling makeAugmentedSchema. There is no problem with creating nodes but creating relationship does not work for me. Please advise on what I am doing wrong here.
Schema:
type Bio{
id: ID!
description: String
}
type Person{
id: ID!
name: String
dob: Date
gender: String
bioRelation: [Bio] @relation(name: "HAS_BIO", direction: "OUT")
}
Mutation:
mutation {
p: CreatePerson(
name: "Anton",
gender: "Male") {
name
gender
id
}
b: CreateBio(
description: "I am a developer") {
description
id
}
r: AddPersonBioRelation(
from: {id: "p"},
to:{id: "b"}
){
from{
name
}
to{
description
}
}
}
You're not able to do the addRelationship mutation in conjunction with node creation mutations. You have to do is separately or create a custom cypher mutation that performs the operation in one go.
I do want to reference newly created nodes by their id when creating relationship within one mutation. Could you please provide with example of such a custom cypher mutation that performs the operation in one go.
@MuddyBootsCode, your answer looks like some sort of trolling, mate. The question is about how to reference newly created nodes along with creating the relationship in one go.
As I see there is an option to assign id at the stage of node creation and pass them to a relationship definition, but it raises the problem of managing the uniqueness of nodes as UUID gets overwritten.
@MuddyBootsCode, you gave an example of creating two generic nodes with a relationship, which is pretty obvious. What would be helpful is the example of a resolver that overwrites autogenerated mutation that connects two newly created nodes keeping their uuid's.
I reckon mutation should looks like this in schema definition:
type Mutation {
AddPersonBio(fromPersonID: ID!, toBioID: ID!): Person @cypher(
statement:"""
MATCH (from:Person {id: $fromPersonID})
MATCH (to:Bio {id: $toBioID})
MERGE (from)-[:HAS_BIO]->(to)
RETURN from.id, to.id
""")
But how do I implement a resolver to be able to reference uuids of newly created objects?
Ok. I think the issue is that you're missing a reciprocal piece of your relationship on your bio type. You also need to add it's relation to a person in your case it could look like:
type Bio {
id: ID!
description: String
person: Person @relation(name: "HAS_BIO", direction: "IN")
}
When you're using relationships you have to specify both types and ensure the in and out directions are correct. Give that a try and let me know.
Sorry the other stuff before but in some of the larger GRANDstack projects I've built I've found it much easier to go the custom Cypher mutation route for most things, rather than trying to coerce the auto-generated mutations into what I was trying to do.
I have defined relation on Person node already (please see my first message). Building symmetric relations is considered as not the best solution as to Neo4j official guidance which I am agreed with:
In your first post you’ve posted your schema. In it you show to have no relationship from Bio to person. In order for makeAugmentedSchema to know how to create the correct relationship you need to include the relationship on both the person and bio type. With a correct in and out direction for the relationship. This does not create a separate relationship just ensures that it’s mapped correctly.
Try that and see if it fixes your mutation. If it doesn’t then holpefully someone else feels like helping you.
In the example in the docs you referenced the id values are provided to the create node mutations, which can then be used to reference the nodes in the add relationship mutation.
If no id value is provided to the node creation mutation then a UUID is generated, and this value must be provided to the add relationship mutation. However, you won't know what those values are, thus necessitating a second operation.
If however, you specify values for the id fields then this can be done in a single operation. So your example becomes:
mutation {
p: CreatePerson(
name: "Anton",
gender: "Male",
id: "p1") {
name
gender
id
}
b: CreateBio(
description: "I am a developer",
id: "b1") {
description
id
}
r: AddPersonBioRelation(
from: {id: "p1"},
to:{id: "b1"}
){
from{
name
}
to{
description
}
}
}
Thank you, @lyonwj. Now, I see that there is no way to pass generated UUID to add relationship mutation in the same operation where nodes get created.
I have come up with an idea of introducing a personId property to both Person and Bio nodes that I can use to connect the two and remove once relationship is created. This allows to perform the operation in one go.
type Bio{
id: ID!
personId: Int
description: String
}
type Person{
id: ID!
personId: Int
name: String
gender: String
bioRelation: [Bio] @neo4j_ignore
}
type Mutation{
AddPersonBioRelation(personId: Int): Person
@cypher(statement:
"""
MERGE (p:Person {personId: $personId})
MERGE (b:Bio {personId: $personId})
CREATE (p)-[r:HAS_BIO]->(b)
REMOVE p.personId, b.personId
RETURN p
"""
)
}
I am not sure if this is considered as a good practice, though. From one side, such implementation defines a property for Person and Bio nodes just to facilitate a creation operation, but not for describing an entity. From the other side, api gets more user friendly for clients to use.
Super-interesting comment! And something I’ve been wondering about. Makes me wonder if there’s a highly-opinionated article online about using GRANDstack that shared (empirical) perspectives like this.
My concern about custom mutations is that they need to be manually updated as the schema definition changes. Do you run tests to compare your custom mutation to a group of more granular (autogenerated) mutations?
The two approaches I tend to use are either chaining operations together in the application with hooks i.e. useQuery or use Mutation until I hit a point where I feel the operation is just cleaner or easier to perform using Cypher. That’s mostly a personal opinion on when that happens. I think it just hits a point where Cypher is easier.
That being said I make a lot of use of input types to ensure that things don’t break or are caught by tests when they do.