Auto-generated Mutation Does Not Create Relationship - neo4j

I want to test auto-generated CRUD mutations created by calling makeAugmentedSchema from 'neo4j-graphql-js'. 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:
I am following the Interface Mutations guidance https://grandstack.io/docs/graphql-interface-union-types to create 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
}
}
}
It create Person and Bio nodes but no any relationship gets created between the two:
{
"data": {
"p": {
"name": "Anton",
"gender": "Male",
"id": "586b63fd-f9a5-4274-890f-26ba567c065c"
},
"b": {
"description": "I am a developer",
"id": "a46b4c22-d23b-4630-ac84-9d6248bdda89"
},
"r": null
}
}
This is how AddPersonBioRelation looks like:
Thank you.

I am new to GRANDstack, and I have also been struggling with these types of issues myself. I have typically broken this out separate mutations (in javascript) and used the return value for each as values for the next mutation. for example:
await createIncident({
variables: {
brief: values.brief,
date,
description: values.description,
recordable: values.recordable,
title: values.title
}
}).then((res) => {
addIncidentUser({
variables: {
from: user.id,
to: res.data.CreateIncident.id
}
});
});
the problem that i see in the example you've provided is that you are specifying a string value for from and to as "p" and "b" respectively and NOT the p.id and b.id return values from the parent mutations.
it's fine of me to point that out but what i can't for the LIFE of me figure out is how to properly reference p.id and b.id in the mutation itself. in other words you are trying to send
from: { id: "586b63fd-f9a5-4274-890f-26ba567c065c"}
to: { id: "a46b4c22-d23b-4630-ac84-9d6248bdda89" }
but in reality you are sending
from: { id: "p"}
to: { id: "b" }
which aren't actually references in neo4j so it fails.
if we can figure out how to properly reference p.id and b.id we should get this working.

Thank you, #dryhurst. It appears that there is no way to reference id of newly created nodes, but I found a solution by introducing temp id property. Please see the discussion of this matter and final solution on:
https://community.neo4j.com/t/auto-generated-mutation-does-not-create-relationship/21412/16.

Related

Loopback POST array of entry?

I want to insert 10 entries with one query against 10 queries.
I read that it's possible to do it by sending an array like this :
But I get this error:
Do I need to set something? I don't know what to do at all.
Repo with a sample : https://github.com/mathias22osterhagen22/loopback-array-post-sample
Edit:
people-model.ts:
import {Entity, model, property} from '#loopback/repository';
#model()
export class People extends Entity {
#property({
type: 'number',
id: true,
generated: true,
})
id?: number;
#property({
type: 'string',
required: true,
})
name: string;
constructor(data?: Partial<People>) {
super(data);
}
}
export interface PeopleRelations {
// describe navigational properties here
}
export type PeopleWithRelations = People & PeopleRelations;
The problem with your code was :
"name": "ValidationError", "message": "The People instance is not
valid. Details: 0 is not defined in the model (value: undefined);
1 is not defined in the model (value: undefined); name can't be
blank (value: undefined).",
Here in above as in your #requestBody schema, you are applying to insert a single object property, where as in your body are sending the array of [people] object.
As you can see in your people.model.ts you have declared property name to be required, so system finds for the property "name", which obviously not available in the given array of object as primary node.
As you are passing index array, so its obvious error that you don't have any property named 0 or 1, so it throws error.
The below is the code hat you should apply to get insert the multiple, items of the type.
#post('/peoples', {
responses: {
'200': {
description: 'People model instance',
content: {
'application/json': {
schema: getModelSchemaRef(People)
}
},
},
},
})
async create(
#requestBody({
content: {
'application/json': {
schema: {
type: 'array',
items: getModelSchemaRef(People, {
title: 'NewPeople',
exclude: ['id'],
}),
}
},
},
})
people: [Omit<People, 'id'>]
): Promise<{}> {
people.forEach(item => this.peopleRepository.create(item))
return people;
}
You can also use this below
Promise<People[]> {
return await this.peopleRepository.createAll(people)
}
You can pass the array of your people model by modifying the request body.If you need more help you can leave comment.
I think you have a clear solution now. "Happy Loopbacking :)"

Falcor - 'get' no longer emits references as leaf values

