leangraph 1.1.8 → 1.2.1
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/README.md +59 -0
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +67 -20
- package/dist/executor.js.map +1 -1
- package/dist/parser.d.ts +29 -3
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +108 -5
- package/dist/parser.js.map +1 -1
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +37 -0
- package/dist/routes.js.map +1 -1
- package/dist/translator.d.ts +11 -0
- package/dist/translator.d.ts.map +1 -1
- package/dist/translator.js +32 -0
- package/dist/translator.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -351,6 +351,10 @@ db.close(); // In-memory DB is discarded
|
|
|
351
351
|
| `DISTINCT` | `RETURN DISTINCT n.category` |
|
|
352
352
|
| `CASE/WHEN` | `RETURN CASE WHEN n.age > 18 THEN 'adult' ELSE 'minor' END` |
|
|
353
353
|
| `CALL` | `CALL db.labels() YIELD label RETURN label` |
|
|
354
|
+
| `CREATE INDEX` | `CREATE INDEX ON (property)` |
|
|
355
|
+
| `DROP INDEX` | `DROP INDEX idx_name` |
|
|
356
|
+
| `CREATE CONSTRAINT` | `CREATE CONSTRAINT ON (n:Label) ASSERT n.prop IS UNIQUE` |
|
|
357
|
+
| `DROP CONSTRAINT` | `DROP CONSTRAINT constraint_name` |
|
|
354
358
|
|
|
355
359
|
### Operators
|
|
356
360
|
|
|
@@ -401,6 +405,61 @@ CALL db.relationshipTypes() YIELD type RETURN type
|
|
|
401
405
|
CALL db.propertyKeys() YIELD key RETURN key
|
|
402
406
|
```
|
|
403
407
|
|
|
408
|
+
### Indexes
|
|
409
|
+
|
|
410
|
+
Create indexes on frequently-queried properties to improve performance:
|
|
411
|
+
|
|
412
|
+
```cypher
|
|
413
|
+
-- Create index (auto-named idx_email)
|
|
414
|
+
CREATE INDEX ON (email)
|
|
415
|
+
|
|
416
|
+
-- Create index with custom name
|
|
417
|
+
CREATE INDEX idx_user_email ON (email)
|
|
418
|
+
|
|
419
|
+
-- Drop index
|
|
420
|
+
DROP INDEX idx_user_email
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Note:** The `:Label` syntax is supported for Neo4j compatibility but labels are ignored—all indexes are global across nodes.
|
|
424
|
+
|
|
425
|
+
```cypher
|
|
426
|
+
-- These are equivalent (label is ignored)
|
|
427
|
+
CREATE INDEX ON (email)
|
|
428
|
+
CREATE INDEX ON :User(email)
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**Built-in indexes** (created automatically):
|
|
432
|
+
- Node primary label lookups
|
|
433
|
+
- Edge type, source, and target for traversal
|
|
434
|
+
|
|
435
|
+
### Constraints
|
|
436
|
+
|
|
437
|
+
Create unique constraints to enforce data integrity:
|
|
438
|
+
|
|
439
|
+
```cypher
|
|
440
|
+
-- Create unique constraint (auto-named constraint_User_email_unique)
|
|
441
|
+
CREATE CONSTRAINT ON (n:User) ASSERT n.email IS UNIQUE
|
|
442
|
+
|
|
443
|
+
-- Create constraint with custom name
|
|
444
|
+
CREATE CONSTRAINT unique_user_email ON (u:User) ASSERT u.email IS UNIQUE
|
|
445
|
+
|
|
446
|
+
-- Drop constraint
|
|
447
|
+
DROP CONSTRAINT unique_user_email
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
Unique constraints:
|
|
451
|
+
- Enforce uniqueness per label (not global)
|
|
452
|
+
- Automatically create an index for the property
|
|
453
|
+
- Reject duplicate values with a clear error message
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
await db.execute('CREATE CONSTRAINT ON (u:User) ASSERT u.email IS UNIQUE');
|
|
457
|
+
await db.execute('CREATE (u:User {email: "alice@example.com"})');
|
|
458
|
+
|
|
459
|
+
// This will fail with "UNIQUE constraint failed"
|
|
460
|
+
await db.execute('CREATE (u:User {email: "alice@example.com"})');
|
|
461
|
+
```
|
|
462
|
+
|
|
404
463
|
## Running the Server (Production)
|
|
405
464
|
|
|
406
465
|
For production deployments, run a dedicated server:
|
package/dist/executor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAoCA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AA2DxC;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAE3B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE7B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE7B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAG7B,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACnC;AA8BD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE;QACL,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG,cAAc,CAAC;AA6E7D,qBAAa,QAAQ;IACnB,OAAO,CAAC,EAAE,CAAgB;IAC1B,OAAO,CAAC,aAAa,CAA8C;IACnE,OAAO,CAAC,iBAAiB,CAA8C;IAEvE,OAAO,CAAC,aAAa,CAA6E;IAElG,OAAO,CAAC,cAAc,CAAiB;gBAE3B,EAAE,EAAE,aAAa;IAK7B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAczB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAczB;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAK/B;;OAEG;IACH,OAAO,CAAC,aAAa;IAkBrB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAgCxB;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,aAAa;
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
|
package/dist/executor.js
CHANGED
|
@@ -317,29 +317,29 @@ export class Executor {
|
|
|
317
317
|
const translator = new Translator(params);
|
|
318
318
|
const translation = translator.translate(parseResult.query);
|
|
319
319
|
// 4. Execute SQL statements
|
|
320
|
+
// Security: inject a LIMIT into the final SELECT if one isn't present,
|
|
321
|
+
// to prevent Cartesian product explosion at the SQL layer (not just post-execution).
|
|
322
|
+
const securityLimit = MAX_RESULTS_LIMIT + 1; // +1 to detect truncation
|
|
320
323
|
let rows = [];
|
|
321
324
|
const returnColumns = translation.returnColumns;
|
|
322
325
|
this.db.transaction(() => {
|
|
323
326
|
for (const stmt of translation.statements) {
|
|
324
|
-
|
|
327
|
+
let sql = stmt.sql;
|
|
328
|
+
const sqlUpper = sql.trim().toUpperCase();
|
|
329
|
+
// Add security LIMIT to SELECT statements that don't already have one
|
|
330
|
+
if (sqlUpper.startsWith("SELECT") && !sqlUpper.includes(" LIMIT ")) {
|
|
331
|
+
sql = `${sql} LIMIT ${securityLimit}`;
|
|
332
|
+
}
|
|
333
|
+
const result = this.db.execute(sql, stmt.params);
|
|
325
334
|
// If this is a SELECT or EXPLAIN (RETURN clause), capture the results
|
|
326
|
-
const sqlUpper = stmt.sql.trim().toUpperCase();
|
|
327
335
|
if (result.rows.length > 0 || sqlUpper.startsWith("SELECT") || sqlUpper.startsWith("EXPLAIN")) {
|
|
328
336
|
rows = result.rows;
|
|
329
337
|
}
|
|
330
338
|
}
|
|
331
339
|
});
|
|
332
|
-
// 5. Format results
|
|
340
|
+
// 5. Format results (with truncation matching makeResult)
|
|
333
341
|
const formattedRows = this.formatResults(rows, returnColumns);
|
|
334
|
-
|
|
335
|
-
return {
|
|
336
|
-
success: true,
|
|
337
|
-
data: formattedRows,
|
|
338
|
-
meta: {
|
|
339
|
-
count: formattedRows.length,
|
|
340
|
-
time_ms: Math.round((endTime - startTime) * 100) / 100,
|
|
341
|
-
},
|
|
342
|
-
};
|
|
342
|
+
return makeResult(formattedRows);
|
|
343
343
|
}
|
|
344
344
|
catch (error) {
|
|
345
345
|
return {
|
|
@@ -447,6 +447,10 @@ export class Executor {
|
|
|
447
447
|
case "SET":
|
|
448
448
|
flags.hasSet = true;
|
|
449
449
|
flags.setClauses.push(clause);
|
|
450
|
+
// A standalone SET clause after MERGE means we need MERGE special handling
|
|
451
|
+
if (flags.hasMerge) {
|
|
452
|
+
flags.mergeHasSetClauses = true;
|
|
453
|
+
}
|
|
450
454
|
break;
|
|
451
455
|
case "DELETE":
|
|
452
456
|
flags.hasDelete = true;
|
|
@@ -6215,6 +6219,7 @@ export class Executor {
|
|
|
6215
6219
|
let createClauses = [];
|
|
6216
6220
|
let withClauses = [];
|
|
6217
6221
|
let mergeClauses = [];
|
|
6222
|
+
let setClauses = [];
|
|
6218
6223
|
let returnClause = null;
|
|
6219
6224
|
for (const clause of clauses) {
|
|
6220
6225
|
if (clause.type === "MERGE") {
|
|
@@ -6232,6 +6237,9 @@ export class Executor {
|
|
|
6232
6237
|
else if (clause.type === "WITH") {
|
|
6233
6238
|
withClauses.push(clause);
|
|
6234
6239
|
}
|
|
6240
|
+
else if (clause.type === "SET") {
|
|
6241
|
+
setClauses.push(clause);
|
|
6242
|
+
}
|
|
6235
6243
|
else {
|
|
6236
6244
|
// Other clause types present - don't handle
|
|
6237
6245
|
return null;
|
|
@@ -6243,11 +6251,12 @@ export class Executor {
|
|
|
6243
6251
|
// Check if any MERGE needs special handling:
|
|
6244
6252
|
// 1. Has relationship patterns
|
|
6245
6253
|
// 2. Has ON CREATE SET or ON MATCH SET
|
|
6246
|
-
// 3. Has
|
|
6247
|
-
// 4.
|
|
6248
|
-
// 5.
|
|
6254
|
+
// 3. Has standalone SET clauses after MERGE
|
|
6255
|
+
// 4. Has RETURN clause (translator can't handle MERGE + RETURN properly for new nodes)
|
|
6256
|
+
// 5. Multiple MERGE clauses (need phased handling)
|
|
6257
|
+
// 6. Has path expressions
|
|
6249
6258
|
const hasRelationshipPattern = mergeClauses.some(m => m.patterns.some(p => this.isRelationshipPattern(p)));
|
|
6250
|
-
const hasSetClauses = mergeClauses.some(m => m.onCreateSet || m.onMatchSet);
|
|
6259
|
+
const hasSetClauses = mergeClauses.some(m => m.onCreateSet || m.onMatchSet) || setClauses.length > 0;
|
|
6251
6260
|
const hasPathExpressions = mergeClauses.some(m => m.pathExpressions && m.pathExpressions.length > 0);
|
|
6252
6261
|
if (!hasRelationshipPattern && !hasSetClauses && !hasPathExpressions && !returnClause && mergeClauses.length === 1) {
|
|
6253
6262
|
// Simple single node MERGE without SET clauses, path expressions, and no RETURN - let translator handle it
|
|
@@ -6261,7 +6270,7 @@ export class Executor {
|
|
|
6261
6270
|
// For single MERGE with SET clauses or relationship patterns, use the original handler
|
|
6262
6271
|
// This preserves the existing behavior for ON CREATE SET / ON MATCH SET
|
|
6263
6272
|
if (mergeClauses.length === 1) {
|
|
6264
|
-
return this.executeMergeWithSetClauses(matchClauses, createClauses, withClauses, mergeClauses[0], returnClause, params);
|
|
6273
|
+
return this.executeMergeWithSetClauses(matchClauses, createClauses, withClauses, mergeClauses[0], setClauses, returnClause, params);
|
|
6265
6274
|
}
|
|
6266
6275
|
// For multiple MERGEs without path expressions, use the multi-merge handler
|
|
6267
6276
|
return this.executeMultipleMergeClauses(matchClauses, createClauses, withClauses, mergeClauses, returnClause, params);
|
|
@@ -6615,7 +6624,7 @@ export class Executor {
|
|
|
6615
6624
|
* For each MATCH row, we execute the MERGE (with its ON CREATE SET / ON MATCH SET)
|
|
6616
6625
|
* and collect results for RETURN.
|
|
6617
6626
|
*/
|
|
6618
|
-
executeMergeWithSetClauses(matchClauses, createClauses, withClauses, mergeClause, returnClause, params) {
|
|
6627
|
+
executeMergeWithSetClauses(matchClauses, createClauses, withClauses, mergeClause, setClauses, returnClause, params) {
|
|
6619
6628
|
// Validate that variables used in ON CREATE SET / ON MATCH SET are defined
|
|
6620
6629
|
const validVariables = new Set();
|
|
6621
6630
|
// Collect variables from MATCH clauses
|
|
@@ -6803,19 +6812,37 @@ export class Executor {
|
|
|
6803
6812
|
// Track all matched nodes and edges for aggregation
|
|
6804
6813
|
const allMatchedNodeRows = [];
|
|
6805
6814
|
const allMatchedEdgeRows = [];
|
|
6815
|
+
// Track merged nodes for standalone SET clauses
|
|
6816
|
+
const mergedNodeVars = new Set();
|
|
6817
|
+
const mergedNodeIds = new Map(); // variable -> nodeId
|
|
6806
6818
|
for (const matchRow of matchRows) {
|
|
6807
6819
|
// Convert matchRow to the format expected by executeMergeNodeForRow
|
|
6808
6820
|
const matchedNodes = new Map(matchRow);
|
|
6809
6821
|
const matchedEdges = new Map();
|
|
6810
6822
|
if (patterns.length === 1 && !this.isRelationshipPattern(patterns[0])) {
|
|
6823
|
+
const nodePattern = patterns[0];
|
|
6811
6824
|
if (hasAggregate) {
|
|
6812
6825
|
// Execute MERGE without RETURN processing - we'll aggregate later
|
|
6813
|
-
this.executeMergeNodeForRow(
|
|
6826
|
+
this.executeMergeNodeForRow(nodePattern, mergeClause, null, params, matchedNodes);
|
|
6814
6827
|
allMatchedNodeRows.push(new Map(matchedNodes));
|
|
6828
|
+
if (nodePattern.variable) {
|
|
6829
|
+
const nodeInfo = matchedNodes.get(nodePattern.variable);
|
|
6830
|
+
if (nodeInfo) {
|
|
6831
|
+
mergedNodeIds.set(nodePattern.variable, nodeInfo.id);
|
|
6832
|
+
}
|
|
6833
|
+
}
|
|
6815
6834
|
}
|
|
6816
6835
|
else {
|
|
6817
|
-
const rowResults = this.executeMergeNodeForRow(
|
|
6836
|
+
const rowResults = this.executeMergeNodeForRow(nodePattern, mergeClause, returnClause, params, matchedNodes);
|
|
6818
6837
|
allResults.push(...rowResults);
|
|
6838
|
+
// Track merged node for standalone SET clauses
|
|
6839
|
+
if (nodePattern.variable) {
|
|
6840
|
+
const nodeInfo = matchedNodes.get(nodePattern.variable);
|
|
6841
|
+
if (nodeInfo) {
|
|
6842
|
+
mergedNodeIds.set(nodePattern.variable, nodeInfo.id);
|
|
6843
|
+
mergedNodeVars.add(nodePattern.variable);
|
|
6844
|
+
}
|
|
6845
|
+
}
|
|
6819
6846
|
}
|
|
6820
6847
|
}
|
|
6821
6848
|
else if (patterns.length === 1 && this.isRelationshipPattern(patterns[0])) {
|
|
@@ -6839,6 +6866,26 @@ export class Executor {
|
|
|
6839
6866
|
if (hasAggregate && returnClause) {
|
|
6840
6867
|
return this.processAggregateReturn(returnClause, allMatchedNodeRows, allMatchedEdgeRows, params);
|
|
6841
6868
|
}
|
|
6869
|
+
// Apply standalone SET clauses after MERGE
|
|
6870
|
+
if (setClauses.length > 0) {
|
|
6871
|
+
// For each merged node, apply the SET clauses
|
|
6872
|
+
for (const setClause of setClauses) {
|
|
6873
|
+
for (const assignment of setClause.assignments) {
|
|
6874
|
+
if (!assignment.variable || !assignment.property || !assignment.value)
|
|
6875
|
+
continue;
|
|
6876
|
+
// Skip if this variable wasn't merged in this query
|
|
6877
|
+
if (!mergedNodeVars.has(assignment.variable))
|
|
6878
|
+
continue;
|
|
6879
|
+
const nodeId = mergedNodeIds.get(assignment.variable);
|
|
6880
|
+
if (!nodeId)
|
|
6881
|
+
continue;
|
|
6882
|
+
const value = this.evaluateExpression(assignment.value, params);
|
|
6883
|
+
this.db.execute(`UPDATE nodes SET properties = json_set(properties, '$.${escSqlStr(assignment.property)}', json(?)) WHERE id = ?`, [JSON.stringify(value), nodeId]);
|
|
6884
|
+
// Invalidate cache after UPDATE
|
|
6885
|
+
this.invalidatePropertyCache(nodeId);
|
|
6886
|
+
}
|
|
6887
|
+
}
|
|
6888
|
+
}
|
|
6842
6889
|
return allResults;
|
|
6843
6890
|
}
|
|
6844
6891
|
/**
|