flowquery 1.0.26 → 1.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowquery",
3
- "version": "1.0.26",
3
+ "version": "1.0.27",
4
4
  "description": "A declarative query language for data processing pipelines.",
5
5
  "main": "dist/index.node.js",
6
6
  "types": "dist/index.node.d.ts",
@@ -3,9 +3,9 @@ import Identifier from "./identifier";
3
3
 
4
4
  /**
5
5
  * Represents a reference to a previously defined variable or expression.
6
- *
6
+ *
7
7
  * References point to values defined earlier in the query (e.g., in WITH or LOAD statements).
8
- *
8
+ *
9
9
  * @example
10
10
  * ```typescript
11
11
  * const ref = new Reference("myVar", previousNode);
@@ -14,10 +14,10 @@ import Identifier from "./identifier";
14
14
  */
15
15
  class Reference extends Identifier {
16
16
  private _referred: ASTNode | undefined = undefined;
17
-
17
+
18
18
  /**
19
19
  * Creates a new Reference to a variable.
20
- *
20
+ *
21
21
  * @param value - The identifier name
22
22
  * @param referred - The node this reference points to (optional)
23
23
  */
@@ -25,6 +25,9 @@ class Reference extends Identifier {
25
25
  super(value);
26
26
  this._referred = referred;
27
27
  }
28
+ public get referred(): ASTNode | undefined {
29
+ return this._referred;
30
+ }
28
31
  public set referred(node: ASTNode) {
29
32
  this._referred = node;
30
33
  }
@@ -39,4 +42,4 @@ class Reference extends Identifier {
39
42
  }
40
43
  }
41
44
 
42
- export default Reference;
45
+ export default Reference;
@@ -438,8 +438,15 @@ class Parser extends BaseParser {
438
438
  node.identifier = identifier;
439
439
  this.variables.set(identifier, node);
440
440
  } else if (identifier !== null) {
441
- const reference = this.variables.get(identifier);
442
- if (reference === undefined || reference.constructor !== Node) {
441
+ let reference = this.variables.get(identifier);
442
+ // Resolve through Expression -> Reference -> Node (e.g., after WITH)
443
+ if (reference instanceof Expression && reference.firstChild() instanceof Reference) {
444
+ const inner = (reference.firstChild() as Reference).referred;
445
+ if (inner instanceof Node) {
446
+ reference = inner;
447
+ }
448
+ }
449
+ if (reference === undefined || !(reference instanceof Node)) {
443
450
  throw new Error(`Undefined node reference: ${identifier}`);
444
451
  }
445
452
  node = new NodeReference(node, reference);
@@ -629,8 +636,15 @@ class Parser extends BaseParser {
629
636
  relationship.identifier = variable;
630
637
  this.variables.set(variable, relationship);
631
638
  } else if (variable !== null) {
632
- const reference = this.variables.get(variable);
633
- if (reference === undefined || reference.constructor !== Relationship) {
639
+ let reference = this.variables.get(variable);
640
+ // Resolve through Expression -> Reference -> Relationship (e.g., after WITH)
641
+ if (reference instanceof Expression && reference.firstChild() instanceof Reference) {
642
+ const inner = (reference.firstChild() as Reference).referred;
643
+ if (inner instanceof Relationship) {
644
+ reference = inner;
645
+ }
646
+ }
647
+ if (reference === undefined || !(reference instanceof Relationship)) {
634
648
  throw new Error(`Undefined relationship reference: ${variable}`);
635
649
  }
636
650
  relationship = new RelationshipReference(relationship, reference);
@@ -1556,3 +1556,49 @@ test("Test reserved keywords as relationship types and labels", async () => {
1556
1556
  expect(results.length).toBe(1);
1557
1557
  expect(results[0]).toEqual({ name1: "Node 1", name2: "Node 2" });
1558
1558
  });
1559
+
1560
+ test("Test match with node reference passed through WITH", async () => {
1561
+ await new Runner(`
1562
+ CREATE VIRTUAL (:User) AS {
1563
+ UNWIND [
1564
+ {id: 1, name: 'Alice', mail: 'alice@test.com', jobTitle: 'CEO'},
1565
+ {id: 2, name: 'Bob', mail: 'bob@test.com', jobTitle: 'VP'},
1566
+ {id: 3, name: 'Carol', mail: 'carol@test.com', jobTitle: 'VP'},
1567
+ {id: 4, name: 'Dave', mail: 'dave@test.com', jobTitle: 'Engineer'}
1568
+ ] AS record
1569
+ RETURN record.id AS id, record.name AS name, record.mail AS mail, record.jobTitle AS jobTitle
1570
+ }
1571
+ `).run();
1572
+ await new Runner(`
1573
+ CREATE VIRTUAL (:User)-[:MANAGES]-(:User) AS {
1574
+ UNWIND [
1575
+ {left_id: 1, right_id: 2},
1576
+ {left_id: 1, right_id: 3},
1577
+ {left_id: 2, right_id: 4}
1578
+ ] AS record
1579
+ RETURN record.left_id AS left_id, record.right_id AS right_id
1580
+ }
1581
+ `).run();
1582
+ // Equivalent to:
1583
+ // MATCH (ceo:User)-[:MANAGES]->(dr1:User)
1584
+ // WHERE ceo.jobTitle = 'CEO'
1585
+ // WITH ceo, dr1
1586
+ // MATCH (ceo)-[:MANAGES]->(dr2:User)
1587
+ // WHERE dr1.mail <> dr2.mail
1588
+ // RETURN ceo, dr1, dr2
1589
+ const match = new Runner(`
1590
+ MATCH (ceo:User)-[:MANAGES]->(dr1:User)
1591
+ WHERE ceo.jobTitle = 'CEO'
1592
+ WITH ceo, dr1
1593
+ MATCH (ceo)-[:MANAGES]->(dr2:User)
1594
+ WHERE dr1.mail <> dr2.mail
1595
+ RETURN ceo.name AS ceo, dr1.name AS dr1, dr2.name AS dr2
1596
+ `);
1597
+ await match.run();
1598
+ const results = match.results;
1599
+ // CEO (Alice) manages Bob and Carol. All distinct pairs:
1600
+ // (Alice, Bob, Carol) and (Alice, Carol, Bob)
1601
+ expect(results.length).toBe(2);
1602
+ expect(results[0]).toEqual({ ceo: "Alice", dr1: "Bob", dr2: "Carol" });
1603
+ expect(results[1]).toEqual({ ceo: "Alice", dr1: "Carol", dr2: "Bob" });
1604
+ });