I've recently upgraded from Falcor 0.x to 1.1.0 (2.x will be the next step)
According to the Falcor migration documentation, when calling model.get references are not emitted as json anymore.
I'm however wondering what would be the best practice in order to manage references in a model.get.
Here is an example.
Having the following json graph:
{
jsonGraph: {
comment: {
123: {
owner: { $type: "ref", value: ["user", "abc"] },
message: "Foo"
}
},
user: {
abc: {
name: "John Doe"
initials: "JD"
}
}
}
}
Calling model.get will result to:
const json = await model.get(["comment", "123", ["owner", "message"]);
{
owner: undefined, // 0.x was returning `["user", "abc"]`
message: "John Doe"
}
However it's possible to get the owner only:
const json = await model.get(["comment", "123", "owner", ["name", "initials"]);
{
name: "John Doe",
initials: "JD"
}
What is the recommendation for handling references in model.get?
Should I manually get the owner (like the last example?) or should I have an ownerId instead of an owner reference in comment model?
model.get can take any number of pathSets (docs). So, break your first pathSet into two and pass as separate arguments:
await model.get(
["comment", "123", "message"],
["comment", "123", "owner", ["name", "initials"]]
);
which should return
{
message: "John Doe"
owner: {
name: "John Doe",
initials: "JD"
}
}
The underlying constraint is that a single pathSet can only include multiple paths of the same depth. So multiple paths of different depth can only be represented by multiple pathSets.

How to add sorting for field object of graphql type which refers to different graphql type?

I am using Neo4j dB and using pattern comprehension to return the values. I have 2 types Person and Friend:
(p:Person)-[:FRIEND_WITH]->(f:Friend)
Type Person{
id: String
name: String
friends: [Friend]
}
Type Friend{
id: String
name: String
}
type Query {
persons( limit:Int = 10): [Person]
friends( limit:Int = 10): [Friend]
}
What i want to do is to pull the array list of field friends (present in Person Type) in ascending order when the "persons" query executes. For e.g.
{
"data": {
"persons": {
"id": "1",
"name": "Timothy",
"friends": [
{
"id": "c3ef473",
"name": "Adam",
},
{
"id": "ef4e373",
"name": "Bryan",
},
(
"id": "e373ln45",
"name": "Craig",
},
How should I do it ? I researched regarding the sorting, but I did not find anything specific on the array object's sorting when we are using pattern comprehension in neo4j. Any suggestions would be really helpful !
I used the sortBy function of lodash to return the result into an ascending order.
And here is the graphql resolver query:
persons(_, params) {
let query = `MATCH (p:Person)
RETURN p{
.id,
.name,
friends: [(p)-[:FRIEND_WITH]->(f:Friend)) | f{.*}]
}
LIMIT $limit;`;
return dbSession().run(query, params)
.then(result => {
return result.records.map(record => {
let item = record.get("p");
item.friends = sortBy(item.friends, [function(i) {
return i.name;
}]);
return item;
})
})
}

How to use Promise.all in graphql resolver for neo4j?

I have multiple user nodes (around 45 users) and I want to return the total users count as well as the User's details too in a single query. (Similar to How to design the following resolver for GraphQL server?)
My schema:
type User {
ID: Int
name: String
}
type Query {
users: [User]
}
And after running the resolver for users query, I want to pull the total count as well as the users details too like below:
{
"data": {
"users": {
"total": 45
"users": [
{
"ID": 1,
"name": "User A"
},
{
"ID": 2,
"name": "User B"
},
...
]
}
But I am confused how to use Promise.all in neo4j. I tried to look how the promise works in neo4j but I did not find any desired info.
So, could you please let me know how should I write my resolver for this case ? Any help would be appreciable !!
Using Promise.all is not different in neo4j. Promise.all is from javascript. To write resolver you can do following:
let countQuery = "MATCH(n:User) RETURN count(n) as count;";
let userQuery = "MATCH(u:User) RETURN u;";
return Promise.all([
dbSession().run(countQuery, params),
dbSession().run(userQuery, params)
]).then((data) => {
return {
total: data[0].records.map(record => {return record.get('count')}
users: data[1].records.map(record => {return record.get('u')}
}
})
In your schema type you can change it to following:
type User {
ID: Int
name: String
}
type PagedData {
total: Int,
users: [User]
}
type Query {
users: PagedData
}

how to implement mutation responses on a local falcor Model dataset

Given that I have an example Model:
var model = new falcor.Model({
cache: {
userById: {
"1": {
name: "User",
email: "user#email.com"
}
},
users: {
current: null
}
}
});
This is a local model that I'm using for testing purposes, and I would like to implement it on a call to users.login so the user so that I can call:
model.call(['users', 'login'], ['user', 'password'])
I realized that if I do this:
var model = new falcor.Model({
cache: {
userById: {
"1": {
name: "User",
email: "user#email.com"
}
},
users: {
current: null,
login: function(user, password) {
console.log('this code is reached', user, password);
// what to return in order to mutate model?
}
},
}
});
When I do the call it gets there, but I can't figure out how to mutate the model as part of the response; on the server side we return the paths with values and invalidates, and it just works, but here I tried:
// trying returning as a jsonGraph response, don't work
login: function() {
return {
jsonGraph: {
users: {
current: {$type: "ref", value: ['userById', '1']}
}
},
paths: [['users', 'current']]
}
}
// trying returning as a path set mutation list, don't work
login: function() {
return [{path: ['users', 'current'], value: {$type: "ref", value: ['userById', '1']}}]
}
// trying force call to set on the model, don't work
login: function() {
this.set([
{path: ['users', 'current'], value: {$type: "ref", value: ['userById', '1']}}
])
}
// trying using ModelResponse, got an example on some external sources, don't work
login: funtion() {
return new ModelResponse((observer) => {
observer.onNext({
jsonGraph: {
users: {
current: {$type: "ref", value: ['userById', '1']}
}
},
paths: [['users', 'current']]
});
observer.onCompleted();
});
}
Now I don't know what else to try; I need a simple way to declare mutations after a call into a local model, if you know how to solve this, please let me know here.
Thanks.
The client model cache only supports JSONGraph, which b/c it is essentially just JSON with some conventions, doesn't support functions. So, when working with a falcor model cache and no dataSource/middle tier router, it is not possible to implement calls.
This can be kind of annoying when prototyping/testing, as a router is conceptually more difficult than a simple JSON cache object. I ran into this a while ago, so I wrote a dataSource module to support it: falcor-local-datasource. The dataSource is initialized with a graph object that does support function nodes, and as with your above examples, will mutate the graph based on the function's returned JSONGraphEnvelope or an array of PathValues.

Resources