@nicia-ai/typegraph 0.11.1 → 0.13.0

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/dist/index.cjs CHANGED
@@ -311,72 +311,6 @@ var core = {
311
311
  implies
312
312
  };
313
313
 
314
- // src/query/compiler/passes/recursive.ts
315
- function runRecursiveTraversalSelectionPass(ast) {
316
- const variableLengthTraversal = ast.traversals.find(
317
- (traversal) => traversal.variableLength !== void 0
318
- );
319
- if (variableLengthTraversal === void 0) {
320
- throw new chunk44SXEVF4_cjs.CompilerInvariantError("No variable-length traversal found");
321
- }
322
- if (ast.traversals.length > 1) {
323
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
324
- "Variable-length traversals with multiple traversals are not yet supported. Please use a single variable-length traversal."
325
- );
326
- }
327
- return variableLengthTraversal;
328
- }
329
-
330
- // src/query/compiler/passes/runner.ts
331
- function runCompilerPass(state, pass) {
332
- const output = pass.execute(state);
333
- return {
334
- state: pass.update(state, output)
335
- };
336
- }
337
- function compileTemporalFilter(options) {
338
- const { mode, asOf, tableAlias, currentTimestamp } = options;
339
- const prefix = tableAlias ? drizzleOrm.sql.raw(`${tableAlias}.`) : drizzleOrm.sql.raw("");
340
- const deletedAt = drizzleOrm.sql`${prefix}deleted_at`;
341
- const validFrom = drizzleOrm.sql`${prefix}valid_from`;
342
- const validTo = drizzleOrm.sql`${prefix}valid_to`;
343
- switch (mode) {
344
- case "current": {
345
- const now = currentTimestamp ?? drizzleOrm.sql`CURRENT_TIMESTAMP`;
346
- return drizzleOrm.sql`${deletedAt} IS NULL AND (${validFrom} IS NULL OR ${validFrom} <= ${now}) AND (${validTo} IS NULL OR ${validTo} > ${now})`;
347
- }
348
- case "asOf": {
349
- const timestamp = asOf;
350
- return drizzleOrm.sql`${deletedAt} IS NULL AND (${validFrom} IS NULL OR ${validFrom} <= ${timestamp}) AND (${validTo} IS NULL OR ${validTo} > ${timestamp})`;
351
- }
352
- case "includeEnded": {
353
- return drizzleOrm.sql`${deletedAt} IS NULL`;
354
- }
355
- case "includeTombstones": {
356
- return drizzleOrm.sql.raw("1=1");
357
- }
358
- }
359
- }
360
- function extractTemporalOptions(ast, tableAlias) {
361
- return {
362
- mode: ast.temporalMode.mode,
363
- asOf: ast.temporalMode.asOf,
364
- tableAlias
365
- };
366
- }
367
-
368
- // src/query/compiler/passes/temporal.ts
369
- function createTemporalFilterPass(ast, currentTimestamp) {
370
- return {
371
- forAlias(tableAlias) {
372
- return compileTemporalFilter({
373
- ...extractTemporalOptions(ast, tableAlias),
374
- currentTimestamp
375
- });
376
- }
377
- };
378
- }
379
-
380
314
  // src/query/subquery-utils.ts
