grafeo-ogm 1.14.0 → 2.1.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/CHANGELOG.md +256 -0
- package/README.md +70 -0
- package/dist/cjs/compilers/fulltext.compiler.d.ts.map +1 -1
- package/dist/cjs/compilers/fulltext.compiler.js +30 -14
- package/dist/cjs/compilers/fulltext.compiler.js.map +1 -1
- package/dist/cjs/compilers/mutation.compiler.d.ts +7 -0
- package/dist/cjs/compilers/mutation.compiler.d.ts.map +1 -1
- package/dist/cjs/compilers/mutation.compiler.js +130 -36
- package/dist/cjs/compilers/mutation.compiler.js.map +1 -1
- package/dist/cjs/compilers/where.compiler.d.ts.map +1 -1
- package/dist/cjs/compilers/where.compiler.js +13 -4
- package/dist/cjs/compilers/where.compiler.js.map +1 -1
- package/dist/cjs/generator/type-emitters/fulltext-emitter.js +24 -8
- package/dist/cjs/generator/type-emitters/fulltext-emitter.js.map +1 -1
- package/dist/cjs/generator/type-emitters/sort-options-emitter.d.ts +2 -1
- package/dist/cjs/generator/type-emitters/sort-options-emitter.d.ts.map +1 -1
- package/dist/cjs/generator/type-emitters/sort-options-emitter.js +12 -5
- package/dist/cjs/generator/type-emitters/sort-options-emitter.js.map +1 -1
- package/dist/cjs/generator/type-emitters/utility-types.d.ts.map +1 -1
- package/dist/cjs/generator/type-emitters/utility-types.js +21 -1
- package/dist/cjs/generator/type-emitters/utility-types.js.map +1 -1
- package/dist/cjs/utils/cypher-sort-projection.d.ts +3 -0
- package/dist/cjs/utils/cypher-sort-projection.d.ts.map +1 -1
- package/dist/cjs/utils/cypher-sort-projection.js +13 -1
- package/dist/cjs/utils/cypher-sort-projection.js.map +1 -1
- package/dist/cjs/utils/validation.d.ts +72 -0
- package/dist/cjs/utils/validation.d.ts.map +1 -1
- package/dist/cjs/utils/validation.js +133 -0
- package/dist/cjs/utils/validation.js.map +1 -1
- package/dist/cjs/utils/write-coercion.d.ts +61 -0
- package/dist/cjs/utils/write-coercion.d.ts.map +1 -0
- package/dist/cjs/utils/write-coercion.js +213 -0
- package/dist/cjs/utils/write-coercion.js.map +1 -0
- package/dist/esm/compilers/fulltext.compiler.d.ts.map +1 -1
- package/dist/esm/compilers/fulltext.compiler.js +31 -15
- package/dist/esm/compilers/fulltext.compiler.js.map +1 -1
- package/dist/esm/compilers/mutation.compiler.d.ts +7 -0
- package/dist/esm/compilers/mutation.compiler.d.ts.map +1 -1
- package/dist/esm/compilers/mutation.compiler.js +130 -36
- package/dist/esm/compilers/mutation.compiler.js.map +1 -1
- package/dist/esm/compilers/where.compiler.d.ts.map +1 -1
- package/dist/esm/compilers/where.compiler.js +13 -4
- package/dist/esm/compilers/where.compiler.js.map +1 -1
- package/dist/esm/generator/type-emitters/fulltext-emitter.js +24 -8
- package/dist/esm/generator/type-emitters/fulltext-emitter.js.map +1 -1
- package/dist/esm/generator/type-emitters/sort-options-emitter.d.ts +2 -1
- package/dist/esm/generator/type-emitters/sort-options-emitter.d.ts.map +1 -1
- package/dist/esm/generator/type-emitters/sort-options-emitter.js +12 -5
- package/dist/esm/generator/type-emitters/sort-options-emitter.js.map +1 -1
- package/dist/esm/generator/type-emitters/utility-types.d.ts.map +1 -1
- package/dist/esm/generator/type-emitters/utility-types.js +21 -1
- package/dist/esm/generator/type-emitters/utility-types.js.map +1 -1
- package/dist/esm/utils/cypher-sort-projection.d.ts +3 -0
- package/dist/esm/utils/cypher-sort-projection.d.ts.map +1 -1
- package/dist/esm/utils/cypher-sort-projection.js +14 -2
- package/dist/esm/utils/cypher-sort-projection.js.map +1 -1
- package/dist/esm/utils/validation.d.ts +72 -0
- package/dist/esm/utils/validation.d.ts.map +1 -1
- package/dist/esm/utils/validation.js +129 -0
- package/dist/esm/utils/validation.js.map +1 -1
- package/dist/esm/utils/write-coercion.d.ts +61 -0
- package/dist/esm/utils/write-coercion.d.ts.map +1 -0
- package/dist/esm/utils/write-coercion.js +206 -0
- package/dist/esm/utils/write-coercion.js.map +1 -0
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ exports.MutationCompiler = void 0;
|
|
|
4
4
|
const errors_1 = require("../errors");
|
|
5
5
|
const utils_1 = require("../schema/utils");
|
|
6
6
|
const validation_1 = require("../utils/validation");
|
|
7
|
+
const write_coercion_1 = require("../utils/write-coercion");
|
|
7
8
|
const where_compiler_1 = require("./where.compiler");
|
|
8
9
|
/**
|
|
9
10
|
* Relationship filter suffixes that require EXISTS subquery patterns.
|
|
@@ -182,9 +183,11 @@ class MutationCompiler {
|
|
|
182
183
|
if (value === undefined)
|
|
183
184
|
continue;
|
|
184
185
|
(0, validation_1.assertSafePropertyName)(key, 'update property');
|
|
186
|
+
const propDef = nodeDef.properties.get(key);
|
|
185
187
|
const paramName = `update_${key}`;
|
|
186
|
-
|
|
187
|
-
|
|
188
|
+
const valueExpr = (0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, value, propDef);
|
|
189
|
+
setClauses.push(`n.${(0, validation_1.escapeIdentifier)(key)} = ${valueExpr}`);
|
|
190
|
+
params[paramName] = (0, write_coercion_1.coerceWriteValue)(value, propDef);
|
|
188
191
|
}
|
|
189
192
|
if (setClauses.length > 0)
|
|
190
193
|
lines.push(`SET ${setClauses.join(', ')}`);
|
|
@@ -262,10 +265,12 @@ class MutationCompiler {
|
|
|
262
265
|
if (key.includes('_') && !nodeDef.properties.has(key))
|
|
263
266
|
continue;
|
|
264
267
|
(0, validation_1.assertSafePropertyName)(key, 'merge key');
|
|
268
|
+
const propDef = nodeDef.properties.get(key);
|
|
265
269
|
const paramName = `merge_${key}`;
|
|
266
|
-
|
|
270
|
+
const valueExpr = (0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, value, propDef);
|
|
271
|
+
mergeProps.push(`${(0, validation_1.escapeIdentifier)(key)}: ${valueExpr}`);
|
|
267
272
|
mergeKeyNames.push(key);
|
|
268
|
-
params[paramName] = value;
|
|
273
|
+
params[paramName] = (0, write_coercion_1.coerceWriteValue)(value, propDef);
|
|
269
274
|
}
|
|
270
275
|
if (mergeProps.length === 0)
|
|
271
276
|
throw new errors_1.OGMError('upsert requires at least one scalar property in "where" for MERGE key');
|
|
@@ -301,9 +306,29 @@ class MutationCompiler {
|
|
|
301
306
|
if (nodeDef.relationships.has(key))
|
|
302
307
|
continue;
|
|
303
308
|
(0, validation_1.assertSafePropertyName)(key, 'create property');
|
|
309
|
+
const propDef = nodeDef.properties.get(key);
|
|
304
310
|
const paramName = `onCreate_${key}`;
|
|
305
|
-
|
|
306
|
-
|
|
311
|
+
const valueExpr = (0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, value, propDef);
|
|
312
|
+
createSets.push(`n.${(0, validation_1.escapeIdentifier)(key)} = ${valueExpr}`);
|
|
313
|
+
params[paramName] = (0, write_coercion_1.coerceWriteValue)(value, propDef);
|
|
314
|
+
}
|
|
315
|
+
// `@default` values apply to the ON CREATE branch — a defaulted
|
|
316
|
+
// property not supplied in `create` and not already fixed by the
|
|
317
|
+
// MERGE key gets its default, mirroring create().
|
|
318
|
+
for (const [, propDef] of nodeDef.properties) {
|
|
319
|
+
if (propDef.defaultValue === undefined)
|
|
320
|
+
continue;
|
|
321
|
+
if (propDef.isGenerated || propDef.isCypher)
|
|
322
|
+
continue;
|
|
323
|
+
if (create[propDef.name] !== undefined)
|
|
324
|
+
continue;
|
|
325
|
+
if (mergeKeyNames.includes(propDef.name))
|
|
326
|
+
continue;
|
|
327
|
+
const paramName = `onCreate_${propDef.name}`;
|
|
328
|
+
const bound = (0, write_coercion_1.resolveDefaultWriteValue)(propDef);
|
|
329
|
+
const valueExpr = (0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, bound, propDef);
|
|
330
|
+
createSets.push(`n.${(0, validation_1.escapeIdentifier)(propDef.name)} = ${valueExpr}`);
|
|
331
|
+
params[paramName] = bound;
|
|
307
332
|
}
|
|
308
333
|
if (createSets.length > 0)
|
|
309
334
|
lines.push(`ON CREATE SET ${createSets.join(', ')}`);
|
|
@@ -315,9 +340,11 @@ class MutationCompiler {
|
|
|
315
340
|
if (nodeDef.relationships.has(key))
|
|
316
341
|
continue;
|
|
317
342
|
(0, validation_1.assertSafePropertyName)(key, 'update property');
|
|
343
|
+
const propDef = nodeDef.properties.get(key);
|
|
318
344
|
const paramName = `onMatch_${key}`;
|
|
319
|
-
|
|
320
|
-
|
|
345
|
+
const valueExpr = (0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, value, propDef);
|
|
346
|
+
updateSets.push(`n.${(0, validation_1.escapeIdentifier)(key)} = ${valueExpr}`);
|
|
347
|
+
params[paramName] = (0, write_coercion_1.coerceWriteValue)(value, propDef);
|
|
321
348
|
}
|
|
322
349
|
if (updateSets.length > 0)
|
|
323
350
|
lines.push(`ON MATCH SET ${updateSets.join(', ')}`);
|
|
@@ -389,12 +416,24 @@ class MutationCompiler {
|
|
|
389
416
|
for (const [, propDef] of nodeDef.properties)
|
|
390
417
|
if (propDef.isGenerated && !scalarKeys.includes(propDef.name))
|
|
391
418
|
generatedFields.push(propDef.name);
|
|
419
|
+
// Whole-column `@default`s: applied only when NO item supplies the
|
|
420
|
+
// field. Per-item gaps inside a provided column stay null — one
|
|
421
|
+
// createMany batch shares a single Cypher text, and a per-item
|
|
422
|
+
// default would need coalesce(), which would also override explicit
|
|
423
|
+
// nulls.
|
|
424
|
+
const defaultedProps = [];
|
|
425
|
+
for (const [, propDef] of nodeDef.properties)
|
|
426
|
+
if (propDef.defaultValue !== undefined &&
|
|
427
|
+
!propDef.isGenerated &&
|
|
428
|
+
!propDef.isCypher &&
|
|
429
|
+
!scalarKeys.includes(propDef.name))
|
|
430
|
+
defaultedProps.push(propDef);
|
|
392
431
|
// Build sanitized items (strip undefined values)
|
|
393
432
|
const sanitizedItems = data.map((item) => {
|
|
394
433
|
const sanitized = {};
|
|
395
434
|
for (const key of scalarKeys)
|
|
396
435
|
if (item[key] !== undefined)
|
|
397
|
-
sanitized[key] = item[key];
|
|
436
|
+
sanitized[key] = (0, write_coercion_1.coerceWriteValue)(item[key], nodeDef.properties.get(key));
|
|
398
437
|
return sanitized;
|
|
399
438
|
});
|
|
400
439
|
params.items = sanitizedItems;
|
|
@@ -411,16 +450,22 @@ class MutationCompiler {
|
|
|
411
450
|
throw new errors_1.OGMError('createMany with skipDuplicates requires at least one @id or @unique field in data');
|
|
412
451
|
// MERGE on unique key(s)
|
|
413
452
|
const mergeProps = mergeKeys
|
|
414
|
-
.map((k) => `${(0, validation_1.escapeIdentifier)(k)}: item.${(0, validation_1.escapeIdentifier)(k)}`)
|
|
453
|
+
.map((k) => `${(0, validation_1.escapeIdentifier)(k)}: ${(0, write_coercion_1.wrapTemporalListItemExpr)(`item.${(0, validation_1.escapeIdentifier)(k)}`, data.map((d) => d[k]), nodeDef.properties.get(k))}`)
|
|
415
454
|
.join(', ');
|
|
416
455
|
lines.push(`MERGE (n:${labelStr} { ${mergeProps} })`);
|
|
417
456
|
// ON CREATE SET — remaining fields + generated IDs
|
|
418
457
|
const onCreateSets = [];
|
|
419
458
|
for (const key of scalarKeys)
|
|
420
459
|
if (!mergeKeys.includes(key))
|
|
421
|
-
onCreateSets.push(`n.${(0, validation_1.escapeIdentifier)(key)} = item.${(0, validation_1.escapeIdentifier)(key)}`);
|
|
460
|
+
onCreateSets.push(`n.${(0, validation_1.escapeIdentifier)(key)} = ${(0, write_coercion_1.wrapTemporalListItemExpr)(`item.${(0, validation_1.escapeIdentifier)(key)}`, data.map((d) => d[key]), nodeDef.properties.get(key))}`);
|
|
422
461
|
for (const field of generatedFields)
|
|
423
462
|
onCreateSets.push(`n.${(0, validation_1.escapeIdentifier)(field)} = randomUUID()`);
|
|
463
|
+
for (const propDef of defaultedProps) {
|
|
464
|
+
const paramName = `default_${propDef.name}`;
|
|
465
|
+
const bound = (0, write_coercion_1.resolveDefaultWriteValue)(propDef);
|
|
466
|
+
onCreateSets.push(`n.${(0, validation_1.escapeIdentifier)(propDef.name)} = ${(0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, bound, propDef)}`);
|
|
467
|
+
params[paramName] = bound;
|
|
468
|
+
}
|
|
424
469
|
if (onCreateSets.length > 0)
|
|
425
470
|
lines.push(`ON CREATE SET ${onCreateSets.join(', ')}`);
|
|
426
471
|
}
|
|
@@ -430,7 +475,13 @@ class MutationCompiler {
|
|
|
430
475
|
for (const field of generatedFields)
|
|
431
476
|
propParts.push(`${(0, validation_1.escapeIdentifier)(field)}: randomUUID()`);
|
|
432
477
|
for (const key of scalarKeys)
|
|
433
|
-
propParts.push(`${(0, validation_1.escapeIdentifier)(key)}: item.${(0, validation_1.escapeIdentifier)(key)}`);
|
|
478
|
+
propParts.push(`${(0, validation_1.escapeIdentifier)(key)}: ${(0, write_coercion_1.wrapTemporalListItemExpr)(`item.${(0, validation_1.escapeIdentifier)(key)}`, data.map((d) => d[key]), nodeDef.properties.get(key))}`);
|
|
479
|
+
for (const propDef of defaultedProps) {
|
|
480
|
+
const paramName = `default_${propDef.name}`;
|
|
481
|
+
const bound = (0, write_coercion_1.resolveDefaultWriteValue)(propDef);
|
|
482
|
+
propParts.push(`${(0, validation_1.escapeIdentifier)(propDef.name)}: ${(0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, bound, propDef)}`);
|
|
483
|
+
params[paramName] = bound;
|
|
484
|
+
}
|
|
434
485
|
lines.push(`CREATE (n:${labelStr} { ${propParts.join(', ')} })`);
|
|
435
486
|
}
|
|
436
487
|
// Extra labels
|
|
@@ -442,6 +493,19 @@ class MutationCompiler {
|
|
|
442
493
|
return { cypher: lines.join('\n'), params };
|
|
443
494
|
}
|
|
444
495
|
// ─── Private helpers ──────────────────────────────────────────────
|
|
496
|
+
/**
|
|
497
|
+
* Resolve the PropertyDefinition of an edge (relationship) property so
|
|
498
|
+
* Int/BigInt edge writes get the same Integer coercion as node writes.
|
|
499
|
+
* Returns undefined when the relationship declares no properties type
|
|
500
|
+
* (the coercion then passes the value through untouched).
|
|
501
|
+
*/
|
|
502
|
+
getEdgePropDef(relDef, propName) {
|
|
503
|
+
if (!relDef.properties)
|
|
504
|
+
return undefined;
|
|
505
|
+
return this.schema.relationshipProperties
|
|
506
|
+
.get(relDef.properties)
|
|
507
|
+
?.properties.get(propName);
|
|
508
|
+
}
|
|
445
509
|
buildCreateProperties(input, nodeDef, prefix) {
|
|
446
510
|
const parts = [];
|
|
447
511
|
const propParams = {};
|
|
@@ -456,9 +520,28 @@ class MutationCompiler {
|
|
|
456
520
|
if (value === undefined)
|
|
457
521
|
continue;
|
|
458
522
|
(0, validation_1.assertSafePropertyName)(key, 'create property');
|
|
523
|
+
const propDef = nodeDef.properties.get(key);
|
|
459
524
|
const paramName = `${prefix}_${key}`;
|
|
460
|
-
|
|
461
|
-
|
|
525
|
+
const valueExpr = (0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, value, propDef);
|
|
526
|
+
parts.push(`${(0, validation_1.escapeIdentifier)(key)}: ${valueExpr}`);
|
|
527
|
+
propParams[paramName] = (0, write_coercion_1.coerceWriteValue)(value, propDef);
|
|
528
|
+
}
|
|
529
|
+
// `@default` values — applied for any property with a default that the
|
|
530
|
+
// input leaves undefined. Explicit null is respected as null: the user
|
|
531
|
+
// asked for null, a default would override intent. Generated (@id) and
|
|
532
|
+
// @cypher (computed, not stored) properties never take defaults.
|
|
533
|
+
for (const [, propDef] of nodeDef.properties) {
|
|
534
|
+
if (propDef.defaultValue === undefined)
|
|
535
|
+
continue;
|
|
536
|
+
if (propDef.isGenerated || propDef.isCypher)
|
|
537
|
+
continue;
|
|
538
|
+
if (input[propDef.name] !== undefined)
|
|
539
|
+
continue;
|
|
540
|
+
const paramName = `${prefix}_${propDef.name}`;
|
|
541
|
+
const bound = (0, write_coercion_1.resolveDefaultWriteValue)(propDef);
|
|
542
|
+
const valueExpr = (0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, bound, propDef);
|
|
543
|
+
parts.push(`${(0, validation_1.escapeIdentifier)(propDef.name)}: ${valueExpr}`);
|
|
544
|
+
propParams[paramName] = bound;
|
|
462
545
|
}
|
|
463
546
|
return { propString: parts.join(', '), propParams };
|
|
464
547
|
}
|
|
@@ -563,9 +646,10 @@ class MutationCompiler {
|
|
|
563
646
|
if (val === undefined)
|
|
564
647
|
continue;
|
|
565
648
|
(0, validation_1.assertSafePropertyName)(prop, 'edge property');
|
|
649
|
+
const edgePropDef = this.getEdgePropDef(relDef, prop);
|
|
566
650
|
const paramName = `${prefix}_create${ci}_edge_${prop}`;
|
|
567
|
-
setItems.push(`${relVar}.${(0, validation_1.escapeIdentifier)(prop)} =
|
|
568
|
-
params[paramName] = val;
|
|
651
|
+
setItems.push(`${relVar}.${(0, validation_1.escapeIdentifier)(prop)} = ${(0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, val, edgePropDef)}`);
|
|
652
|
+
params[paramName] = (0, write_coercion_1.coerceWriteValue)(val, edgePropDef);
|
|
569
653
|
}
|
|
570
654
|
if (setItems.length > 0)
|
|
571
655
|
lines.push(`SET ${setItems.join(', ')}`);
|
|
@@ -615,9 +699,10 @@ class MutationCompiler {
|
|
|
615
699
|
if (val === undefined)
|
|
616
700
|
continue;
|
|
617
701
|
(0, validation_1.assertSafePropertyName)(prop, 'edge property');
|
|
702
|
+
const edgePropDef = this.getEdgePropDef(relDef, prop);
|
|
618
703
|
const paramName = `${prefix}_conn${ci}_edge_${prop}`;
|
|
619
|
-
setItems.push(`r_conn_${startCounter + counter - 1}.${(0, validation_1.escapeIdentifier)(prop)} =
|
|
620
|
-
params[paramName] = val;
|
|
704
|
+
setItems.push(`r_conn_${startCounter + counter - 1}.${(0, validation_1.escapeIdentifier)(prop)} = ${(0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, val, edgePropDef)}`);
|
|
705
|
+
params[paramName] = (0, write_coercion_1.coerceWriteValue)(val, edgePropDef);
|
|
621
706
|
}
|
|
622
707
|
if (setItems.length > 0)
|
|
623
708
|
lines.push(`SET ${setItems.join(', ')}`);
|
|
@@ -686,9 +771,10 @@ class MutationCompiler {
|
|
|
686
771
|
if (val === undefined)
|
|
687
772
|
continue;
|
|
688
773
|
(0, validation_1.assertSafePropertyName)(prop, 'edge property');
|
|
774
|
+
const edgePropDef = this.getEdgePropDef(relDef, prop);
|
|
689
775
|
const paramName = `connect_${fieldName}_${ci}_edge_${prop}`;
|
|
690
|
-
setItems.push(`${relVar}.${(0, validation_1.escapeIdentifier)(prop)} =
|
|
691
|
-
params[paramName] = val;
|
|
776
|
+
setItems.push(`${relVar}.${(0, validation_1.escapeIdentifier)(prop)} = ${(0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, val, edgePropDef)}`);
|
|
777
|
+
params[paramName] = (0, write_coercion_1.coerceWriteValue)(val, edgePropDef);
|
|
692
778
|
}
|
|
693
779
|
if (setItems.length > 0)
|
|
694
780
|
lines.push(`SET ${setItems.join(', ')}`);
|
|
@@ -716,7 +802,7 @@ class MutationCompiler {
|
|
|
716
802
|
const cleaned = {};
|
|
717
803
|
for (const [k, v] of Object.entries(edge))
|
|
718
804
|
if (v !== undefined)
|
|
719
|
-
cleaned[k] = v;
|
|
805
|
+
cleaned[k] = (0, write_coercion_1.coerceWriteValue)(v, this.getEdgePropDef(relDef, k));
|
|
720
806
|
return { ...item, edge: cleaned };
|
|
721
807
|
});
|
|
722
808
|
lines.push('WITH n');
|
|
@@ -740,7 +826,7 @@ class MutationCompiler {
|
|
|
740
826
|
`silently dropped. Split into separate connect calls, or normalise the items ` +
|
|
741
827
|
`to share the same shape.`);
|
|
742
828
|
}
|
|
743
|
-
const whereConditions = this.extractConnectWhereConditions(firstItem, 'target', 'connItem');
|
|
829
|
+
const whereConditions = this.extractConnectWhereConditions(firstItem, 'target', 'connItem', targetNodeDef);
|
|
744
830
|
const policyPredicate = this.buildTargetPolicyPredicate(targetNodeDef, 'target', params, policyContext, paramCounter);
|
|
745
831
|
const unwindConditions = policyPredicate
|
|
746
832
|
? [...whereConditions, policyPredicate]
|
|
@@ -765,7 +851,7 @@ class MutationCompiler {
|
|
|
765
851
|
// Replace MERGE line with one that has a variable
|
|
766
852
|
lines[lines.length - 1] = `MERGE ${mergeWithVar}`;
|
|
767
853
|
// r.prop is relationship property (escape), connItem.edge.prop is parameter map (no escape)
|
|
768
|
-
const setItems = edgeProps.map((p) => `r.${(0, validation_1.escapeIdentifier)(p)} = connItem.edge.${p}`);
|
|
854
|
+
const setItems = edgeProps.map((p) => `r.${(0, validation_1.escapeIdentifier)(p)} = ${(0, write_coercion_1.wrapTemporalListItemExpr)(`connItem.edge.${p}`, spec.map((it) => it.edge?.[p]), this.getEdgePropDef(relDef, p))}`);
|
|
769
855
|
lines.push(`SET ${setItems.join(', ')}`);
|
|
770
856
|
}
|
|
771
857
|
}
|
|
@@ -796,9 +882,10 @@ class MutationCompiler {
|
|
|
796
882
|
for (const [prop, val] of Object.entries(edgeInput)) {
|
|
797
883
|
if (val === undefined)
|
|
798
884
|
continue;
|
|
885
|
+
const edgePropDef = this.getEdgePropDef(relDef, prop);
|
|
799
886
|
const paramName = `connect_${fieldName}_edge_${prop}`;
|
|
800
|
-
setItems.push(`r.${(0, validation_1.escapeIdentifier)(prop)} =
|
|
801
|
-
params[paramName] = val;
|
|
887
|
+
setItems.push(`r.${(0, validation_1.escapeIdentifier)(prop)} = ${(0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, val, edgePropDef)}`);
|
|
888
|
+
params[paramName] = (0, write_coercion_1.coerceWriteValue)(val, edgePropDef);
|
|
802
889
|
}
|
|
803
890
|
if (setItems.length > 0)
|
|
804
891
|
lines.push(`SET ${setItems.join(', ')}`);
|
|
@@ -1111,9 +1198,10 @@ class MutationCompiler {
|
|
|
1111
1198
|
if (val === undefined)
|
|
1112
1199
|
continue;
|
|
1113
1200
|
(0, validation_1.assertSafePropertyName)(prop, 'edge property');
|
|
1201
|
+
const edgePropDef = this.getEdgePropDef(relDef, prop);
|
|
1114
1202
|
const paramName = `${itemPrefix}_conn${ci}_edge_${prop}`;
|
|
1115
|
-
setItems.push(`r_conn_${key}_${ci}.${(0, validation_1.escapeIdentifier)(prop)} =
|
|
1116
|
-
params[paramName] = val;
|
|
1203
|
+
setItems.push(`r_conn_${key}_${ci}.${(0, validation_1.escapeIdentifier)(prop)} = ${(0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, val, edgePropDef)}`);
|
|
1204
|
+
params[paramName] = (0, write_coercion_1.coerceWriteValue)(val, edgePropDef);
|
|
1117
1205
|
}
|
|
1118
1206
|
if (setItems.length > 0)
|
|
1119
1207
|
lines.push(`SET ${setItems.join(', ')}`);
|
|
@@ -1149,9 +1237,10 @@ class MutationCompiler {
|
|
|
1149
1237
|
if (val === undefined)
|
|
1150
1238
|
continue;
|
|
1151
1239
|
(0, validation_1.assertSafePropertyName)(prop, 'nested update property');
|
|
1240
|
+
const nodePropDef = targetNodeDef.properties.get(prop);
|
|
1152
1241
|
const paramName = `${itemPrefix}_set_${prop}`;
|
|
1153
|
-
setClauses.push(`${updateVar}.${(0, validation_1.escapeIdentifier)(prop)} =
|
|
1154
|
-
setParams[paramName] = val;
|
|
1242
|
+
setClauses.push(`${updateVar}.${(0, validation_1.escapeIdentifier)(prop)} = ${(0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, val, nodePropDef)}`);
|
|
1243
|
+
setParams[paramName] = (0, write_coercion_1.coerceWriteValue)(val, nodePropDef);
|
|
1155
1244
|
}
|
|
1156
1245
|
// Pre-compute SET clauses for edge (relationship) properties
|
|
1157
1246
|
const edgeClauses = [];
|
|
@@ -1161,9 +1250,10 @@ class MutationCompiler {
|
|
|
1161
1250
|
if (val === undefined)
|
|
1162
1251
|
continue;
|
|
1163
1252
|
(0, validation_1.assertSafePropertyName)(prop, 'edge update property');
|
|
1253
|
+
const edgePropDef = this.getEdgePropDef(relDef, prop);
|
|
1164
1254
|
const paramName = `${itemPrefix}_edge_${prop}`;
|
|
1165
|
-
edgeClauses.push(`${relVar}.${(0, validation_1.escapeIdentifier)(prop)} =
|
|
1166
|
-
edgeParams[paramName] = val;
|
|
1255
|
+
edgeClauses.push(`${relVar}.${(0, validation_1.escapeIdentifier)(prop)} = ${(0, write_coercion_1.wrapTemporalWriteExpr)(`$${paramName}`, val, edgePropDef)}`);
|
|
1256
|
+
edgeParams[paramName] = (0, write_coercion_1.coerceWriteValue)(val, edgePropDef);
|
|
1167
1257
|
}
|
|
1168
1258
|
// Pre-compute nested relationship operations
|
|
1169
1259
|
const nestedParams = {};
|
|
@@ -1249,15 +1339,16 @@ class MutationCompiler {
|
|
|
1249
1339
|
(0, validation_1.assertSafePropertyName)(baseProp, 'where property');
|
|
1250
1340
|
const paramName = `${prefix}_NOT_${notProp}`;
|
|
1251
1341
|
const escapedProp = (0, validation_1.escapeIdentifier)(baseProp);
|
|
1342
|
+
const paramRef = (0, write_coercion_1.wrapTemporalWhereParam)(`$${paramName}`, notVal, targetNodeDef?.properties.get(baseProp), notProp.slice(baseProp.length));
|
|
1252
1343
|
let expr;
|
|
1253
1344
|
if (template === '%v = $%p')
|
|
1254
1345
|
// Simple equality → negate to <>
|
|
1255
|
-
expr = `${targetVar}.${escapedProp} <>
|
|
1346
|
+
expr = `${targetVar}.${escapedProp} <> ${paramRef}`;
|
|
1256
1347
|
else {
|
|
1257
1348
|
// Operator suffix — wrap with NOT if not already negated
|
|
1258
1349
|
const resolved = template
|
|
1259
1350
|
.replace('%v', `${targetVar}.${escapedProp}`)
|
|
1260
|
-
.replace('$%p',
|
|
1351
|
+
.replace('$%p', paramRef);
|
|
1261
1352
|
expr = resolved.startsWith('NOT ') ? resolved : `NOT ${resolved}`;
|
|
1262
1353
|
}
|
|
1263
1354
|
conditions.push(expr);
|
|
@@ -1274,9 +1365,10 @@ class MutationCompiler {
|
|
|
1274
1365
|
const { baseProp, template } = this.parseOperatorSuffix(prop);
|
|
1275
1366
|
(0, validation_1.assertSafePropertyName)(baseProp, 'where property');
|
|
1276
1367
|
const paramName = `${prefix}_${prop}`;
|
|
1368
|
+
const paramRef = (0, write_coercion_1.wrapTemporalWhereParam)(`$${paramName}`, val, targetNodeDef?.properties.get(baseProp), prop.slice(baseProp.length));
|
|
1277
1369
|
conditions.push(template
|
|
1278
1370
|
.replace('%v', `${targetVar}.${(0, validation_1.escapeIdentifier)(baseProp)}`)
|
|
1279
|
-
.replace('$%p',
|
|
1371
|
+
.replace('$%p', paramRef));
|
|
1280
1372
|
params[paramName] = val;
|
|
1281
1373
|
}
|
|
1282
1374
|
return conditions;
|
|
@@ -1441,7 +1533,7 @@ class MutationCompiler {
|
|
|
1441
1533
|
}
|
|
1442
1534
|
return false;
|
|
1443
1535
|
}
|
|
1444
|
-
extractConnectWhereConditions(item, targetVar, itemVar) {
|
|
1536
|
+
extractConnectWhereConditions(item, targetVar, itemVar, targetNodeDef) {
|
|
1445
1537
|
const conditions = [];
|
|
1446
1538
|
const whereSpec = item.where;
|
|
1447
1539
|
if (!whereSpec)
|
|
@@ -1452,7 +1544,9 @@ class MutationCompiler {
|
|
|
1452
1544
|
(0, validation_1.assertSafePropertyName)(baseProp, 'connect where property');
|
|
1453
1545
|
// Replace template placeholders with dynamic UNWIND references
|
|
1454
1546
|
// Note: itemVar map access is NOT escaped (parameter map keys, not Cypher identifiers)
|
|
1455
|
-
|
|
1547
|
+
// Temporal wrap keys off the FIRST item's value type — the UNWIND
|
|
1548
|
+
// fast path already requires shape-homogeneous items.
|
|
1549
|
+
const valueRef = (0, write_coercion_1.wrapTemporalWhereParam)(`${itemVar}.where.node.${key}`, nodeWhere[key], targetNodeDef?.properties.get(baseProp), key.slice(baseProp.length));
|
|
1456
1550
|
conditions.push(template
|
|
1457
1551
|
.replace('%v', `${targetVar}.${(0, validation_1.escapeIdentifier)(baseProp)}`)
|
|
1458
1552
|
.replace('$%p', valueRef));
|