Skip to content

Commit c568daa

Browse files
authored
Merge pull request #575 from neo4j/remove-import-with
Remove importWith
2 parents d55560f + 315de05 commit c568daa

File tree

6 files changed

+35
-276
lines changed

6 files changed

+35
-276
lines changed

.changeset/sharp-women-kick.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
"@neo4j/cypher-builder": major
3+
---
4+
5+
Remove `.importWith` from `Call` clauses in favor of constructor options
6+
7+
_Before_
8+
9+
```js
10+
const clause = new Cypher.Call(nestedClause).importWith(movieNode, actorNode);
11+
```
12+
13+
```cypher
14+
CALL {
15+
WITH var0, var1
16+
// Nested clause
17+
}
18+
```
19+
20+
_After_
21+
22+
```js
23+
const clause = new Cypher.Call(nestedClause, [movieNode, actorNode]);
24+
```
25+
26+
```cypher
27+
CALL (var0, var1){
28+
// Nested clause
29+
}
30+
```

src/clauses/Call.test.ts

Lines changed: 0 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -71,138 +71,6 @@ describe("CypherBuilder Call", () => {
7171
`);
7272
});
7373

74-
test("CALL with import with", () => {
75-
const node = new Cypher.Node();
76-
77-
const matchClause = new Cypher.Match(new Cypher.Pattern(node, { labels: ["Movie"] }))
78-
.where(Cypher.eq(new Cypher.Param("aa"), new Cypher.Param("bb")))
79-
.return([node.property("title"), "movie"]);
80-
81-
const clause = new Cypher.Call(matchClause).importWith(node);
82-
const queryResult = clause.build();
83-
expect(queryResult.cypher).toMatchInlineSnapshot(`
84-
"CALL {
85-
WITH this0
86-
MATCH (this0:Movie)
87-
WHERE $param0 = $param1
88-
RETURN this0.title AS movie
89-
}"
90-
`);
91-
92-
expect(queryResult.params).toMatchInlineSnapshot(`
93-
{
94-
"param0": "aa",
95-
"param1": "bb",
96-
}
97-
`);
98-
});
99-
100-
test("CALL with import with *", () => {
101-
const node = new Cypher.Node();
102-
103-
const matchClause = new Cypher.Match(new Cypher.Pattern(node, { labels: ["Movie"] })).return([
104-
node.property("title"),
105-
"movie",
106-
]);
107-
108-
const clause = new Cypher.Call(matchClause).importWith("*");
109-
const queryResult = clause.build();
110-
expect(queryResult.cypher).toMatchInlineSnapshot(`
111-
"CALL {
112-
WITH *
113-
MATCH (this0:Movie)
114-
RETURN this0.title AS movie
115-
}"
116-
`);
117-
118-
expect(queryResult.params).toMatchInlineSnapshot(`{}`);
119-
});
120-
121-
test("CALL with import with * and extra fields", () => {
122-
const node = new Cypher.Node();
123-
124-
const matchClause = new Cypher.Match(new Cypher.Pattern(node, { labels: ["Movie"] })).return([
125-
node.property("title"),
126-
"movie",
127-
]);
128-
129-
const clause = new Cypher.Call(matchClause).importWith(node, "*");
130-
const queryResult = clause.build();
131-
expect(queryResult.cypher).toMatchInlineSnapshot(`
132-
"CALL {
133-
WITH *, this0
134-
MATCH (this0:Movie)
135-
RETURN this0.title AS movie
136-
}"
137-
`);
138-
139-
expect(queryResult.params).toMatchInlineSnapshot(`{}`);
140-
});
141-
142-
test("CALL with import with without parameters", () => {
143-
const node = new Cypher.Node();
144-
145-
const matchClause = new Cypher.Match(new Cypher.Pattern(node, { labels: ["Movie"] }))
146-
.where(Cypher.eq(new Cypher.Param("aa"), new Cypher.Param("bb")))
147-
.return([node.property("title"), "movie"]);
148-
149-
const clause = new Cypher.Call(matchClause).importWith();
150-
const queryResult = clause.build();
151-
expect(queryResult.cypher).toMatchInlineSnapshot(`
152-
"CALL {
153-
MATCH (this0:Movie)
154-
WHERE $param0 = $param1
155-
RETURN this0.title AS movie
156-
}"
157-
`);
158-
159-
expect(queryResult.params).toMatchInlineSnapshot(`
160-
{
161-
"param0": "aa",
162-
"param1": "bb",
163-
}
164-
`);
165-
});
166-
167-
test("CALL with import with multiple parameters", () => {
168-
const node = new Cypher.Node();
169-
170-
const matchClause = new Cypher.Match(new Cypher.Pattern(node, { labels: ["Movie"] }))
171-
.where(Cypher.eq(new Cypher.Param("aa"), new Cypher.Param("bb")))
172-
.return([node.property("title"), "movie"]);
173-
174-
const clause = new Cypher.Call(matchClause).importWith(node, new Cypher.Variable());
175-
const queryResult = clause.build();
176-
expect(queryResult.cypher).toMatchInlineSnapshot(`
177-
"CALL {
178-
WITH this0, var1
179-
MATCH (this0:Movie)
180-
WHERE $param0 = $param1
181-
RETURN this0.title AS movie
182-
}"
183-
`);
184-
185-
expect(queryResult.params).toMatchInlineSnapshot(`
186-
{
187-
"param0": "aa",
188-
"param1": "bb",
189-
}
190-
`);
191-
});
192-
193-
test("CALL with import with fails if import with is already set", () => {
194-
const node = new Cypher.Node();
195-
196-
const matchClause = new Cypher.Match(new Cypher.Pattern(node, { labels: ["Movie"] }))
197-
.where(Cypher.eq(new Cypher.Param("aa"), new Cypher.Param("bb")))
198-
.return([node.property("title"), "movie"]);
199-
200-
const clause = new Cypher.Call(matchClause).importWith(node);
201-
expect(() => {
202-
clause.importWith(node);
203-
}).toThrow(`Call import "WITH" already set`);
204-
});
205-
20674
test("CALL with external with", () => {
20775
const node = new Cypher.Node();
20876

@@ -700,19 +568,6 @@ RETURN this0"
700568
expect(queryResult.params).toMatchInlineSnapshot(`{}`);
701569
});
702570

703-
test("ImportWith fails when variable scope is set", () => {
704-
const movieNode = new Cypher.Node();
705-
706-
const call = new Cypher.Call(
707-
new Cypher.Create(new Cypher.Pattern(movieNode).related().to(new Cypher.Node())),
708-
[movieNode]
709-
);
710-
711-
expect(() => {
712-
call.importWith("*");
713-
}).toThrow(`Call import cannot be used along with scope clauses "Call (<variable>)"`);
714-
});
715-
716571
test("Call with empty variable scope", () => {
717572
const movieNode = new Cypher.Node();
718573
const actorNode = new Cypher.Node();

src/clauses/Call.ts

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import { compileCypherIfExists } from "../utils/compile-cypher-if-exists";
2424
import { isNumber } from "../utils/is-number";
2525
import { padBlock } from "../utils/pad-block";
2626
import { Clause } from "./Clause";
27-
import { Union } from "./Union";
2827
import { WithCreate } from "./mixins/clauses/WithCreate";
2928
import { WithMatch } from "./mixins/clauses/WithMatch";
3029
import { WithMerge } from "./mixins/clauses/WithMerge";
@@ -34,8 +33,6 @@ import { WithWith } from "./mixins/clauses/WithWith";
3433
import { WithDelete } from "./mixins/sub-clauses/WithDelete";
3534
import { WithOrder } from "./mixins/sub-clauses/WithOrder";
3635
import { WithSetRemove } from "./mixins/sub-clauses/WithSetRemove";
37-
import { ImportWith } from "./sub-clauses/ImportWith";
38-
import { CompositeClause } from "./utils/concat";
3936
import { mixin } from "./utils/mixin";
4037

4138
export interface Call
@@ -69,7 +66,6 @@ export type CallInTransactionOptions = {
6966
@mixin(WithReturn, WithWith, WithUnwind, WithDelete, WithSetRemove, WithMatch, WithCreate, WithMerge, WithOrder)
7067
export class Call extends Clause {
7168
private readonly subquery: CypherASTNode;
72-
private _importWith?: ImportWith;
7369
private inTransactionsConfig?: CallInTransactionOptions;
7470
private readonly variableScope?: Variable[] | "*";
7571
private _optional: boolean = false;
@@ -82,24 +78,6 @@ export class Call extends Clause {
8278
this.variableScope = variableScope;
8379
}
8480

85-
/** Adds a `WITH` statement inside `CALL {`, this `WITH` can is used to import variables outside of the subquery
86-
* @see {@link https://neo4j.com/docs/cypher-manual/current/subqueries/call-subquery/#call-importing-variables | Cypher Documentation}
87-
* @deprecated Use constructor parameter `variableScope` instead
88-
*/
89-
public importWith(...params: Array<Variable | "*">): this {
90-
if (this._importWith) {
91-
throw new Error(`Call import "WITH" already set`);
92-
}
93-
if (this.variableScope) {
94-
throw new Error(`Call import cannot be used along with scope clauses "Call (<variable>)"`);
95-
}
96-
if (params.length > 0) {
97-
this._importWith = new ImportWith(this, [...params]);
98-
this.addChildren(this._importWith);
99-
}
100-
return this;
101-
}
102-
10381
public inTransactions(config: CallInTransactionOptions = {}): this {
10482
this.inTransactionsConfig = config;
10583
return this;
@@ -116,30 +94,19 @@ export class Call extends Clause {
11694

11795
/** @internal */
11896
public getCypher(env: CypherEnvironment): string {
119-
const importWithCypher = compileCypherIfExists(this._importWith, env, { suffix: "\n" });
120-
121-
const subQueryStr = this.getSubqueryCypher(env, importWithCypher);
97+
const subQueryStr = this.subquery.getCypher(env);
12298

12399
const setCypher = this.compileSetCypher(env);
124100
const deleteCypher = compileCypherIfExists(this.deleteClause, env, { prefix: "\n" });
125101
const orderByCypher = compileCypherIfExists(this.orderByStatement, env, { prefix: "\n" });
126102
const inTransactionCypher = this.generateInTransactionsStr();
127103

128-
const inCallBlock = `${importWithCypher}${subQueryStr}`;
129104
const variableScopeStr = this.generateVariableScopeStr(env);
130105
const nextClause = this.compileNextClause(env);
131106

132107
const optionalStr = this._optional ? "OPTIONAL " : "";
133108

134-
return `${optionalStr}CALL${variableScopeStr} {\n${padBlock(inCallBlock)}\n}${inTransactionCypher}${setCypher}${deleteCypher}${orderByCypher}${nextClause}`;
135-
}
136-
137-
private getSubqueryCypher(env: CypherEnvironment, importWithCypher: string | undefined): string {
138-
// This ensures the import with is added to all the union subqueries
139-
if (this.subquery instanceof Union || this.subquery instanceof CompositeClause) {
140-
return this.subquery.getCypher(env, importWithCypher);
141-
}
142-
return this.subquery.getCypher(env);
109+
return `${optionalStr}CALL${variableScopeStr} {\n${padBlock(subQueryStr)}\n}${inTransactionCypher}${setCypher}${deleteCypher}${orderByCypher}${nextClause}`;
143110
}
144111

145112
private generateVariableScopeStr(env: CypherEnvironment): string {

src/clauses/Union.test.ts

Lines changed: 0 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -89,93 +89,4 @@ describe("CypherBuilder UNION", () => {
8989
RETURN this3 AS var1"
9090
`);
9191
});
92-
93-
test("Union in CALL statement with import with", () => {
94-
const returnVar = new Cypher.Variable();
95-
const n1 = new Cypher.Node();
96-
const query1 = new Cypher.Match(new Cypher.Pattern(n1, { labels: ["Movie"] })).return([n1, returnVar]);
97-
const n2 = new Cypher.Node();
98-
const query2 = new Cypher.Match(new Cypher.Pattern(n2, { labels: ["Movie"] })).return([n2, returnVar]);
99-
const n3 = new Cypher.Node();
100-
const query3 = new Cypher.Match(new Cypher.Pattern(n3, { labels: ["Movie"] })).return([n3, returnVar]);
101-
102-
const unionQuery = new Cypher.Union(query1, query2, query3);
103-
const callQuery = new Cypher.Call(unionQuery).importWith(new Cypher.Variable());
104-
const queryResult = callQuery.build();
105-
expect(queryResult.cypher).toMatchInlineSnapshot(`
106-
"CALL {
107-
WITH var0
108-
MATCH (this1:Movie)
109-
RETURN this1 AS var2
110-
UNION
111-
WITH var0
112-
MATCH (this3:Movie)
113-
RETURN this3 AS var2
114-
UNION
115-
WITH var0
116-
MATCH (this4:Movie)
117-
RETURN this4 AS var2
118-
}"
119-
`);
120-
expect(queryResult.params).toMatchInlineSnapshot(`{}`);
121-
});
122-
test("Union in concat in CALL statement with import with", () => {
123-
const returnVar = new Cypher.Variable();
124-
const n1 = new Cypher.Node();
125-
const query1 = new Cypher.Match(new Cypher.Pattern(n1, { labels: ["Movie"] })).return([n1, returnVar]);
126-
const n2 = new Cypher.Node();
127-
const query2 = new Cypher.Match(new Cypher.Pattern(n2, { labels: ["Movie"] })).return([n2, returnVar]);
128-
const n3 = new Cypher.Node();
129-
const query3 = new Cypher.Match(new Cypher.Pattern(n3, { labels: ["Movie"] })).return([n3, returnVar]);
130-
131-
const unionQuery = new Cypher.Union(query1, query2, query3);
132-
const callQuery = new Cypher.Call(Cypher.utils.concat(unionQuery)).importWith(new Cypher.Variable());
133-
const queryResult = callQuery.build();
134-
expect(queryResult.cypher).toMatchInlineSnapshot(`
135-
"CALL {
136-
WITH var0
137-
MATCH (this1:Movie)
138-
RETURN this1 AS var2
139-
UNION
140-
WITH var0
141-
MATCH (this3:Movie)
142-
RETURN this3 AS var2
143-
UNION
144-
WITH var0
145-
MATCH (this4:Movie)
146-
RETURN this4 AS var2
147-
}"
148-
`);
149-
expect(queryResult.params).toMatchInlineSnapshot(`{}`);
150-
});
151-
152-
test("Union in nested CALL statement should not append top import with", () => {
153-
const returnVar = new Cypher.Variable();
154-
const n1 = new Cypher.Node();
155-
const query1 = new Cypher.Match(new Cypher.Pattern(n1, { labels: ["Movie"] })).return([n1, returnVar]);
156-
const n2 = new Cypher.Node();
157-
const query2 = new Cypher.Match(new Cypher.Pattern(n2, { labels: ["Movie"] })).return([n2, returnVar]);
158-
const n3 = new Cypher.Node();
159-
const query3 = new Cypher.Match(new Cypher.Pattern(n3, { labels: ["Movie"] })).return([n3, returnVar]);
160-
161-
const unionQuery = new Cypher.Union(query1, query2, query3);
162-
const callQuery = new Cypher.Call(new Cypher.Call(unionQuery)).importWith(new Cypher.Variable());
163-
const queryResult = callQuery.build();
164-
expect(queryResult.cypher).toMatchInlineSnapshot(`
165-
"CALL {
166-
WITH var0
167-
CALL {
168-
MATCH (this1:Movie)
169-
RETURN this1 AS var2
170-
UNION
171-
MATCH (this3:Movie)
172-
RETURN this3 AS var2
173-
UNION
174-
MATCH (this4:Movie)
175-
RETURN this4 AS var2
176-
}
177-
}"
178-
`);
179-
expect(queryResult.params).toMatchInlineSnapshot(`{}`);
180-
});
18192
});

src/clauses/Union.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ export class Union extends Clause {
5454
* If importWithCypher is provided, it will be added at the beginning of each subquery except first
5555
* @internal
5656
*/
57-
public getCypher(env: CypherEnvironment, importWithCypher?: string): string {
57+
public getCypher(env: CypherEnvironment): string {
5858
const subqueriesStr = this.subqueries.map((s) => s.getCypher(env));
5959
const unionTypeStr = this.unionType ? ` ${this.unionType}` : "";
6060

61-
return subqueriesStr.join(`\nUNION${unionTypeStr}\n${importWithCypher ?? ""}`);
61+
return subqueriesStr.join(`\nUNION${unionTypeStr}\n`);
6262
}
6363
}

0 commit comments

Comments
 (0)