381
315
  function normalizeValueType(valueType) {
382
316
  if (valueType === void 0 || valueType === "unknown") {
@@ -1463,1878 +1397,2477 @@ function extractVectorSimilarityPredicates(predicates) {
1463
1397
  return results;
1464
1398
  }
1465
1399
 
1466
- // src/query/compiler/passes/vector.ts
1467
- function runVectorPredicatePass(ast, dialect) {
1468
- const vectorPredicates = extractVectorSimilarityPredicates(ast.predicates);
1469
- if (vectorPredicates.length > 1) {
1470
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
1471
- "Multiple vector similarity predicates in a single query are not supported"
1400
+ // src/query/compiler/predicate-utils.ts
1401
+ var EMPTY_PREDICATES = [];
1402
+ function buildPredicateIndexKey(alias, targetType) {
1403
+ return `${alias}\0${targetType}`;
1404
+ }
1405
+ function resolvePredicateTargetType(predicate2) {
1406
+ return predicate2.targetType === "edge" ? "edge" : "node";
1407
+ }
1408
+ function buildPredicateIndex(ast) {
1409
+ const byAliasAndType = /* @__PURE__ */ new Map();
1410
+ for (const predicate2 of ast.predicates) {
1411
+ const key = buildPredicateIndexKey(
1412
+ predicate2.targetAlias,
1413
+ resolvePredicateTargetType(predicate2)
1472
1414
  );
1415
+ const existing = byAliasAndType.get(key);
1416
+ if (existing === void 0) {
1417
+ byAliasAndType.set(key, [predicate2]);
1418
+ } else {
1419
+ existing.push(predicate2);
1420
+ }
1473
1421
  }
1474
- const vectorPredicate = vectorPredicates[0];
1475
- if (vectorPredicate === void 0) {
1476
- return { vectorPredicate: void 0 };
1422
+ return { byAliasAndType };
1423
+ }
1424
+ function getPredicatesForAlias(predicateIndex, alias, targetType) {
1425
+ return predicateIndex.byAliasAndType.get(
1426
+ buildPredicateIndexKey(alias, targetType)
1427
+ ) ?? EMPTY_PREDICATES;
1428
+ }
1429
+ function compilePredicateClauses(predicates, predicateContext) {
1430
+ return predicates.map(
1431
+ (predicate2) => compilePredicateExpression(predicate2.expression, predicateContext)
1432
+ );
1433
+ }
1434
+ function compileKindFilter(column, kinds) {
1435
+ if (kinds.length === 0) {
1436
+ return drizzleOrm.sql`1 = 0`;
1477
1437
  }
1478
- const vectorStrategy = dialect.capabilities.vectorPredicateStrategy;
1479
- if (vectorStrategy === "unsupported" || !dialect.supportsVectors) {
1480
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
1481
- `Vector similarity predicates are not supported for dialect "${dialect.name}"`
1482
- );
1438
+ if (kinds.length === 1) {
1439
+ return drizzleOrm.sql`${column} = ${kinds[0]}`;
1483
1440
  }
1484
- if (!dialect.capabilities.vectorMetrics.includes(vectorPredicate.metric)) {
1485
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
1486
- `Vector metric "${vectorPredicate.metric}" is not supported for dialect "${dialect.name}"`
1487
- );
1441
+ return drizzleOrm.sql`${column} IN (${drizzleOrm.sql.join(
1442
+ kinds.map((kind) => drizzleOrm.sql`${kind}`),
1443
+ drizzleOrm.sql`, `
1444
+ )})`;
1445
+ }
1446
+ function getNodeKindsForAlias(ast, alias) {
1447
+ if (alias === ast.start.alias) {
1448
+ return ast.start.kinds;
1488
1449
  }
1489
- if (!Number.isFinite(vectorPredicate.limit) || vectorPredicate.limit <= 0) {
1490
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
1491
- `Vector predicate limit must be a positive finite number, got ${String(vectorPredicate.limit)}`
1492
- );
1450
+ for (const traversal of ast.traversals) {
1451
+ if (traversal.nodeAlias === alias) {
1452
+ return traversal.nodeKinds;
1453
+ }
1493
1454
  }
1494
- const { minScore } = vectorPredicate;
1495
- if (minScore !== void 0) {
1496
- if (!Number.isFinite(minScore)) {
1497
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
1498
- `Vector minScore must be a finite number, got ${String(minScore)}`
1499
- );
1455
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(`Unknown traversal source alias: ${alias}`);
1456
+ }
1457
+
1458
+ // src/query/compiler/emitter/plan-inspector.ts
1459
+ function collectPlanOperations(node, ops) {
1460
+ ops.add(node.op);
1461
+ switch (node.op) {
1462
+ case "aggregate":
1463
+ case "filter":
1464
+ case "join":
1465
+ case "limit_offset":
1466
+ case "project":
1467
+ case "recursive_expand":
1468
+ case "sort":
1469
+ case "vector_knn": {
1470
+ collectPlanOperations(node.input, ops);
1471
+ return;
1500
1472
  }
1501
- if (vectorPredicate.metric === "cosine" && (minScore < -1 || minScore > 1)) {
1502
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
1503
- `Cosine minScore must be between -1 and 1, got ${String(minScore)}`
1504
- );
1473
+ case "set_op": {
1474
+ collectPlanOperations(node.left, ops);
1475
+ collectPlanOperations(node.right, ops);
1476
+ return;
1477
+ }
1478
+ case "scan": {
1479
+ return;
1505
1480
  }
1506
1481
  }
1507
- return { vectorPredicate };
1508
1482
  }
1509
- function resolveVectorAwareLimit(astLimit, vectorPredicate) {
1510
- if (vectorPredicate === void 0) {
1511
- return astLimit;
1512
- }
1513
- if (astLimit === void 0) {
1514
- return vectorPredicate.limit;
1483
+ function findUnaryNodeInProjectChain(rootNode, op) {
1484
+ let currentNode = rootNode.input;
1485
+ for (; ; ) {
1486
+ if (currentNode.op === op) {
1487
+ return currentNode;
1488
+ }
1489
+ switch (currentNode.op) {
1490
+ case "aggregate":
1491
+ case "filter":
1492
+ case "join":
1493
+ case "limit_offset":
1494
+ case "recursive_expand":
1495
+ case "sort":
1496
+ case "vector_knn": {
1497
+ currentNode = currentNode.input;
1498
+ continue;
1499
+ }
1500
+ case "project":
1501
+ case "scan":
1502
+ case "set_op": {
1503
+ return void 0;
1504
+ }
1505
+ }
1515
1506
  }
1516
- return Math.min(astLimit, vectorPredicate.limit);
1517
1507
  }
1518
-
1519
- // src/query/compiler/plan/lowering.ts
1520
- function createPlanNodeIdFactory() {
1521
- let current = 0;
1522
- return function nextPlanNodeId() {
1523
- current += 1;
1524
- return `plan_${current.toString(36)}`;
1508
+ function inspectProjectPlan(logicalPlan) {
1509
+ if (logicalPlan.root.op !== "project") {
1510
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1511
+ `SQL emitter expected logical plan root to be "project", got "${logicalPlan.root.op}"`,
1512
+ { component: "plan-inspector" }
1513
+ );
1514
+ }
1515
+ const operations = /* @__PURE__ */ new Set();
1516
+ collectPlanOperations(logicalPlan.root, operations);
1517
+ const limitOffsetNode = findUnaryNodeInProjectChain(
1518
+ logicalPlan.root,
1519
+ "limit_offset"
1520
+ );
1521
+ const sortNode = findUnaryNodeInProjectChain(
1522
+ logicalPlan.root,
1523
+ "sort"
1524
+ );
1525
+ return {
1526
+ hasAggregate: operations.has("aggregate"),
1527
+ hasLimitOffset: operations.has("limit_offset"),
1528
+ hasRecursiveExpand: operations.has("recursive_expand"),
1529
+ hasSetOperation: operations.has("set_op"),
1530
+ hasSort: operations.has("sort"),
1531
+ hasVectorKnn: operations.has("vector_knn"),
1532
+ limitOffsetNode,
1533
+ rootProjectNode: logicalPlan.root,
1534
+ sortNode
1525
1535
  };
1526
1536
  }
1527
- function extractAggregateExpressions(ast) {
1528
- const aggregates = [];
1529
- for (const field2 of ast.projection.fields) {
1530
- if ("__type" in field2.source && field2.source.__type === "aggregate") {
1531
- aggregates.push(field2.source);
1532
- }
1537
+ function inspectStandardProjectPlan(logicalPlan) {
1538
+ const shape = inspectProjectPlan(logicalPlan);
1539
+ if (shape.hasSetOperation) {
1540
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1541
+ 'Standard SQL emitter does not support plans containing "set_op" nodes',
1542
+ { component: "plan-inspector" }
1543
+ );
1533
1544
  }
1534
- return aggregates;
1545
+ if (shape.hasRecursiveExpand) {
1546
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1547
+ 'Standard SQL emitter does not support plans containing "recursive_expand" nodes',
1548
+ { component: "plan-inspector" }
1549
+ );
1550
+ }
1551
+ return shape;
1535
1552
  }
1536
- function getAliasPredicates(ast, alias, predicateTargetType) {
1537
- return ast.predicates.filter((predicate2) => {
1538
- const targetType = predicate2.targetType ?? "node";
1539
- return predicate2.targetAlias === alias && targetType === predicateTargetType;
1540
- });
1553
+ function inspectRecursiveProjectPlan(logicalPlan) {
1554
+ const shape = inspectProjectPlan(logicalPlan);
1555
+ if (!shape.hasRecursiveExpand) {
1556
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1557
+ 'Recursive SQL emitter expected logical plan to contain a "recursive_expand" node',
1558
+ { component: "plan-inspector" }
1559
+ );
1560
+ }
1561
+ if (shape.hasSetOperation) {
1562
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1563
+ 'Recursive SQL emitter does not support plans containing "set_op" nodes',
1564
+ { component: "plan-inspector" }
1565
+ );
1566
+ }
1567
+ return shape;
1541
1568
  }
1542
- function wrapWithAliasFilterNode(currentNode, ast, alias, predicateTargetType, nextPlanNodeId) {
1543
- const aliasPredicates = getAliasPredicates(ast, alias, predicateTargetType);
1544
- if (aliasPredicates.length === 0) {
1545
- return currentNode;
1569
+ function findTopLevelLimitOffsetNode(rootNode) {
1570
+ let currentNode = rootNode;
1571
+ for (; ; ) {
1572
+ if (currentNode.op === "limit_offset") {
1573
+ return currentNode;
1574
+ }
1575
+ switch (currentNode.op) {
1576
+ case "aggregate":
1577
+ case "filter":
1578
+ case "join":
1579
+ case "project":
1580
+ case "recursive_expand":
1581
+ case "sort":
1582
+ case "vector_knn": {
1583
+ currentNode = currentNode.input;
1584
+ continue;
1585
+ }
1586
+ case "scan":
1587
+ case "set_op": {
1588
+ return void 0;
1589
+ }
1590
+ }
1591
+ }
1592
+ }
1593
+ function findTopLevelSortNode(rootNode) {
1594
+ let currentNode = rootNode;
1595
+ for (; ; ) {
1596
+ if (currentNode.op === "sort") {
1597
+ return currentNode;
1598
+ }
1599
+ switch (currentNode.op) {
1600
+ case "aggregate":
1601
+ case "filter":
1602
+ case "join":
1603
+ case "limit_offset":
1604
+ case "project":
1605
+ case "recursive_expand":
1606
+ case "vector_knn": {
1607
+ currentNode = currentNode.input;
1608
+ continue;
1609
+ }
1610
+ case "scan":
1611
+ case "set_op": {
1612
+ return void 0;
1613
+ }
1614
+ }
1615
+ }
1616
+ }
1617
+ function inspectSetOperationPlan(logicalPlan) {
1618
+ const operations = /* @__PURE__ */ new Set();
1619
+ collectPlanOperations(logicalPlan.root, operations);
1620
+ if (!operations.has("set_op")) {
1621
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1622
+ 'Set-operation SQL emitter expected logical plan to contain a "set_op" node',
1623
+ { component: "plan-inspector" }
1624
+ );
1546
1625
  }
1626
+ const limitOffsetNode = findTopLevelLimitOffsetNode(logicalPlan.root);
1627
+ const sortNode = findTopLevelSortNode(logicalPlan.root);
1547
1628
  return {
1548
- alias,
1549
- id: nextPlanNodeId(),
1550
- input: currentNode,
1551
- op: "filter",
1552
- predicateTargetType,
1553
- predicates: aliasPredicates.map((predicate2) => predicate2.expression)
1629
+ hasLimitOffset: limitOffsetNode !== void 0,
1630
+ hasSetOperation: true,
1631
+ hasSort: sortNode !== void 0,
1632
+ limitOffsetNode,
1633
+ sortNode
1554
1634
  };
1555
1635
  }
1556
- function appendAggregateSortLimitAndProjectNodes(currentNode, ast, nextPlanNodeId, limit, collapsedTraversalCteAlias) {
1557
- let node = currentNode;
1558
- const aggregateExpressions = extractAggregateExpressions(ast);
1559
- if (aggregateExpressions.length > 0 || ast.groupBy !== void 0 || ast.having !== void 0) {
1560
- const aggregateNode = {
1561
- aggregates: aggregateExpressions,
1562
- groupBy: ast.groupBy?.fields ?? [],
1563
- id: nextPlanNodeId(),
1564
- input: node,
1565
- op: "aggregate"
1566
- };
1567
- node = ast.having === void 0 ? aggregateNode : { ...aggregateNode, having: ast.having };
1636
+ function assertRecursiveEmitterClauseAlignment(logicalPlan, input) {
1637
+ const planShape = inspectRecursiveProjectPlan(logicalPlan);
1638
+ if (planShape.hasSort && input.orderBy === void 0) {
1639
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1640
+ "Recursive SQL emitter expected ORDER BY clause for plan containing a sort node",
1641
+ { component: "recursive-emitter" }
1642
+ );
1568
1643
  }
1569
- if (ast.orderBy !== void 0 && ast.orderBy.length > 0) {
1570
- node = {
1571
- id: nextPlanNodeId(),
1572
- input: node,
1573
- op: "sort",
1574
- orderBy: ast.orderBy
1575
- };
1644
+ if (!planShape.hasSort && input.orderBy !== void 0) {
1645
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1646
+ "Recursive SQL emitter received ORDER BY clause for a plan without sort nodes",
1647
+ { component: "recursive-emitter" }
1648
+ );
1576
1649
  }
1577
- if (limit !== void 0 || ast.offset !== void 0) {
1578
- const limitOffsetNodeBase = {
1579
- id: nextPlanNodeId(),
1580
- input: node,
1581
- op: "limit_offset"
1582
- };
1583
- const hasLimit = limit !== void 0;
1584
- const hasOffset = ast.offset !== void 0;
1585
- if (hasLimit && hasOffset) {
1586
- node = {
1587
- ...limitOffsetNodeBase,
1588
- limit,
1589
- offset: ast.offset
1590
- };
1591
- } else if (hasLimit) {
1592
- node = { ...limitOffsetNodeBase, limit };
1593
- } else if (hasOffset) {
1594
- node = { ...limitOffsetNodeBase, offset: ast.offset };
1595
- } else {
1650
+ if (planShape.hasLimitOffset && input.limitOffset === void 0) {
1651
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1652
+ "Recursive SQL emitter expected LIMIT/OFFSET clause for plan containing a limit_offset node",
1653
+ { component: "recursive-emitter" }
1654
+ );
1655
+ }
1656
+ if (!planShape.hasLimitOffset && input.limitOffset !== void 0) {
1657
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1658
+ "Recursive SQL emitter received LIMIT/OFFSET clause for a plan without limit_offset nodes",
1659
+ { component: "recursive-emitter" }
1660
+ );
1661
+ }
1662
+ }
1663
+ function emitRecursiveQuerySql(input) {
1664
+ assertRecursiveEmitterClauseAlignment(input.logicalPlan, input);
1665
+ const parts = [
1666
+ drizzleOrm.sql`WITH RECURSIVE`,
1667
+ input.recursiveCte,
1668
+ drizzleOrm.sql`SELECT ${input.projection}`,
1669
+ drizzleOrm.sql`FROM recursive_cte`,
1670
+ input.depthFilter
1671
+ ];
1672
+ if (input.orderBy !== void 0) {
1673
+ parts.push(input.orderBy);
1674
+ }
1675
+ if (input.limitOffset !== void 0) {
1676
+ parts.push(input.limitOffset);
1677
+ }
1678
+ return drizzleOrm.sql.join(parts, drizzleOrm.sql` `);
1679
+ }
1680
+ function assertSetOperationEmitterClauseAlignment(logicalPlan, suffixClauses) {
1681
+ const shape = inspectSetOperationPlan(logicalPlan);
1682
+ const hasSuffixClauses = suffixClauses !== void 0 && suffixClauses.length > 0;
1683
+ if (!shape.hasSort && !shape.hasLimitOffset && hasSuffixClauses) {
1684
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1685
+ "Set-operation SQL emitter received suffix clauses for a plan without top-level sort or limit_offset nodes",
1686
+ { component: "set-operation-emitter" }
1687
+ );
1688
+ }
1689
+ if (!hasSuffixClauses) {
1690
+ if (shape.hasSort || shape.hasLimitOffset) {
1596
1691
  throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1597
- "limit_offset node requires limit or offset to be present"
1692
+ "Set-operation SQL emitter expected suffix clauses for plan containing top-level sort or limit_offset nodes",
1693
+ { component: "set-operation-emitter" }
1598
1694
  );
1599
1695
  }
1696
+ return;
1600
1697
  }
1601
- const projectNodeBase = {
1602
- fields: ast.projection.fields,
1603
- id: nextPlanNodeId(),
1604
- input: node,
1605
- op: "project"
1606
- };
1607
- return collapsedTraversalCteAlias === void 0 ? projectNodeBase : {
1608
- ...projectNodeBase,
1609
- collapsedTraversalAlias: collapsedTraversalCteAlias
1610
- };
1611
- }
1612
- function lowerStandardQueryToLogicalPlanNode(input) {
1613
- const { ast, nextPlanNodeId } = input;
1614
- let currentNode = {
1615
- alias: ast.start.alias,
1616
- graphId: input.graphId,
1617
- id: nextPlanNodeId(),
1618
- kinds: ast.start.kinds,
1619
- op: "scan",
1620
- source: "nodes"
1621
- };
1622
- currentNode = wrapWithAliasFilterNode(
1623
- currentNode,
1624
- ast,
1625
- ast.start.alias,
1626
- "node",
1627
- nextPlanNodeId
1628
- );
1629
- for (const traversal of ast.traversals) {
1630
- currentNode = {
1631
- direction: traversal.direction,
1632
- edgeAlias: traversal.edgeAlias,
1633
- edgeKinds: traversal.edgeKinds,
1634
- id: nextPlanNodeId(),
1635
- input: currentNode,
1636
- inverseEdgeKinds: traversal.inverseEdgeKinds ?? [],
1637
- joinFromAlias: traversal.joinFromAlias,
1638
- joinType: traversal.optional ? "left" : "inner",
1639
- nodeAlias: traversal.nodeAlias,
1640
- nodeKinds: traversal.nodeKinds,
1641
- op: "join"
1642
- };
1643
- currentNode = wrapWithAliasFilterNode(
1644
- currentNode,
1645
- ast,
1646
- traversal.edgeAlias,
1647
- "edge",
1648
- nextPlanNodeId
1649
- );
1650
- currentNode = wrapWithAliasFilterNode(
1651
- currentNode,
1652
- ast,
1653
- traversal.nodeAlias,
1654
- "node",
1655
- nextPlanNodeId
1698
+ const limitOffsetClauseCount = shape.limitOffsetNode === void 0 ? 0 : (shape.limitOffsetNode.limit === void 0 ? 0 : 1) + (shape.limitOffsetNode.offset === void 0 ? 0 : 1);
1699
+ const expectedClauseCount = (shape.sortNode === void 0 ? 0 : 1) + limitOffsetClauseCount;
1700
+ if (suffixClauses.length !== expectedClauseCount) {
1701
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1702
+ `Set-operation SQL emitter expected ${String(expectedClauseCount)} top-level suffix clause(s) from logical plan, got ${String(suffixClauses.length)}`,
1703
+ { component: "set-operation-emitter" }
1656
1704
  );
1657
1705
  }
1658
- if (input.vectorPredicate !== void 0) {
1659
- currentNode = {
1660
- id: nextPlanNodeId(),
1661
- input: currentNode,
1662
- op: "vector_knn",
1663
- predicate: input.vectorPredicate
1664
- };
1665
- }
1666
- return appendAggregateSortLimitAndProjectNodes(
1667
- currentNode,
1668
- ast,
1669
- nextPlanNodeId,
1670
- input.effectiveLimit,
1671
- input.collapsedTraversalCteAlias
1672
- );
1673
1706
  }
1674
- function lowerRecursiveQueryToLogicalPlanNode(input) {
1675
- const { ast, nextPlanNodeId } = input;
1676
- const traversal = input.traversal ?? runRecursiveTraversalSelectionPass(input.ast);
1677
- let currentNode = {
1678
- alias: ast.start.alias,
1679
- graphId: input.graphId,
1680
- id: nextPlanNodeId(),
1681
- kinds: ast.start.kinds,
1682
- op: "scan",
1683
- source: "nodes"
1684
- };
1685
- currentNode = wrapWithAliasFilterNode(
1686
- currentNode,
1687
- ast,
1688
- ast.start.alias,
1689
- "node",
1690
- nextPlanNodeId
1691
- );
1692
- currentNode = {
1693
- edgeAlias: traversal.edgeAlias,
1694
- edgeKinds: traversal.edgeKinds,
1695
- id: nextPlanNodeId(),
1696
- input: currentNode,
1697
- inverseEdgeKinds: traversal.inverseEdgeKinds ?? [],
1698
- nodeAlias: traversal.nodeAlias,
1699
- nodeKinds: traversal.nodeKinds,
1700
- op: "recursive_expand",
1701
- traversal: traversal.variableLength
1702
- };
1703
- currentNode = wrapWithAliasFilterNode(
1704
- currentNode,
1705
- ast,
1706
- traversal.edgeAlias,
1707
- "edge",
1708
- nextPlanNodeId
1709
- );
1710
- currentNode = wrapWithAliasFilterNode(
1711
- currentNode,
1712
- ast,
1713
- traversal.nodeAlias,
1714
- "node",
1715
- nextPlanNodeId
1716
- );
1717
- return appendAggregateSortLimitAndProjectNodes(
1718
- currentNode,
1719
- ast,
1720
- nextPlanNodeId,
1721
- ast.limit
1707
+ function emitSetOperationQuerySql(input) {
1708
+ assertSetOperationEmitterClauseAlignment(
1709
+ input.logicalPlan,
1710
+ input.suffixClauses
1722
1711
  );
1712
+ const parts = [];
1713
+ if (input.ctes !== void 0 && input.ctes.length > 0) {
1714
+ parts.push(drizzleOrm.sql`WITH ${drizzleOrm.sql.join([...input.ctes], drizzleOrm.sql`, `)}`);
1715
+ }
1716
+ parts.push(input.baseQuery);
1717
+ if (input.suffixClauses !== void 0 && input.suffixClauses.length > 0) {
1718
+ parts.push(...input.suffixClauses);
1719
+ }
1720
+ return drizzleOrm.sql.join(parts, drizzleOrm.sql` `);
1723
1721
  }
1724
- function lowerComposableQueryToLogicalPlanNode(query, dialect, graphId, nextPlanNodeId) {
1725
- if ("__type" in query) {
1726
- return lowerSetOperationToLogicalPlanNode(
1727
- query,
1728
- graphId,
1729
- dialect,
1730
- nextPlanNodeId
1722
+ function assertStandardEmitterClauseAlignment(logicalPlan, input) {
1723
+ const planShape = inspectStandardProjectPlan(logicalPlan);
1724
+ if (input.groupBy !== void 0 && !planShape.hasAggregate) {
1725
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1726
+ "Standard SQL emitter received GROUP BY clause for a plan without aggregate nodes",
1727
+ { component: "standard-emitter" }
1731
1728
  );
1732
1729
  }
1733
- const hasVariableLengthTraversal2 = query.traversals.some(
1734
- (traversal) => traversal.variableLength !== void 0
1735
- );
1736
- if (hasVariableLengthTraversal2) {
1737
- return lowerRecursiveQueryToLogicalPlanNode({
1738
- ast: query,
1739
- graphId,
1740
- nextPlanNodeId
1741
- });
1730
+ if (input.having !== void 0 && !planShape.hasAggregate) {
1731
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1732
+ "Standard SQL emitter received HAVING clause for a plan without aggregate nodes",
1733
+ { component: "standard-emitter" }
1734
+ );
1742
1735
  }
1743
- const vectorPredicate = runVectorPredicatePass(
1744
- query,
1745
- chunk2WVFEIHR_cjs.getDialect(dialect)
1746
- ).vectorPredicate;
1747
- const effectiveLimit = resolveVectorAwareLimit(query.limit, vectorPredicate);
1748
- const loweringInput = {
1749
- ast: query,
1750
- graphId,
1751
- nextPlanNodeId,
1752
- ...effectiveLimit === void 0 ? {} : { effectiveLimit },
1753
- ...vectorPredicate === void 0 ? {} : { vectorPredicate }
1754
- };
1755
- return lowerStandardQueryToLogicalPlanNode(loweringInput);
1756
- }
1757
- function lowerSetOperationToLogicalPlanNode(op, graphId, dialect, nextPlanNodeId) {
1758
- let currentNode = {
1759
- id: nextPlanNodeId(),
1760
- left: lowerComposableQueryToLogicalPlanNode(
1761
- op.left,
1762
- dialect,
1763
- graphId,
1764
- nextPlanNodeId
1765
- ),
1766
- op: "set_op",
1767
- operator: op.operator,
1768
- right: lowerComposableQueryToLogicalPlanNode(
1769
- op.right,
1770
- dialect,
1771
- graphId,
1772
- nextPlanNodeId
1773
- )
1774
- };
1775
- if (op.orderBy !== void 0 && op.orderBy.length > 0) {
1776
- currentNode = {
1777
- id: nextPlanNodeId(),
1778
- input: currentNode,
1779
- op: "sort",
1780
- orderBy: op.orderBy
1781
- };
1736
+ const expectsOrderBy = planShape.hasSort || planShape.hasVectorKnn;
1737
+ if (expectsOrderBy && input.orderBy === void 0) {
1738
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1739
+ "Standard SQL emitter expected ORDER BY clause for plan containing a sort or vector_knn node",
1740
+ { component: "standard-emitter" }
1741
+ );
1782
1742
  }
1783
- if (op.limit !== void 0 || op.offset !== void 0) {
1784
- const limitOffsetBase = {
1785
- id: nextPlanNodeId(),
1786
- input: currentNode,
1787
- op: "limit_offset"
1788
- };
1789
- if (op.limit !== void 0 && op.offset !== void 0) {
1790
- currentNode = { ...limitOffsetBase, limit: op.limit, offset: op.offset };
1791
- } else if (op.limit === void 0) {
1792
- currentNode = { ...limitOffsetBase, offset: op.offset };
1793
- } else {
1794
- currentNode = { ...limitOffsetBase, limit: op.limit };
1795
- }
1743
+ if (!expectsOrderBy && input.orderBy !== void 0) {
1744
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1745
+ "Standard SQL emitter received ORDER BY clause for a plan without sort or vector_knn nodes",
1746
+ { component: "standard-emitter" }
1747
+ );
1748
+ }
1749
+ if (planShape.hasLimitOffset && input.limitOffset === void 0) {
1750
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1751
+ "Standard SQL emitter expected LIMIT/OFFSET clause for plan containing a limit_offset node",
1752
+ { component: "standard-emitter" }
1753
+ );
1754
+ }
1755
+ if (!planShape.hasLimitOffset && input.limitOffset !== void 0) {
1756
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1757
+ "Standard SQL emitter received LIMIT/OFFSET clause for a plan without limit_offset nodes",
1758
+ { component: "standard-emitter" }
1759
+ );
1796
1760
  }
1797
- return currentNode;
1798
- }
1799
- function lowerStandardQueryToLogicalPlan(input) {
1800
- const nextPlanNodeId = createPlanNodeIdFactory();
1801
- return {
1802
- metadata: {
1803
- dialect: input.dialect,
1804
- graphId: input.graphId
1805
- },
1806
- root: lowerStandardQueryToLogicalPlanNode({
1807
- ...input,
1808
- nextPlanNodeId
1809
- })
1810
- };
1811
- }
1812
- function lowerRecursiveQueryToLogicalPlan(input) {
1813
- const nextPlanNodeId = createPlanNodeIdFactory();
1814
- return {
1815
- metadata: {
1816
- dialect: input.dialect,
1817
- graphId: input.graphId
1818
- },
1819
- root: lowerRecursiveQueryToLogicalPlanNode({
1820
- ...input,
1821
- nextPlanNodeId
1822
- })
1823
- };
1824
1761
  }
1825
- function lowerSetOperationToLogicalPlan(input) {
1826
- const nextPlanNodeId = createPlanNodeIdFactory();
1827
- return {
1828
- metadata: {
1829
- dialect: input.dialect,
1830
- graphId: input.graphId
1831
- },
1832
- root: lowerSetOperationToLogicalPlanNode(
1833
- input.op,
1834
- input.graphId,
1835
- input.dialect,
1836
- nextPlanNodeId
1837
- )
1838
- };
1762
+ function emitStandardQuerySql(input) {
1763
+ assertStandardEmitterClauseAlignment(input.logicalPlan, input);
1764
+ const parts = [];
1765
+ if (input.ctes.length > 0) {
1766
+ parts.push(drizzleOrm.sql`WITH ${drizzleOrm.sql.join([...input.ctes], drizzleOrm.sql`, `)}`);
1767
+ }
1768
+ parts.push(drizzleOrm.sql`SELECT ${input.projection}`, input.fromClause);
1769
+ if (input.groupBy !== void 0) {
1770
+ parts.push(input.groupBy);
1771
+ }
1772
+ if (input.having !== void 0) {
1773
+ parts.push(input.having);
1774
+ }
1775
+ if (input.orderBy !== void 0) {
1776
+ parts.push(input.orderBy);
1777
+ }
1778
+ if (input.limitOffset !== void 0) {
1779
+ parts.push(input.limitOffset);
1780
+ }
1781
+ return drizzleOrm.sql.join(parts, drizzleOrm.sql` `);
1839
1782
  }
1840
1783
 
1841
- // src/query/compiler/emitter/plan-inspector.ts
1842
- function collectPlanOperations(node, ops) {
1843
- ops.add(node.op);
1844
- switch (node.op) {
1845
- case "aggregate":
1846
- case "filter":
1847
- case "join":
1848
- case "limit_offset":
1849
- case "project":
1850
- case "recursive_expand":
1851
- case "sort":
1852
- case "vector_knn": {
1853
- collectPlanOperations(node.input, ops);
1854
- return;
1784
+ // src/query/compiler/typed-json-extract.ts
1785
+ function compileTypedJsonExtract(input) {
1786
+ const { column, dialect, pointer, valueType } = input;
1787
+ const fallback = input.fallback ?? "json";
1788
+ switch (valueType) {
1789
+ case "string": {
1790
+ return dialect.jsonExtractText(column, pointer);
1855
1791
  }
1856
- case "set_op": {
1857
- collectPlanOperations(node.left, ops);
1858
- collectPlanOperations(node.right, ops);
1859
- return;
1792
+ case "number": {
1793
+ return dialect.jsonExtractNumber(column, pointer);
1860
1794
  }
1861
- case "scan": {
1862
- return;
1795
+ case "boolean": {
1796
+ return dialect.jsonExtractBoolean(column, pointer);
1863
1797
  }
1864
- }
1865
- }
1866
- function findUnaryNodeInProjectChain(rootNode, op) {
1867
- let currentNode = rootNode.input;
1868
- for (; ; ) {
1869
- if (currentNode.op === op) {
1870
- return currentNode;
1798
+ case "date": {
1799
+ return dialect.jsonExtractDate(column, pointer);
1871
1800
  }
1872
- switch (currentNode.op) {
1873
- case "aggregate":
1874
- case "filter":
1875
- case "join":
1876
- case "limit_offset":
1877
- case "recursive_expand":
1878
- case "sort":
1879
- case "vector_knn": {
1880
- currentNode = currentNode.input;
1881
- continue;
1882
- }
1883
- case "project":
1884
- case "scan":
1885
- case "set_op": {
1886
- return void 0;
1887
- }
1801
+ case "array":
1802
+ case "object":
1803
+ case "embedding":
1804
+ case "unknown":
1805
+ case void 0: {
1806
+ return fallback === "text" ? dialect.jsonExtractText(column, pointer) : dialect.jsonExtract(column, pointer);
1807
+ }
1808
+ default: {
1809
+ return fallback === "text" ? dialect.jsonExtractText(column, pointer) : dialect.jsonExtract(column, pointer);
1888
1810
  }
1889
1811
  }
1890
1812
  }
1891
- function inspectProjectPlan(logicalPlan) {
1892
- if (logicalPlan.root.op !== "project") {
1893
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1894
- `SQL emitter expected logical plan root to be "project", got "${logicalPlan.root.op}"`,
1895
- { component: "plan-inspector" }
1896
- );
1897
- }
1898
- const operations = /* @__PURE__ */ new Set();
1899
- collectPlanOperations(logicalPlan.root, operations);
1900
- const limitOffsetNode = findUnaryNodeInProjectChain(
1901
- logicalPlan.root,
1902
- "limit_offset"
1903
- );
1904
- const sortNode = findUnaryNodeInProjectChain(
1905
- logicalPlan.root,
1906
- "sort"
1907
- );
1908
- return {
1909
- hasAggregate: operations.has("aggregate"),
1910
- hasLimitOffset: operations.has("limit_offset"),
1911
- hasRecursiveExpand: operations.has("recursive_expand"),
1912
- hasSetOperation: operations.has("set_op"),
1913
- hasSort: operations.has("sort"),
1914
- hasVectorKnn: operations.has("vector_knn"),
1915
- limitOffsetNode,
1916
- rootProjectNode: logicalPlan.root,
1917
- sortNode
1918
- };
1813
+ var NODE_COLUMNS = [
1814
+ "id",
1815
+ "kind",
1816
+ "props",
1817
+ "version",
1818
+ "valid_from",
1819
+ "valid_to",
1820
+ "created_at",
1821
+ "updated_at",
1822
+ "deleted_at"
1823
+ ];
1824
+ var EDGE_COLUMNS = [
1825
+ "id",
1826
+ "kind",
1827
+ "from_id",
1828
+ "to_id",
1829
+ "props",
1830
+ "valid_from",
1831
+ "valid_to",
1832
+ "created_at",
1833
+ "updated_at",
1834
+ "deleted_at"
1835
+ ];
1836
+ var EMPTY_REQUIRED_COLUMNS = /* @__PURE__ */ new Set();
1837
+ function quoteIdentifier(identifier) {
1838
+ return drizzleOrm.sql.raw(`"${identifier.replaceAll('"', '""')}"`);
1919
1839
  }
1920
- function inspectStandardProjectPlan(logicalPlan) {
1921
- const shape = inspectProjectPlan(logicalPlan);
1922
- if (shape.hasSetOperation) {
1923
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1924
- 'Standard SQL emitter does not support plans containing "set_op" nodes',
1925
- { component: "plan-inspector" }
1926
- );
1927
- }
1928
- if (shape.hasRecursiveExpand) {
1929
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1930
- 'Standard SQL emitter does not support plans containing "recursive_expand" nodes',
1931
- { component: "plan-inspector" }
1932
- );
1933
- }
1934
- return shape;
1840
+ function shouldProjectColumn(requiredColumns, column, alwaysRequiredColumns) {
1841
+ if (alwaysRequiredColumns?.has(column)) return true;
1842
+ if (requiredColumns === void 0) return true;
1843
+ return requiredColumns.has(column);
1935
1844
  }
1936
- function inspectRecursiveProjectPlan(logicalPlan) {
1937
- const shape = inspectProjectPlan(logicalPlan);
1938
- if (!shape.hasRecursiveExpand) {
1939
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1940
- 'Recursive SQL emitter expected logical plan to contain a "recursive_expand" node',
1941
- { component: "plan-inspector" }
1942
- );
1943
- }
1944
- if (shape.hasSetOperation) {
1945
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1946
- 'Recursive SQL emitter does not support plans containing "set_op" nodes',
1947
- { component: "plan-inspector" }
1948
- );
1845
+ function addRequiredColumn(requiredColumnsByAlias, alias, column) {
1846
+ const existing = requiredColumnsByAlias.get(alias);
1847
+ if (existing) {
1848
+ existing.add(column);
1849
+ return;
1949
1850
  }
1950
- return shape;
1851
+ requiredColumnsByAlias.set(alias, /* @__PURE__ */ new Set([column]));
1951
1852
  }
1952
- function findTopLevelLimitOffsetNode(rootNode) {
1953
- let currentNode = rootNode;
1954
- for (; ; ) {
1955
- if (currentNode.op === "limit_offset") {
1956
- return currentNode;
1957
- }
1958
- switch (currentNode.op) {
1959
- case "aggregate":
1960
- case "filter":
1961
- case "join":
1962
- case "project":
1963
- case "recursive_expand":
1964
- case "sort":
1965
- case "vector_knn": {
1966
- currentNode = currentNode.input;
1967
- continue;
1968
- }
1969
- case "scan":
1970
- case "set_op": {
1971
- return void 0;
1972
- }
1973
- }
1974
- }
1853
+ function markFieldRefAsRequired(requiredColumnsByAlias, field2) {
1854
+ const column = field2.path[0];
1855
+ if (column === void 0) return;
1856
+ addRequiredColumn(requiredColumnsByAlias, field2.alias, column);
1975
1857
  }
1976
- function findTopLevelSortNode(rootNode) {
1977
- let currentNode = rootNode;
1978
- for (; ; ) {
1979
- if (currentNode.op === "sort") {
1980
- return currentNode;
1981
- }
1982
- switch (currentNode.op) {
1983
- case "aggregate":
1984
- case "filter":
1985
- case "join":
1986
- case "limit_offset":
1987
- case "project":
1988
- case "recursive_expand":
1989
- case "vector_knn": {
1990
- currentNode = currentNode.input;
1991
- continue;
1992
- }
1993
- case "scan":
1994
- case "set_op": {
1995
- return void 0;
1996
- }
1997
- }
1858
+ function mapSelectiveSystemFieldToColumn(field2) {
1859
+ if (field2 === "fromId") return "from_id";
1860
+ if (field2 === "toId") return "to_id";
1861
+ if (field2.startsWith("meta.")) {
1862
+ return field2.slice(5).replaceAll(/([A-Z])/g, "_$1").toLowerCase();
1998
1863
  }
1864
+ return field2;
1999
1865
  }
2000
- function inspectSetOperationPlan(logicalPlan) {
2001
- const operations = /* @__PURE__ */ new Set();
2002
- collectPlanOperations(logicalPlan.root, operations);
2003
- if (!operations.has("set_op")) {
2004
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2005
- 'Set-operation SQL emitter expected logical plan to contain a "set_op" node',
2006
- { component: "plan-inspector" }
1866
+ function markSelectiveFieldAsRequired(requiredColumnsByAlias, field2) {
1867
+ if (field2.isSystemField) {
1868
+ addRequiredColumn(
1869
+ requiredColumnsByAlias,
1870
+ field2.alias,
1871
+ mapSelectiveSystemFieldToColumn(field2.field)
2007
1872
  );
1873
+ return;
2008
1874
  }
2009
- const limitOffsetNode = findTopLevelLimitOffsetNode(logicalPlan.root);
2010
- const sortNode = findTopLevelSortNode(logicalPlan.root);
2011
- return {
2012
- hasLimitOffset: limitOffsetNode !== void 0,
2013
- hasSetOperation: true,
2014
- hasSort: sortNode !== void 0,
2015
- limitOffsetNode,
2016
- sortNode
2017
- };
1875
+ addRequiredColumn(requiredColumnsByAlias, field2.alias, "props");
2018
1876
  }
2019
- function assertRecursiveEmitterClauseAlignment(logicalPlan, input) {
2020
- const planShape = inspectRecursiveProjectPlan(logicalPlan);
2021
- if (planShape.hasSort && input.orderBy === void 0) {
2022
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2023
- "Recursive SQL emitter expected ORDER BY clause for plan containing a sort node",
2024
- { component: "recursive-emitter" }
2025
- );
2026
- }
2027
- if (!planShape.hasSort && input.orderBy !== void 0) {
2028
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2029
- "Recursive SQL emitter received ORDER BY clause for a plan without sort nodes",
2030
- { component: "recursive-emitter" }
2031
- );
2032
- }
2033
- if (planShape.hasLimitOffset && input.limitOffset === void 0) {
2034
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2035
- "Recursive SQL emitter expected LIMIT/OFFSET clause for plan containing a limit_offset node",
2036
- { component: "recursive-emitter" }
2037
- );
2038
- }
2039
- if (!planShape.hasLimitOffset && input.limitOffset !== void 0) {
2040
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2041
- "Recursive SQL emitter received LIMIT/OFFSET clause for a plan without limit_offset nodes",
2042
- { component: "recursive-emitter" }
2043
- );
1877
+ function isIdFieldRef(field2) {
1878
+ return field2.path.length === 1 && field2.path[0] === "id" && field2.jsonPointer === void 0;
1879
+ }
1880
+ function isAggregateExpr(source) {
1881
+ return "__type" in source && source.__type === "aggregate";
1882
+ }
1883
+
1884
+ // src/query/compiler/emitter/standard-builders.ts
1885
+ function compileColumnReference(tableAlias, column) {
1886
+ if (tableAlias === void 0) {
1887
+ return drizzleOrm.sql.raw(column);
2044
1888
  }
1889
+ return drizzleOrm.sql`${drizzleOrm.sql.raw(tableAlias)}.${drizzleOrm.sql.raw(column)}`;
2045
1890
  }
2046
- function emitRecursiveQuerySql(input) {
2047
- assertRecursiveEmitterClauseAlignment(input.logicalPlan, input);
2048
- const parts = [
2049
- drizzleOrm.sql`WITH RECURSIVE`,
2050
- input.recursiveCte,
2051
- drizzleOrm.sql`SELECT ${input.projection}`,
2052
- drizzleOrm.sql`FROM recursive_cte`,
2053
- input.depthFilter
1891
+ function compileNodeSelectColumns(tableAlias, alias, requiredColumns) {
1892
+ return NODE_COLUMNS.filter(
1893
+ (column) => column === "id" || column === "kind" || shouldProjectColumn(requiredColumns, column)
1894
+ ).map(
1895
+ (column) => drizzleOrm.sql`${compileColumnReference(tableAlias, column)} AS ${drizzleOrm.sql.raw(`${alias}_${column}`)}`
1896
+ );
1897
+ }
1898
+ function compileEdgeSelectColumns(tableAlias, alias, requiredColumns) {
1899
+ return EDGE_COLUMNS.filter(
1900
+ (column) => shouldProjectColumn(requiredColumns, column)
1901
+ ).map(
1902
+ (column) => drizzleOrm.sql`${compileColumnReference(tableAlias, column)} AS ${drizzleOrm.sql.raw(`${alias}_${column}`)}`
1903
+ );
1904
+ }
1905
+ function buildStandardStartCte(input) {
1906
+ const { ast, ctx, graphId, predicateIndex, requiredColumnsByAlias } = input;
1907
+ const alias = ast.start.alias;
1908
+ const kinds = ast.start.kinds;
1909
+ const kindFilter = compileKindFilter(drizzleOrm.sql.raw("kind"), kinds);
1910
+ const temporalFilter = input.temporalFilterPass.forAlias();
1911
+ const cteContext = { ...ctx, cteColumnPrefix: "" };
1912
+ const predicateClauses = compilePredicateClauses(
1913
+ getPredicatesForAlias(predicateIndex, alias, "node"),
1914
+ cteContext
1915
+ );
1916
+ const whereClauses = [
1917
+ drizzleOrm.sql`graph_id = ${graphId}`,
1918
+ kindFilter,
1919
+ temporalFilter,
1920
+ ...predicateClauses
1921
+ ];
1922
+ const effectiveRequiredColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(alias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
1923
+ return drizzleOrm.sql`
1924
+ cte_${drizzleOrm.sql.raw(alias)} AS (
1925
+ SELECT ${drizzleOrm.sql.join(
1926
+ compileNodeSelectColumns(void 0, alias, effectiveRequiredColumns),
1927
+ drizzleOrm.sql`, `
1928
+ )}
1929
+ FROM ${ctx.schema.nodesTable}
1930
+ WHERE ${drizzleOrm.sql.join(whereClauses, drizzleOrm.sql` AND `)}
1931
+ )
1932
+ `;
1933
+ }
1934
+ function buildStandardTraversalCte(input) {
1935
+ const {
1936
+ ast,
1937
+ carryForwardPreviousColumns,
1938
+ ctx,
1939
+ graphId,
1940
+ materializeCte,
1941
+ predicateIndex,
1942
+ requiredColumnsByAlias,
1943
+ temporalFilterPass,
1944
+ traversalIndex,
1945
+ traversalLimit
1946
+ } = input;
1947
+ const traversal = ast.traversals[traversalIndex];
1948
+ const traversalLimitValue = traversalIndex === ast.traversals.length - 1 ? traversalLimit : void 0;
1949
+ const previousNodeKinds = getNodeKindsForAlias(ast, traversal.joinFromAlias);
1950
+ const directEdgeKinds = [...new Set(traversal.edgeKinds)];
1951
+ const inverseEdgeKinds = traversal.inverseEdgeKinds === void 0 ? [] : [...new Set(traversal.inverseEdgeKinds)];
1952
+ const nodeKinds = traversal.nodeKinds;
1953
+ const nodeKindFilter = compileKindFilter(drizzleOrm.sql.raw("n.kind"), nodeKinds);
1954
+ const edgeTemporalFilter = temporalFilterPass.forAlias("e");
1955
+ const nodeTemporalFilter = temporalFilterPass.forAlias("n");
1956
+ const nodeCteContext = {
1957
+ ...ctx,
1958
+ cteColumnPrefix: "n"
1959
+ };
1960
+ const nodePredicateClauses = compilePredicateClauses(
1961
+ getPredicatesForAlias(predicateIndex, traversal.nodeAlias, "node"),
1962
+ nodeCteContext
1963
+ );
1964
+ const edgeCteContext = {
1965
+ ...ctx,
1966
+ cteColumnPrefix: "e"
1967
+ };
1968
+ const edgePredicateClauses = compilePredicateClauses(
1969
+ getPredicatesForAlias(predicateIndex, traversal.edgeAlias, "edge"),
1970
+ edgeCteContext
1971
+ );
1972
+ const baseWhereClauses = [
1973
+ drizzleOrm.sql`e.graph_id = ${graphId}`,
1974
+ nodeKindFilter,
1975
+ edgeTemporalFilter,
1976
+ nodeTemporalFilter,
1977
+ ...nodePredicateClauses,
1978
+ ...edgePredicateClauses
1979
+ ];
1980
+ const previousAlias = traversal.joinFromAlias;
1981
+ const edgeAlias = traversal.edgeAlias;
1982
+ const nodeAlias = traversal.nodeAlias;
1983
+ const requiredNodeColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(nodeAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
1984
+ const requiredEdgeColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(edgeAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
1985
+ const previousRowColumns = carryForwardPreviousColumns ? [drizzleOrm.sql`cte_${drizzleOrm.sql.raw(previousAlias)}.*`] : [
1986
+ drizzleOrm.sql`cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id AS ${drizzleOrm.sql.raw(previousAlias)}_id`,
1987
+ drizzleOrm.sql`cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind AS ${drizzleOrm.sql.raw(previousAlias)}_kind`
1988
+ ];
1989
+ const selectColumns = [
1990
+ ...previousRowColumns,
1991
+ ...compileEdgeSelectColumns("e", edgeAlias, requiredEdgeColumns),
1992
+ ...compileNodeSelectColumns("n", nodeAlias, requiredNodeColumns)
1993
+ ];
1994
+ const cteMaterialization = materializeCte ? drizzleOrm.sql`MATERIALIZED ` : drizzleOrm.sql``;
1995
+ function compileTraversalBranch(branch) {
1996
+ const whereClauses = [
1997
+ ...baseWhereClauses,
1998
+ compileKindFilter(drizzleOrm.sql.raw("e.kind"), branch.edgeKinds),
1999
+ compileKindFilter(
2000
+ drizzleOrm.sql.raw(`e.${branch.joinKindField}`),
2001
+ previousNodeKinds
2002
+ ),
2003
+ compileKindFilter(drizzleOrm.sql.raw(`e.${branch.targetKindField}`), nodeKinds)
2004
+ ];
2005
+ if (branch.duplicateGuard !== void 0) {
2006
+ whereClauses.push(branch.duplicateGuard);
2007
+ }
2008
+ return drizzleOrm.sql`
2009
+ SELECT ${drizzleOrm.sql.join(selectColumns, drizzleOrm.sql`, `)}
2010
+ FROM cte_${drizzleOrm.sql.raw(previousAlias)}
2011
+ JOIN ${ctx.schema.edgesTable} e ON cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id = e.${drizzleOrm.sql.raw(branch.joinField)}
2012
+ AND cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind = e.${drizzleOrm.sql.raw(branch.joinKindField)}
2013
+ JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
2014
+ AND n.id = e.${drizzleOrm.sql.raw(branch.targetField)}
2015
+ AND n.kind = e.${drizzleOrm.sql.raw(branch.targetKindField)}
2016
+ WHERE ${drizzleOrm.sql.join(whereClauses, drizzleOrm.sql` AND `)}
2017
+ `;
2018
+ }
2019
+ const directJoinField = traversal.direction === "out" ? "from_id" : "to_id";
2020
+ const directTargetField = traversal.direction === "out" ? "to_id" : "from_id";
2021
+ const directJoinKindField = traversal.direction === "out" ? "from_kind" : "to_kind";
2022
+ const directTargetKindField = traversal.direction === "out" ? "to_kind" : "from_kind";
2023
+ const directBranch = compileTraversalBranch({
2024
+ edgeKinds: directEdgeKinds,
2025
+ joinField: directJoinField,
2026
+ joinKindField: directJoinKindField,
2027
+ targetField: directTargetField,
2028
+ targetKindField: directTargetKindField
2029
+ });
2030
+ if (inverseEdgeKinds.length === 0) {
2031
+ if (traversalLimitValue !== void 0) {
2032
+ return drizzleOrm.sql`
2033
+ cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2034
+ SELECT * FROM (
2035
+ ${directBranch}
2036
+ ) AS traversal_rows
2037
+ LIMIT ${traversalLimitValue}
2038
+ )
2039
+ `;
2040
+ }
2041
+ return drizzleOrm.sql`
2042
+ cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2043
+ ${directBranch}
2044
+ )
2045
+ `;
2046
+ }
2047
+ const inverseJoinField = traversal.direction === "out" ? "to_id" : "from_id";
2048
+ const inverseTargetField = traversal.direction === "out" ? "from_id" : "to_id";
2049
+ const inverseJoinKindField = traversal.direction === "out" ? "to_kind" : "from_kind";
2050
+ const inverseTargetKindField = traversal.direction === "out" ? "from_kind" : "to_kind";
2051
+ const overlappingKinds = inverseEdgeKinds.filter(
2052
+ (kind) => directEdgeKinds.includes(kind)
2053
+ );
2054
+ const duplicateGuard = overlappingKinds.length > 0 ? drizzleOrm.sql`NOT (e.from_id = e.to_id AND ${compileKindFilter(
2055
+ drizzleOrm.sql.raw("e.kind"),
2056
+ overlappingKinds
2057
+ )})` : void 0;
2058
+ const inverseBranch = compileTraversalBranch({
2059
+ duplicateGuard,
2060
+ edgeKinds: inverseEdgeKinds,
2061
+ joinField: inverseJoinField,
2062
+ joinKindField: inverseJoinKindField,
2063
+ targetField: inverseTargetField,
2064
+ targetKindField: inverseTargetKindField
2065
+ });
2066
+ if (traversalLimitValue !== void 0) {
2067
+ return drizzleOrm.sql`
2068
+ cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2069
+ SELECT * FROM (
2070
+ ${directBranch}
2071
+ UNION ALL
2072
+ ${inverseBranch}
2073
+ ) AS traversal_rows
2074
+ LIMIT ${traversalLimitValue}
2075
+ )
2076
+ `;
2077
+ }
2078
+ return drizzleOrm.sql`
2079
+ cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2080
+ ${directBranch}
2081
+ UNION ALL
2082
+ ${inverseBranch}
2083
+ )
2084
+ `;
2085
+ }
2086
+ function compileAggregateExprFromSource(expr, dialect) {
2087
+ const { field: field2 } = expr;
2088
+ const fn = expr.function;
2089
+ switch (fn) {
2090
+ case "count":
2091
+ case "countDistinct":
2092
+ case "sum":
2093
+ case "avg":
2094
+ case "min":
2095
+ case "max": {
2096
+ const cteAlias = `cte_${field2.alias}`;
2097
+ const column = compileFieldValue(
2098
+ field2,
2099
+ dialect,
2100
+ field2.valueType,
2101
+ cteAlias
2102
+ );
2103
+ if (fn === "countDistinct") {
2104
+ return drizzleOrm.sql`COUNT(DISTINCT ${column})`;
2105
+ }
2106
+ return drizzleOrm.sql`${drizzleOrm.sql.raw(fn.toUpperCase())}(${column})`;
2107
+ }
2108
+ default: {
2109
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2110
+ `Unknown aggregate function: ${String(fn)}`
2111
+ );
2112
+ }
2113
+ }
2114
+ }
2115
+ function compileProjectedSource(field2, dialect) {
2116
+ if (isAggregateExpr(field2.source)) {
2117
+ return compileAggregateExprFromSource(field2.source, dialect);
2118
+ }
2119
+ const cteAlias = field2.cteAlias ?? `cte_${field2.source.alias}`;
2120
+ return compileFieldValue(
2121
+ field2.source,
2122
+ dialect,
2123
+ field2.source.valueType,
2124
+ cteAlias
2125
+ );
2126
+ }
2127
+ function buildStandardProjection(input) {
2128
+ const { ast, collapsedTraversalCteAlias, dialect } = input;
2129
+ if (ast.selectiveFields && ast.selectiveFields.length > 0) {
2130
+ return compileSelectiveProjection(
2131
+ ast.selectiveFields,
2132
+ dialect,
2133
+ ast,
2134
+ collapsedTraversalCteAlias
2135
+ );
2136
+ }
2137
+ const fields = ast.projection.fields;
2138
+ if (fields.length === 0) {
2139
+ return drizzleOrm.sql.raw("*");
2140
+ }
2141
+ const projectedFields = fields.map((field2) => {
2142
+ const source = compileProjectedSource(field2, dialect);
2143
+ return drizzleOrm.sql`${source} AS ${quoteIdentifier(field2.outputName)}`;
2144
+ });
2145
+ return drizzleOrm.sql.join(projectedFields, drizzleOrm.sql`, `);
2146
+ }
2147
+ function compileSelectiveProjection(fields, dialect, ast, collapsedTraversalCteAlias) {
2148
+ const aliasToCte = /* @__PURE__ */ new Map([
2149
+ [ast.start.alias, `cte_${ast.start.alias}`]
2150
+ ]);
2151
+ for (const traversal of ast.traversals) {
2152
+ aliasToCte.set(traversal.nodeAlias, `cte_${traversal.nodeAlias}`);
2153
+ aliasToCte.set(traversal.edgeAlias, `cte_${traversal.nodeAlias}`);
2154
+ }
2155
+ const columns = fields.map((field2) => {
2156
+ const cteAlias = collapsedTraversalCteAlias ?? aliasToCte.get(field2.alias) ?? `cte_${field2.alias}`;
2157
+ if (field2.isSystemField) {
2158
+ const dbColumn = mapSelectiveSystemFieldToColumn(field2.field);
2159
+ return drizzleOrm.sql`${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${quoteIdentifier(field2.outputName)}`;
2160
+ }
2161
+ const propsColumn = `${field2.alias}_props`;
2162
+ const column = drizzleOrm.sql`${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(propsColumn)}`;
2163
+ const pointer = chunkP5CNM325_cjs.jsonPointer([field2.field]);
2164
+ const extracted = compileTypedJsonExtract({
2165
+ column,
2166
+ dialect,
2167
+ pointer,
2168
+ valueType: field2.valueType
2169
+ });
2170
+ return drizzleOrm.sql`${extracted} AS ${quoteIdentifier(field2.outputName)}`;
2171
+ });
2172
+ return drizzleOrm.sql.join(columns, drizzleOrm.sql`, `);
2173
+ }
2174
+ function buildStandardFromClause(input) {
2175
+ const { ast, collapsedTraversalCteAlias, vectorPredicate } = input;
2176
+ if (collapsedTraversalCteAlias !== void 0) {
2177
+ return drizzleOrm.sql`FROM ${drizzleOrm.sql.raw(collapsedTraversalCteAlias)}`;
2178
+ }
2179
+ const startAlias = ast.start.alias;
2180
+ const fromClause = drizzleOrm.sql`FROM cte_${drizzleOrm.sql.raw(startAlias)}`;
2181
+ const joins = [];
2182
+ for (const traversal of ast.traversals) {
2183
+ const cteAlias = `cte_${traversal.nodeAlias}`;
2184
+ const previousAlias = traversal.joinFromAlias;
2185
+ const joinType = traversal.optional ? "LEFT JOIN" : "INNER JOIN";
2186
+ joins.push(
2187
+ drizzleOrm.sql`${drizzleOrm.sql.raw(joinType)} ${drizzleOrm.sql.raw(cteAlias)} ON ${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id = cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id AND ${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind = cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind`
2188
+ );
2189
+ }
2190
+ if (vectorPredicate) {
2191
+ const nodeAlias = vectorPredicate.field.alias;
2192
+ joins.push(
2193
+ drizzleOrm.sql`INNER JOIN cte_embeddings ON cte_embeddings.node_id = cte_${drizzleOrm.sql.raw(nodeAlias)}.${drizzleOrm.sql.raw(nodeAlias)}_id`
2194
+ );
2195
+ }
2196
+ return joins.length === 0 ? fromClause : drizzleOrm.sql`${fromClause} ${drizzleOrm.sql.join(joins, drizzleOrm.sql` `)}`;
2197
+ }
2198
+ function buildStandardOrderBy(input) {
2199
+ const { ast, collapsedTraversalCteAlias, dialect } = input;
2200
+ if (!ast.orderBy || ast.orderBy.length === 0) {
2201
+ return void 0;
2202
+ }
2203
+ const parts = [];
2204
+ for (const orderSpec of ast.orderBy) {
2205
+ const valueType = orderSpec.field.valueType;
2206
+ if (valueType === "array" || valueType === "object") {
2207
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2208
+ "Ordering by JSON arrays or objects is not supported"
2209
+ );
2210
+ }
2211
+ const cteAlias = collapsedTraversalCteAlias ?? `cte_${orderSpec.field.alias}`;
2212
+ const field2 = compileFieldValue(
2213
+ orderSpec.field,
2214
+ dialect,
2215
+ valueType,
2216
+ cteAlias
2217
+ );
2218
+ const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
2219
+ const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
2220
+ const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
2221
+ parts.push(
2222
+ drizzleOrm.sql`(${field2} IS NULL) ${nullsDirection}`,
2223
+ drizzleOrm.sql`${field2} ${direction}`
2224
+ );
2225
+ }
2226
+ return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join(parts, drizzleOrm.sql`, `)}`;
2227
+ }
2228
+ function fieldRefKey(field2) {
2229
+ const pointer = field2.jsonPointer ?? "";
2230
+ return `${field2.alias}:${field2.path.join(".")}:${pointer}`;
2231
+ }
2232
+ function buildStandardGroupBy(input) {
2233
+ const { ast, dialect } = input;
2234
+ if (!ast.groupBy || ast.groupBy.fields.length === 0) {
2235
+ return void 0;
2236
+ }
2237
+ const seenKeys = /* @__PURE__ */ new Set();
2238
+ const allFields = [];
2239
+ for (const projectedField of ast.projection.fields) {
2240
+ if (projectedField.source.__type === "field_ref") {
2241
+ const key = fieldRefKey(projectedField.source);
2242
+ if (!seenKeys.has(key)) {
2243
+ seenKeys.add(key);
2244
+ allFields.push(projectedField.source);
2245
+ }
2246
+ }
2247
+ }
2248
+ for (const field2 of ast.groupBy.fields) {
2249
+ const key = fieldRefKey(field2);
2250
+ if (!seenKeys.has(key)) {
2251
+ seenKeys.add(key);
2252
+ allFields.push(field2);
2253
+ }
2254
+ }
2255
+ if (allFields.length === 0) {
2256
+ return void 0;
2257
+ }
2258
+ const parts = allFields.map(
2259
+ (field2) => compileFieldValue(field2, dialect, field2.valueType, `cte_${field2.alias}`)
2260
+ );
2261
+ return drizzleOrm.sql`GROUP BY ${drizzleOrm.sql.join(parts, drizzleOrm.sql`, `)}`;
2262
+ }
2263
+ function buildStandardHaving(input) {
2264
+ const { ast, ctx } = input;
2265
+ if (!ast.having) {
2266
+ return void 0;
2267
+ }
2268
+ const condition = compilePredicateExpression(ast.having, ctx);
2269
+ return drizzleOrm.sql`HAVING ${condition}`;
2270
+ }
2271
+ function buildStandardEmbeddingsCte(input) {
2272
+ const { ctx, graphId, vectorPredicate } = input;
2273
+ const { dialect } = ctx;
2274
+ const { field: field2, metric, minScore, queryEmbedding } = vectorPredicate;
2275
+ const fieldPath = field2.jsonPointer ? field2.jsonPointer : field2.path.length > 1 && field2.path[0] === "props" ? `/${field2.path.slice(1).join("/")}` : `/${field2.path.join("/")}`;
2276
+ const distanceExpr = dialect.vectorDistance(
2277
+ drizzleOrm.sql.raw("embedding"),
2278
+ queryEmbedding,
2279
+ metric
2280
+ );
2281
+ const conditions = [
2282
+ drizzleOrm.sql`graph_id = ${graphId}`,
2283
+ drizzleOrm.sql`field_path = ${fieldPath}`
2054
2284
  ];
2055
- if (input.orderBy !== void 0) {
2056
- parts.push(input.orderBy);
2285
+ if (minScore !== void 0) {
2286
+ conditions.push(
2287
+ compileVectorMinScoreCondition(distanceExpr, metric, minScore)
2288
+ );
2289
+ }
2290
+ const scoreExpr = compileVectorScoreExpression(distanceExpr, metric);
2291
+ return drizzleOrm.sql`
2292
+ cte_embeddings AS (
2293
+ SELECT
2294
+ node_id,
2295
+ ${distanceExpr} AS distance,
2296
+ ${scoreExpr} AS score
2297
+ FROM ${ctx.schema.embeddingsTable}
2298
+ WHERE ${drizzleOrm.sql.join(conditions, drizzleOrm.sql` AND `)}
2299
+ ORDER BY ${distanceExpr} ASC
2300
+ )
2301
+ `;
2302
+ }
2303
+ function compileVectorScoreExpression(distanceExpr, metric) {
2304
+ switch (metric) {
2305
+ case "cosine": {
2306
+ return drizzleOrm.sql`(1.0 - ${distanceExpr})`;
2307
+ }
2308
+ case "l2":
2309
+ case "inner_product": {
2310
+ return distanceExpr;
2311
+ }
2312
+ }
2313
+ }
2314
+ function compileVectorMinScoreCondition(distanceExpr, metric, minScore) {
2315
+ switch (metric) {
2316
+ case "cosine": {
2317
+ const threshold = 1 - minScore;
2318
+ return drizzleOrm.sql`${distanceExpr} <= ${threshold}`;
2319
+ }
2320
+ case "l2": {
2321
+ return drizzleOrm.sql`${distanceExpr} <= ${minScore}`;
2322
+ }
2323
+ case "inner_product": {
2324
+ const negativeThreshold = -minScore;
2325
+ return drizzleOrm.sql`${distanceExpr} <= ${negativeThreshold}`;
2326
+ }
2327
+ }
2328
+ }
2329
+ function buildStandardVectorOrderBy(input) {
2330
+ const { ast, dialect } = input;
2331
+ const distanceOrder = drizzleOrm.sql`cte_embeddings.distance ASC`;
2332
+ const additionalOrders = [];
2333
+ if (ast.orderBy && ast.orderBy.length > 0) {
2334
+ for (const orderSpec of ast.orderBy) {
2335
+ const valueType = orderSpec.field.valueType;
2336
+ if (valueType === "array" || valueType === "object") {
2337
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2338
+ "Ordering by JSON arrays or objects is not supported"
2339
+ );
2340
+ }
2341
+ const cteAlias = `cte_${orderSpec.field.alias}`;
2342
+ const field2 = compileFieldValue(
2343
+ orderSpec.field,
2344
+ dialect,
2345
+ valueType,
2346
+ cteAlias
2347
+ );
2348
+ const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
2349
+ const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
2350
+ const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
2351
+ additionalOrders.push(
2352
+ drizzleOrm.sql`(${field2} IS NULL) ${nullsDirection}`,
2353
+ drizzleOrm.sql`${field2} ${direction}`
2354
+ );
2355
+ }
2356
+ }
2357
+ const allOrders = [distanceOrder, ...additionalOrders];
2358
+ return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join(allOrders, drizzleOrm.sql`, `)}`;
2359
+ }
2360
+ function buildLimitOffsetClause(input) {
2361
+ const { limit, offset } = input;
2362
+ const parts = [];
2363
+ if (limit !== void 0) {
2364
+ parts.push(drizzleOrm.sql`LIMIT ${limit}`);
2365
+ }
2366
+ if (offset !== void 0) {
2367
+ parts.push(drizzleOrm.sql`OFFSET ${offset}`);
2368
+ }
2369
+ return parts.length > 0 ? drizzleOrm.sql.join(parts, drizzleOrm.sql` `) : void 0;
2370
+ }
2371
+
2372
+ // src/query/compiler/passes/recursive.ts
2373
+ function runRecursiveTraversalSelectionPass(ast) {
2374
+ const variableLengthTraversal = ast.traversals.find(
2375
+ (traversal) => traversal.variableLength !== void 0
2376
+ );
2377
+ if (variableLengthTraversal === void 0) {
2378
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError("No variable-length traversal found");
2379
+ }
2380
+ if (ast.traversals.length > 1) {
2381
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2382
+ "Variable-length traversals with multiple traversals are not yet supported. Please use a single variable-length traversal."
2383
+ );
2384
+ }
2385
+ return variableLengthTraversal;
2386
+ }
2387
+
2388
+ // src/query/compiler/passes/runner.ts
2389
+ function runCompilerPass(state, pass) {
2390
+ const output = pass.execute(state);
2391
+ return {
2392
+ state: pass.update(state, output)
2393
+ };
2394
+ }
2395
+ function compileTemporalFilter(options) {
2396
+ const { mode, asOf, tableAlias, currentTimestamp } = options;
2397
+ const prefix = tableAlias ? drizzleOrm.sql.raw(`${tableAlias}.`) : drizzleOrm.sql.raw("");
2398
+ const deletedAt = drizzleOrm.sql`${prefix}deleted_at`;
2399
+ const validFrom = drizzleOrm.sql`${prefix}valid_from`;
2400
+ const validTo = drizzleOrm.sql`${prefix}valid_to`;
2401
+ switch (mode) {
2402
+ case "current": {
2403
+ const now = currentTimestamp ?? drizzleOrm.sql`CURRENT_TIMESTAMP`;
2404
+ return drizzleOrm.sql`${deletedAt} IS NULL AND (${validFrom} IS NULL OR ${validFrom} <= ${now}) AND (${validTo} IS NULL OR ${validTo} > ${now})`;
2405
+ }
2406
+ case "asOf": {
2407
+ const timestamp = asOf;
2408
+ return drizzleOrm.sql`${deletedAt} IS NULL AND (${validFrom} IS NULL OR ${validFrom} <= ${timestamp}) AND (${validTo} IS NULL OR ${validTo} > ${timestamp})`;
2409
+ }
2410
+ case "includeEnded": {
2411
+ return drizzleOrm.sql`${deletedAt} IS NULL`;
2412
+ }
2413
+ case "includeTombstones": {
2414
+ return drizzleOrm.sql.raw("1=1");
2415
+ }
2416
+ }
2417
+ }
2418
+ function extractTemporalOptions(ast, tableAlias) {
2419
+ return {
2420
+ mode: ast.temporalMode.mode,
2421
+ asOf: ast.temporalMode.asOf,
2422
+ tableAlias
2423
+ };
2424
+ }
2425
+
2426
+ // src/query/compiler/passes/temporal.ts
2427
+ function createTemporalFilterPass(ast, currentTimestamp) {
2428
+ return {
2429
+ forAlias(tableAlias) {
2430
+ return compileTemporalFilter({
2431
+ ...extractTemporalOptions(ast, tableAlias),
2432
+ currentTimestamp
2433
+ });
2434
+ }
2435
+ };
2436
+ }
2437
+
2438
+ // src/query/compiler/passes/vector.ts
2439
+ function runVectorPredicatePass(ast, dialect) {
2440
+ const vectorPredicates = extractVectorSimilarityPredicates(ast.predicates);
2441
+ if (vectorPredicates.length > 1) {
2442
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2443
+ "Multiple vector similarity predicates in a single query are not supported"
2444
+ );
2445
+ }
2446
+ const vectorPredicate = vectorPredicates[0];
2447
+ if (vectorPredicate === void 0) {
2448
+ return { vectorPredicate: void 0 };
2449
+ }
2450
+ const vectorStrategy = dialect.capabilities.vectorPredicateStrategy;
2451
+ if (vectorStrategy === "unsupported" || !dialect.supportsVectors) {
2452
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2453
+ `Vector similarity predicates are not supported for dialect "${dialect.name}"`
2454
+ );
2455
+ }
2456
+ if (!dialect.capabilities.vectorMetrics.includes(vectorPredicate.metric)) {
2457
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2458
+ `Vector metric "${vectorPredicate.metric}" is not supported for dialect "${dialect.name}"`
2459
+ );
2460
+ }
2461
+ if (!Number.isFinite(vectorPredicate.limit) || vectorPredicate.limit <= 0) {
2462
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2463
+ `Vector predicate limit must be a positive finite number, got ${String(vectorPredicate.limit)}`
2464
+ );
2465
+ }
2466
+ const { minScore } = vectorPredicate;
2467
+ if (minScore !== void 0) {
2468
+ if (!Number.isFinite(minScore)) {
2469
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2470
+ `Vector minScore must be a finite number, got ${String(minScore)}`
2471
+ );
2472
+ }
2473
+ if (vectorPredicate.metric === "cosine" && (minScore < -1 || minScore > 1)) {
2474
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2475
+ `Cosine minScore must be between -1 and 1, got ${String(minScore)}`
2476
+ );
2477
+ }
2478
+ }
2479
+ return { vectorPredicate };
2480
+ }
2481
+ function resolveVectorAwareLimit(astLimit, vectorPredicate) {
2482
+ if (vectorPredicate === void 0) {
2483
+ return astLimit;
2484
+ }
2485
+ if (astLimit === void 0) {
2486
+ return vectorPredicate.limit;
2487
+ }
2488
+ return Math.min(astLimit, vectorPredicate.limit);
2489
+ }
2490
+
2491
+ // src/query/compiler/plan/lowering.ts
2492
+ function createPlanNodeIdFactory() {
2493
+ let current = 0;
2494
+ return function nextPlanNodeId() {
2495
+ current += 1;
2496
+ return `plan_${current.toString(36)}`;
2497
+ };
2498
+ }
2499
+ function extractAggregateExpressions(ast) {
2500
+ const aggregates = [];
2501
+ for (const field2 of ast.projection.fields) {
2502
+ if ("__type" in field2.source && field2.source.__type === "aggregate") {
2503
+ aggregates.push(field2.source);
2504
+ }
2057
2505
  }
2058
- if (input.limitOffset !== void 0) {
2059
- parts.push(input.limitOffset);
2506
+ return aggregates;
2507
+ }
2508
+ function getAliasPredicates(ast, alias, predicateTargetType) {
2509
+ return ast.predicates.filter((predicate2) => {
2510
+ const targetType = predicate2.targetType ?? "node";
2511
+ return predicate2.targetAlias === alias && targetType === predicateTargetType;
2512
+ });
2513
+ }
2514
+ function wrapWithAliasFilterNode(currentNode, ast, alias, predicateTargetType, nextPlanNodeId) {
2515
+ const aliasPredicates = getAliasPredicates(ast, alias, predicateTargetType);
2516
+ if (aliasPredicates.length === 0) {
2517
+ return currentNode;
2060
2518
  }
2061
- return drizzleOrm.sql.join(parts, drizzleOrm.sql` `);
2519
+ return {
2520
+ alias,
2521
+ id: nextPlanNodeId(),
2522
+ input: currentNode,
2523
+ op: "filter",
2524
+ predicateTargetType,
2525
+ predicates: aliasPredicates.map((predicate2) => predicate2.expression)
2526
+ };
2062
2527
  }
2063
- function assertSetOperationEmitterClauseAlignment(logicalPlan, suffixClauses) {
2064
- const shape = inspectSetOperationPlan(logicalPlan);
2065
- const hasSuffixClauses = suffixClauses !== void 0 && suffixClauses.length > 0;
2066
- if (!shape.hasSort && !shape.hasLimitOffset && hasSuffixClauses) {
2067
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2068
- "Set-operation SQL emitter received suffix clauses for a plan without top-level sort or limit_offset nodes",
2069
- { component: "set-operation-emitter" }
2070
- );
2528
+ function appendAggregateSortLimitAndProjectNodes(currentNode, ast, nextPlanNodeId, limit, collapsedTraversalCteAlias) {
2529
+ let node = currentNode;
2530
+ const aggregateExpressions = extractAggregateExpressions(ast);
2531
+ if (aggregateExpressions.length > 0 || ast.groupBy !== void 0 || ast.having !== void 0) {
2532
+ const aggregateNode = {
2533
+ aggregates: aggregateExpressions,
2534
+ groupBy: ast.groupBy?.fields ?? [],
2535
+ id: nextPlanNodeId(),
2536
+ input: node,
2537
+ op: "aggregate"
2538
+ };
2539
+ node = ast.having === void 0 ? aggregateNode : { ...aggregateNode, having: ast.having };
2071
2540
  }
2072
- if (!hasSuffixClauses) {
2073
- if (shape.hasSort || shape.hasLimitOffset) {
2541
+ if (ast.orderBy !== void 0 && ast.orderBy.length > 0) {
2542
+ node = {
2543
+ id: nextPlanNodeId(),
2544
+ input: node,
2545
+ op: "sort",
2546
+ orderBy: ast.orderBy
2547
+ };
2548
+ }
2549
+ if (limit !== void 0 || ast.offset !== void 0) {
2550
+ const limitOffsetNodeBase = {
2551
+ id: nextPlanNodeId(),
2552
+ input: node,
2553
+ op: "limit_offset"
2554
+ };
2555
+ const hasLimit = limit !== void 0;
2556
+ const hasOffset = ast.offset !== void 0;
2557
+ if (hasLimit && hasOffset) {
2558
+ node = {
2559
+ ...limitOffsetNodeBase,
2560
+ limit,
2561
+ offset: ast.offset
2562
+ };
2563
+ } else if (hasLimit) {
2564
+ node = { ...limitOffsetNodeBase, limit };
2565
+ } else if (hasOffset) {
2566
+ node = { ...limitOffsetNodeBase, offset: ast.offset };
2567
+ } else {
2074
2568
  throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2075
- "Set-operation SQL emitter expected suffix clauses for plan containing top-level sort or limit_offset nodes",
2076
- { component: "set-operation-emitter" }
2569
+ "limit_offset node requires limit or offset to be present"
2077
2570
  );
2078
2571
  }
2079
- return;
2080
- }
2081
- const limitOffsetClauseCount = shape.limitOffsetNode === void 0 ? 0 : (shape.limitOffsetNode.limit === void 0 ? 0 : 1) + (shape.limitOffsetNode.offset === void 0 ? 0 : 1);
2082
- const expectedClauseCount = (shape.sortNode === void 0 ? 0 : 1) + limitOffsetClauseCount;
2083
- if (suffixClauses.length !== expectedClauseCount) {
2084
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2085
- `Set-operation SQL emitter expected ${String(expectedClauseCount)} top-level suffix clause(s) from logical plan, got ${String(suffixClauses.length)}`,
2086
- { component: "set-operation-emitter" }
2087
- );
2088
2572
  }
2573
+ const projectNodeBase = {
2574
+ fields: ast.projection.fields,
2575
+ id: nextPlanNodeId(),
2576
+ input: node,
2577
+ op: "project"
2578
+ };
2579
+ return collapsedTraversalCteAlias === void 0 ? projectNodeBase : {
2580
+ ...projectNodeBase,
2581
+ collapsedTraversalAlias: collapsedTraversalCteAlias
2582
+ };
2089
2583
  }
2090
- function emitSetOperationQuerySql(input) {
2091
- assertSetOperationEmitterClauseAlignment(
2092
- input.logicalPlan,
2093
- input.suffixClauses
2584
+ function lowerStandardQueryToLogicalPlanNode(input) {
2585
+ const { ast, nextPlanNodeId } = input;
2586
+ let currentNode = {
2587
+ alias: ast.start.alias,
2588
+ graphId: input.graphId,
2589
+ id: nextPlanNodeId(),
2590
+ kinds: ast.start.kinds,
2591
+ op: "scan",
2592
+ source: "nodes"
2593
+ };
2594
+ currentNode = wrapWithAliasFilterNode(
2595
+ currentNode,
2596
+ ast,
2597
+ ast.start.alias,
2598
+ "node",
2599
+ nextPlanNodeId
2094
2600
  );
2095
- const parts = [];
2096
- if (input.ctes !== void 0 && input.ctes.length > 0) {
2097
- parts.push(drizzleOrm.sql`WITH ${drizzleOrm.sql.join([...input.ctes], drizzleOrm.sql`, `)}`);
2098
- }
2099
- parts.push(input.baseQuery);
2100
- if (input.suffixClauses !== void 0 && input.suffixClauses.length > 0) {
2101
- parts.push(...input.suffixClauses);
2102
- }
2103
- return drizzleOrm.sql.join(parts, drizzleOrm.sql` `);
2104
- }
2105
- function assertStandardEmitterClauseAlignment(logicalPlan, input) {
2106
- const planShape = inspectStandardProjectPlan(logicalPlan);
2107
- if (input.groupBy !== void 0 && !planShape.hasAggregate) {
2108
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2109
- "Standard SQL emitter received GROUP BY clause for a plan without aggregate nodes",
2110
- { component: "standard-emitter" }
2111
- );
2112
- }
2113
- if (input.having !== void 0 && !planShape.hasAggregate) {
2114
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2115
- "Standard SQL emitter received HAVING clause for a plan without aggregate nodes",
2116
- { component: "standard-emitter" }
2601
+ for (const traversal of ast.traversals) {
2602
+ currentNode = {
2603
+ direction: traversal.direction,
2604
+ edgeAlias: traversal.edgeAlias,
2605
+ edgeKinds: traversal.edgeKinds,
2606
+ id: nextPlanNodeId(),
2607
+ input: currentNode,
2608
+ inverseEdgeKinds: traversal.inverseEdgeKinds ?? [],
2609
+ joinFromAlias: traversal.joinFromAlias,
2610
+ joinType: traversal.optional ? "left" : "inner",
2611
+ nodeAlias: traversal.nodeAlias,
2612
+ nodeKinds: traversal.nodeKinds,
2613
+ op: "join"
2614
+ };
2615
+ currentNode = wrapWithAliasFilterNode(
2616
+ currentNode,
2617
+ ast,
2618
+ traversal.edgeAlias,
2619
+ "edge",
2620
+ nextPlanNodeId
2117
2621
  );
2118
- }
2119
- const expectsOrderBy = planShape.hasSort || planShape.hasVectorKnn;
2120
- if (expectsOrderBy && input.orderBy === void 0) {
2121
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2122
- "Standard SQL emitter expected ORDER BY clause for plan containing a sort or vector_knn node",
2123
- { component: "standard-emitter" }
2622
+ currentNode = wrapWithAliasFilterNode(
2623
+ currentNode,
2624
+ ast,
2625
+ traversal.nodeAlias,
2626
+ "node",
2627
+ nextPlanNodeId
2124
2628
  );
2125
2629
  }
2126
- if (!expectsOrderBy && input.orderBy !== void 0) {
2127
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2128
- "Standard SQL emitter received ORDER BY clause for a plan without sort or vector_knn nodes",
2129
- { component: "standard-emitter" }
2130
- );
2630
+ if (input.vectorPredicate !== void 0) {
2631
+ currentNode = {
2632
+ id: nextPlanNodeId(),
2633
+ input: currentNode,
2634
+ op: "vector_knn",
2635
+ predicate: input.vectorPredicate
2636
+ };
2131
2637
  }
2132
- if (planShape.hasLimitOffset && input.limitOffset === void 0) {
2133
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2134
- "Standard SQL emitter expected LIMIT/OFFSET clause for plan containing a limit_offset node",
2135
- { component: "standard-emitter" }
2638
+ return appendAggregateSortLimitAndProjectNodes(
2639
+ currentNode,
2640
+ ast,
2641
+ nextPlanNodeId,
2642
+ input.effectiveLimit,
2643
+ input.collapsedTraversalCteAlias
2644
+ );
2645
+ }
2646
+ function lowerRecursiveQueryToLogicalPlanNode(input) {
2647
+ const { ast, nextPlanNodeId } = input;
2648
+ const traversal = input.traversal ?? runRecursiveTraversalSelectionPass(input.ast);
2649
+ let currentNode = {
2650
+ alias: ast.start.alias,
2651
+ graphId: input.graphId,
2652
+ id: nextPlanNodeId(),
2653
+ kinds: ast.start.kinds,
2654
+ op: "scan",
2655
+ source: "nodes"
2656
+ };
2657
+ currentNode = wrapWithAliasFilterNode(
2658
+ currentNode,
2659
+ ast,
2660
+ ast.start.alias,
2661
+ "node",
2662
+ nextPlanNodeId
2663
+ );
2664
+ currentNode = {
2665
+ edgeAlias: traversal.edgeAlias,
2666
+ edgeKinds: traversal.edgeKinds,
2667
+ id: nextPlanNodeId(),
2668
+ input: currentNode,
2669
+ inverseEdgeKinds: traversal.inverseEdgeKinds ?? [],
2670
+ nodeAlias: traversal.nodeAlias,
2671
+ nodeKinds: traversal.nodeKinds,
2672
+ op: "recursive_expand",
2673
+ traversal: traversal.variableLength
2674
+ };
2675
+ currentNode = wrapWithAliasFilterNode(
2676
+ currentNode,
2677
+ ast,
2678
+ traversal.edgeAlias,
2679
+ "edge",
2680
+ nextPlanNodeId
2681
+ );
2682
+ currentNode = wrapWithAliasFilterNode(
2683
+ currentNode,
2684
+ ast,
2685
+ traversal.nodeAlias,
2686
+ "node",
2687
+ nextPlanNodeId
2688
+ );
2689
+ return appendAggregateSortLimitAndProjectNodes(
2690
+ currentNode,
2691
+ ast,
2692
+ nextPlanNodeId,
2693
+ ast.limit
2694
+ );
2695
+ }
2696
+ function lowerComposableQueryToLogicalPlanNode(query, dialect, graphId, nextPlanNodeId) {
2697
+ if ("__type" in query) {
2698
+ return lowerSetOperationToLogicalPlanNode(
2699
+ query,
2700
+ graphId,
2701
+ dialect,
2702
+ nextPlanNodeId
2136
2703
  );
2137
2704
  }
2138
- if (!planShape.hasLimitOffset && input.limitOffset !== void 0) {
2139
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2140
- "Standard SQL emitter received LIMIT/OFFSET clause for a plan without limit_offset nodes",
2141
- { component: "standard-emitter" }
2142
- );
2705
+ const hasVariableLengthTraversal2 = query.traversals.some(
2706
+ (traversal) => traversal.variableLength !== void 0
2707
+ );
2708
+ if (hasVariableLengthTraversal2) {
2709
+ return lowerRecursiveQueryToLogicalPlanNode({
2710
+ ast: query,
2711
+ graphId,
2712
+ nextPlanNodeId
2713
+ });
2143
2714
  }
2715
+ const vectorPredicate = runVectorPredicatePass(
2716
+ query,
2717
+ chunk2WVFEIHR_cjs.getDialect(dialect)
2718
+ ).vectorPredicate;
2719
+ const effectiveLimit = resolveVectorAwareLimit(query.limit, vectorPredicate);
2720
+ const loweringInput = {
2721
+ ast: query,
2722
+ graphId,
2723
+ nextPlanNodeId,
2724
+ ...effectiveLimit === void 0 ? {} : { effectiveLimit },
2725
+ ...vectorPredicate === void 0 ? {} : { vectorPredicate }
2726
+ };
2727
+ return lowerStandardQueryToLogicalPlanNode(loweringInput);
2144
2728
  }
2145
- function emitStandardQuerySql(input) {
2146
- assertStandardEmitterClauseAlignment(input.logicalPlan, input);
2147
- const parts = [];
2148
- if (input.ctes.length > 0) {
2149
- parts.push(drizzleOrm.sql`WITH ${drizzleOrm.sql.join([...input.ctes], drizzleOrm.sql`, `)}`);
2150
- }
2151
- parts.push(drizzleOrm.sql`SELECT ${input.projection}`, input.fromClause);
2152
- if (input.groupBy !== void 0) {
2153
- parts.push(input.groupBy);
2154
- }
2155
- if (input.having !== void 0) {
2156
- parts.push(input.having);
2157
- }
2158
- if (input.orderBy !== void 0) {
2159
- parts.push(input.orderBy);
2160
- }
2161
- if (input.limitOffset !== void 0) {
2162
- parts.push(input.limitOffset);
2729
+ function lowerSetOperationToLogicalPlanNode(op, graphId, dialect, nextPlanNodeId) {
2730
+ let currentNode = {
2731
+ id: nextPlanNodeId(),
2732
+ left: lowerComposableQueryToLogicalPlanNode(
2733
+ op.left,
2734
+ dialect,
2735
+ graphId,
2736
+ nextPlanNodeId
2737
+ ),
2738
+ op: "set_op",
2739
+ operator: op.operator,
2740
+ right: lowerComposableQueryToLogicalPlanNode(
2741
+ op.right,
2742
+ dialect,
2743
+ graphId,
2744
+ nextPlanNodeId
2745
+ )
2746
+ };
2747
+ if (op.orderBy !== void 0 && op.orderBy.length > 0) {
2748
+ currentNode = {
2749
+ id: nextPlanNodeId(),
2750
+ input: currentNode,
2751
+ op: "sort",
2752
+ orderBy: op.orderBy
2753
+ };
2163
2754
  }
2164
- return drizzleOrm.sql.join(parts, drizzleOrm.sql` `);
2165
- }
2166
- var EMPTY_PREDICATES = [];
2167
- function buildPredicateIndexKey(alias, targetType) {
2168
- return `${alias}\0${targetType}`;
2169
- }
2170
- function resolvePredicateTargetType(predicate2) {
2171
- return predicate2.targetType === "edge" ? "edge" : "node";
2172
- }
2173
- function buildPredicateIndex(ast) {
2174
- const byAliasAndType = /* @__PURE__ */ new Map();
2175
- for (const predicate2 of ast.predicates) {
2176
- const key = buildPredicateIndexKey(
2177
- predicate2.targetAlias,
2178
- resolvePredicateTargetType(predicate2)
2179
- );
2180
- const existing = byAliasAndType.get(key);
2181
- if (existing === void 0) {
2182
- byAliasAndType.set(key, [predicate2]);
2755
+ if (op.limit !== void 0 || op.offset !== void 0) {
2756
+ const limitOffsetBase = {
2757
+ id: nextPlanNodeId(),
2758
+ input: currentNode,
2759
+ op: "limit_offset"
2760
+ };
2761
+ if (op.limit !== void 0 && op.offset !== void 0) {
2762
+ currentNode = { ...limitOffsetBase, limit: op.limit, offset: op.offset };
2763
+ } else if (op.limit === void 0) {
2764
+ currentNode = { ...limitOffsetBase, offset: op.offset };
2183
2765
  } else {
2184
- existing.push(predicate2);
2766
+ currentNode = { ...limitOffsetBase, limit: op.limit };
2185
2767
  }
2186
2768
  }
2187
- return { byAliasAndType };
2188
- }
2189
- function getPredicatesForAlias(predicateIndex, alias, targetType) {
2190
- return predicateIndex.byAliasAndType.get(
2191
- buildPredicateIndexKey(alias, targetType)
2192
- ) ?? EMPTY_PREDICATES;
2769
+ return currentNode;
2193
2770
  }
2194
- function compilePredicateClauses(predicates, predicateContext) {
2195
- return predicates.map(
2196
- (predicate2) => compilePredicateExpression(predicate2.expression, predicateContext)
2197
- );
2771
+ function lowerStandardQueryToLogicalPlan(input) {
2772
+ const nextPlanNodeId = createPlanNodeIdFactory();
2773
+ return {
2774
+ metadata: {
2775
+ dialect: input.dialect,
2776
+ graphId: input.graphId
2777
+ },
2778
+ root: lowerStandardQueryToLogicalPlanNode({
2779
+ ...input,
2780
+ nextPlanNodeId
2781
+ })
2782
+ };
2198
2783
  }
2199
- function compileKindFilter(column, kinds) {
2200
- if (kinds.length === 0) {
2201
- return drizzleOrm.sql`1 = 0`;
2202
- }
2203
- if (kinds.length === 1) {
2204
- return drizzleOrm.sql`${column} = ${kinds[0]}`;
2205
- }
2206
- return drizzleOrm.sql`${column} IN (${drizzleOrm.sql.join(
2207
- kinds.map((kind) => drizzleOrm.sql`${kind}`),
2208
- drizzleOrm.sql`, `
2209
- )})`;
2784
+ function lowerRecursiveQueryToLogicalPlan(input) {
2785
+ const nextPlanNodeId = createPlanNodeIdFactory();
2786
+ return {
2787
+ metadata: {
2788
+ dialect: input.dialect,
2789
+ graphId: input.graphId
2790
+ },
2791
+ root: lowerRecursiveQueryToLogicalPlanNode({
2792
+ ...input,
2793
+ nextPlanNodeId
2794
+ })
2795
+ };
2210
2796
  }
2211
- function getNodeKindsForAlias(ast, alias) {
2212
- if (alias === ast.start.alias) {
2213
- return ast.start.kinds;
2214
- }
2215
- for (const traversal of ast.traversals) {
2216
- if (traversal.nodeAlias === alias) {
2217
- return traversal.nodeKinds;
2218
- }
2219
- }
2220
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(`Unknown traversal source alias: ${alias}`);
2797
+ function lowerSetOperationToLogicalPlan(input) {
2798
+ const nextPlanNodeId = createPlanNodeIdFactory();
2799
+ return {
2800
+ metadata: {
2801
+ dialect: input.dialect,
2802
+ graphId: input.graphId
2803
+ },
2804
+ root: lowerSetOperationToLogicalPlanNode(
2805
+ input.op,
2806
+ input.graphId,
2807
+ input.dialect,
2808
+ nextPlanNodeId
2809
+ )
2810
+ };
2221
2811
  }
2222
2812
 
2223
- // src/query/compiler/typed-json-extract.ts
2224
- function compileTypedJsonExtract(input) {
2225
- const { column, dialect, pointer, valueType } = input;
2226
- const fallback = input.fallback ?? "json";
2227
- switch (valueType) {
2228
- case "string": {
2229
- return dialect.jsonExtractText(column, pointer);
2230
- }
2231
- case "number": {
2232
- return dialect.jsonExtractNumber(column, pointer);
2233
- }
2234
- case "boolean": {
2235
- return dialect.jsonExtractBoolean(column, pointer);
2813
+ // src/query/compiler/recursive.ts
2814
+ var MAX_RECURSIVE_DEPTH = 100;
2815
+ var MAX_EXPLICIT_RECURSIVE_DEPTH = 1e3;
2816
+ var NO_ALWAYS_REQUIRED_COLUMNS = /* @__PURE__ */ new Set();
2817
+ function runRecursiveQueryPassPipeline(ast, graphId, ctx) {
2818
+ let state = {
2819
+ ast,
2820
+ ctx,
2821
+ graphId,
2822
+ logicalPlan: void 0,
2823
+ requiredColumnsByAlias: void 0,
2824
+ temporalFilterPass: void 0,
2825
+ traversal: void 0
2826
+ };
2827
+ const recursiveTraversalPass = runCompilerPass(state, {
2828
+ name: "recursive_traversal",
2829
+ execute(currentState) {
2830
+ return runRecursiveTraversalSelectionPass(currentState.ast);
2831
+ },
2832
+ update(currentState, traversal) {
2833
+ return {
2834
+ ...currentState,
2835
+ traversal
2836
+ };
2236
2837
  }
2237
- case "date": {
2238
- return dialect.jsonExtractDate(column, pointer);
2838
+ });
2839
+ state = recursiveTraversalPass.state;
2840
+ const temporalPass = runCompilerPass(state, {
2841
+ name: "temporal_filters",
2842
+ execute(currentState) {
2843
+ return createTemporalFilterPass(
2844
+ currentState.ast,
2845
+ currentState.ctx.dialect.currentTimestamp()
2846
+ );
2847
+ },
2848
+ update(currentState, temporalFilterPass) {
2849
+ return {
2850
+ ...currentState,
2851
+ temporalFilterPass
2852
+ };
2239
2853
  }
2240
- case "array":
2241
- case "object":
2242
- case "embedding":
2243
- case "unknown":
2244
- case void 0: {
2245
- return fallback === "text" ? dialect.jsonExtractText(column, pointer) : dialect.jsonExtract(column, pointer);
2854
+ });
2855
+ state = temporalPass.state;
2856
+ const columnPruningPass = runCompilerPass(state, {
2857
+ name: "column_pruning",
2858
+ execute(currentState) {
2859
+ const traversal = currentState.traversal;
2860
+ if (traversal === void 0) {
2861
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2862
+ "Recursive traversal pass did not select traversal"
2863
+ );
2864
+ }
2865
+ return collectRequiredColumnsByAlias(currentState.ast, traversal);
2866
+ },
2867
+ update(currentState, requiredColumnsByAlias) {
2868
+ return {
2869
+ ...currentState,
2870
+ requiredColumnsByAlias
2871
+ };
2246
2872
  }
2247
- default: {
2248
- return fallback === "text" ? dialect.jsonExtractText(column, pointer) : dialect.jsonExtract(column, pointer);
2873
+ });
2874
+ state = columnPruningPass.state;
2875
+ const logicalPlanPass = runCompilerPass(state, {
2876
+ name: "logical_plan",
2877
+ execute(currentState) {
2878
+ const loweringInput = {
2879
+ ast: currentState.ast,
2880
+ dialect: currentState.ctx.dialect.name,
2881
+ graphId: currentState.graphId,
2882
+ ...currentState.traversal === void 0 ? {} : { traversal: currentState.traversal }
2883
+ };
2884
+ return lowerRecursiveQueryToLogicalPlan(loweringInput);
2885
+ },
2886
+ update(currentState, logicalPlan) {
2887
+ return {
2888
+ ...currentState,
2889
+ logicalPlan
2890
+ };
2249
2891
  }
2250
- }
2251
- }
2252
- var NODE_COLUMNS = [
2253
- "id",
2254
- "kind",
2255
- "props",
2256
- "version",
2257
- "valid_from",
2258
- "valid_to",
2259
- "created_at",
2260
- "updated_at",
2261
- "deleted_at"
2262
- ];
2263
- var EDGE_COLUMNS = [
2264
- "id",
2265
- "kind",
2266
- "from_id",
2267
- "to_id",
2268
- "props",
2269
- "valid_from",
2270
- "valid_to",
2271
- "created_at",
2272
- "updated_at",
2273
- "deleted_at"
2274
- ];
2275
- var EMPTY_REQUIRED_COLUMNS = /* @__PURE__ */ new Set();
2276
- function quoteIdentifier(identifier) {
2277
- return drizzleOrm.sql.raw(`"${identifier.replaceAll('"', '""')}"`);
2278
- }
2279
- function shouldProjectColumn(requiredColumns, column, alwaysRequiredColumns) {
2280
- if (alwaysRequiredColumns?.has(column)) return true;
2281
- if (requiredColumns === void 0) return true;
2282
- return requiredColumns.has(column);
2283
- }
2284
- function addRequiredColumn(requiredColumnsByAlias, alias, column) {
2285
- const existing = requiredColumnsByAlias.get(alias);
2286
- if (existing) {
2287
- existing.add(column);
2288
- return;
2289
- }
2290
- requiredColumnsByAlias.set(alias, /* @__PURE__ */ new Set([column]));
2892
+ });
2893
+ state = logicalPlanPass.state;
2894
+ return state;
2291
2895
  }
2292
- function markFieldRefAsRequired(requiredColumnsByAlias, field2) {
2293
- const column = field2.path[0];
2294
- if (column === void 0) return;
2295
- addRequiredColumn(requiredColumnsByAlias, field2.alias, column);
2896
+ function compileVariableLengthQuery(ast, graphId, ctx) {
2897
+ const strategy = ctx.dialect.capabilities.recursiveQueryStrategy;
2898
+ const handler = RECURSIVE_QUERY_STRATEGY_HANDLERS[strategy];
2899
+ return handler(ast, graphId, ctx);
2296
2900
  }
2297
- function mapSelectiveSystemFieldToColumn(field2) {
2298
- if (field2 === "fromId") return "from_id";
2299
- if (field2 === "toId") return "to_id";
2300
- if (field2.startsWith("meta.")) {
2301
- return field2.slice(5).replaceAll(/([A-Z])/g, "_$1").toLowerCase();
2901
+ var RECURSIVE_QUERY_STRATEGY_HANDLERS = {
2902
+ recursive_cte: compileVariableLengthQueryWithRecursiveCteStrategy
2903
+ };
2904
+ function compileVariableLengthQueryWithRecursiveCteStrategy(ast, graphId, ctx) {
2905
+ const passState = runRecursiveQueryPassPipeline(ast, graphId, ctx);
2906
+ const { dialect } = ctx;
2907
+ const {
2908
+ logicalPlan,
2909
+ requiredColumnsByAlias,
2910
+ temporalFilterPass,
2911
+ traversal: vlTraversal
2912
+ } = passState;
2913
+ if (temporalFilterPass === void 0) {
2914
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2915
+ "Temporal filter pass did not initialize temporal state"
2916
+ );
2302
2917
  }
2303
- return field2;
2304
- }
2305
- function markSelectiveFieldAsRequired(requiredColumnsByAlias, field2) {
2306
- if (field2.isSystemField) {
2307
- addRequiredColumn(
2308
- requiredColumnsByAlias,
2309
- field2.alias,
2310
- mapSelectiveSystemFieldToColumn(field2.field)
2918
+ if (logicalPlan === void 0) {
2919
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2920
+ "Logical plan pass did not initialize plan state"
2311
2921
  );
2312
- return;
2313
2922
  }
2314
- addRequiredColumn(requiredColumnsByAlias, field2.alias, "props");
2315
- }
2316
- function isIdFieldRef(field2) {
2317
- return field2.path.length === 1 && field2.path[0] === "id" && field2.jsonPointer === void 0;
2318
- }
2319
- function isAggregateExpr(source) {
2320
- return "__type" in source && source.__type === "aggregate";
2321
- }
2322
-
2323
- // src/query/compiler/emitter/standard-builders.ts
2324
- function compileColumnReference(tableAlias, column) {
2325
- if (tableAlias === void 0) {
2326
- return drizzleOrm.sql.raw(column);
2923
+ if (vlTraversal === void 0) {
2924
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2925
+ "Recursive traversal pass did not select traversal"
2926
+ );
2327
2927
  }
2328
- return drizzleOrm.sql`${drizzleOrm.sql.raw(tableAlias)}.${drizzleOrm.sql.raw(column)}`;
2329
- }
2330
- function compileNodeSelectColumns(tableAlias, alias, requiredColumns) {
2331
- return NODE_COLUMNS.filter(
2332
- (column) => column === "id" || column === "kind" || shouldProjectColumn(requiredColumns, column)
2333
- ).map(
2334
- (column) => drizzleOrm.sql`${compileColumnReference(tableAlias, column)} AS ${drizzleOrm.sql.raw(`${alias}_${column}`)}`
2335
- );
2336
- }
2337
- function compileEdgeSelectColumns(tableAlias, alias, requiredColumns) {
2338
- return EDGE_COLUMNS.filter(
2339
- (column) => shouldProjectColumn(requiredColumns, column)
2340
- ).map(
2341
- (column) => drizzleOrm.sql`${compileColumnReference(tableAlias, column)} AS ${drizzleOrm.sql.raw(`${alias}_${column}`)}`
2342
- );
2343
- }
2344
- function buildStandardStartCte(input) {
2345
- const { ast, ctx, graphId, predicateIndex, requiredColumnsByAlias } = input;
2346
- const alias = ast.start.alias;
2347
- const kinds = ast.start.kinds;
2348
- const kindFilter = compileKindFilter(drizzleOrm.sql.raw("kind"), kinds);
2349
- const temporalFilter = input.temporalFilterPass.forAlias();
2350
- const cteContext = { ...ctx, cteColumnPrefix: "" };
2351
- const predicateClauses = compilePredicateClauses(
2352
- getPredicatesForAlias(predicateIndex, alias, "node"),
2353
- cteContext
2354
- );
2355
- const whereClauses = [
2356
- drizzleOrm.sql`graph_id = ${graphId}`,
2357
- kindFilter,
2358
- temporalFilter,
2359
- ...predicateClauses
2360
- ];
2361
- const effectiveRequiredColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(alias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
2362
- return drizzleOrm.sql`
2363
- cte_${drizzleOrm.sql.raw(alias)} AS (
2364
- SELECT ${drizzleOrm.sql.join(
2365
- compileNodeSelectColumns(void 0, alias, effectiveRequiredColumns),
2366
- drizzleOrm.sql`, `
2367
- )}
2368
- FROM ${ctx.schema.nodesTable}
2369
- WHERE ${drizzleOrm.sql.join(whereClauses, drizzleOrm.sql` AND `)}
2370
- )
2371
- `;
2372
- }
2373
- function buildStandardTraversalCte(input) {
2374
- const {
2928
+ const recursiveCte = compileRecursiveCte(
2375
2929
  ast,
2376
- carryForwardPreviousColumns,
2377
- ctx,
2930
+ vlTraversal,
2378
2931
  graphId,
2379
- materializeCte,
2380
- predicateIndex,
2932
+ ctx,
2381
2933
  requiredColumnsByAlias,
2382
- temporalFilterPass,
2383
- traversalIndex,
2384
- traversalLimit
2385
- } = input;
2386
- const traversal = ast.traversals[traversalIndex];
2387
- const traversalLimitValue = traversalIndex === ast.traversals.length - 1 ? traversalLimit : void 0;
2388
- const previousNodeKinds = getNodeKindsForAlias(ast, traversal.joinFromAlias);
2934
+ temporalFilterPass
2935
+ );
2936
+ const projection = compileRecursiveProjection(ast, vlTraversal, dialect);
2937
+ const minDepth = vlTraversal.variableLength.minDepth;
2938
+ const depthFilter = minDepth > 0 ? drizzleOrm.sql`WHERE depth >= ${minDepth}` : drizzleOrm.sql.raw("");
2939
+ const orderBy = compileRecursiveOrderBy(ast, dialect);
2940
+ const limitOffset = compileLimitOffset(ast);
2941
+ return emitRecursiveQuerySql({
2942
+ depthFilter,
2943
+ ...limitOffset === void 0 ? {} : { limitOffset },
2944
+ logicalPlan,
2945
+ ...orderBy === void 0 ? {} : { orderBy },
2946
+ projection,
2947
+ recursiveCte
2948
+ });
2949
+ }
2950
+ function hasVariableLengthTraversal(ast) {
2951
+ return ast.traversals.some((t) => t.variableLength !== void 0);
2952
+ }
2953
+ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlias, temporalFilterPass) {
2954
+ const { dialect } = ctx;
2955
+ const startAlias = ast.start.alias;
2956
+ const startKinds = ast.start.kinds;
2957
+ const nodeAlias = traversal.nodeAlias;
2389
2958
  const directEdgeKinds = [...new Set(traversal.edgeKinds)];
2390
2959
  const inverseEdgeKinds = traversal.inverseEdgeKinds === void 0 ? [] : [...new Set(traversal.inverseEdgeKinds)];
2960
+ const forceWorktableOuterJoinOrder = dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
2391
2961
  const nodeKinds = traversal.nodeKinds;
2392
- const nodeKindFilter = compileKindFilter(drizzleOrm.sql.raw("n.kind"), nodeKinds);
2962
+ const previousNodeKinds = [.../* @__PURE__ */ new Set([...startKinds, ...nodeKinds])];
2963
+ const direction = traversal.direction;
2964
+ const vl = traversal.variableLength;
2965
+ const shouldEnforceCycleCheck = vl.cyclePolicy !== "allow";
2966
+ const shouldTrackPath = shouldEnforceCycleCheck || vl.pathAlias !== void 0;
2967
+ const recursiveJoinRequiredColumns = /* @__PURE__ */ new Set(["id"]);
2968
+ if (previousNodeKinds.length > 1) {
2969
+ recursiveJoinRequiredColumns.add("kind");
2970
+ }
2971
+ const requiredStartColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(startAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
2972
+ const requiredNodeColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(nodeAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
2973
+ const startColumnsFromBase = compileNodeSelectColumnsFromTable(
2974
+ "n0",
2975
+ startAlias,
2976
+ requiredStartColumns,
2977
+ NO_ALWAYS_REQUIRED_COLUMNS
2978
+ );
2979
+ const startColumnsFromRecursive = compileNodeSelectColumnsFromRecursiveRow(
2980
+ startAlias,
2981
+ requiredStartColumns,
2982
+ NO_ALWAYS_REQUIRED_COLUMNS
2983
+ );
2984
+ const nodeColumnsFromBase = compileNodeSelectColumnsFromTable(
2985
+ "n0",
2986
+ nodeAlias,
2987
+ requiredNodeColumns,
2988
+ recursiveJoinRequiredColumns
2989
+ );
2990
+ const nodeColumnsFromRecursive = compileNodeSelectColumnsFromTable(
2991
+ "n",
2992
+ nodeAlias,
2993
+ requiredNodeColumns,
2994
+ recursiveJoinRequiredColumns
2995
+ );
2996
+ const startKindFilter = compileKindFilter2(startKinds, "n0.kind");
2997
+ const nodeKindFilter = compileKindFilter2(nodeKinds, "n.kind");
2998
+ const startTemporalFilter = temporalFilterPass.forAlias("n0");
2393
2999
  const edgeTemporalFilter = temporalFilterPass.forAlias("e");
2394
3000
  const nodeTemporalFilter = temporalFilterPass.forAlias("n");
2395
- const nodeCteContext = {
2396
- ...ctx,
2397
- cteColumnPrefix: "n"
2398
- };
2399
- const nodePredicateClauses = compilePredicateClauses(
2400
- getPredicatesForAlias(predicateIndex, traversal.nodeAlias, "node"),
2401
- nodeCteContext
2402
- );
2403
- const edgeCteContext = {
2404
- ...ctx,
2405
- cteColumnPrefix: "e"
2406
- };
2407
- const edgePredicateClauses = compilePredicateClauses(
2408
- getPredicatesForAlias(predicateIndex, traversal.edgeAlias, "edge"),
2409
- edgeCteContext
3001
+ const startContext = { ...ctx, cteColumnPrefix: "" };
3002
+ const startPredicates = compileNodePredicates(ast, startAlias, startContext);
3003
+ const edgeContext = { ...ctx, cteColumnPrefix: "e" };
3004
+ const edgePredicates = compileEdgePredicates(
3005
+ ast,
3006
+ traversal.edgeAlias,
3007
+ edgeContext
3008
+ );
3009
+ const targetContext = { ...ctx, cteColumnPrefix: "n" };
3010
+ const targetNodePredicates = compileNodePredicates(
3011
+ ast,
3012
+ nodeAlias,
3013
+ targetContext
2410
3014
  );
3015
+ if (vl.maxDepth > MAX_EXPLICIT_RECURSIVE_DEPTH) {
3016
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
3017
+ `Recursive traversal maxHops(${vl.maxDepth}) exceeds maximum explicit depth of ${MAX_EXPLICIT_RECURSIVE_DEPTH}`
3018
+ );
3019
+ }
3020
+ const effectiveMaxDepth = vl.maxDepth > 0 ? vl.maxDepth : MAX_RECURSIVE_DEPTH;
3021
+ const maxDepthCondition = drizzleOrm.sql`r.depth < ${effectiveMaxDepth}`;
3022
+ const cycleCheck = shouldEnforceCycleCheck ? dialect.cycleCheck(drizzleOrm.sql.raw("n.id"), drizzleOrm.sql.raw("r.path")) : void 0;
3023
+ const initialPath = shouldTrackPath ? dialect.initializePath(drizzleOrm.sql.raw("n0.id")) : void 0;
3024
+ const pathExtension = shouldTrackPath ? dialect.extendPath(drizzleOrm.sql.raw("r.path"), drizzleOrm.sql.raw("n.id")) : void 0;
2411
3025
  const baseWhereClauses = [
3026
+ drizzleOrm.sql`n0.graph_id = ${graphId}`,
3027
+ startKindFilter,
3028
+ startTemporalFilter,
3029
+ ...startPredicates
3030
+ ];
3031
+ const recursiveBaseWhereClauses = [
2412
3032
  drizzleOrm.sql`e.graph_id = ${graphId}`,
2413
3033
  nodeKindFilter,
2414
3034
  edgeTemporalFilter,
2415
3035
  nodeTemporalFilter,
2416
- ...nodePredicateClauses,
2417
- ...edgePredicateClauses
2418
- ];
2419
- const previousAlias = traversal.joinFromAlias;
2420
- const edgeAlias = traversal.edgeAlias;
2421
- const nodeAlias = traversal.nodeAlias;
2422
- const requiredNodeColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(nodeAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
2423
- const requiredEdgeColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(edgeAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
2424
- const previousRowColumns = carryForwardPreviousColumns ? [drizzleOrm.sql`cte_${drizzleOrm.sql.raw(previousAlias)}.*`] : [
2425
- drizzleOrm.sql`cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id AS ${drizzleOrm.sql.raw(previousAlias)}_id`,
2426
- drizzleOrm.sql`cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind AS ${drizzleOrm.sql.raw(previousAlias)}_kind`
2427
- ];
2428
- const selectColumns = [
2429
- ...previousRowColumns,
2430
- ...compileEdgeSelectColumns("e", edgeAlias, requiredEdgeColumns),
2431
- ...compileNodeSelectColumns("n", nodeAlias, requiredNodeColumns)
3036
+ maxDepthCondition
2432
3037
  ];
2433
- const cteMaterialization = materializeCte ? drizzleOrm.sql`MATERIALIZED ` : drizzleOrm.sql``;
2434
- function compileTraversalBranch(branch) {
2435
- const whereClauses = [
2436
- ...baseWhereClauses,
2437
- compileKindFilter(drizzleOrm.sql.raw("e.kind"), branch.edgeKinds),
2438
- compileKindFilter(
2439
- drizzleOrm.sql.raw(`e.${branch.joinKindField}`),
2440
- previousNodeKinds
2441
- ),
2442
- compileKindFilter(drizzleOrm.sql.raw(`e.${branch.targetKindField}`), nodeKinds)
3038
+ if (cycleCheck !== void 0) {
3039
+ recursiveBaseWhereClauses.push(cycleCheck);
3040
+ }
3041
+ recursiveBaseWhereClauses.push(...edgePredicates, ...targetNodePredicates);
3042
+ function compileRecursiveBranch2(branch) {
3043
+ const recursiveFilterClauses = [
3044
+ ...recursiveBaseWhereClauses,
3045
+ compileKindFilter2(branch.edgeKinds, "e.kind"),
3046
+ compileKindFilter2(previousNodeKinds, `e.${branch.joinKindField}`),
3047
+ compileKindFilter2(nodeKinds, `e.${branch.targetKindField}`)
2443
3048
  ];
2444
3049
  if (branch.duplicateGuard !== void 0) {
2445
- whereClauses.push(branch.duplicateGuard);
2446
- }
2447
- return drizzleOrm.sql`
2448
- SELECT ${drizzleOrm.sql.join(selectColumns, drizzleOrm.sql`, `)}
2449
- FROM cte_${drizzleOrm.sql.raw(previousAlias)}
2450
- JOIN ${ctx.schema.edgesTable} e ON cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id = e.${drizzleOrm.sql.raw(branch.joinField)}
2451
- AND cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind = e.${drizzleOrm.sql.raw(branch.joinKindField)}
2452
- JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
2453
- AND n.id = e.${drizzleOrm.sql.raw(branch.targetField)}
2454
- AND n.kind = e.${drizzleOrm.sql.raw(branch.targetKindField)}
2455
- WHERE ${drizzleOrm.sql.join(whereClauses, drizzleOrm.sql` AND `)}
2456
- `;
2457
- }
2458
- const directJoinField = traversal.direction === "out" ? "from_id" : "to_id";
2459
- const directTargetField = traversal.direction === "out" ? "to_id" : "from_id";
2460
- const directJoinKindField = traversal.direction === "out" ? "from_kind" : "to_kind";
2461
- const directTargetKindField = traversal.direction === "out" ? "to_kind" : "from_kind";
2462
- const directBranch = compileTraversalBranch({
2463
- edgeKinds: directEdgeKinds,
2464
- joinField: directJoinField,
2465
- joinKindField: directJoinKindField,
2466
- targetField: directTargetField,
2467
- targetKindField: directTargetKindField
2468
- });
2469
- if (inverseEdgeKinds.length === 0) {
2470
- if (traversalLimitValue !== void 0) {
2471
- return drizzleOrm.sql`
2472
- cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2473
- SELECT * FROM (
2474
- ${directBranch}
2475
- ) AS traversal_rows
2476
- LIMIT ${traversalLimitValue}
2477
- )
2478
- `;
3050
+ recursiveFilterClauses.push(branch.duplicateGuard);
2479
3051
  }
2480
- return drizzleOrm.sql`
2481
- cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2482
- ${directBranch}
2483
- )
2484
- `;
2485
- }
2486
- const inverseJoinField = traversal.direction === "out" ? "to_id" : "from_id";
2487
- const inverseTargetField = traversal.direction === "out" ? "from_id" : "to_id";
2488
- const inverseJoinKindField = traversal.direction === "out" ? "to_kind" : "from_kind";
2489
- const inverseTargetKindField = traversal.direction === "out" ? "from_kind" : "to_kind";
2490
- const overlappingKinds = inverseEdgeKinds.filter(
2491
- (kind) => directEdgeKinds.includes(kind)
2492
- );
2493
- const duplicateGuard = overlappingKinds.length > 0 ? drizzleOrm.sql`NOT (e.from_id = e.to_id AND ${compileKindFilter(
2494
- drizzleOrm.sql.raw("e.kind"),
2495
- overlappingKinds
2496
- )})` : void 0;
2497
- const inverseBranch = compileTraversalBranch({
2498
- duplicateGuard,
2499
- edgeKinds: inverseEdgeKinds,
2500
- joinField: inverseJoinField,
2501
- joinKindField: inverseJoinKindField,
2502
- targetField: inverseTargetField,
2503
- targetKindField: inverseTargetKindField
2504
- });
2505
- if (traversalLimitValue !== void 0) {
2506
- return drizzleOrm.sql`
2507
- cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2508
- SELECT * FROM (
2509
- ${directBranch}
2510
- UNION ALL
2511
- ${inverseBranch}
2512
- ) AS traversal_rows
2513
- LIMIT ${traversalLimitValue}
2514
- )
2515
- `;
2516
- }
2517
- return drizzleOrm.sql`
2518
- cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2519
- ${directBranch}
2520
- UNION ALL
2521
- ${inverseBranch}
2522
- )
2523
- `;
2524
- }
2525
- function compileAggregateExprFromSource(expr, dialect) {
2526
- const { field: field2 } = expr;
2527
- const fn = expr.function;
2528
- switch (fn) {
2529
- case "count":
2530
- case "countDistinct":
2531
- case "sum":
2532
- case "avg":
2533
- case "min":
2534
- case "max": {
2535
- const cteAlias = `cte_${field2.alias}`;
2536
- const column = compileFieldValue(
2537
- field2,
2538
- dialect,
2539
- field2.valueType,
2540
- cteAlias
2541
- );
2542
- if (fn === "countDistinct") {
2543
- return drizzleOrm.sql`COUNT(DISTINCT ${column})`;
2544
- }
2545
- return drizzleOrm.sql`${drizzleOrm.sql.raw(fn.toUpperCase())}(${column})`;
3052
+ const recursiveSelectColumns = [
3053
+ ...startColumnsFromRecursive,
3054
+ ...nodeColumnsFromRecursive,
3055
+ drizzleOrm.sql`r.depth + 1 AS depth`
3056
+ ];
3057
+ if (pathExtension !== void 0) {
3058
+ recursiveSelectColumns.push(drizzleOrm.sql`${pathExtension} AS path`);
2546
3059
  }
2547
- default: {
2548
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2549
- `Unknown aggregate function: ${String(fn)}`
3060
+ const recursiveJoinClauses = [
3061
+ drizzleOrm.sql`e.${drizzleOrm.sql.raw(branch.joinField)} = r.${drizzleOrm.sql.raw(nodeAlias)}_id`
3062
+ ];
3063
+ if (previousNodeKinds.length > 1) {
3064
+ recursiveJoinClauses.push(
3065
+ drizzleOrm.sql`e.${drizzleOrm.sql.raw(branch.joinKindField)} = r.${drizzleOrm.sql.raw(nodeAlias)}_kind`
2550
3066
  );
2551
3067
  }
3068
+ if (forceWorktableOuterJoinOrder) {
3069
+ const recursiveWhereClauses = [
3070
+ ...recursiveJoinClauses,
3071
+ ...recursiveFilterClauses
3072
+ ];
3073
+ return drizzleOrm.sql`
3074
+ SELECT ${drizzleOrm.sql.join(recursiveSelectColumns, drizzleOrm.sql`, `)}
3075
+ FROM recursive_cte r
3076
+ CROSS JOIN ${ctx.schema.edgesTable} e
3077
+ JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
3078
+ AND n.id = e.${drizzleOrm.sql.raw(branch.targetField)}
3079
+ AND n.kind = e.${drizzleOrm.sql.raw(branch.targetKindField)}
3080
+ WHERE ${drizzleOrm.sql.join(recursiveWhereClauses, drizzleOrm.sql` AND `)}
3081
+ `;
3082
+ }
3083
+ return drizzleOrm.sql`
3084
+ SELECT ${drizzleOrm.sql.join(recursiveSelectColumns, drizzleOrm.sql`, `)}
3085
+ FROM recursive_cte r
3086
+ JOIN ${ctx.schema.edgesTable} e ON ${drizzleOrm.sql.join(recursiveJoinClauses, drizzleOrm.sql` AND `)}
3087
+ JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
3088
+ AND n.id = e.${drizzleOrm.sql.raw(branch.targetField)}
3089
+ AND n.kind = e.${drizzleOrm.sql.raw(branch.targetKindField)}
3090
+ WHERE ${drizzleOrm.sql.join(recursiveFilterClauses, drizzleOrm.sql` AND `)}
3091
+ `;
2552
3092
  }
2553
- }
2554
- function compileProjectedSource(field2, dialect) {
2555
- if (isAggregateExpr(field2.source)) {
2556
- return compileAggregateExprFromSource(field2.source, dialect);
2557
- }
2558
- const cteAlias = field2.cteAlias ?? `cte_${field2.source.alias}`;
2559
- return compileFieldValue(
2560
- field2.source,
2561
- dialect,
2562
- field2.source.valueType,
2563
- cteAlias
2564
- );
2565
- }
2566
- function buildStandardProjection(input) {
2567
- const { ast, collapsedTraversalCteAlias, dialect } = input;
2568
- if (ast.selectiveFields && ast.selectiveFields.length > 0) {
2569
- return compileSelectiveProjection(
2570
- ast.selectiveFields,
2571
- dialect,
2572
- ast,
2573
- collapsedTraversalCteAlias
2574
- );
2575
- }
2576
- const fields = ast.projection.fields;
2577
- if (fields.length === 0) {
2578
- return drizzleOrm.sql.raw("*");
2579
- }
2580
- const projectedFields = fields.map((field2) => {
2581
- const source = compileProjectedSource(field2, dialect);
2582
- return drizzleOrm.sql`${source} AS ${quoteIdentifier(field2.outputName)}`;
2583
- });
2584
- return drizzleOrm.sql.join(projectedFields, drizzleOrm.sql`, `);
2585
- }
2586
- function compileSelectiveProjection(fields, dialect, ast, collapsedTraversalCteAlias) {
2587
- const aliasToCte = /* @__PURE__ */ new Map([
2588
- [ast.start.alias, `cte_${ast.start.alias}`]
2589
- ]);
2590
- for (const traversal of ast.traversals) {
2591
- aliasToCte.set(traversal.nodeAlias, `cte_${traversal.nodeAlias}`);
2592
- aliasToCte.set(traversal.edgeAlias, `cte_${traversal.nodeAlias}`);
2593
- }
2594
- const columns = fields.map((field2) => {
2595
- const cteAlias = collapsedTraversalCteAlias ?? aliasToCte.get(field2.alias) ?? `cte_${field2.alias}`;
2596
- if (field2.isSystemField) {
2597
- const dbColumn = mapSelectiveSystemFieldToColumn(field2.field);
2598
- return drizzleOrm.sql`${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${quoteIdentifier(field2.outputName)}`;
2599
- }
2600
- const propsColumn = `${field2.alias}_props`;
2601
- const column = drizzleOrm.sql`${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(propsColumn)}`;
2602
- const pointer = chunkP5CNM325_cjs.jsonPointer([field2.field]);
2603
- const extracted = compileTypedJsonExtract({
2604
- column,
2605
- dialect,
2606
- pointer,
2607
- valueType: field2.valueType
2608
- });
2609
- return drizzleOrm.sql`${extracted} AS ${quoteIdentifier(field2.outputName)}`;
2610
- });
2611
- return drizzleOrm.sql.join(columns, drizzleOrm.sql`, `);
2612
- }
2613
- function buildStandardFromClause(input) {
2614
- const { ast, collapsedTraversalCteAlias, vectorPredicate } = input;
2615
- if (collapsedTraversalCteAlias !== void 0) {
2616
- return drizzleOrm.sql`FROM ${drizzleOrm.sql.raw(collapsedTraversalCteAlias)}`;
2617
- }
2618
- const startAlias = ast.start.alias;
2619
- const fromClause = drizzleOrm.sql`FROM cte_${drizzleOrm.sql.raw(startAlias)}`;
2620
- const joins = [];
2621
- for (const traversal of ast.traversals) {
2622
- const cteAlias = `cte_${traversal.nodeAlias}`;
2623
- const previousAlias = traversal.joinFromAlias;
2624
- const joinType = traversal.optional ? "LEFT JOIN" : "INNER JOIN";
2625
- joins.push(
2626
- drizzleOrm.sql`${drizzleOrm.sql.raw(joinType)} ${drizzleOrm.sql.raw(cteAlias)} ON ${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id = cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id AND ${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind = cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind`
3093
+ const directJoinField = direction === "out" ? "from_id" : "to_id";
3094
+ const directTargetField = direction === "out" ? "to_id" : "from_id";
3095
+ const directJoinKindField = direction === "out" ? "from_kind" : "to_kind";
3096
+ const directTargetKindField = direction === "out" ? "to_kind" : "from_kind";
3097
+ const directBranch = compileRecursiveBranch2({
3098
+ joinField: directJoinField,
3099
+ targetField: directTargetField,
3100
+ joinKindField: directJoinKindField,
3101
+ targetKindField: directTargetKindField,
3102
+ edgeKinds: directEdgeKinds
3103
+ });
3104
+ function compileInverseRecursiveBranch() {
3105
+ const inverseJoinField = direction === "out" ? "to_id" : "from_id";
3106
+ const inverseTargetField = direction === "out" ? "from_id" : "to_id";
3107
+ const inverseJoinKindField = direction === "out" ? "to_kind" : "from_kind";
3108
+ const inverseTargetKindField = direction === "out" ? "from_kind" : "to_kind";
3109
+ const overlappingKinds = inverseEdgeKinds.filter(
3110
+ (kind) => directEdgeKinds.includes(kind)
2627
3111
  );
3112
+ const duplicateGuard = overlappingKinds.length > 0 ? drizzleOrm.sql`NOT (e.from_id = e.to_id AND ${compileKindFilter2(overlappingKinds, "e.kind")})` : void 0;
3113
+ const inverseBranch = compileRecursiveBranch2({
3114
+ joinField: inverseJoinField,
3115
+ targetField: inverseTargetField,
3116
+ joinKindField: inverseJoinKindField,
3117
+ targetKindField: inverseTargetKindField,
3118
+ edgeKinds: inverseEdgeKinds,
3119
+ duplicateGuard
3120
+ });
3121
+ return drizzleOrm.sql`
3122
+ ${directBranch}
3123
+ UNION ALL
3124
+ ${inverseBranch}
3125
+ `;
2628
3126
  }
2629
- if (vectorPredicate) {
2630
- const nodeAlias = vectorPredicate.field.alias;
2631
- joins.push(
2632
- drizzleOrm.sql`INNER JOIN cte_embeddings ON cte_embeddings.node_id = cte_${drizzleOrm.sql.raw(nodeAlias)}.${drizzleOrm.sql.raw(nodeAlias)}_id`
2633
- );
3127
+ const recursiveBranchSql = inverseEdgeKinds.length === 0 ? directBranch : compileInverseRecursiveBranch();
3128
+ const baseSelectColumns = [
3129
+ ...startColumnsFromBase,
3130
+ ...nodeColumnsFromBase,
3131
+ drizzleOrm.sql`0 AS depth`
3132
+ ];
3133
+ if (initialPath !== void 0) {
3134
+ baseSelectColumns.push(drizzleOrm.sql`${initialPath} AS path`);
2634
3135
  }
2635
- return joins.length === 0 ? fromClause : drizzleOrm.sql`${fromClause} ${drizzleOrm.sql.join(joins, drizzleOrm.sql` `)}`;
3136
+ return drizzleOrm.sql`
3137
+ recursive_cte AS (
3138
+ -- Base case: starting nodes
3139
+ SELECT ${drizzleOrm.sql.join(baseSelectColumns, drizzleOrm.sql`, `)}
3140
+ FROM ${ctx.schema.nodesTable} n0
3141
+ WHERE ${drizzleOrm.sql.join(baseWhereClauses, drizzleOrm.sql` AND `)}
3142
+
3143
+ UNION ALL
3144
+
3145
+ -- Recursive case: follow edges
3146
+ ${recursiveBranchSql}
3147
+ )
3148
+ `;
2636
3149
  }
2637
- function buildStandardOrderBy(input) {
2638
- const { ast, collapsedTraversalCteAlias, dialect } = input;
2639
- if (!ast.orderBy || ast.orderBy.length === 0) {
2640
- return void 0;
2641
- }
2642
- const parts = [];
2643
- for (const orderSpec of ast.orderBy) {
2644
- const valueType = orderSpec.field.valueType;
2645
- if (valueType === "array" || valueType === "object") {
2646
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2647
- "Ordering by JSON arrays or objects is not supported"
2648
- );
2649
- }
2650
- const cteAlias = collapsedTraversalCteAlias ?? `cte_${orderSpec.field.alias}`;
2651
- const field2 = compileFieldValue(
2652
- orderSpec.field,
2653
- dialect,
2654
- valueType,
2655
- cteAlias
2656
- );
2657
- const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
2658
- const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
2659
- const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
2660
- parts.push(
2661
- drizzleOrm.sql`(${field2} IS NULL) ${nullsDirection}`,
2662
- drizzleOrm.sql`${field2} ${direction}`
2663
- );
2664
- }
2665
- return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join(parts, drizzleOrm.sql`, `)}`;
3150
+ function compileKindFilter2(kinds, columnExpr) {
3151
+ return compileKindFilter(drizzleOrm.sql.raw(columnExpr), kinds);
2666
3152
  }
2667
- function fieldRefKey(field2) {
2668
- const pointer = field2.jsonPointer ?? "";
2669
- return `${field2.alias}:${field2.path.join(".")}:${pointer}`;
3153
+ function compileNodePredicates(ast, alias, ctx) {
3154
+ return ast.predicates.filter((p) => p.targetAlias === alias && p.targetType !== "edge").map((p) => compilePredicateExpression(p.expression, ctx));
2670
3155
  }
2671
- function buildStandardGroupBy(input) {
2672
- const { ast, dialect } = input;
2673
- if (!ast.groupBy || ast.groupBy.fields.length === 0) {
3156
+ function compileEdgePredicates(ast, edgeAlias, ctx) {
3157
+ return ast.predicates.filter((p) => p.targetAlias === edgeAlias && p.targetType === "edge").map((p) => compilePredicateExpression(p.expression, ctx));
3158
+ }
3159
+ function collectRequiredColumnsByAlias(ast, traversal) {
3160
+ const selectiveFields = ast.selectiveFields;
3161
+ if (selectiveFields === void 0 || selectiveFields.length === 0) {
2674
3162
  return void 0;
2675
3163
  }
2676
- const seenKeys = /* @__PURE__ */ new Set();
2677
- const allFields = [];
2678
- for (const projectedField of ast.projection.fields) {
2679
- if (projectedField.source.__type === "field_ref") {
2680
- const key = fieldRefKey(projectedField.source);
2681
- if (!seenKeys.has(key)) {
2682
- seenKeys.add(key);
2683
- allFields.push(projectedField.source);
2684
- }
2685
- }
3164
+ const requiredColumnsByAlias = /* @__PURE__ */ new Map();
3165
+ const previousNodeKinds = [
3166
+ .../* @__PURE__ */ new Set([...ast.start.kinds, ...traversal.nodeKinds])
3167
+ ];
3168
+ addRequiredColumn(requiredColumnsByAlias, traversal.nodeAlias, "id");
3169
+ if (previousNodeKinds.length > 1) {
3170
+ addRequiredColumn(requiredColumnsByAlias, traversal.nodeAlias, "kind");
2686
3171
  }
2687
- for (const field2 of ast.groupBy.fields) {
2688
- const key = fieldRefKey(field2);
2689
- if (!seenKeys.has(key)) {
2690
- seenKeys.add(key);
2691
- allFields.push(field2);
2692
- }
3172
+ for (const field2 of selectiveFields) {
3173
+ markSelectiveFieldAsRequired(requiredColumnsByAlias, field2);
2693
3174
  }
2694
- if (allFields.length === 0) {
2695
- return void 0;
3175
+ if (ast.orderBy) {
3176
+ for (const orderSpec of ast.orderBy) {
3177
+ markFieldRefAsRequired(requiredColumnsByAlias, orderSpec.field);
3178
+ }
2696
3179
  }
2697
- const parts = allFields.map(
2698
- (field2) => compileFieldValue(field2, dialect, field2.valueType, `cte_${field2.alias}`)
3180
+ return requiredColumnsByAlias;
3181
+ }
3182
+ function compileNodeSelectColumnsFromTable(tableAlias, alias, requiredColumns, alwaysRequiredColumns) {
3183
+ return NODE_COLUMNS.filter(
3184
+ (column) => shouldProjectColumn(requiredColumns, column, alwaysRequiredColumns)
3185
+ ).map(
3186
+ (column) => drizzleOrm.sql`${drizzleOrm.sql.raw(tableAlias)}.${drizzleOrm.sql.raw(column)} AS ${drizzleOrm.sql.raw(`${alias}_${column}`)}`
2699
3187
  );
2700
- return drizzleOrm.sql`GROUP BY ${drizzleOrm.sql.join(parts, drizzleOrm.sql`, `)}`;
2701
3188
  }
2702
- function buildStandardHaving(input) {
2703
- const { ast, ctx } = input;
2704
- if (!ast.having) {
2705
- return void 0;
2706
- }
2707
- const condition = compilePredicateExpression(ast.having, ctx);
2708
- return drizzleOrm.sql`HAVING ${condition}`;
3189
+ function compileNodeSelectColumnsFromRecursiveRow(alias, requiredColumns, alwaysRequiredColumns) {
3190
+ return NODE_COLUMNS.filter(
3191
+ (column) => shouldProjectColumn(requiredColumns, column, alwaysRequiredColumns)
3192
+ ).map((column) => {
3193
+ const projected = `${alias}_${column}`;
3194
+ return drizzleOrm.sql`r.${drizzleOrm.sql.raw(projected)} AS ${drizzleOrm.sql.raw(projected)}`;
3195
+ });
2709
3196
  }
2710
- function buildStandardEmbeddingsCte(input) {
2711
- const { ctx, graphId, vectorPredicate } = input;
2712
- const { dialect } = ctx;
2713
- const { field: field2, metric, minScore, queryEmbedding } = vectorPredicate;
2714
- const fieldPath = field2.jsonPointer ? field2.jsonPointer : field2.path.length > 1 && field2.path[0] === "props" ? `/${field2.path.slice(1).join("/")}` : `/${field2.path.join("/")}`;
2715
- const distanceExpr = dialect.vectorDistance(
2716
- drizzleOrm.sql.raw("embedding"),
2717
- queryEmbedding,
2718
- metric
2719
- );
2720
- const conditions = [
2721
- drizzleOrm.sql`graph_id = ${graphId}`,
2722
- drizzleOrm.sql`field_path = ${fieldPath}`
2723
- ];
2724
- if (minScore !== void 0) {
2725
- conditions.push(
2726
- compileVectorMinScoreCondition(distanceExpr, metric, minScore)
3197
+ function compileRecursiveProjection(ast, traversal, dialect) {
3198
+ if (ast.selectiveFields && ast.selectiveFields.length > 0) {
3199
+ return compileRecursiveSelectiveProjection(
3200
+ ast.selectiveFields,
3201
+ ast,
3202
+ traversal,
3203
+ dialect
2727
3204
  );
2728
3205
  }
2729
- const scoreExpr = compileVectorScoreExpression(distanceExpr, metric);
2730
- return drizzleOrm.sql`
2731
- cte_embeddings AS (
2732
- SELECT
2733
- node_id,
2734
- ${distanceExpr} AS distance,
2735
- ${scoreExpr} AS score
2736
- FROM ${ctx.schema.embeddingsTable}
2737
- WHERE ${drizzleOrm.sql.join(conditions, drizzleOrm.sql` AND `)}
2738
- ORDER BY ${distanceExpr} ASC
2739
- )
2740
- `;
2741
- }
2742
- function compileVectorScoreExpression(distanceExpr, metric) {
2743
- switch (metric) {
2744
- case "cosine": {
2745
- return drizzleOrm.sql`(1.0 - ${distanceExpr})`;
2746
- }
2747
- case "l2":
2748
- case "inner_product": {
2749
- return distanceExpr;
2750
- }
3206
+ const startAlias = ast.start.alias;
3207
+ const nodeAlias = traversal.nodeAlias;
3208
+ const vl = traversal.variableLength;
3209
+ const fields = [
3210
+ // Start alias fields with metadata
3211
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_id`,
3212
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_kind`,
3213
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_props`,
3214
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_version`,
3215
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_valid_from`,
3216
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_valid_to`,
3217
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_created_at`,
3218
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_updated_at`,
3219
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_deleted_at`,
3220
+ // Node alias fields with metadata
3221
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_id`,
3222
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_kind`,
3223
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_props`,
3224
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_version`,
3225
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_valid_from`,
3226
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_valid_to`,
3227
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_created_at`,
3228
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_updated_at`,
3229
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_deleted_at`
3230
+ ];
3231
+ if (vl.depthAlias !== void 0) {
3232
+ fields.push(drizzleOrm.sql`depth AS ${quoteIdentifier(vl.depthAlias)}`);
3233
+ }
3234
+ if (vl.pathAlias !== void 0) {
3235
+ fields.push(drizzleOrm.sql`path AS ${quoteIdentifier(vl.pathAlias)}`);
2751
3236
  }
3237
+ return drizzleOrm.sql.join(fields, drizzleOrm.sql`, `);
2752
3238
  }
2753
- function compileVectorMinScoreCondition(distanceExpr, metric, minScore) {
2754
- switch (metric) {
2755
- case "cosine": {
2756
- const threshold = 1 - minScore;
2757
- return drizzleOrm.sql`${distanceExpr} <= ${threshold}`;
2758
- }
2759
- case "l2": {
2760
- return drizzleOrm.sql`${distanceExpr} <= ${minScore}`;
3239
+ function compileRecursiveSelectiveProjection(fields, ast, traversal, dialect) {
3240
+ const allowedAliases = /* @__PURE__ */ new Set([ast.start.alias, traversal.nodeAlias]);
3241
+ const columns = fields.map((field2) => {
3242
+ if (!allowedAliases.has(field2.alias)) {
3243
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
3244
+ `Selective projection for recursive traversals does not support alias "${field2.alias}"`
3245
+ );
2761
3246
  }
2762
- case "inner_product": {
2763
- const negativeThreshold = -minScore;
2764
- return drizzleOrm.sql`${distanceExpr} <= ${negativeThreshold}`;
3247
+ if (field2.isSystemField) {
3248
+ const dbColumn = mapSelectiveSystemFieldToColumn(field2.field);
3249
+ return drizzleOrm.sql`${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${quoteIdentifier(field2.outputName)}`;
2765
3250
  }
3251
+ const column = drizzleOrm.sql.raw(`${field2.alias}_props`);
3252
+ const extracted = compileTypedJsonExtract({
3253
+ column,
3254
+ dialect,
3255
+ pointer: chunkP5CNM325_cjs.jsonPointer([field2.field]),
3256
+ valueType: field2.valueType
3257
+ });
3258
+ return drizzleOrm.sql`${extracted} AS ${quoteIdentifier(field2.outputName)}`;
3259
+ });
3260
+ const vl = traversal.variableLength;
3261
+ if (vl.depthAlias !== void 0) {
3262
+ columns.push(drizzleOrm.sql`depth AS ${quoteIdentifier(vl.depthAlias)}`);
2766
3263
  }
2767
- }
2768
- function buildStandardVectorOrderBy(input) {
2769
- const { ast, dialect } = input;
2770
- const distanceOrder = drizzleOrm.sql`cte_embeddings.distance ASC`;
2771
- const additionalOrders = [];
2772
- if (ast.orderBy && ast.orderBy.length > 0) {
2773
- for (const orderSpec of ast.orderBy) {
2774
- const valueType = orderSpec.field.valueType;
2775
- if (valueType === "array" || valueType === "object") {
2776
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2777
- "Ordering by JSON arrays or objects is not supported"
2778
- );
2779
- }
2780
- const cteAlias = `cte_${orderSpec.field.alias}`;
2781
- const field2 = compileFieldValue(
2782
- orderSpec.field,
2783
- dialect,
2784
- valueType,
2785
- cteAlias
2786
- );
2787
- const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
2788
- const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
2789
- const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
2790
- additionalOrders.push(
2791
- drizzleOrm.sql`(${field2} IS NULL) ${nullsDirection}`,
2792
- drizzleOrm.sql`${field2} ${direction}`
2793
- );
2794
- }
3264
+ if (vl.pathAlias !== void 0) {
3265
+ columns.push(drizzleOrm.sql`path AS ${quoteIdentifier(vl.pathAlias)}`);
2795
3266
  }
2796
- const allOrders = [distanceOrder, ...additionalOrders];
2797
- return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join(allOrders, drizzleOrm.sql`, `)}`;
3267
+ return drizzleOrm.sql.join(columns, drizzleOrm.sql`, `);
2798
3268
  }
2799
- function buildLimitOffsetClause(input) {
2800
- const { limit, offset } = input;
2801
- const parts = [];
2802
- if (limit !== void 0) {
2803
- parts.push(drizzleOrm.sql`LIMIT ${limit}`);
2804
- }
2805
- if (offset !== void 0) {
2806
- parts.push(drizzleOrm.sql`OFFSET ${offset}`);
3269
+ function compileRecursiveOrderBy(ast, dialect) {
3270
+ if (!ast.orderBy || ast.orderBy.length === 0) {
3271
+ return void 0;
2807
3272
  }
2808
- return parts.length > 0 ? drizzleOrm.sql.join(parts, drizzleOrm.sql` `) : void 0;
2809
- }
2810
-
2811
- // src/query/compiler/recursive.ts
2812
- var MAX_RECURSIVE_DEPTH = 100;
2813
- var MAX_EXPLICIT_RECURSIVE_DEPTH = 1e3;
2814
- var NO_ALWAYS_REQUIRED_COLUMNS = /* @__PURE__ */ new Set();
2815
- function runRecursiveQueryPassPipeline(ast, graphId, ctx) {
2816
- let state = {
2817
- ast,
2818
- ctx,
2819
- graphId,
2820
- logicalPlan: void 0,
2821
- requiredColumnsByAlias: void 0,
2822
- temporalFilterPass: void 0,
2823
- traversal: void 0
2824
- };
2825
- const recursiveTraversalPass = runCompilerPass(state, {
2826
- name: "recursive_traversal",
2827
- execute(currentState) {
2828
- return runRecursiveTraversalSelectionPass(currentState.ast);
2829
- },
2830
- update(currentState, traversal) {
2831
- return {
2832
- ...currentState,
2833
- traversal
2834
- };
2835
- }
2836
- });
2837
- state = recursiveTraversalPass.state;
2838
- const temporalPass = runCompilerPass(state, {
2839
- name: "temporal_filters",
2840
- execute(currentState) {
2841
- return createTemporalFilterPass(
2842
- currentState.ast,
2843
- currentState.ctx.dialect.currentTimestamp()
3273
+ const parts = [];
3274
+ for (const orderSpec of ast.orderBy) {
3275
+ const valueType = orderSpec.field.valueType;
3276
+ if (valueType === "array" || valueType === "object") {
3277
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
3278
+ "Ordering by JSON arrays or objects is not supported"
2844
3279
  );
2845
- },
2846
- update(currentState, temporalFilterPass) {
2847
- return {
2848
- ...currentState,
2849
- temporalFilterPass
2850
- };
2851
3280
  }
2852
- });
2853
- state = temporalPass.state;
2854
- const columnPruningPass = runCompilerPass(state, {
2855
- name: "column_pruning",
2856
- execute(currentState) {
2857
- const traversal = currentState.traversal;
2858
- if (traversal === void 0) {
2859
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2860
- "Recursive traversal pass did not select traversal"
2861
- );
2862
- }
2863
- return collectRequiredColumnsByAlias(currentState.ast, traversal);
2864
- },
2865
- update(currentState, requiredColumnsByAlias) {
2866
- return {
2867
- ...currentState,
2868
- requiredColumnsByAlias
2869
- };
2870
- }
2871
- });
2872
- state = columnPruningPass.state;
2873
- const logicalPlanPass = runCompilerPass(state, {
2874
- name: "logical_plan",
2875
- execute(currentState) {
2876
- const loweringInput = {
2877
- ast: currentState.ast,
2878
- dialect: currentState.ctx.dialect.name,
2879
- graphId: currentState.graphId,
2880
- ...currentState.traversal === void 0 ? {} : { traversal: currentState.traversal }
2881
- };
2882
- return lowerRecursiveQueryToLogicalPlan(loweringInput);
2883
- },
2884
- update(currentState, logicalPlan) {
2885
- return {
2886
- ...currentState,
2887
- logicalPlan
2888
- };
2889
- }
2890
- });
2891
- state = logicalPlanPass.state;
2892
- return state;
2893
- }
2894
- function compileVariableLengthQuery(ast, graphId, ctx) {
2895
- const strategy = ctx.dialect.capabilities.recursiveQueryStrategy;
2896
- const handler = RECURSIVE_QUERY_STRATEGY_HANDLERS[strategy];
2897
- return handler(ast, graphId, ctx);
2898
- }
2899
- var RECURSIVE_QUERY_STRATEGY_HANDLERS = {
2900
- recursive_cte: compileVariableLengthQueryWithRecursiveCteStrategy
2901
- };
2902
- function compileVariableLengthQueryWithRecursiveCteStrategy(ast, graphId, ctx) {
2903
- const passState = runRecursiveQueryPassPipeline(ast, graphId, ctx);
2904
- const { dialect } = ctx;
2905
- const {
2906
- logicalPlan,
2907
- requiredColumnsByAlias,
2908
- temporalFilterPass,
2909
- traversal: vlTraversal
2910
- } = passState;
2911
- if (temporalFilterPass === void 0) {
2912
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2913
- "Temporal filter pass did not initialize temporal state"
3281
+ const field2 = compileFieldValue(orderSpec.field, dialect, valueType);
3282
+ const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
3283
+ const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
3284
+ const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
3285
+ parts.push(
3286
+ drizzleOrm.sql`(${field2} IS NULL) ${nullsDirection}`,
3287
+ drizzleOrm.sql`${field2} ${direction}`
2914
3288
  );
2915
3289
  }
2916
- if (logicalPlan === void 0) {
2917
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2918
- "Logical plan pass did not initialize plan state"
2919
- );
3290
+ return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join(parts, drizzleOrm.sql`, `)}`;
3291
+ }
3292
+ function compileLimitOffset(ast) {
3293
+ const parts = [];
3294
+ if (ast.limit !== void 0) {
3295
+ parts.push(drizzleOrm.sql`LIMIT ${ast.limit}`);
2920
3296
  }
2921
- if (vlTraversal === void 0) {
2922
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2923
- "Recursive traversal pass did not select traversal"
2924
- );
3297
+ if (ast.offset !== void 0) {
3298
+ parts.push(drizzleOrm.sql`OFFSET ${ast.offset}`);
2925
3299
  }
2926
- const recursiveCte = compileRecursiveCte(
2927
- ast,
2928
- vlTraversal,
2929
- graphId,
2930
- ctx,
2931
- requiredColumnsByAlias,
2932
- temporalFilterPass
2933
- );
2934
- const projection = compileRecursiveProjection(ast, vlTraversal, dialect);
2935
- const minDepth = vlTraversal.variableLength.minDepth;
2936
- const depthFilter = minDepth > 0 ? drizzleOrm.sql`WHERE depth >= ${minDepth}` : drizzleOrm.sql.raw("");
2937
- const orderBy = compileRecursiveOrderBy(ast, dialect);
2938
- const limitOffset = compileLimitOffset(ast);
2939
- return emitRecursiveQuerySql({
2940
- depthFilter,
2941
- ...limitOffset === void 0 ? {} : { limitOffset },
2942
- logicalPlan,
2943
- ...orderBy === void 0 ? {} : { orderBy },
2944
- projection,
2945
- recursiveCte
2946
- });
2947
- }
2948
- function hasVariableLengthTraversal(ast) {
2949
- return ast.traversals.some((t) => t.variableLength !== void 0);
3300
+ return parts.length > 0 ? drizzleOrm.sql.join(parts, drizzleOrm.sql` `) : void 0;
2950
3301
  }
2951
- function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlias, temporalFilterPass) {
2952
- const { dialect } = ctx;
2953
- const startAlias = ast.start.alias;
2954
- const startKinds = ast.start.kinds;
2955
- const nodeAlias = traversal.nodeAlias;
2956
- const directEdgeKinds = [...new Set(traversal.edgeKinds)];
2957
- const inverseEdgeKinds = traversal.inverseEdgeKinds === void 0 ? [] : [...new Set(traversal.inverseEdgeKinds)];
2958
- const forceWorktableOuterJoinOrder = dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
2959
- const nodeKinds = traversal.nodeKinds;
2960
- const previousNodeKinds = [.../* @__PURE__ */ new Set([...startKinds, ...nodeKinds])];
2961
- const direction = traversal.direction;
2962
- const vl = traversal.variableLength;
2963
- const shouldEnforceCycleCheck = vl.cyclePolicy !== "allow";
2964
- const shouldTrackPath = shouldEnforceCycleCheck || vl.pathAlias !== void 0;
2965
- const recursiveJoinRequiredColumns = /* @__PURE__ */ new Set(["id"]);
2966
- if (previousNodeKinds.length > 1) {
2967
- recursiveJoinRequiredColumns.add("kind");
3302
+ var DEFAULT_TABLE_NAMES = {
3303
+ nodes: "typegraph_nodes",
3304
+ edges: "typegraph_edges",
3305
+ embeddings: "typegraph_node_embeddings"
3306
+ };
3307
+ var MAX_IDENTIFIER_LENGTH = 63;
3308
+ var VALID_IDENTIFIER_PATTERN = /^[a-z_][a-z0-9_$]*$/i;
3309
+ function validateTableName(name, label) {
3310
+ if (!name || name.length === 0) {
3311
+ throw new chunk44SXEVF4_cjs.ConfigurationError(`${label} table name cannot be empty`);
2968
3312
  }
2969
- const requiredStartColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(startAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
2970
- const requiredNodeColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(nodeAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
2971
- const startColumnsFromBase = compileNodeSelectColumnsFromTable(
2972
- "n0",
2973
- startAlias,
2974
- requiredStartColumns,
2975
- NO_ALWAYS_REQUIRED_COLUMNS
2976
- );
2977
- const startColumnsFromRecursive = compileNodeSelectColumnsFromRecursiveRow(
2978
- startAlias,
2979
- requiredStartColumns,
2980
- NO_ALWAYS_REQUIRED_COLUMNS
2981
- );
2982
- const nodeColumnsFromBase = compileNodeSelectColumnsFromTable(
2983
- "n0",
2984
- nodeAlias,
2985
- requiredNodeColumns,
2986
- recursiveJoinRequiredColumns
2987
- );
2988
- const nodeColumnsFromRecursive = compileNodeSelectColumnsFromTable(
2989
- "n",
2990
- nodeAlias,
2991
- requiredNodeColumns,
2992
- recursiveJoinRequiredColumns
2993
- );
2994
- const startKindFilter = compileKindFilter2(startKinds, "n0.kind");
2995
- const nodeKindFilter = compileKindFilter2(nodeKinds, "n.kind");
2996
- const startTemporalFilter = temporalFilterPass.forAlias("n0");
2997
- const edgeTemporalFilter = temporalFilterPass.forAlias("e");
2998
- const nodeTemporalFilter = temporalFilterPass.forAlias("n");
2999
- const startContext = { ...ctx, cteColumnPrefix: "" };
3000
- const startPredicates = compileNodePredicates(ast, startAlias, startContext);
3001
- const edgeContext = { ...ctx, cteColumnPrefix: "e" };
3002
- const edgePredicates = compileEdgePredicates(
3003
- ast,
3004
- traversal.edgeAlias,
3005
- edgeContext
3006
- );
3007
- const targetContext = { ...ctx, cteColumnPrefix: "n" };
3008
- const targetNodePredicates = compileNodePredicates(
3009
- ast,
3010
- nodeAlias,
3011
- targetContext
3012
- );
3013
- if (vl.maxDepth > MAX_EXPLICIT_RECURSIVE_DEPTH) {
3014
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
3015
- `Recursive traversal maxHops(${vl.maxDepth}) exceeds maximum explicit depth of ${MAX_EXPLICIT_RECURSIVE_DEPTH}`
3313
+ if (name.length > MAX_IDENTIFIER_LENGTH) {
3314
+ throw new chunk44SXEVF4_cjs.ConfigurationError(
3315
+ `${label} table name exceeds maximum length of ${MAX_IDENTIFIER_LENGTH} characters`
3016
3316
  );
3017
3317
  }
3018
- const effectiveMaxDepth = vl.maxDepth > 0 ? vl.maxDepth : MAX_RECURSIVE_DEPTH;
3019
- const maxDepthCondition = drizzleOrm.sql`r.depth < ${effectiveMaxDepth}`;
3020
- const cycleCheck = shouldEnforceCycleCheck ? dialect.cycleCheck(drizzleOrm.sql.raw("n.id"), drizzleOrm.sql.raw("r.path")) : void 0;
3021
- const initialPath = shouldTrackPath ? dialect.initializePath(drizzleOrm.sql.raw("n0.id")) : void 0;
3022
- const pathExtension = shouldTrackPath ? dialect.extendPath(drizzleOrm.sql.raw("r.path"), drizzleOrm.sql.raw("n.id")) : void 0;
3023
- const baseWhereClauses = [
3024
- drizzleOrm.sql`n0.graph_id = ${graphId}`,
3025
- startKindFilter,
3026
- startTemporalFilter,
3027
- ...startPredicates
3028
- ];
3029
- const recursiveBaseWhereClauses = [
3030
- drizzleOrm.sql`e.graph_id = ${graphId}`,
3031
- nodeKindFilter,
3032
- edgeTemporalFilter,
3033
- nodeTemporalFilter,
3034
- maxDepthCondition
3035
- ];
3036
- if (cycleCheck !== void 0) {
3037
- recursiveBaseWhereClauses.push(cycleCheck);
3318
+ if (!VALID_IDENTIFIER_PATTERN.test(name)) {
3319
+ throw new chunk44SXEVF4_cjs.ConfigurationError(
3320
+ `${label} table name "${name}" is not a valid SQL identifier. Table names must start with a letter or underscore and contain only letters, digits, underscores, or dollar signs.`
3321
+ );
3038
3322
  }
3039
- recursiveBaseWhereClauses.push(...edgePredicates, ...targetNodePredicates);
3040
- function compileRecursiveBranch2(branch) {
3041
- const recursiveFilterClauses = [
3042
- ...recursiveBaseWhereClauses,
3043
- compileKindFilter2(branch.edgeKinds, "e.kind"),
3044
- compileKindFilter2(previousNodeKinds, `e.${branch.joinKindField}`),
3045
- compileKindFilter2(nodeKinds, `e.${branch.targetKindField}`)
3046
- ];
3047
- if (branch.duplicateGuard !== void 0) {
3048
- recursiveFilterClauses.push(branch.duplicateGuard);
3323
+ }
3324
+ function quoteIdentifier2(name) {
3325
+ return `"${name.replaceAll('"', '""')}"`;
3326
+ }
3327
+ function createSqlSchema(names = {}) {
3328
+ const tables = { ...DEFAULT_TABLE_NAMES, ...names };
3329
+ validateTableName(tables.nodes, "nodes");
3330
+ validateTableName(tables.edges, "edges");
3331
+ validateTableName(tables.embeddings, "embeddings");
3332
+ return {
3333
+ tables,
3334
+ nodesTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.nodes)),
3335
+ edgesTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.edges)),
3336
+ embeddingsTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.embeddings))
3337
+ };
3338
+ }
3339
+ var DEFAULT_SQL_SCHEMA = createSqlSchema();
3340
+
3341
+ // src/query/execution/value-decoder.ts
3342
+ function nullToUndefined(value) {
3343
+ return value === null ? void 0 : value;
3344
+ }
3345
+ function decodeSelectedValue(value, typeInfo) {
3346
+ const normalized = nullToUndefined(value);
3347
+ if (normalized === void 0) return void 0;
3348
+ if (typeInfo === void 0) {
3349
+ return normalized;
3350
+ }
3351
+ return decodeByValueType(normalized, typeInfo.valueType);
3352
+ }
3353
+ function decodeByValueType(value, valueType) {
3354
+ switch (valueType) {
3355
+ case "boolean": {
3356
+ if (typeof value === "boolean") return value;
3357
+ if (typeof value === "number") return value !== 0;
3358
+ if (typeof value === "string") {
3359
+ if (value === "0") return false;
3360
+ if (value === "1") return true;
3361
+ if (value.toLowerCase() === "true") return true;
3362
+ if (value.toLowerCase() === "false") return false;
3363
+ }
3364
+ return Boolean(value);
3049
3365
  }
3050
- const recursiveSelectColumns = [
3051
- ...startColumnsFromRecursive,
3052
- ...nodeColumnsFromRecursive,
3053
- drizzleOrm.sql`r.depth + 1 AS depth`
3054
- ];
3055
- if (pathExtension !== void 0) {
3056
- recursiveSelectColumns.push(drizzleOrm.sql`${pathExtension} AS path`);
3366
+ case "number": {
3367
+ if (typeof value === "number") return value;
3368
+ if (typeof value === "string") {
3369
+ const parsed = Number(value);
3370
+ return Number.isNaN(parsed) ? value : parsed;
3371
+ }
3372
+ return value;
3057
3373
  }
3058
- const recursiveJoinClauses = [
3059
- drizzleOrm.sql`e.${drizzleOrm.sql.raw(branch.joinField)} = r.${drizzleOrm.sql.raw(nodeAlias)}_id`
3060
- ];
3061
- if (previousNodeKinds.length > 1) {
3062
- recursiveJoinClauses.push(
3063
- drizzleOrm.sql`e.${drizzleOrm.sql.raw(branch.joinKindField)} = r.${drizzleOrm.sql.raw(nodeAlias)}_kind`
3064
- );
3374
+ case "array":
3375
+ case "object":
3376
+ case "embedding": {
3377
+ if (typeof value !== "string") return value;
3378
+ const trimmed = value.trim();
3379
+ const looksJson = trimmed.startsWith("[") || trimmed.startsWith("{");
3380
+ if (!looksJson) return value;
3381
+ try {
3382
+ return JSON.parse(trimmed);
3383
+ } catch {
3384
+ return value;
3385
+ }
3065
3386
  }
3066
- if (forceWorktableOuterJoinOrder) {
3067
- const recursiveWhereClauses = [
3068
- ...recursiveJoinClauses,
3069
- ...recursiveFilterClauses
3070
- ];
3071
- return drizzleOrm.sql`
3072
- SELECT ${drizzleOrm.sql.join(recursiveSelectColumns, drizzleOrm.sql`, `)}
3073
- FROM recursive_cte r
3074
- CROSS JOIN ${ctx.schema.edgesTable} e
3075
- JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
3076
- AND n.id = e.${drizzleOrm.sql.raw(branch.targetField)}
3077
- AND n.kind = e.${drizzleOrm.sql.raw(branch.targetKindField)}
3078
- WHERE ${drizzleOrm.sql.join(recursiveWhereClauses, drizzleOrm.sql` AND `)}
3079
- `;
3387
+ case "string":
3388
+ case "date":
3389
+ case "unknown": {
3390
+ return value;
3391
+ }
3392
+ default: {
3393
+ return value;
3080
3394
  }
3081
- return drizzleOrm.sql`
3082
- SELECT ${drizzleOrm.sql.join(recursiveSelectColumns, drizzleOrm.sql`, `)}
3083
- FROM recursive_cte r
3084
- JOIN ${ctx.schema.edgesTable} e ON ${drizzleOrm.sql.join(recursiveJoinClauses, drizzleOrm.sql` AND `)}
3085
- JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
3086
- AND n.id = e.${drizzleOrm.sql.raw(branch.targetField)}
3087
- AND n.kind = e.${drizzleOrm.sql.raw(branch.targetKindField)}
3088
- WHERE ${drizzleOrm.sql.join(recursiveFilterClauses, drizzleOrm.sql` AND `)}
3089
- `;
3090
3395
  }
3091
- const directJoinField = direction === "out" ? "from_id" : "to_id";
3092
- const directTargetField = direction === "out" ? "to_id" : "from_id";
3093
- const directJoinKindField = direction === "out" ? "from_kind" : "to_kind";
3094
- const directTargetKindField = direction === "out" ? "to_kind" : "from_kind";
3095
- const directBranch = compileRecursiveBranch2({
3096
- joinField: directJoinField,
3097
- targetField: directTargetField,
3098
- joinKindField: directJoinKindField,
3099
- targetKindField: directTargetKindField,
3100
- edgeKinds: directEdgeKinds
3101
- });
3102
- function compileInverseRecursiveBranch() {
3103
- const inverseJoinField = direction === "out" ? "to_id" : "from_id";
3104
- const inverseTargetField = direction === "out" ? "from_id" : "to_id";
3105
- const inverseJoinKindField = direction === "out" ? "to_kind" : "from_kind";
3106
- const inverseTargetKindField = direction === "out" ? "from_kind" : "to_kind";
3107
- const overlappingKinds = inverseEdgeKinds.filter(
3108
- (kind) => directEdgeKinds.includes(kind)
3396
+ }
3397
+
3398
+ // src/store/reserved-keys.ts
3399
+ var RESERVED_NODE_KEYS2 = /* @__PURE__ */ new Set([
3400
+ "id",
3401
+ "kind",
3402
+ "meta"
3403
+ ]);
3404
+ var RESERVED_EDGE_KEYS2 = /* @__PURE__ */ new Set([
3405
+ "id",
3406
+ "kind",
3407
+ "meta",
3408
+ "fromKind",
3409
+ "fromId",
3410
+ "toKind",
3411
+ "toId"
3412
+ ]);
3413
+ var PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set([
3414
+ "__proto__",
3415
+ "constructor",
3416
+ "prototype"
3417
+ ]);
3418
+ function validateProjectionField(field2, entityType, kind) {
3419
+ const reserved = entityType === "node" ? RESERVED_NODE_KEYS2 : RESERVED_EDGE_KEYS2;
3420
+ if (reserved.has(field2)) {
3421
+ throw new chunk44SXEVF4_cjs.ConfigurationError(
3422
+ `Projection field "${field2}" on ${entityType} kind "${kind}" conflicts with a reserved structural key`,
3423
+ { field: field2, kind, entityType, reservedKeys: [...reserved] },
3424
+ {
3425
+ suggestion: `Remove "${field2}" from the projection. Structural fields (${[...reserved].join(", ")}) are included automatically when relevant.`
3426
+ }
3109
3427
  );
3110
- const duplicateGuard = overlappingKinds.length > 0 ? drizzleOrm.sql`NOT (e.from_id = e.to_id AND ${compileKindFilter2(overlappingKinds, "e.kind")})` : void 0;
3111
- const inverseBranch = compileRecursiveBranch2({
3112
- joinField: inverseJoinField,
3113
- targetField: inverseTargetField,
3114
- joinKindField: inverseJoinKindField,
3115
- targetKindField: inverseTargetKindField,
3116
- edgeKinds: inverseEdgeKinds,
3117
- duplicateGuard
3118
- });
3119
- return drizzleOrm.sql`
3120
- ${directBranch}
3121
- UNION ALL
3122
- ${inverseBranch}
3123
- `;
3124
3428
  }
3125
- const recursiveBranchSql = inverseEdgeKinds.length === 0 ? directBranch : compileInverseRecursiveBranch();
3126
- const baseSelectColumns = [
3127
- ...startColumnsFromBase,
3128
- ...nodeColumnsFromBase,
3129
- drizzleOrm.sql`0 AS depth`
3130
- ];
3131
- if (initialPath !== void 0) {
3132
- baseSelectColumns.push(drizzleOrm.sql`${initialPath} AS path`);
3429
+ if (PROTOTYPE_POLLUTION_KEYS.has(field2)) {
3430
+ throw new chunk44SXEVF4_cjs.ConfigurationError(
3431
+ `Projection field "${field2}" on ${entityType} kind "${kind}" is not allowed`,
3432
+ { field: field2, kind, entityType },
3433
+ {
3434
+ suggestion: `"${field2}" cannot be used as a projection field name.`
3435
+ }
3436
+ );
3133
3437
  }
3134
- return drizzleOrm.sql`
3135
- recursive_cte AS (
3136
- -- Base case: starting nodes
3137
- SELECT ${drizzleOrm.sql.join(baseSelectColumns, drizzleOrm.sql`, `)}
3138
- FROM ${ctx.schema.nodesTable} n0
3139
- WHERE ${drizzleOrm.sql.join(baseWhereClauses, drizzleOrm.sql` AND `)}
3140
-
3141
- UNION ALL
3438
+ }
3439
+ function filterReservedKeys(props, reservedKeys) {
3440
+ const filtered = {};
3441
+ for (const [key, value] of Object.entries(props)) {
3442
+ if (!reservedKeys.has(key)) {
3443
+ filtered[key] = value;
3444
+ }
3445
+ }
3446
+ return filtered;
3447
+ }
3142
3448
 
3143
- -- Recursive case: follow edges
3144
- ${recursiveBranchSql}
3145
- )
3146
- `;
3449
+ // src/store/row-mappers.ts
3450
+ function nullToUndefined2(value) {
3451
+ return value === null ? void 0 : value;
3147
3452
  }
3148
- function compileKindFilter2(kinds, columnExpr) {
3149
- return compileKindFilter(drizzleOrm.sql.raw(columnExpr), kinds);
3453
+ function rowToNode(row) {
3454
+ const rawProps = JSON.parse(row.props);
3455
+ const props = filterReservedKeys(rawProps, RESERVED_NODE_KEYS2);
3456
+ return {
3457
+ kind: row.kind,
3458
+ id: row.id,
3459
+ meta: rowToNodeMeta(row),
3460
+ ...props
3461
+ };
3150
3462
  }
3151
- function compileNodePredicates(ast, alias, ctx) {
3152
- return ast.predicates.filter((p) => p.targetAlias === alias && p.targetType !== "edge").map((p) => compilePredicateExpression(p.expression, ctx));
3463
+ function rowToNodeMeta(row) {
3464
+ return {
3465
+ version: row.version,
3466
+ validFrom: nullToUndefined2(row.valid_from),
3467
+ validTo: nullToUndefined2(row.valid_to),
3468
+ createdAt: row.created_at,
3469
+ updatedAt: row.updated_at,
3470
+ deletedAt: nullToUndefined2(row.deleted_at)
3471
+ };
3153
3472
  }
3154
- function compileEdgePredicates(ast, edgeAlias, ctx) {
3155
- return ast.predicates.filter((p) => p.targetAlias === edgeAlias && p.targetType === "edge").map((p) => compilePredicateExpression(p.expression, ctx));
3473
+ function rowToEdge(row) {
3474
+ const rawProps = JSON.parse(row.props);
3475
+ const props = filterReservedKeys(rawProps, RESERVED_EDGE_KEYS2);
3476
+ return {
3477
+ id: row.id,
3478
+ kind: row.kind,
3479
+ fromKind: row.from_kind,
3480
+ fromId: row.from_id,
3481
+ toKind: row.to_kind,
3482
+ toId: row.to_id,
3483
+ meta: rowToEdgeMeta(row),
3484
+ ...props
3485
+ };
3156
3486
  }
3157
- function collectRequiredColumnsByAlias(ast, traversal) {
3158
- const selectiveFields = ast.selectiveFields;
3159
- if (selectiveFields === void 0 || selectiveFields.length === 0) {
3160
- return void 0;
3161
- }
3162
- const requiredColumnsByAlias = /* @__PURE__ */ new Map();
3163
- const previousNodeKinds = [
3164
- .../* @__PURE__ */ new Set([...ast.start.kinds, ...traversal.nodeKinds])
3165
- ];
3166
- addRequiredColumn(requiredColumnsByAlias, traversal.nodeAlias, "id");
3167
- if (previousNodeKinds.length > 1) {
3168
- addRequiredColumn(requiredColumnsByAlias, traversal.nodeAlias, "kind");
3487
+ function rowToEdgeMeta(row) {
3488
+ return {
3489
+ validFrom: nullToUndefined2(row.valid_from),
3490
+ validTo: nullToUndefined2(row.valid_to),
3491
+ createdAt: row.created_at,
3492
+ updatedAt: row.updated_at,
3493
+ deletedAt: nullToUndefined2(row.deleted_at)
3494
+ };
3495
+ }
3496
+
3497
+ // src/store/subgraph.ts
3498
+ var DEFAULT_SUBGRAPH_MAX_DEPTH = 10;
3499
+ var MAX_PG_IDENTIFIER_LENGTH = 63;
3500
+ function fnv1aBase36(input) {
3501
+ let hash = 2166136261;
3502
+ for (const character of input) {
3503
+ const codePoint = character.codePointAt(0);
3504
+ if (codePoint === void 0) continue;
3505
+ hash ^= codePoint;
3506
+ hash = Math.imul(hash, 16777619);
3507
+ }
3508
+ return (hash >>> 0).toString(36);
3509
+ }
3510
+ var TEXT_ENCODER = new TextEncoder();
3511
+ function truncateToBytes(value, maxBytes) {
3512
+ const encoded = TEXT_ENCODER.encode(value);
3513
+ if (encoded.byteLength <= maxBytes) return value;
3514
+ let end = maxBytes;
3515
+ while (end > 0 && encoded[end] >= 128 && encoded[end] < 192) {
3516
+ end--;
3517
+ }
3518
+ return new TextDecoder().decode(encoded.slice(0, end));
3519
+ }
3520
+ function projectionAlias(entityPrefix, kind, field2) {
3521
+ const prefix = entityPrefix === "node" ? "sg_n" : "sg_e";
3522
+ const hash = fnv1aBase36(`${kind}\0${field2}`);
3523
+ const fixedBytes = prefix.length + 1 + 1 + hash.length;
3524
+ const maxKindBytes = MAX_PG_IDENTIFIER_LENGTH - fixedBytes;
3525
+ const truncatedKind = truncateToBytes(kind, maxKindBytes);
3526
+ return `${prefix}_${truncatedKind}_${hash}`;
3527
+ }
3528
+ function normalizeProps(value) {
3529
+ return typeof value === "string" ? value : JSON.stringify(value ?? {});
3530
+ }
3531
+ function defineSubgraphProject(_graph) {
3532
+ return (project) => project;
3533
+ }
3534
+ async function executeSubgraph(params) {
3535
+ const { options } = params;
3536
+ if (options.edges.length === 0) {
3537
+ return { nodes: [], edges: [] };
3169
3538
  }
3170
- for (const field2 of selectiveFields) {
3171
- markSelectiveFieldAsRequired(requiredColumnsByAlias, field2);
3539
+ const maxDepth = Math.min(
3540
+ options.maxDepth ?? DEFAULT_SUBGRAPH_MAX_DEPTH,
3541
+ MAX_RECURSIVE_DEPTH
3542
+ );
3543
+ const ctx = {
3544
+ graphId: params.graphId,
3545
+ rootId: params.rootId,
3546
+ edgeKinds: options.edges,
3547
+ maxDepth,
3548
+ includeKinds: options.includeKinds,
3549
+ excludeRoot: options.excludeRoot ?? false,
3550
+ direction: options.direction ?? "out",
3551
+ cyclePolicy: options.cyclePolicy ?? "prevent",
3552
+ dialect: params.dialect,
3553
+ schema: params.schema ?? DEFAULT_SQL_SCHEMA,
3554
+ backend: params.backend
3555
+ };
3556
+ const schemaIntrospector = getSubgraphSchemaIntrospector(params.graph);
3557
+ const nodeProjectionPlan = buildProjectionPlan(
3558
+ getIncludedNodeKinds(params.graph, options.includeKinds),
3559
+ options.project?.nodes,
3560
+ (kind, field2) => schemaIntrospector.getFieldTypeInfo(kind, field2),
3561
+ "node"
3562
+ );
3563
+ const edgeProjectionPlan = buildProjectionPlan(
3564
+ dedupeStrings(options.edges),
3565
+ options.project?.edges,
3566
+ (kind, field2) => schemaIntrospector.getEdgeFieldTypeInfo(kind, field2),
3567
+ "edge"
3568
+ );
3569
+ const reachableCte = buildReachableCte(ctx);
3570
+ const includedIdsCte = buildIncludedIdsCte(ctx);
3571
+ const [nodeRows, edgeRows] = await Promise.all([
3572
+ fetchSubgraphNodes(ctx, reachableCte, includedIdsCte, nodeProjectionPlan),
3573
+ fetchSubgraphEdges(ctx, reachableCte, includedIdsCte, edgeProjectionPlan)
3574
+ ]);
3575
+ const nodes = nodeRows.map(
3576
+ (row) => mapSubgraphNodeRow(row, nodeProjectionPlan)
3577
+ );
3578
+ const edges = edgeRows.map(
3579
+ (row) => mapSubgraphEdgeRow(row, edgeProjectionPlan)
3580
+ );
3581
+ return {
3582
+ nodes,
3583
+ edges
3584
+ };
3585
+ }
3586
+ var introspectorCache = /* @__PURE__ */ new WeakMap();
3587
+ function getSubgraphSchemaIntrospector(graph) {
3588
+ const cached = introspectorCache.get(graph);
3589
+ if (cached !== void 0) return cached;
3590
+ const nodeKinds = new Map(
3591
+ Object.entries(graph.nodes).map(([kind, definition]) => [
3592
+ kind,
3593
+ { schema: definition.type.schema }
3594
+ ])
3595
+ );
3596
+ const edgeKinds = new Map(
3597
+ Object.entries(graph.edges).map(([kind, definition]) => [
3598
+ kind,
3599
+ { schema: definition.type.schema }
3600
+ ])
3601
+ );
3602
+ const introspector = chunkP5CNM325_cjs.createSchemaIntrospector(nodeKinds, edgeKinds);
3603
+ introspectorCache.set(graph, introspector);
3604
+ return introspector;
3605
+ }
3606
+ function buildProjectionPlan(kinds, projectionMap, resolveFieldType, entityPrefix) {
3607
+ const projectedKinds = /* @__PURE__ */ new Map();
3608
+ const fullKinds = [];
3609
+ for (const kind of kinds) {
3610
+ const selection = projectionMap?.[kind];
3611
+ if (selection === void 0) {
3612
+ fullKinds.push(kind);
3613
+ continue;
3614
+ }
3615
+ projectedKinds.set(
3616
+ kind,
3617
+ buildKindProjectionPlan(kind, selection, resolveFieldType, entityPrefix)
3618
+ );
3172
3619
  }
3173
- if (ast.orderBy) {
3174
- for (const orderSpec of ast.orderBy) {
3175
- markFieldRefAsRequired(requiredColumnsByAlias, orderSpec.field);
3620
+ return { fullKinds, projectedKinds };
3621
+ }
3622
+ function buildKindProjectionPlan(kind, selection, resolveFieldType, entityPrefix) {
3623
+ const propertyFields = /* @__PURE__ */ new Map();
3624
+ let includeMeta = false;
3625
+ for (const field2 of selection) {
3626
+ if (field2 === "meta") {
3627
+ includeMeta = true;
3628
+ continue;
3629
+ }
3630
+ validateProjectionField(field2, entityPrefix, kind);
3631
+ if (!propertyFields.has(field2)) {
3632
+ propertyFields.set(field2, {
3633
+ field: field2,
3634
+ outputName: projectionAlias(entityPrefix, kind, field2),
3635
+ typeInfo: resolveFieldType(kind, field2)
3636
+ });
3176
3637
  }
3177
3638
  }
3178
- return requiredColumnsByAlias;
3639
+ return {
3640
+ includeMeta,
3641
+ propertyFields: [...propertyFields.values()]
3642
+ };
3179
3643
  }
3180
- function compileNodeSelectColumnsFromTable(tableAlias, alias, requiredColumns, alwaysRequiredColumns) {
3181
- return NODE_COLUMNS.filter(
3182
- (column) => shouldProjectColumn(requiredColumns, column, alwaysRequiredColumns)
3183
- ).map(
3184
- (column) => drizzleOrm.sql`${drizzleOrm.sql.raw(tableAlias)}.${drizzleOrm.sql.raw(column)} AS ${drizzleOrm.sql.raw(`${alias}_${column}`)}`
3185
- );
3644
+ function getIncludedNodeKinds(graph, includeKinds) {
3645
+ if (includeKinds === void 0 || includeKinds.length === 0) {
3646
+ return Object.keys(graph.nodes);
3647
+ }
3648
+ return dedupeStrings(includeKinds);
3186
3649
  }
3187
- function compileNodeSelectColumnsFromRecursiveRow(alias, requiredColumns, alwaysRequiredColumns) {
3188
- return NODE_COLUMNS.filter(
3189
- (column) => shouldProjectColumn(requiredColumns, column, alwaysRequiredColumns)
3190
- ).map((column) => {
3191
- const projected = `${alias}_${column}`;
3192
- return drizzleOrm.sql`r.${drizzleOrm.sql.raw(projected)} AS ${drizzleOrm.sql.raw(projected)}`;
3193
- });
3650
+ function dedupeStrings(values) {
3651
+ return [...new Set(values)];
3194
3652
  }
3195
- function compileRecursiveProjection(ast, traversal, dialect) {
3196
- if (ast.selectiveFields && ast.selectiveFields.length > 0) {
3197
- return compileRecursiveSelectiveProjection(
3198
- ast.selectiveFields,
3199
- ast,
3200
- traversal,
3201
- dialect
3202
- );
3653
+ function buildReachableCte(ctx) {
3654
+ const shouldTrackPath = ctx.cyclePolicy === "prevent";
3655
+ const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
3656
+ const initialPath = shouldTrackPath ? ctx.dialect.initializePath(drizzleOrm.sql.raw("n.id")) : void 0;
3657
+ const pathExtension = shouldTrackPath ? ctx.dialect.extendPath(drizzleOrm.sql.raw("r.path"), drizzleOrm.sql.raw("n.id")) : void 0;
3658
+ const cycleCheck = shouldTrackPath ? ctx.dialect.cycleCheck(drizzleOrm.sql.raw("n.id"), drizzleOrm.sql.raw("r.path")) : void 0;
3659
+ const baseColumns = [drizzleOrm.sql`n.id`, drizzleOrm.sql`n.kind`, drizzleOrm.sql`0 AS depth`];
3660
+ if (initialPath !== void 0) {
3661
+ baseColumns.push(drizzleOrm.sql`${initialPath} AS path`);
3203
3662
  }
3204
- const startAlias = ast.start.alias;
3205
- const nodeAlias = traversal.nodeAlias;
3206
- const vl = traversal.variableLength;
3207
- const fields = [
3208
- // Start alias fields with metadata
3209
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_id`,
3210
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_kind`,
3211
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_props`,
3212
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_version`,
3213
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_valid_from`,
3214
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_valid_to`,
3215
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_created_at`,
3216
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_updated_at`,
3217
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_deleted_at`,
3218
- // Node alias fields with metadata
3219
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_id`,
3220
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_kind`,
3221
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_props`,
3222
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_version`,
3223
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_valid_from`,
3224
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_valid_to`,
3225
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_created_at`,
3226
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_updated_at`,
3227
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_deleted_at`
3663
+ const baseCase = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(baseColumns, drizzleOrm.sql`, `)} FROM ${ctx.schema.nodesTable} n WHERE n.graph_id = ${ctx.graphId} AND n.id = ${ctx.rootId} AND n.deleted_at IS NULL`;
3664
+ const recursiveColumns = [
3665
+ drizzleOrm.sql`n.id`,
3666
+ drizzleOrm.sql`n.kind`,
3667
+ drizzleOrm.sql`r.depth + 1 AS depth`
3228
3668
  ];
3229
- if (vl.depthAlias !== void 0) {
3230
- fields.push(drizzleOrm.sql`depth AS ${quoteIdentifier(vl.depthAlias)}`);
3669
+ if (pathExtension !== void 0) {
3670
+ recursiveColumns.push(drizzleOrm.sql`${pathExtension} AS path`);
3671
+ }
3672
+ const recursiveWhereClauses = [
3673
+ drizzleOrm.sql`e.graph_id = ${ctx.graphId}`,
3674
+ edgeKindFilter,
3675
+ drizzleOrm.sql`e.deleted_at IS NULL`,
3676
+ drizzleOrm.sql`n.deleted_at IS NULL`,
3677
+ drizzleOrm.sql`r.depth < ${ctx.maxDepth}`
3678
+ ];
3679
+ if (cycleCheck !== void 0) {
3680
+ recursiveWhereClauses.push(cycleCheck);
3681
+ }
3682
+ const forceWorktableOuterJoinOrder = ctx.dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
3683
+ const recursiveCase = ctx.direction === "both" ? compileBidirectionalBranch({
3684
+ recursiveColumns,
3685
+ whereClauses: recursiveWhereClauses,
3686
+ forceWorktableOuterJoinOrder,
3687
+ schema: ctx.schema
3688
+ }) : compileRecursiveBranch({
3689
+ recursiveColumns,
3690
+ whereClauses: recursiveWhereClauses,
3691
+ joinField: "from_id",
3692
+ targetField: "to_id",
3693
+ targetKindField: "to_kind",
3694
+ forceWorktableOuterJoinOrder,
3695
+ schema: ctx.schema
3696
+ });
3697
+ return drizzleOrm.sql`WITH RECURSIVE reachable AS (${baseCase} UNION ALL ${recursiveCase})`;
3698
+ }
3699
+ function compileRecursiveBranch(params) {
3700
+ const columns = [...params.recursiveColumns];
3701
+ const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
3702
+ const nodeJoin = drizzleOrm.sql`JOIN ${params.schema.nodesTable} n ON n.graph_id = e.graph_id AND n.id = e.${drizzleOrm.sql.raw(params.targetField)} AND n.kind = e.${drizzleOrm.sql.raw(params.targetKindField)}`;
3703
+ if (params.forceWorktableOuterJoinOrder) {
3704
+ const allWhere = [
3705
+ ...params.whereClauses,
3706
+ drizzleOrm.sql`e.${drizzleOrm.sql.raw(params.joinField)} = r.id`
3707
+ ];
3708
+ return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
3709
+ }
3710
+ const where = [...params.whereClauses];
3711
+ return drizzleOrm.sql`${selectClause} FROM reachable r JOIN ${params.schema.edgesTable} e ON e.${drizzleOrm.sql.raw(params.joinField)} = r.id ${nodeJoin} WHERE ${drizzleOrm.sql.join(where, drizzleOrm.sql` AND `)}`;
3712
+ }
3713
+ function compileBidirectionalBranch(params) {
3714
+ const columns = [...params.recursiveColumns];
3715
+ const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
3716
+ const nodeJoin = drizzleOrm.sql`JOIN ${params.schema.nodesTable} n ON n.graph_id = e.graph_id AND ((e.to_id = r.id AND n.id = e.from_id AND n.kind = e.from_kind) OR (e.from_id = r.id AND n.id = e.to_id AND n.kind = e.to_kind))`;
3717
+ if (params.forceWorktableOuterJoinOrder) {
3718
+ const allWhere = [
3719
+ ...params.whereClauses,
3720
+ drizzleOrm.sql`(e.from_id = r.id OR e.to_id = r.id)`
3721
+ ];
3722
+ return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
3723
+ }
3724
+ return drizzleOrm.sql`${selectClause} FROM reachable r JOIN ${params.schema.edgesTable} e ON (e.from_id = r.id OR e.to_id = r.id) ${nodeJoin} WHERE ${drizzleOrm.sql.join([...params.whereClauses], drizzleOrm.sql` AND `)}`;
3725
+ }
3726
+ function buildIncludedIdsCte(ctx) {
3727
+ const filters = [];
3728
+ if (ctx.includeKinds !== void 0 && ctx.includeKinds.length > 0) {
3729
+ filters.push(compileKindFilter(drizzleOrm.sql.raw("kind"), ctx.includeKinds));
3231
3730
  }
3232
- if (vl.pathAlias !== void 0) {
3233
- fields.push(drizzleOrm.sql`path AS ${quoteIdentifier(vl.pathAlias)}`);
3731
+ if (ctx.excludeRoot) {
3732
+ filters.push(drizzleOrm.sql`id != ${ctx.rootId}`);
3234
3733
  }
3235
- return drizzleOrm.sql.join(fields, drizzleOrm.sql`, `);
3734
+ const whereClause = filters.length > 0 ? drizzleOrm.sql` WHERE ${drizzleOrm.sql.join(filters, drizzleOrm.sql` AND `)}` : drizzleOrm.sql``;
3735
+ return drizzleOrm.sql`, included_ids AS (SELECT DISTINCT id FROM reachable${whereClause})`;
3236
3736
  }
3237
- function compileRecursiveSelectiveProjection(fields, ast, traversal, dialect) {
3238
- const allowedAliases = /* @__PURE__ */ new Set([ast.start.alias, traversal.nodeAlias]);
3239
- const columns = fields.map((field2) => {
3240
- if (!allowedAliases.has(field2.alias)) {
3241
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
3242
- `Selective projection for recursive traversals does not support alias "${field2.alias}"`
3243
- );
3244
- }
3245
- if (field2.isSystemField) {
3246
- const dbColumn = mapSelectiveSystemFieldToColumn(field2.field);
3247
- return drizzleOrm.sql`${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${quoteIdentifier(field2.outputName)}`;
3248
- }
3249
- const column = drizzleOrm.sql.raw(`${field2.alias}_props`);
3250
- const extracted = compileTypedJsonExtract({
3251
- column,
3252
- dialect,
3253
- pointer: chunkP5CNM325_cjs.jsonPointer([field2.field]),
3254
- valueType: field2.valueType
3255
- });
3256
- return drizzleOrm.sql`${extracted} AS ${quoteIdentifier(field2.outputName)}`;
3257
- });
3258
- const vl = traversal.variableLength;
3259
- if (vl.depthAlias !== void 0) {
3260
- columns.push(drizzleOrm.sql`depth AS ${quoteIdentifier(vl.depthAlias)}`);
3737
+ async function fetchSubgraphNodes(ctx, reachableCte, includedIdsCte, projectionPlan) {
3738
+ const columns = [
3739
+ drizzleOrm.sql`n.kind`,
3740
+ drizzleOrm.sql`n.id`,
3741
+ buildFullPropsColumn("n", projectionPlan),
3742
+ ...buildMetadataColumns("n", projectionPlan, [
3743
+ "version",
3744
+ "valid_from",
3745
+ "valid_to",
3746
+ "created_at",
3747
+ "updated_at",
3748
+ "deleted_at"
3749
+ ]),
3750
+ ...buildProjectedPropertyColumns("n", projectionPlan, ctx.dialect)
3751
+ ];
3752
+ const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)} FROM ${ctx.schema.nodesTable} n WHERE n.graph_id = ${ctx.graphId} AND n.id IN (SELECT id FROM included_ids)`;
3753
+ return ctx.backend.execute(query);
3754
+ }
3755
+ async function fetchSubgraphEdges(ctx, reachableCte, includedIdsCte, projectionPlan) {
3756
+ const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
3757
+ const columns = [
3758
+ drizzleOrm.sql`e.id`,
3759
+ drizzleOrm.sql`e.kind`,
3760
+ drizzleOrm.sql`e.from_kind`,
3761
+ drizzleOrm.sql`e.from_id`,
3762
+ drizzleOrm.sql`e.to_kind`,
3763
+ drizzleOrm.sql`e.to_id`,
3764
+ buildFullPropsColumn("e", projectionPlan),
3765
+ ...buildMetadataColumns("e", projectionPlan, [
3766
+ "valid_from",
3767
+ "valid_to",
3768
+ "created_at",
3769
+ "updated_at",
3770
+ "deleted_at"
3771
+ ]),
3772
+ ...buildProjectedPropertyColumns("e", projectionPlan, ctx.dialect)
3773
+ ];
3774
+ const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)} FROM ${ctx.schema.edgesTable} e WHERE e.graph_id = ${ctx.graphId} AND ${edgeKindFilter} AND e.deleted_at IS NULL AND e.from_id IN (SELECT id FROM included_ids) AND e.to_id IN (SELECT id FROM included_ids)`;
3775
+ return ctx.backend.execute(query);
3776
+ }
3777
+ function buildMetadataColumns(alias, plan, columns) {
3778
+ if (plan.projectedKinds.size === 0) {
3779
+ return columns.map((col) => drizzleOrm.sql`${drizzleOrm.sql.raw(`${alias}.${col}`)}`);
3261
3780
  }
3262
- if (vl.pathAlias !== void 0) {
3263
- columns.push(drizzleOrm.sql`path AS ${quoteIdentifier(vl.pathAlias)}`);
3781
+ const metaKinds = [...plan.fullKinds];
3782
+ for (const [kind, kindPlan] of plan.projectedKinds) {
3783
+ if (kindPlan.includeMeta) metaKinds.push(kind);
3264
3784
  }
3265
- return drizzleOrm.sql.join(columns, drizzleOrm.sql`, `);
3266
- }
3267
- function compileRecursiveOrderBy(ast, dialect) {
3268
- if (!ast.orderBy || ast.orderBy.length === 0) {
3269
- return void 0;
3785
+ if (metaKinds.length === 0) {
3786
+ return columns.map((col) => drizzleOrm.sql`NULL AS ${drizzleOrm.sql.raw(col)}`);
3270
3787
  }
3271
- const parts = [];
3272
- for (const orderSpec of ast.orderBy) {
3273
- const valueType = orderSpec.field.valueType;
3274
- if (valueType === "array" || valueType === "object") {
3275
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
3276
- "Ordering by JSON arrays or objects is not supported"
3277
- );
3278
- }
3279
- const field2 = compileFieldValue(orderSpec.field, dialect, valueType);
3280
- const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
3281
- const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
3282
- const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
3283
- parts.push(
3284
- drizzleOrm.sql`(${field2} IS NULL) ${nullsDirection}`,
3285
- drizzleOrm.sql`${field2} ${direction}`
3286
- );
3788
+ if (metaKinds.length === plan.fullKinds.length + plan.projectedKinds.size) {
3789
+ return columns.map((col) => drizzleOrm.sql`${drizzleOrm.sql.raw(`${alias}.${col}`)}`);
3287
3790
  }
3288
- return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join(parts, drizzleOrm.sql`, `)}`;
3791
+ const filter = compileKindFilter(drizzleOrm.sql.raw(`${alias}.kind`), metaKinds);
3792
+ return columns.map(
3793
+ (col) => drizzleOrm.sql`CASE WHEN ${filter} THEN ${drizzleOrm.sql.raw(`${alias}.${col}`)} ELSE NULL END AS ${drizzleOrm.sql.raw(col)}`
3794
+ );
3289
3795
  }
3290
- function compileLimitOffset(ast) {
3291
- const parts = [];
3292
- if (ast.limit !== void 0) {
3293
- parts.push(drizzleOrm.sql`LIMIT ${ast.limit}`);
3796
+ function buildFullPropsColumn(alias, plan) {
3797
+ if (plan.projectedKinds.size === 0) {
3798
+ return drizzleOrm.sql`${drizzleOrm.sql.raw(`${alias}.props`)} AS props`;
3294
3799
  }
3295
- if (ast.offset !== void 0) {
3296
- parts.push(drizzleOrm.sql`OFFSET ${ast.offset}`);
3800
+ if (plan.fullKinds.length === 0) {
3801
+ return drizzleOrm.sql`NULL AS props`;
3297
3802
  }
3298
- return parts.length > 0 ? drizzleOrm.sql.join(parts, drizzleOrm.sql` `) : void 0;
3803
+ const filter = compileKindFilter(drizzleOrm.sql.raw(`${alias}.kind`), plan.fullKinds);
3804
+ return drizzleOrm.sql`CASE WHEN ${filter} THEN ${drizzleOrm.sql.raw(`${alias}.props`)} ELSE NULL END AS props`;
3299
3805
  }
3300
- var DEFAULT_TABLE_NAMES = {
3301
- nodes: "typegraph_nodes",
3302
- edges: "typegraph_edges",
3303
- embeddings: "typegraph_node_embeddings"
3304
- };
3305
- var MAX_IDENTIFIER_LENGTH = 63;
3306
- var VALID_IDENTIFIER_PATTERN = /^[a-z_][a-z0-9_$]*$/i;
3307
- function validateTableName(name, label) {
3308
- if (!name || name.length === 0) {
3309
- throw new chunk44SXEVF4_cjs.ConfigurationError(`${label} table name cannot be empty`);
3310
- }
3311
- if (name.length > MAX_IDENTIFIER_LENGTH) {
3312
- throw new chunk44SXEVF4_cjs.ConfigurationError(
3313
- `${label} table name exceeds maximum length of ${MAX_IDENTIFIER_LENGTH} characters`
3314
- );
3806
+ function buildProjectedPropertyColumns(alias, plan, dialect) {
3807
+ const columns = [];
3808
+ for (const [kind, kindPlan] of plan.projectedKinds.entries()) {
3809
+ for (const fieldPlan of kindPlan.propertyFields) {
3810
+ const extracted = compileTypedJsonExtract({
3811
+ column: drizzleOrm.sql.raw(`${alias}.props`),
3812
+ dialect,
3813
+ pointer: chunkP5CNM325_cjs.jsonPointer([fieldPlan.field]),
3814
+ valueType: fieldPlan.typeInfo?.valueType
3815
+ });
3816
+ columns.push(
3817
+ drizzleOrm.sql`CASE WHEN ${drizzleOrm.sql.raw(alias)}.kind = ${kind} THEN ${extracted} ELSE NULL END AS ${quoteIdentifier(fieldPlan.outputName)}`
3818
+ );
3819
+ }
3315
3820
  }
3316
- if (!VALID_IDENTIFIER_PATTERN.test(name)) {
3317
- throw new chunk44SXEVF4_cjs.ConfigurationError(
3318
- `${label} table name "${name}" is not a valid SQL identifier. Table names must start with a letter or underscore and contain only letters, digits, underscores, or dollar signs.`
3821
+ return columns;
3822
+ }
3823
+ function applyProjectedFields(target, row, kindPlan) {
3824
+ for (const fieldPlan of kindPlan.propertyFields) {
3825
+ target[fieldPlan.field] = decodeSelectedValue(
3826
+ row[fieldPlan.outputName],
3827
+ fieldPlan.typeInfo
3319
3828
  );
3320
3829
  }
3321
3830
  }
3322
- function quoteIdentifier2(name) {
3323
- return `"${name.replaceAll('"', '""')}"`;
3324
- }
3325
- function createSqlSchema(names = {}) {
3326
- const tables = { ...DEFAULT_TABLE_NAMES, ...names };
3327
- validateTableName(tables.nodes, "nodes");
3328
- validateTableName(tables.edges, "edges");
3329
- validateTableName(tables.embeddings, "embeddings");
3330
- return {
3331
- tables,
3332
- nodesTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.nodes)),
3333
- edgesTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.edges)),
3334
- embeddingsTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.embeddings))
3831
+ function mapSubgraphNodeRow(row, projectionPlan) {
3832
+ const kindPlan = projectionPlan.projectedKinds.get(row.kind);
3833
+ if (kindPlan === void 0) {
3834
+ return rowToNode({
3835
+ ...row,
3836
+ props: normalizeProps(row.props)
3837
+ });
3838
+ }
3839
+ const projectedNode = {
3840
+ kind: row.kind,
3841
+ id: row.id
3842
+ };
3843
+ if (kindPlan.includeMeta) {
3844
+ projectedNode.meta = rowToNodeMeta(row);
3845
+ }
3846
+ applyProjectedFields(projectedNode, row, kindPlan);
3847
+ return projectedNode;
3848
+ }
3849
+ function mapSubgraphEdgeRow(row, projectionPlan) {
3850
+ const kindPlan = projectionPlan.projectedKinds.get(row.kind);
3851
+ if (kindPlan === void 0) {
3852
+ return rowToEdge({
3853
+ ...row,
3854
+ props: normalizeProps(row.props)
3855
+ });
3856
+ }
3857
+ const projectedEdge = {
3858
+ id: row.id,
3859
+ kind: row.kind,
3860
+ fromKind: row.from_kind,
3861
+ fromId: row.from_id,
3862
+ toKind: row.to_kind,
3863
+ toId: row.to_id
3335
3864
  };
3865
+ if (kindPlan.includeMeta) {
3866
+ projectedEdge.meta = rowToEdgeMeta(row);
3867
+ }
3868
+ applyProjectedFields(projectedEdge, row, kindPlan);
3869
+ return projectedEdge;
3336
3870
  }
3337
- var DEFAULT_SQL_SCHEMA = createSqlSchema();
3338
3871
  var OPERATOR_MAP = {
3339
3872
  union: "UNION",
3340
3873
  unionAll: "UNION ALL",
@@ -4703,9 +5236,9 @@ function transformPathColumns(rows, state, _dialect) {
4703
5236
  }
4704
5237
  return changed ? result : rows;
4705
5238
  }
4706
- var RESERVED_NODE_KEYS2 = /* @__PURE__ */ new Set(["id", "kind", "meta"]);
4707
- var RESERVED_EDGE_KEYS2 = /* @__PURE__ */ new Set(["id", "kind", "fromId", "toId", "meta"]);
4708
- function nullToUndefined(value) {
5239
+ var RESERVED_NODE_KEYS3 = /* @__PURE__ */ new Set(["id", "kind", "meta"]);
5240
+ var RESERVED_EDGE_KEYS3 = /* @__PURE__ */ new Set(["id", "kind", "fromId", "toId", "meta"]);
5241
+ function nullToUndefined3(value) {
4709
5242
  return value === null ? void 0 : value;
4710
5243
  }
4711
5244
  function assignPropsExcludingReserved(target, props, reservedKeys) {
@@ -4721,13 +5254,13 @@ function buildSelectableNode(row, alias) {
4721
5254
  const propsRaw = row[`${alias}_props`];
4722
5255
  const rawProps = typeof propsRaw === "string" ? JSON.parse(propsRaw) : propsRaw ?? {};
4723
5256
  const version = row[`${alias}_version`];
4724
- const validFrom = nullToUndefined(
5257
+ const validFrom = nullToUndefined3(
4725
5258
  row[`${alias}_valid_from`]
4726
5259
  );
4727
- const validTo = nullToUndefined(row[`${alias}_valid_to`]);
5260
+ const validTo = nullToUndefined3(row[`${alias}_valid_to`]);
4728
5261
  const createdAt = row[`${alias}_created_at`];
4729
5262
  const updatedAt = row[`${alias}_updated_at`];
4730
- const deletedAt = nullToUndefined(
5263
+ const deletedAt = nullToUndefined3(
4731
5264
  row[`${alias}_deleted_at`]
4732
5265
  );
4733
5266
  const result = {
@@ -4742,7 +5275,7 @@ function buildSelectableNode(row, alias) {
4742
5275
  deletedAt
4743
5276
  }
4744
5277
  };
4745
- assignPropsExcludingReserved(result, rawProps, RESERVED_NODE_KEYS2);
5278
+ assignPropsExcludingReserved(result, rawProps, RESERVED_NODE_KEYS3);
4746
5279
  return result;
4747
5280
  }
4748
5281
  function buildSelectableNodeOrUndefined(row, alias) {
@@ -4762,13 +5295,13 @@ function buildSelectableEdge(row, alias) {
4762
5295
  const toId = row[`${alias}_to_id`];
4763
5296
  const propsRaw = row[`${alias}_props`];
4764
5297
  const rawProps = typeof propsRaw === "string" ? JSON.parse(propsRaw) : propsRaw ?? {};
4765
- const validFrom = nullToUndefined(
5298
+ const validFrom = nullToUndefined3(
4766
5299
  row[`${alias}_valid_from`]
4767
5300
  );
4768
- const validTo = nullToUndefined(row[`${alias}_valid_to`]);
5301
+ const validTo = nullToUndefined3(row[`${alias}_valid_to`]);
4769
5302
  const createdAt = row[`${alias}_created_at`];
4770
5303
  const updatedAt = row[`${alias}_updated_at`];
4771
- const deletedAt = nullToUndefined(
5304
+ const deletedAt = nullToUndefined3(
4772
5305
  row[`${alias}_deleted_at`]
4773
5306
  );
4774
5307
  const result = {
@@ -4784,7 +5317,7 @@ function buildSelectableEdge(row, alias) {
4784
5317
  deletedAt
4785
5318
  }
4786
5319
  };
4787
- assignPropsExcludingReserved(result, rawProps, RESERVED_EDGE_KEYS2);
5320
+ assignPropsExcludingReserved(result, rawProps, RESERVED_EDGE_KEYS3);
4788
5321
  return result;
4789
5322
  }
4790
5323
  function buildSelectContext(row, startAlias, traversals) {
@@ -5170,63 +5703,6 @@ function getPlaceholderForValueType(valueType, mode) {
5170
5703
  }
5171
5704
  }
5172
5705
 
5173
- // src/query/execution/value-decoder.ts
5174
- function nullToUndefined2(value) {
5175
- return value === null ? void 0 : value;
5176
- }
5177
- function decodeSelectedValue(value, typeInfo) {
5178
- const normalized = nullToUndefined2(value);
5179
- if (normalized === void 0) return void 0;
5180
- if (typeInfo === void 0) {
5181
- return normalized;
5182
- }
5183
- return decodeByValueType(normalized, typeInfo.valueType);
5184
- }
5185
- function decodeByValueType(value, valueType) {
5186
- switch (valueType) {
5187
- case "boolean": {
5188
- if (typeof value === "boolean") return value;
5189
- if (typeof value === "number") return value !== 0;
5190
- if (typeof value === "string") {
5191
- if (value === "0") return false;
5192
- if (value === "1") return true;
5193
- if (value.toLowerCase() === "true") return true;
5194
- if (value.toLowerCase() === "false") return false;
5195
- }
5196
- return Boolean(value);
5197
- }
5198
- case "number": {
5199
- if (typeof value === "number") return value;
5200
- if (typeof value === "string") {
5201
- const parsed = Number(value);
5202
- return Number.isNaN(parsed) ? value : parsed;
5203
- }
5204
- return value;
5205
- }
5206
- case "array":
5207
- case "object":
5208
- case "embedding": {
5209
- if (typeof value !== "string") return value;
5210
- const trimmed = value.trim();
5211
- const looksJson = trimmed.startsWith("[") || trimmed.startsWith("{");
5212
- if (!looksJson) return value;
5213
- try {
5214
- return JSON.parse(trimmed);
5215
- } catch {
5216
- return value;
5217
- }
5218
- }
5219
- case "string":
5220
- case "date":
5221
- case "unknown": {
5222
- return value;
5223
- }
5224
- default: {
5225
- return value;
5226
- }
5227
- }
5228
- }
5229
-
5230
5706
  // src/query/execution/selective-result-mapper.ts
5231
5707
  var MissingSelectiveFieldError = class extends Error {
5232
5708
  alias;
@@ -5395,12 +5871,12 @@ function buildRequiredAliasValue(row, plan) {
5395
5871
  }
5396
5872
  };
5397
5873
  for (const field2 of plan.systemFields) {
5398
- base[field2.field] = nullToUndefined2(row[field2.outputName]);
5874
+ base[field2.field] = nullToUndefined(row[field2.outputName]);
5399
5875
  }
5400
5876
  if (plan.metaFields.length > 0) {
5401
5877
  const meta = {};
5402
5878
  for (const field2 of plan.metaFields) {
5403
- meta[field2.metaKey] = nullToUndefined2(row[field2.outputName]);
5879
+ meta[field2.metaKey] = nullToUndefined(row[field2.outputName]);
5404
5880
  }
5405
5881
  base.meta = createGuardedProxy(meta, `${plan.alias}.meta`);
5406
5882
  }
@@ -6367,24 +6843,102 @@ var ExecutableQuery = class _ExecutableQuery {
6367
6843
  if (optimizedResult !== void 0) {
6368
6844
  return optimizedResult;
6369
6845
  }
6370
- const compiled = this.compile();
6371
- const rawRows = await this.#config.backend.execute(compiled);
6372
- this.#config.dialect ?? "sqlite";
6373
- const rows = transformPathColumns(rawRows, this.#state);
6374
- return mapResults(
6375
- rows,
6376
- this.#state.startAlias,
6377
- this.#state.traversals,
6378
- this.#selectFn
6379
- );
6846
+ const compiled = this.compile();
6847
+ const rawRows = await this.#config.backend.execute(compiled);
6848
+ this.#config.dialect ?? "sqlite";
6849
+ const rows = transformPathColumns(rawRows, this.#state);
6850
+ return mapResults(
6851
+ rows,
6852
+ this.#state.startAlias,
6853
+ this.#state.traversals,
6854
+ this.#selectFn
6855
+ );
6856
+ }
6857
+ /**
6858
+ * Executes the query against a provided backend.
6859
+ *
6860
+ * Used by `store.batch()` to run multiple queries over a single connection
6861
+ * (e.g., within a transaction). The full compile → execute → transform
6862
+ * pipeline runs identically to `execute()`, but against the given backend.
6863
+ */
6864
+ async executeOn(backend) {
6865
+ if (hasParameterReferences(this.toAst())) {
6866
+ throw new Error(
6867
+ "Query contains param() references. Use .prepare().execute({...}) instead of .execute()."
6868
+ );
6869
+ }
6870
+ const optimizedResult = await this.#tryOptimizedExecutionOn(backend);
6871
+ if (optimizedResult !== void 0) {
6872
+ return optimizedResult;
6873
+ }
6874
+ const compiled = this.compile();
6875
+ const rawRows = await backend.execute(compiled);
6876
+ this.#config.dialect ?? "sqlite";
6877
+ const rows = transformPathColumns(rawRows, this.#state);
6878
+ return mapResults(
6879
+ rows,
6880
+ this.#state.startAlias,
6881
+ this.#state.traversals,
6882
+ this.#selectFn
6883
+ );
6884
+ }
6885
+ /**
6886
+ * Attempts optimized execution by tracking which fields the select callback accesses.
6887
+ *
6888
+ * Returns undefined if optimization is not possible (callback uses method calls,
6889
+ * computations, or returns whole nodes).
6890
+ */
6891
+ async #tryOptimizedExecution() {
6892
+ const selectiveFields = this.#getSelectiveFieldsForExecute();
6893
+ if (selectiveFields === void 0) {
6894
+ return void 0;
6895
+ }
6896
+ let compiled;
6897
+ if (this.#cachedOptimizedCompiled === NOT_COMPUTED) {
6898
+ const baseAst = buildQueryAst(this.#config, this.#state);
6899
+ const selectiveAst = {
6900
+ ...baseAst,
6901
+ selectiveFields
6902
+ };
6903
+ compiled = compileQuery(
6904
+ selectiveAst,
6905
+ this.#config.graphId,
6906
+ this.#compileOptions()
6907
+ );
6908
+ this.#cachedOptimizedCompiled = compiled;
6909
+ } else {
6910
+ compiled = this.#cachedOptimizedCompiled;
6911
+ }
6912
+ const rawSelectiveRows = await this.#requireBackend().execute(compiled);
6913
+ this.#config.dialect ?? "sqlite";
6914
+ const rows = transformPathColumns(rawSelectiveRows, this.#state);
6915
+ try {
6916
+ return mapSelectiveResults(
6917
+ rows,
6918
+ this.#state,
6919
+ selectiveFields,
6920
+ this.#config.schemaIntrospector,
6921
+ this.#selectFn
6922
+ );
6923
+ } catch (error) {
6924
+ if (error instanceof MissingSelectiveFieldError) {
6925
+ this.#cachedSelectiveFieldsForExecute = void 0;
6926
+ this.#cachedOptimizedCompiled = NOT_COMPUTED;
6927
+ return void 0;
6928
+ }
6929
+ if (error instanceof chunk44SXEVF4_cjs.UnsupportedPredicateError) {
6930
+ this.#cachedSelectiveFieldsForExecute = void 0;
6931
+ this.#cachedOptimizedCompiled = NOT_COMPUTED;
6932
+ return void 0;
6933
+ }
6934
+ throw error;
6935
+ }
6380
6936
  }
6381
6937
  /**
6382
- * Attempts optimized execution by tracking which fields the select callback accesses.
6383
- *
6384
- * Returns undefined if optimization is not possible (callback uses method calls,
6385
- * computations, or returns whole nodes).
6938
+ * Attempts optimized execution against a provided backend.
6939
+ * Mirror of #tryOptimizedExecution but delegates to the given backend.
6386
6940
  */
6387
- async #tryOptimizedExecution() {
6941
+ async #tryOptimizedExecutionOn(backend) {
6388
6942
  const selectiveFields = this.#getSelectiveFieldsForExecute();
6389
6943
  if (selectiveFields === void 0) {
6390
6944
  return void 0;
@@ -6405,7 +6959,7 @@ var ExecutableQuery = class _ExecutableQuery {
6405
6959
  } else {
6406
6960
  compiled = this.#cachedOptimizedCompiled;
6407
6961
  }
6408
- const rawSelectiveRows = await this.#requireBackend().execute(compiled);
6962
+ const rawSelectiveRows = await backend.execute(compiled);
6409
6963
  this.#config.dialect ?? "sqlite";
6410
6964
  const rows = transformPathColumns(rawSelectiveRows, this.#state);
6411
6965
  try {
@@ -6680,7 +7234,7 @@ var ExecutableQuery = class _ExecutableQuery {
6680
7234
  throw new MissingSelectiveFieldError(alias, fieldName);
6681
7235
  }
6682
7236
  const aliasObject2 = this.#getOrCreateAliasObject(cursorContext, alias);
6683
- aliasObject2[fieldName] = nullToUndefined2(row[outputName2]);
7237
+ aliasObject2[fieldName] = nullToUndefined(row[outputName2]);
6684
7238
  continue;
6685
7239
  }
6686
7240
  const segments = chunkP5CNM325_cjs.parseJsonPointer(jsonPointer2);
@@ -8063,6 +8617,29 @@ var UnionableQuery = class _UnionableQuery {
8063
8617
  }
8064
8618
  return rows;
8065
8619
  }
8620
+ /**
8621
+ * Executes the combined query against a provided backend.
8622
+ *
8623
+ * Used by `store.batch()` to run multiple queries over a single connection.
8624
+ */
8625
+ async executeOn(backend) {
8626
+ if (composableQueryHasParameterReferences(this.toAst())) {
8627
+ throw new Error(
8628
+ "Query contains param() references. Use .prepare().execute({...}) instead of .execute()."
8629
+ );
8630
+ }
8631
+ const compiled = this.compile();
8632
+ const rows = await backend.execute(compiled);
8633
+ if (this.#state.selectFn && this.#state.startAlias) {
8634
+ return mapResults(
8635
+ rows,
8636
+ this.#state.startAlias,
8637
+ this.#state.traversals ?? [],
8638
+ this.#state.selectFn
8639
+ );
8640
+ }
8641
+ return rows;
8642
+ }
8066
8643
  };
8067
8644
 
8068
8645
  // src/query/builder/aggregates.ts
@@ -8340,6 +8917,35 @@ function createEdgeCollection(config) {
8340
8917
  executeHardDelete: executeEdgeHardDelete2,
8341
8918
  matchesTemporalMode
8342
8919
  } = config;
8920
+ const mapRows = (rows) => rows.map((row) => narrowEdge(rowToEdge2(row)));
8921
+ async function findEdgesFrom(from, target) {
8922
+ const rows = await target.findEdgesByKind({
8923
+ graphId,
8924
+ kind,
8925
+ fromKind: from.kind,
8926
+ fromId: from.id,
8927
+ excludeDeleted: true
8928
+ });
8929
+ return mapRows(rows);
8930
+ }
8931
+ async function findEdgesTo(to, target) {
8932
+ const rows = await target.findEdgesByKind({
8933
+ graphId,
8934
+ kind,
8935
+ toKind: to.kind,
8936
+ toId: to.id,
8937
+ excludeDeleted: true
8938
+ });
8939
+ return mapRows(rows);
8940
+ }
8941
+ function buildFindByEndpointsOptions(options) {
8942
+ const result = {};
8943
+ if (options?.matchOn !== void 0)
8944
+ result.matchOn = options.matchOn;
8945
+ if (options?.props !== void 0)
8946
+ result.props = options.props;
8947
+ return result;
8948
+ }
8343
8949
  return {
8344
8950
  async create(from, to, props, options) {
8345
8951
  const result = await executeEdgeCreate2(
@@ -8395,24 +9001,32 @@ function createEdgeCollection(config) {
8395
9001
  return narrowEdge(result);
8396
9002
  },
8397
9003
  async findFrom(from) {
8398
- const rows = await backend.findEdgesByKind({
8399
- graphId,
8400
- kind,
8401
- fromKind: from.kind,
8402
- fromId: from.id,
8403
- excludeDeleted: true
8404
- });
8405
- return rows.map((row) => narrowEdge(rowToEdge2(row)));
9004
+ return findEdgesFrom(from, backend);
8406
9005
  },
8407
9006
  async findTo(to) {
8408
- const rows = await backend.findEdgesByKind({
8409
- graphId,
8410
- kind,
8411
- toKind: to.kind,
8412
- toId: to.id,
8413
- excludeDeleted: true
8414
- });
8415
- return rows.map((row) => narrowEdge(rowToEdge2(row)));
9007
+ return findEdgesTo(to, backend);
9008
+ },
9009
+ batchFindFrom(from) {
9010
+ return { executeOn: (target) => findEdgesFrom(from, target) };
9011
+ },
9012
+ batchFindTo(to) {
9013
+ return { executeOn: (target) => findEdgesTo(to, target) };
9014
+ },
9015
+ batchFindByEndpoints(from, to, options) {
9016
+ return {
9017
+ executeOn: async (target) => {
9018
+ const result = await config.executeFindByEndpoints(
9019
+ kind,
9020
+ from.kind,
9021
+ from.id,
9022
+ to.kind,
9023
+ to.id,
9024
+ target,
9025
+ buildFindByEndpointsOptions(options)
9026
+ );
9027
+ return result === void 0 ? [] : [narrowEdge(result)];
9028
+ }
9029
+ };
8416
9030
  },
8417
9031
  async delete(id) {
8418
9032
  await executeEdgeDelete2(id, backend);
@@ -8446,7 +9060,7 @@ function createEdgeCollection(config) {
8446
9060
  if (options?.limit !== void 0) params.limit = options.limit;
8447
9061
  if (options?.offset !== void 0) params.offset = options.offset;
8448
9062
  const rows = await backend.findEdgesByKind(params);
8449
- return rows.map((row) => narrowEdge(rowToEdge2(row)));
9063
+ return mapRows(rows);
8450
9064
  },
8451
9065
  async count(options) {
8452
9066
  const mode = options?.temporalMode ?? defaultTemporalMode;
@@ -8575,11 +9189,6 @@ function createEdgeCollection(config) {
8575
9189
  await deleteAll(backend);
8576
9190
  },
8577
9191
  async findByEndpoints(from, to, options) {
8578
- const findOptions = {};
8579
- if (options?.matchOn !== void 0)
8580
- findOptions.matchOn = options.matchOn;
8581
- if (options?.props !== void 0)
8582
- findOptions.props = options.props;
8583
9192
  const result = await config.executeFindByEndpoints(
8584
9193
  kind,
8585
9194
  from.kind,
@@ -8587,7 +9196,7 @@ function createEdgeCollection(config) {
8587
9196
  to.kind,
8588
9197
  to.id,
8589
9198
  backend,
8590
- findOptions
9199
+ buildFindByEndpointsOptions(options)
8591
9200
  );
8592
9201
  return result === void 0 ? void 0 : narrowEdge(result);
8593
9202
  },
@@ -9283,67 +9892,6 @@ async function checkCardinalityConstraint(ctx, edgeKind, cardinality, fromKind,
9283
9892
  }
9284
9893
  }
9285
9894
 
9286
- // src/store/row-mappers.ts
9287
- var RESERVED_NODE_KEYS3 = /* @__PURE__ */ new Set(["id", "kind", "meta"]);
9288
- function nullToUndefined3(value) {
9289
- return value === null ? void 0 : value;
9290
- }
9291
- function filterReservedKeys(props, reservedKeys) {
9292
- const filtered = {};
9293
- for (const [key, value] of Object.entries(props)) {
9294
- if (!reservedKeys.has(key)) {
9295
- filtered[key] = value;
9296
- }
9297
- }
9298
- return filtered;
9299
- }
9300
- function rowToNode(row) {
9301
- const rawProps = JSON.parse(row.props);
9302
- const props = filterReservedKeys(rawProps, RESERVED_NODE_KEYS3);
9303
- return {
9304
- kind: row.kind,
9305
- id: row.id,
9306
- meta: {
9307
- version: row.version,
9308
- validFrom: nullToUndefined3(row.valid_from),
9309
- validTo: nullToUndefined3(row.valid_to),
9310
- createdAt: row.created_at,
9311
- updatedAt: row.updated_at,
9312
- deletedAt: nullToUndefined3(row.deleted_at)
9313
- },
9314
- ...props
9315
- };
9316
- }
9317
- var RESERVED_EDGE_KEYS3 = /* @__PURE__ */ new Set([
9318
- "id",
9319
- "kind",
9320
- "meta",
9321
- "fromKind",
9322
- "fromId",
9323
- "toKind",
9324
- "toId"
9325
- ]);
9326
- function rowToEdge(row) {
9327
- const rawProps = JSON.parse(row.props);
9328
- const props = filterReservedKeys(rawProps, RESERVED_EDGE_KEYS3);
9329
- return {
9330
- id: row.id,
9331
- kind: row.kind,
9332
- fromKind: row.from_kind,
9333
- fromId: row.from_id,
9334
- toKind: row.to_kind,
9335
- toId: row.to_id,
9336
- meta: {
9337
- validFrom: nullToUndefined3(row.valid_from),
9338
- validTo: nullToUndefined3(row.valid_to),
9339
- createdAt: row.created_at,
9340
- updatedAt: row.updated_at,
9341
- deletedAt: nullToUndefined3(row.deleted_at)
9342
- },
9343
- ...props
9344
- };
9345
- }
9346
-
9347
9895
  // src/store/operations/edge-operations.ts
9348
9896
  function getEdgeRegistration(graph, kind) {
9349
9897
  const registration = graph.edges[kind];
@@ -11217,142 +11765,6 @@ async function executeNodeBulkGetOrCreateByConstraint(ctx, kind, constraintName,
11217
11765
  }
11218
11766
  return results;
11219
11767
  }
11220
- var DEFAULT_SUBGRAPH_MAX_DEPTH = 10;
11221
- function normalizeProps(value) {
11222
- return typeof value === "string" ? value : JSON.stringify(value ?? {});
11223
- }
11224
- async function executeSubgraph(params) {
11225
- const { options } = params;
11226
- if (options.edges.length === 0) {
11227
- return { nodes: [], edges: [] };
11228
- }
11229
- const maxDepth = Math.min(
11230
- options.maxDepth ?? DEFAULT_SUBGRAPH_MAX_DEPTH,
11231
- MAX_RECURSIVE_DEPTH
11232
- );
11233
- const ctx = {
11234
- graphId: params.graphId,
11235
- rootId: params.rootId,
11236
- edgeKinds: options.edges,
11237
- maxDepth,
11238
- includeKinds: options.includeKinds,
11239
- excludeRoot: options.excludeRoot ?? false,
11240
- direction: options.direction ?? "out",
11241
- cyclePolicy: options.cyclePolicy ?? "prevent",
11242
- dialect: params.dialect,
11243
- schema: params.schema ?? DEFAULT_SQL_SCHEMA,
11244
- backend: params.backend
11245
- };
11246
- const reachableCte = buildReachableCte(ctx);
11247
- const includedIdsCte = buildIncludedIdsCte(ctx);
11248
- const [nodeRows, edgeRows] = await Promise.all([
11249
- fetchSubgraphNodes(ctx, reachableCte, includedIdsCte),
11250
- fetchSubgraphEdges(ctx, reachableCte, includedIdsCte)
11251
- ]);
11252
- const nodes = nodeRows.map(
11253
- (row) => rowToNode({ ...row, props: normalizeProps(row.props) })
11254
- );
11255
- const edges = edgeRows.map(
11256
- (row) => rowToEdge({ ...row, props: normalizeProps(row.props) })
11257
- );
11258
- return {
11259
- nodes,
11260
- edges
11261
- };
11262
- }
11263
- function buildReachableCte(ctx) {
11264
- const shouldTrackPath = ctx.cyclePolicy === "prevent";
11265
- const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
11266
- const initialPath = shouldTrackPath ? ctx.dialect.initializePath(drizzleOrm.sql.raw("n.id")) : void 0;
11267
- const pathExtension = shouldTrackPath ? ctx.dialect.extendPath(drizzleOrm.sql.raw("r.path"), drizzleOrm.sql.raw("n.id")) : void 0;
11268
- const cycleCheck = shouldTrackPath ? ctx.dialect.cycleCheck(drizzleOrm.sql.raw("n.id"), drizzleOrm.sql.raw("r.path")) : void 0;
11269
- const baseColumns = [drizzleOrm.sql`n.id`, drizzleOrm.sql`n.kind`, drizzleOrm.sql`0 AS depth`];
11270
- if (initialPath !== void 0) {
11271
- baseColumns.push(drizzleOrm.sql`${initialPath} AS path`);
11272
- }
11273
- const baseCase = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(baseColumns, drizzleOrm.sql`, `)} FROM ${ctx.schema.nodesTable} n WHERE n.graph_id = ${ctx.graphId} AND n.id = ${ctx.rootId} AND n.deleted_at IS NULL`;
11274
- const recursiveColumns = [
11275
- drizzleOrm.sql`n.id`,
11276
- drizzleOrm.sql`n.kind`,
11277
- drizzleOrm.sql`r.depth + 1 AS depth`
11278
- ];
11279
- if (pathExtension !== void 0) {
11280
- recursiveColumns.push(drizzleOrm.sql`${pathExtension} AS path`);
11281
- }
11282
- const recursiveWhereClauses = [
11283
- drizzleOrm.sql`e.graph_id = ${ctx.graphId}`,
11284
- edgeKindFilter,
11285
- drizzleOrm.sql`e.deleted_at IS NULL`,
11286
- drizzleOrm.sql`n.deleted_at IS NULL`,
11287
- drizzleOrm.sql`r.depth < ${ctx.maxDepth}`
11288
- ];
11289
- if (cycleCheck !== void 0) {
11290
- recursiveWhereClauses.push(cycleCheck);
11291
- }
11292
- const forceWorktableOuterJoinOrder = ctx.dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
11293
- const recursiveCase = ctx.direction === "both" ? compileBidirectionalBranch({
11294
- recursiveColumns,
11295
- whereClauses: recursiveWhereClauses,
11296
- forceWorktableOuterJoinOrder,
11297
- schema: ctx.schema
11298
- }) : compileRecursiveBranch({
11299
- recursiveColumns,
11300
- whereClauses: recursiveWhereClauses,
11301
- joinField: "from_id",
11302
- targetField: "to_id",
11303
- targetKindField: "to_kind",
11304
- forceWorktableOuterJoinOrder,
11305
- schema: ctx.schema
11306
- });
11307
- return drizzleOrm.sql`WITH RECURSIVE reachable AS (${baseCase} UNION ALL ${recursiveCase})`;
11308
- }
11309
- function compileRecursiveBranch(params) {
11310
- const columns = [...params.recursiveColumns];
11311
- const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
11312
- const nodeJoin = drizzleOrm.sql`JOIN ${params.schema.nodesTable} n ON n.graph_id = e.graph_id AND n.id = e.${drizzleOrm.sql.raw(params.targetField)} AND n.kind = e.${drizzleOrm.sql.raw(params.targetKindField)}`;
11313
- if (params.forceWorktableOuterJoinOrder) {
11314
- const allWhere = [
11315
- ...params.whereClauses,
11316
- drizzleOrm.sql`e.${drizzleOrm.sql.raw(params.joinField)} = r.id`
11317
- ];
11318
- return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
11319
- }
11320
- const where = [...params.whereClauses];
11321
- return drizzleOrm.sql`${selectClause} FROM reachable r JOIN ${params.schema.edgesTable} e ON e.${drizzleOrm.sql.raw(params.joinField)} = r.id ${nodeJoin} WHERE ${drizzleOrm.sql.join(where, drizzleOrm.sql` AND `)}`;
11322
- }
11323
- function compileBidirectionalBranch(params) {
11324
- const columns = [...params.recursiveColumns];
11325
- const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
11326
- const nodeJoin = drizzleOrm.sql`JOIN ${params.schema.nodesTable} n ON n.graph_id = e.graph_id AND ((e.to_id = r.id AND n.id = e.from_id AND n.kind = e.from_kind) OR (e.from_id = r.id AND n.id = e.to_id AND n.kind = e.to_kind))`;
11327
- if (params.forceWorktableOuterJoinOrder) {
11328
- const allWhere = [
11329
- ...params.whereClauses,
11330
- drizzleOrm.sql`(e.from_id = r.id OR e.to_id = r.id)`
11331
- ];
11332
- return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
11333
- }
11334
- return drizzleOrm.sql`${selectClause} FROM reachable r JOIN ${params.schema.edgesTable} e ON (e.from_id = r.id OR e.to_id = r.id) ${nodeJoin} WHERE ${drizzleOrm.sql.join([...params.whereClauses], drizzleOrm.sql` AND `)}`;
11335
- }
11336
- function buildIncludedIdsCte(ctx) {
11337
- const filters = [];
11338
- if (ctx.includeKinds !== void 0 && ctx.includeKinds.length > 0) {
11339
- filters.push(compileKindFilter(drizzleOrm.sql.raw("kind"), ctx.includeKinds));
11340
- }
11341
- if (ctx.excludeRoot) {
11342
- filters.push(drizzleOrm.sql`id != ${ctx.rootId}`);
11343
- }
11344
- const whereClause = filters.length > 0 ? drizzleOrm.sql` WHERE ${drizzleOrm.sql.join(filters, drizzleOrm.sql` AND `)}` : drizzleOrm.sql``;
11345
- return drizzleOrm.sql`, included_ids AS (SELECT DISTINCT id FROM reachable${whereClause})`;
11346
- }
11347
- async function fetchSubgraphNodes(ctx, reachableCte, includedIdsCte) {
11348
- const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT n.kind, n.id, n.props, n.version, n.valid_from, n.valid_to, n.created_at, n.updated_at, n.deleted_at FROM ${ctx.schema.nodesTable} n WHERE n.graph_id = ${ctx.graphId} AND n.id IN (SELECT id FROM included_ids)`;
11349
- return ctx.backend.execute(query);
11350
- }
11351
- async function fetchSubgraphEdges(ctx, reachableCte, includedIdsCte) {
11352
- const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
11353
- const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT e.id, e.kind, e.from_kind, e.from_id, e.to_kind, e.to_id, e.props, e.valid_from, e.valid_to, e.created_at, e.updated_at, e.deleted_at FROM ${ctx.schema.edgesTable} e WHERE e.graph_id = ${ctx.graphId} AND ${edgeKindFilter} AND e.deleted_at IS NULL AND e.from_id IN (SELECT id FROM included_ids) AND e.to_id IN (SELECT id FROM included_ids)`;
11354
- return ctx.backend.execute(query);
11355
- }
11356
11768
 
11357
11769
  // src/store/store.ts
11358
11770
  var Store = class {
@@ -11556,6 +11968,46 @@ var Store = class {
11556
11968
  query() {
11557
11969
  return this.#createQueryForBackend(this.#backend);
11558
11970
  }
11971
+ // === Batch Query Execution ===
11972
+ /**
11973
+ * Executes multiple queries over a single connection with snapshot consistency.
11974
+ *
11975
+ * Acquires one connection via an implicit transaction, executes each query
11976
+ * sequentially on that connection, and returns a typed tuple of results.
11977
+ * Each query preserves its own result type, projection, filtering,
11978
+ * sorting, and pagination.
11979
+ *
11980
+ * Read-only — use `bulkCreate`, `bulkInsert`, etc. for write batching.
11981
+ *
11982
+ * @example
11983
+ * ```typescript
11984
+ * const [people, companies] = await store.batch(
11985
+ * store.query()
11986
+ * .from("Person", "p")
11987
+ * .select((ctx) => ({ id: ctx.p.id, name: ctx.p.name })),
11988
+ * store.query()
11989
+ * .from("Company", "c")
11990
+ * .select((ctx) => ({ id: ctx.c.id, name: ctx.c.name }))
11991
+ * .orderBy("c", "name", "asc")
11992
+ * .limit(5),
11993
+ * );
11994
+ * // people: readonly { id: string; name: string }[]
11995
+ * // companies: readonly { id: string; name: string }[]
11996
+ * ```
11997
+ *
11998
+ * @param queries - Two or more executable queries (from `.select()` or set operations)
11999
+ * @returns A tuple with per-query typed results, preserving input order
12000
+ */
12001
+ async batch(...queries) {
12002
+ return this.#backend.transaction(async (txBackend) => {
12003
+ const results = [];
12004
+ for (const query of queries) {
12005
+ const result = await query.executeOn(txBackend);
12006
+ results.push(result);
12007
+ }
12008
+ return results;
12009
+ });
12010
+ }
11559
12011
  // === Subgraph Extraction ===
11560
12012
  /**
11561
12013
  * Extracts a typed subgraph by traversing from a root node.
@@ -11581,6 +12033,7 @@ var Store = class {
11581
12033
  */
11582
12034
  async subgraph(rootId, options) {
11583
12035
  return executeSubgraph({
12036
+ graph: this.#graph,
11584
12037
  graphId: this.graphId,
11585
12038
  rootId,
11586
12039
  backend: this.#backend,
@@ -11942,6 +12395,7 @@ exports.createStore = createStore;
11942
12395
  exports.createStoreWithSchema = createStoreWithSchema;
11943
12396
  exports.defineEdge = defineEdge;
11944
12397
  exports.defineNode = defineNode;
12398
+ exports.defineSubgraphProject = defineSubgraphProject;
11945
12399
  exports.differentFrom = differentFrom;
11946
12400
  exports.disjointWith = disjointWith;
11947
12401
  exports.equivalentTo = equivalentTo;