diff --git a/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts b/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts index d305db5497..2aa656d0d4 100644 --- a/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts +++ b/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts @@ -26,17 +26,15 @@ import type { QueryASTContext } from "../QueryASTContext"; import { EntitySelection, type SelectionClause } from "./EntitySelection"; export class RelationshipSelection extends EntitySelection { - protected relationship: RelationshipAdapter; + private relationship: RelationshipAdapter; // Overrides relationship target for composite entities private targetOverride: ConcreteEntityAdapter | undefined; private alias: string | undefined; - private directed?: boolean; private optional: boolean; constructor({ relationship, alias, - directed, targetOverride, optional, }: { @@ -49,7 +47,6 @@ export class RelationshipSelection extends EntitySelection { super(); this.relationship = relationship; this.alias = alias; - this.directed = directed; this.targetOverride = targetOverride; this.optional = optional ?? false; } @@ -64,7 +61,7 @@ export class RelationshipSelection extends EntitySelection { const relationshipTarget = this.targetOverride ?? this.relationship.target; const targetNode = createNode(this.alias); const labels = getEntityLabels(relationshipTarget, context.neo4jGraphQLContext); - const relDirection = this.getRelationshipDirection(); + const relDirection = this.relationship.getCypherDirection(); const pattern = new Cypher.Pattern(context.target) .related(relVar, { direction: relDirection, type: this.relationship.type }) @@ -81,15 +78,4 @@ export class RelationshipSelection extends EntitySelection { selection: match, }; } - - protected getRelationshipDirection(): "left" | "right" | "undirected" { - return this.relationship.getCypherDirection(this.directed); - } -} - -/** Enforces direction on the relationship selection, regardless of direction configuration **/ -export class DirectedRelationshipSelection extends RelationshipSelection { - protected getRelationshipDirection(): "left" | "right" { - return this.relationship.cypherDirectionFromRelDirection(); - } } diff --git a/packages/graphql/src/translate/queryAST/factory/Operations/AggregateFactory.ts b/packages/graphql/src/translate/queryAST/factory/Operations/AggregateFactory.ts index ac6148c423..e8fade40ca 100644 --- a/packages/graphql/src/translate/queryAST/factory/Operations/AggregateFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/Operations/AggregateFactory.ts @@ -66,7 +66,6 @@ export class AggregateFactory { const selection = new RelationshipSelection({ relationship: entityOrRel, - directed: Boolean(resolveTree.args?.directed ?? true), }); const operation = new AggregationOperation({ diff --git a/packages/graphql/src/translate/queryAST/factory/Operations/ConnectionFactory.ts b/packages/graphql/src/translate/queryAST/factory/Operations/ConnectionFactory.ts index 0fb0c8deb7..d2cf75d839 100644 --- a/packages/graphql/src/translate/queryAST/factory/Operations/ConnectionFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/Operations/ConnectionFactory.ts @@ -68,7 +68,6 @@ export class ConnectionFactory { resolveTree: ResolveTree; context: Neo4jGraphQLTranslationContext; }): CompositeConnectionReadOperation { - const directed = resolveTree.args.directed as boolean | undefined; const resolveTreeWhere: Record = this.queryASTFactory.operationsFactory.getWhereArgs(resolveTree); let nodeWhere: Record; @@ -85,7 +84,6 @@ export class ConnectionFactory { if (relationship) { selection = new RelationshipSelection({ relationship, - directed, targetOverride: concreteEntity, }); resolveTreeEdgeFields = this.parseConnectionFields({ @@ -165,7 +163,6 @@ export class ConnectionFactory { if (relationship) { selection = new RelationshipSelection({ relationship, - directed: resolveTree.args.directed as boolean | undefined, }); resolveTreeEdgeFields = this.parseConnectionFields({ entityOrRel: relationship, @@ -199,9 +196,8 @@ export class ConnectionFactory { }); } - // eslint-disable-next-line @typescript-eslint/comma-dangle private hydrateConnectionOperationsASTWithSort< - T extends ConnectionReadOperation | CompositeConnectionReadOperation + T extends ConnectionReadOperation | CompositeConnectionReadOperation, >({ entityOrRel, resolveTree, diff --git a/packages/graphql/src/translate/queryAST/factory/Operations/DeleteFactory.ts b/packages/graphql/src/translate/queryAST/factory/Operations/DeleteFactory.ts index 1ebe90cdbc..11b02d2a1b 100644 --- a/packages/graphql/src/translate/queryAST/factory/Operations/DeleteFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/Operations/DeleteFactory.ts @@ -27,7 +27,7 @@ import { checkEntityAuthentication } from "../../../authorization/check-authenti import type { Filter } from "../../ast/filters/Filter"; import { DeleteOperation } from "../../ast/operations/DeleteOperation"; import { NodeSelection } from "../../ast/selection/NodeSelection"; -import { DirectedRelationshipSelection } from "../../ast/selection/RelationshipSelection"; +import { RelationshipSelection } from "../../ast/selection/RelationshipSelection"; import { getConcreteEntities } from "../../utils/get-concrete-entities"; import { isInterfaceEntity } from "../../utils/is-interface-entity"; import { isUnionEntity } from "../../utils/is-union-entity"; @@ -204,7 +204,7 @@ export class DeleteFactory { context, }); - const selection = new DirectedRelationshipSelection({ + const selection = new RelationshipSelection({ relationship, optional: true, targetOverride: target, diff --git a/packages/graphql/src/translate/queryAST/factory/Operations/ReadFactory.ts b/packages/graphql/src/translate/queryAST/factory/Operations/ReadFactory.ts index 525502a122..f7f298c955 100644 --- a/packages/graphql/src/translate/queryAST/factory/Operations/ReadFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/Operations/ReadFactory.ts @@ -75,7 +75,6 @@ export class ReadFactory { if (relationship) { selection = new RelationshipSelection({ relationship, - directed: Boolean(resolveTree.args?.directed ?? true), }); } else { selection = new NodeSelection({ @@ -105,7 +104,6 @@ export class ReadFactory { if (relationship) { selection = new RelationshipSelection({ relationship, - directed: Boolean(resolveTree.args?.directed ?? true), targetOverride: concreteEntity, }); } else { diff --git a/packages/graphql/tests/integration/directives/relationship/query-direction.int.test.ts b/packages/graphql/tests/integration/directives/relationship/query-direction.int.test.ts index fff48effe4..561babed52 100644 --- a/packages/graphql/tests/integration/directives/relationship/query-direction.int.test.ts +++ b/packages/graphql/tests/integration/directives/relationship/query-direction.int.test.ts @@ -62,7 +62,7 @@ describe("query-direction", () => { test("should return related node using the queryDirection DIRECTED", async () => { const query = /* GraphQL */ ` { - ${Person.plural}(where: { name: "${mike}" }) { + ${Person.plural}(where: { name_EQ: "${mike}" }) { name friends { name @@ -96,7 +96,7 @@ describe("query-direction", () => { test("should return person with friend named Mike (directed out)", async () => { const query = /* GraphQL */ ` { - ${Person.plural}(where: { friends_SOME: { name: "${mike}" } }) { + ${Person.plural}(where: { friends_SOME: { name_EQ: "${mike}" } }) { name } } @@ -122,7 +122,7 @@ describe("query-direction", () => { test("should delete two nodes when performing nested delete under delete (always directed)", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.delete}(where: { name: "${mike}" }, delete: { friends: { where: { } } }) { + ${Person.operations.delete}(where: { name_EQ: "${mike}" }, delete: { friends: { where: { } } }) { nodesDeleted } } @@ -146,7 +146,7 @@ describe("query-direction", () => { test("should only delete one when performing nested delete under update (always directed)", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { delete: { where: { } } } }) { + ${Person.operations.update}(where: { name_EQ: "${mike}" }, update: { friends: { delete: { where: { } } } }) { ${Person.plural} { name friends { @@ -186,7 +186,7 @@ describe("query-direction", () => { test("should only disconnect one when performing nested disconnect under update (always directed)", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { disconnect: { where: { } } } }) { + ${Person.operations.update}(where: { name_EQ: "${mike}" }, update: { friends: { disconnect: { where: { } } } }) { ${Person.plural} { name friends { @@ -226,217 +226,7 @@ describe("query-direction", () => { test("should only update one when performing nested update under update (always directed)", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { update: { node: { name: "Bob" } } } }) { - ${Person.plural} { - name - friends { - name - } - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.update]: { - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: expect.toIncludeSameMembers([{ name: "Bob" }]), - }, - ]), - }, - }); - }); - }); - - describe("DEFAULT_DIRECTED", () => { - beforeEach(async () => { - const typeDefs = /* GraphQL */ ` - type ${Person} @node { - name: String! - friends: [${Person}!]! @relationship(type: "HAS_FRIEND", direction: OUT, queryDirection: DEFAULT_DIRECTED) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - test("should return related node using the queryDirection DIRECTED", async () => { - const query = /* GraphQL */ ` - { - ${Person.plural}(where: { name: "${mike}" }) { - name - friends { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: expect.toIncludeSameMembers([ - { - name: charlie, - }, - ]), - }, - ]), - }); - }); - - test("should return person with friend named Mike (directed out)", async () => { - const query = /* GraphQL */ ` - { - ${Person.plural}(where: { friends_SOME: { name: "${mike}" } }) { - name - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.plural]: expect.toIncludeSameMembers([ - { - name: stefan, - }, - ]), - }); - }); - - test("should delete two nodes when performing nested delete under delete (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.delete}(where: { name: "${mike}" }, delete: { friends: { where: { } } }) { - nodesDeleted - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.delete]: { - nodesDeleted: 2, - }, - }); - }); - - test("should only delete one when performing nested delete under update (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { delete: { where: { } } } }) { - ${Person.plural} { - name - friends { - name - } - } - info { - nodesDeleted - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.update]: { - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: [], - }, - ]), - info: { - nodesDeleted: 1, - }, - }, - }); - }); - - test("should only disconnect one when performing nested disconnect under update (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { disconnect: { where: { } } } }) { - ${Person.plural} { - name - friends { - name - } - } - info { - relationshipsDeleted - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.update]: { - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: [], - }, - ]), - info: { - relationshipsDeleted: 1, - }, - }, - }); - }); - - test("should only update one when performing nested update under update (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { update: { node: { name: "Bob" } } } }) { + ${Person.operations.update}(where: { name_EQ: "${mike}" }, update: { friends: { update: { node: { name_SET: "Bob" } } } }) { ${Person.plural} { name friends { @@ -482,223 +272,7 @@ describe("query-direction", () => { test("should return related node using the queryDirection UNDIRECTED", async () => { const query = /* GraphQL */ ` { - ${Person.plural}(where: { name: "${mike}" }) { - name - friends { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: expect.toIncludeSameMembers([ - { - name: stefan, - }, - { - name: charlie, - }, - ]), - }, - ]), - }); - }); - - test("should return person with friend named Mike (undirected)", async () => { - const query = /* GraphQL */ ` - { - ${Person.plural}(where: { friends_SOME: { name: "${mike}" } }) { - name - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.plural]: expect.toIncludeSameMembers([ - { - name: stefan, - }, - { - name: charlie, - }, - ]), - }); - }); - - test("should delete two nodes when performing nested delete under delete (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.delete}(where: { name: "${mike}" }, delete: { friends: { where: { } } }) { - nodesDeleted - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.delete]: { - nodesDeleted: 2, - }, - }); - }); - - test("should only delete one when performing nested delete under update (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { delete: { where: { } } } }) { - ${Person.plural} { - name - friends { - name - } - } - info { - nodesDeleted - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.update]: { - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: expect.toIncludeSameMembers([{ name: stefan }]), - }, - ]), - info: { - nodesDeleted: 1, - }, - }, - }); - }); - - test("should only disconnect one when performing nested disconnect under update (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { disconnect: { where: { } } } }) { - ${Person.plural} { - name - friends { - name - } - } - info { - relationshipsDeleted - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.update]: { - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: expect.toIncludeSameMembers([{ name: stefan }]), - }, - ]), - info: { - relationshipsDeleted: 1, - }, - }, - }); - }); - - test("should only update one when performing nested update under update (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { update: { node: { name: "Bob" } } } }) { - ${Person.plural} { - name - friends { - name - } - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.update]: { - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: expect.toIncludeSameMembers([{ name: stefan }, { name: "Bob" }]), - }, - ]), - }, - }); - }); - }); - - describe("DEFAULT_UNDIRECTED", () => { - beforeEach(async () => { - const typeDefs = /* GraphQL */ ` - type ${Person} @node { - name: String! - friends: [${Person}!]! @relationship(type: "HAS_FRIEND", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - test("should return related node using the queryDirection UNDIRECTED", async () => { - const query = /* GraphQL */ ` - { - ${Person.plural}(where: { name: "${mike}" }) { + ${Person.plural}(where: { name_EQ: "${mike}" }) { name friends { name @@ -735,7 +309,7 @@ describe("query-direction", () => { test("should return person with friend named Mike (undirected)", async () => { const query = /* GraphQL */ ` { - ${Person.plural}(where: { friends_SOME: { name: "${mike}" } }) { + ${Person.plural}(where: { friends_SOME: { name_EQ: "${mike}" } }) { name } } @@ -761,10 +335,10 @@ describe("query-direction", () => { }); }); - test("should delete two nodes when performing nested delete under delete (always directed)", async () => { + test("should delete all three nodes when performing nested delete under delete", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.delete}(where: { name: "${mike}" }, delete: { friends: { where: { } } }) { + ${Person.operations.delete}(where: { name_EQ: "${mike}" }, delete: { friends: { where: { } } }) { nodesDeleted } } @@ -780,7 +354,7 @@ describe("query-direction", () => { expect(gqlResult.data).toEqual({ [Person.operations.delete]: { - nodesDeleted: 2, + nodesDeleted: 3, }, }); }); @@ -788,7 +362,7 @@ describe("query-direction", () => { test("should only delete one when performing nested delete under update (always directed)", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { delete: { where: { } } } }) { + ${Person.operations.update}(where: { name_EQ: "${mike}" }, update: { friends: { delete: { where: { } } } }) { ${Person.plural} { name friends { @@ -828,7 +402,7 @@ describe("query-direction", () => { test("should only disconnect one when performing nested disconnect under update (always directed)", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { disconnect: { where: { } } } }) { + ${Person.operations.update}(where: { name_EQ: "${mike}" }, update: { friends: { disconnect: { where: { } } } }) { ${Person.plural} { name friends { @@ -868,7 +442,7 @@ describe("query-direction", () => { test("should only update one when performing nested update under update (always directed)", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { update: { node: { name: "Bob" } } } }) { + ${Person.operations.update}(where: { name_EQ: "${mike}" }, update: { friends: { update: { node: { name_SET: "Bob" } } } }) { ${Person.plural} { name friends { diff --git a/packages/graphql/tests/tck/undirected-relationships/query-direction.test.ts b/packages/graphql/tests/tck/undirected-relationships/query-direction.test.ts index 7ae1c1d04c..8921e200fa 100644 --- a/packages/graphql/tests/tck/undirected-relationships/query-direction.test.ts +++ b/packages/graphql/tests/tck/undirected-relationships/query-direction.test.ts @@ -21,14 +21,14 @@ import { Neo4jGraphQL } from "../../../src"; import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; describe("queryDirection in relationships", () => { - describe("DIRECTED_ONLY", () => { + describe("DIRECTED", () => { let neoSchema: Neo4jGraphQL; beforeAll(() => { const typeDefs = /* GraphQL */ ` type User @node { name: String! - friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DIRECTED_ONLY) + friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DIRECTED) } `; @@ -68,7 +68,7 @@ describe("queryDirection in relationships", () => { test("query with filter", async () => { const query = /* GraphQL */ ` query { - users(where: { friends_SOME: { name: "John Smith" } }) { + users(where: { friends_SOME: { name_EQ: "John Smith" } }) { name friends { name @@ -105,8 +105,8 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { disconnect: { where: { node: { name: "Jane Smith" } } } } } + where: { friends_SOME: { name_EQ: "John Smith" } } + update: { friends: { disconnect: { where: { node: { name_EQ: "Jane Smith" } } } } } ) { users { name @@ -162,7 +162,7 @@ describe("queryDirection in relationships", () => { { \\"where\\": { \\"node\\": { - \\"name\\": \\"Jane Smith\\" + \\"name_EQ\\": \\"Jane Smith\\" } } } @@ -181,8 +181,8 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { delete: { where: { node: { name: "Jane Smith" } } } } } + where: { friends_SOME: { name_EQ: "John Smith" } } + update: { friends: { delete: { where: { node: { name_EQ: "Jane Smith" } } } } } ) { users { name @@ -237,7 +237,7 @@ describe("queryDirection in relationships", () => { { \\"where\\": { \\"node\\": { - \\"name\\": \\"Jane Smith\\" + \\"name_EQ\\": \\"Jane Smith\\" } } } @@ -256,11 +256,11 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { friends_SOME: { name: "John Smith" } } + where: { friends_SOME: { name_EQ: "John Smith" } } update: { friends: { - where: { node: { name: "Jane Smith" } } - update: { node: { name: "Janet Smith" } } + where: { node: { name_EQ: "Jane Smith" } } + update: { node: { name_SET: "Janet Smith" } } } } ) { @@ -287,7 +287,7 @@ describe("queryDirection in relationships", () => { WITH this MATCH (this)-[this_friends_with0_relationship:FRIENDS_WITH]->(this_friends0:User) WHERE this_friends0.name = $updateUsers_args_update_friends0_where_this_friends0param0 - SET this_friends0.name = $this_update_friends0_name + SET this_friends0.name = $this_update_friends0_name_SET RETURN count(*) AS update_this_friends0 } WITH * @@ -304,7 +304,7 @@ describe("queryDirection in relationships", () => { "{ \\"param0\\": \\"John Smith\\", \\"updateUsers_args_update_friends0_where_this_friends0param0\\": \\"Jane Smith\\", - \\"this_update_friends0_name\\": \\"Janet Smith\\", + \\"this_update_friends0_name_SET\\": \\"Janet Smith\\", \\"updateUsers\\": { \\"args\\": { \\"update\\": { @@ -312,12 +312,12 @@ describe("queryDirection in relationships", () => { { \\"where\\": { \\"node\\": { - \\"name\\": \\"Jane Smith\\" + \\"name_EQ\\": \\"Jane Smith\\" } }, \\"update\\": { \\"node\\": { - \\"name\\": \\"Janet Smith\\" + \\"name_SET\\": \\"Janet Smith\\" } } } @@ -334,8 +334,8 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { deleteUsers( - where: { friends_SOME: { name: "John Smith" } } - delete: { friends: { where: { node: { name: "Jane Smith" } } } } + where: { friends_SOME: { name_EQ: "John Smith" } } + delete: { friends: { where: { node: { name_EQ: "Jane Smith" } } } } ) { nodesDeleted } @@ -375,724 +375,14 @@ describe("queryDirection in relationships", () => { }); }); - describe("DEFAULT_DIRECTED", () => { + describe("UNDIRECTED", () => { let neoSchema: Neo4jGraphQL; beforeAll(() => { const typeDefs = /* GraphQL */ ` type User @node { name: String! - friends: [User!]! - @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DEFAULT_DIRECTED) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("query", async () => { - const query = /* GraphQL */ ` - query { - users { - name - friends: friends { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]->(this1:User) - WITH this1 { .name } AS this1 - RETURN collect(this1) AS var2 - } - RETURN this { .name, friends: var2 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("query with filter", async () => { - const query = /* GraphQL */ ` - query { - users(where: { friends_SOME: { name: "John Smith" } }) { - name - friends { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]->(this0:User) - WHERE this0.name = $param0 - } - CALL { - WITH this - MATCH (this)-[this1:FRIENDS_WITH]->(this2:User) - WITH this2 { .name } AS this2 - RETURN collect(this2) AS var3 - } - RETURN this { .name, friends: var3 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\" - }" - `); - }); - - test("disconnect", async () => { - const query = /* GraphQL */ ` - mutation { - updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { disconnect: { where: { node: { name: "Jane Smith" } } } } } - ) { - users { - name - friends { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]->(this0:User) - WHERE this0.name = $param0 - } - WITH this - CALL { - WITH this - OPTIONAL MATCH (this)-[this_friends0_disconnect0_rel:FRIENDS_WITH]->(this_friends0_disconnect0:User) - WHERE this_friends0_disconnect0.name = $updateUsers_args_update_friends0_disconnect0_where_User_this_friends0_disconnect0param0 - CALL { - WITH this_friends0_disconnect0, this_friends0_disconnect0_rel, this - WITH collect(this_friends0_disconnect0) as this_friends0_disconnect0, this_friends0_disconnect0_rel, this - UNWIND this_friends0_disconnect0 as x - DELETE this_friends0_disconnect0_rel - } - RETURN count(*) AS disconnect_this_friends0_disconnect_User - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:FRIENDS_WITH]->(update_this1:User) - WITH update_this1 { .name } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .name, friends: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"updateUsers_args_update_friends0_disconnect0_where_User_this_friends0_disconnect0param0\\": \\"Jane Smith\\", - \\"updateUsers\\": { - \\"args\\": { - \\"update\\": { - \\"friends\\": [ - { - \\"disconnect\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name\\": \\"Jane Smith\\" - } - } - } - ] - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("update with nested delete", async () => { - const query = /* GraphQL */ ` - mutation { - updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { delete: { where: { node: { name: "Jane Smith" } } } } } - ) { - users { - name - friends { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]->(this0:User) - WHERE this0.name = $param0 - } - WITH * - CALL { - WITH * - OPTIONAL MATCH (this)-[this_friends0_delete0_relationship:FRIENDS_WITH]->(this_friends0_delete0:User) - WHERE this_friends0_delete0.name = $updateUsers_args_update_friends0_delete0_where_this_friends0_delete0param0 - WITH this_friends0_delete0_relationship, collect(DISTINCT this_friends0_delete0) AS this_friends0_delete0_to_delete - CALL { - WITH this_friends0_delete0_to_delete - UNWIND this_friends0_delete0_to_delete AS x - DETACH DELETE x - } - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:FRIENDS_WITH]->(update_this1:User) - WITH update_this1 { .name } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .name, friends: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"updateUsers_args_update_friends0_delete0_where_this_friends0_delete0param0\\": \\"Jane Smith\\", - \\"updateUsers\\": { - \\"args\\": { - \\"update\\": { - \\"friends\\": [ - { - \\"delete\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name\\": \\"Jane Smith\\" - } - } - } - ] - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("update", async () => { - const query = /* GraphQL */ ` - mutation { - updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { - friends: { - where: { node: { name: "Jane Smith" } } - update: { node: { name: "Janet Smith" } } - } - } - ) { - users { - name - friends { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]->(this0:User) - WHERE this0.name = $param0 - } - WITH this - CALL { - WITH this - MATCH (this)-[this_friends_with0_relationship:FRIENDS_WITH]->(this_friends0:User) - WHERE this_friends0.name = $updateUsers_args_update_friends0_where_this_friends0param0 - SET this_friends0.name = $this_update_friends0_name - RETURN count(*) AS update_this_friends0 - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:FRIENDS_WITH]->(update_this1:User) - WITH update_this1 { .name } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .name, friends: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"updateUsers_args_update_friends0_where_this_friends0param0\\": \\"Jane Smith\\", - \\"this_update_friends0_name\\": \\"Janet Smith\\", - \\"updateUsers\\": { - \\"args\\": { - \\"update\\": { - \\"friends\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name\\": \\"Jane Smith\\" - } - }, - \\"update\\": { - \\"node\\": { - \\"name\\": \\"Janet Smith\\" - } - } - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("delete with nested delete", async () => { - const query = /* GraphQL */ ` - mutation { - deleteUsers( - where: { friends_SOME: { name: "John Smith" } } - delete: { friends: { where: { node: { name: "Jane Smith" } } } } - ) { - nodesDeleted - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]->(this0:User) - WHERE this0.name = $param0 - } - WITH * - CALL { - WITH * - OPTIONAL MATCH (this)-[this1:FRIENDS_WITH]->(this2:User) - WHERE this2.name = $param1 - WITH this1, collect(DISTINCT this2) AS var3 - CALL { - WITH var3 - UNWIND var3 AS var4 - DETACH DELETE var4 - } - } - WITH * - DETACH DELETE this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"param1\\": \\"Jane Smith\\" - }" - `); - }); - }); - describe("UNDIRECTED_ONLY", () => { - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - const typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! - @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: UNDIRECTED_ONLY) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("query", async () => { - const query = /* GraphQL */ ` - query { - users { - name - friends: friends { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]-(this1:User) - WITH this1 { .name } AS this1 - RETURN collect(this1) AS var2 - } - RETURN this { .name, friends: var2 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("query with filter", async () => { - const query = /* GraphQL */ ` - query { - users(where: { friends_SOME: { name: "John Smith" } }) { - name - friends { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]-(this0:User) - WHERE this0.name = $param0 - } - CALL { - WITH this - MATCH (this)-[this1:FRIENDS_WITH]-(this2:User) - WITH this2 { .name } AS this2 - RETURN collect(this2) AS var3 - } - RETURN this { .name, friends: var3 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\" - }" - `); - }); - - test("disconnect", async () => { - const query = /* GraphQL */ ` - mutation { - updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { disconnect: { where: { node: { name: "Jane Smith" } } } } } - ) { - users { - name - friends { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]-(this0:User) - WHERE this0.name = $param0 - } - WITH this - CALL { - WITH this - OPTIONAL MATCH (this)-[this_friends0_disconnect0_rel:FRIENDS_WITH]->(this_friends0_disconnect0:User) - WHERE this_friends0_disconnect0.name = $updateUsers_args_update_friends0_disconnect0_where_User_this_friends0_disconnect0param0 - CALL { - WITH this_friends0_disconnect0, this_friends0_disconnect0_rel, this - WITH collect(this_friends0_disconnect0) as this_friends0_disconnect0, this_friends0_disconnect0_rel, this - UNWIND this_friends0_disconnect0 as x - DELETE this_friends0_disconnect0_rel - } - RETURN count(*) AS disconnect_this_friends0_disconnect_User - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:FRIENDS_WITH]-(update_this1:User) - WITH update_this1 { .name } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .name, friends: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"updateUsers_args_update_friends0_disconnect0_where_User_this_friends0_disconnect0param0\\": \\"Jane Smith\\", - \\"updateUsers\\": { - \\"args\\": { - \\"update\\": { - \\"friends\\": [ - { - \\"disconnect\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name\\": \\"Jane Smith\\" - } - } - } - ] - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("update with nested delete", async () => { - const query = /* GraphQL */ ` - mutation { - updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { delete: { where: { node: { name: "Jane Smith" } } } } } - ) { - users { - name - friends { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]-(this0:User) - WHERE this0.name = $param0 - } - WITH * - CALL { - WITH * - OPTIONAL MATCH (this)-[this_friends0_delete0_relationship:FRIENDS_WITH]->(this_friends0_delete0:User) - WHERE this_friends0_delete0.name = $updateUsers_args_update_friends0_delete0_where_this_friends0_delete0param0 - WITH this_friends0_delete0_relationship, collect(DISTINCT this_friends0_delete0) AS this_friends0_delete0_to_delete - CALL { - WITH this_friends0_delete0_to_delete - UNWIND this_friends0_delete0_to_delete AS x - DETACH DELETE x - } - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:FRIENDS_WITH]-(update_this1:User) - WITH update_this1 { .name } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .name, friends: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"updateUsers_args_update_friends0_delete0_where_this_friends0_delete0param0\\": \\"Jane Smith\\", - \\"updateUsers\\": { - \\"args\\": { - \\"update\\": { - \\"friends\\": [ - { - \\"delete\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name\\": \\"Jane Smith\\" - } - } - } - ] - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("update", async () => { - const query = /* GraphQL */ ` - mutation { - updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { - friends: { - where: { node: { name: "Jane Smith" } } - update: { node: { name: "Janet Smith" } } - } - } - ) { - users { - name - friends { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]-(this0:User) - WHERE this0.name = $param0 - } - WITH this - CALL { - WITH this - MATCH (this)-[this_friends_with0_relationship:FRIENDS_WITH]->(this_friends0:User) - WHERE this_friends0.name = $updateUsers_args_update_friends0_where_this_friends0param0 - SET this_friends0.name = $this_update_friends0_name - RETURN count(*) AS update_this_friends0 - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:FRIENDS_WITH]-(update_this1:User) - WITH update_this1 { .name } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .name, friends: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"updateUsers_args_update_friends0_where_this_friends0param0\\": \\"Jane Smith\\", - \\"this_update_friends0_name\\": \\"Janet Smith\\", - \\"updateUsers\\": { - \\"args\\": { - \\"update\\": { - \\"friends\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name\\": \\"Jane Smith\\" - } - }, - \\"update\\": { - \\"node\\": { - \\"name\\": \\"Janet Smith\\" - } - } - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("delete with nested delete", async () => { - const query = /* GraphQL */ ` - mutation { - deleteUsers( - where: { friends_SOME: { name: "John Smith" } } - delete: { friends: { where: { node: { name: "Jane Smith" } } } } - ) { - nodesDeleted - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]-(this0:User) - WHERE this0.name = $param0 - } - WITH * - CALL { - WITH * - OPTIONAL MATCH (this)-[this1:FRIENDS_WITH]->(this2:User) - WHERE this2.name = $param1 - WITH this1, collect(DISTINCT this2) AS var3 - CALL { - WITH var3 - UNWIND var3 AS var4 - DETACH DELETE var4 - } - } - WITH * - DETACH DELETE this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"param1\\": \\"Jane Smith\\" - }" - `); - }); - }); - - describe("DEFAULT_UNDIRECTED", () => { - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - const typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! - @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) + friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: UNDIRECTED) } `; @@ -1132,7 +422,7 @@ describe("queryDirection in relationships", () => { test("query with filter", async () => { const query = /* GraphQL */ ` query { - users(where: { friends_SOME: { name: "John Smith" } }) { + users(where: { friends_SOME: { name_EQ: "John Smith" } }) { name friends { name @@ -1169,8 +459,8 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { disconnect: { where: { node: { name: "Jane Smith" } } } } } + where: { friends_SOME: { name_EQ: "John Smith" } } + update: { friends: { disconnect: { where: { node: { name_EQ: "Jane Smith" } } } } } ) { users { name @@ -1226,7 +516,7 @@ describe("queryDirection in relationships", () => { { \\"where\\": { \\"node\\": { - \\"name\\": \\"Jane Smith\\" + \\"name_EQ\\": \\"Jane Smith\\" } } } @@ -1245,8 +535,8 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { delete: { where: { node: { name: "Jane Smith" } } } } } + where: { friends_SOME: { name_EQ: "John Smith" } } + update: { friends: { delete: { where: { node: { name_EQ: "Jane Smith" } } } } } ) { users { name @@ -1301,7 +591,7 @@ describe("queryDirection in relationships", () => { { \\"where\\": { \\"node\\": { - \\"name\\": \\"Jane Smith\\" + \\"name_EQ\\": \\"Jane Smith\\" } } } @@ -1320,11 +610,11 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { friends_SOME: { name: "John Smith" } } + where: { friends_SOME: { name_EQ: "John Smith" } } update: { friends: { - where: { node: { name: "Jane Smith" } } - update: { node: { name: "Janet Smith" } } + where: { node: { name_EQ: "Jane Smith" } } + update: { node: { name_SET: "Janet Smith" } } } } ) { @@ -1351,7 +641,7 @@ describe("queryDirection in relationships", () => { WITH this MATCH (this)-[this_friends_with0_relationship:FRIENDS_WITH]->(this_friends0:User) WHERE this_friends0.name = $updateUsers_args_update_friends0_where_this_friends0param0 - SET this_friends0.name = $this_update_friends0_name + SET this_friends0.name = $this_update_friends0_name_SET RETURN count(*) AS update_this_friends0 } WITH * @@ -1368,7 +658,7 @@ describe("queryDirection in relationships", () => { "{ \\"param0\\": \\"John Smith\\", \\"updateUsers_args_update_friends0_where_this_friends0param0\\": \\"Jane Smith\\", - \\"this_update_friends0_name\\": \\"Janet Smith\\", + \\"this_update_friends0_name_SET\\": \\"Janet Smith\\", \\"updateUsers\\": { \\"args\\": { \\"update\\": { @@ -1376,12 +666,12 @@ describe("queryDirection in relationships", () => { { \\"where\\": { \\"node\\": { - \\"name\\": \\"Jane Smith\\" + \\"name_EQ\\": \\"Jane Smith\\" } }, \\"update\\": { \\"node\\": { - \\"name\\": \\"Janet Smith\\" + \\"name_SET\\": \\"Janet Smith\\" } } } @@ -1398,8 +688,8 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { deleteUsers( - where: { friends_SOME: { name: "John Smith" } } - delete: { friends: { where: { node: { name: "Jane Smith" } } } } + where: { friends_SOME: { name_EQ: "John Smith" } } + delete: { friends: { where: { node: { name_EQ: "Jane Smith" } } } } ) { nodesDeleted } @@ -1417,7 +707,7 @@ describe("queryDirection in relationships", () => { WITH * CALL { WITH * - OPTIONAL MATCH (this)-[this1:FRIENDS_WITH]->(this2:User) + OPTIONAL MATCH (this)-[this1:FRIENDS_WITH]-(this2:User) WHERE this2.name = $param1 WITH this1, collect(DISTINCT this2) AS var3 CALL {