grafeo-ogm 2.0.0 → 2.1.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/CHANGELOG.md +105 -0
- 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/utility-types.js +1 -1
- package/dist/cjs/utils/write-coercion.d.ts +62 -0
- package/dist/cjs/utils/write-coercion.d.ts.map +1 -0
- package/dist/cjs/utils/write-coercion.js +283 -0
- package/dist/cjs/utils/write-coercion.js.map +1 -0
- 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/utility-types.js +1 -1
- package/dist/esm/utils/write-coercion.d.ts +62 -0
- package/dist/esm/utils/write-coercion.d.ts.map +1 -0
- package/dist/esm/utils/write-coercion.js +276 -0
- package/dist/esm/utils/write-coercion.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OGMError } from '../errors';
|
|
2
2
|
import { buildRelPattern, getTargetLabelString, resolveTargetDef, } from '../schema/utils';
|
|
3
3
|
import { assertSafeIdentifier, assertSafeLabel, assertSafePropertyName, escapeIdentifier, mergeParams, } from '../utils/validation';
|
|
4
|
+
import { coerceWriteValue, resolveDefaultWriteValue, wrapListItemExpr, wrapWhereParam, wrapWriteExpr, } from '../utils/write-coercion';
|
|
4
5
|
import { WhereCompiler } from './where.compiler';
|
|
5
6
|
/**
|
|
6
7
|
* Relationship filter suffixes that require EXISTS subquery patterns.
|
|
@@ -179,9 +180,11 @@ export class MutationCompiler {
|
|
|
179
180
|
if (value === undefined)
|
|
180
181
|
continue;
|
|
181
182
|
assertSafePropertyName(key, 'update property');
|
|
183
|
+
const propDef = nodeDef.properties.get(key);
|
|
182
184
|
const paramName = `update_${key}`;
|
|
183
|
-
|
|
184
|
-
|
|
185
|
+
const valueExpr = wrapWriteExpr(`$${paramName}`, value, propDef);
|
|
186
|
+
setClauses.push(`n.${escapeIdentifier(key)} = ${valueExpr}`);
|
|
187
|
+
params[paramName] = coerceWriteValue(value, propDef);
|
|
185
188
|
}
|
|
186
189
|
if (setClauses.length > 0)
|
|
187
190
|
lines.push(`SET ${setClauses.join(', ')}`);
|
|
@@ -259,10 +262,12 @@ export class MutationCompiler {
|
|
|
259
262
|
if (key.includes('_') && !nodeDef.properties.has(key))
|
|
260
263
|
continue;
|
|
261
264
|
assertSafePropertyName(key, 'merge key');
|
|
265
|
+
const propDef = nodeDef.properties.get(key);
|
|
262
266
|
const paramName = `merge_${key}`;
|
|
263
|
-
|
|
267
|
+
const valueExpr = wrapWriteExpr(`$${paramName}`, value, propDef);
|
|
268
|
+
mergeProps.push(`${escapeIdentifier(key)}: ${valueExpr}`);
|
|
264
269
|
mergeKeyNames.push(key);
|
|
265
|
-
params[paramName] = value;
|
|
270
|
+
params[paramName] = coerceWriteValue(value, propDef);
|
|
266
271
|
}
|
|
267
272
|
if (mergeProps.length === 0)
|
|
268
273
|
throw new OGMError('upsert requires at least one scalar property in "where" for MERGE key');
|
|
@@ -298,9 +303,29 @@ export class MutationCompiler {
|
|
|
298
303
|
if (nodeDef.relationships.has(key))
|
|
299
304
|
continue;
|
|
300
305
|
assertSafePropertyName(key, 'create property');
|
|
306
|
+
const propDef = nodeDef.properties.get(key);
|
|
301
307
|
const paramName = `onCreate_${key}`;
|
|
302
|
-
|
|
303
|
-
|
|
308
|
+
const valueExpr = wrapWriteExpr(`$${paramName}`, value, propDef);
|
|
309
|
+
createSets.push(`n.${escapeIdentifier(key)} = ${valueExpr}`);
|
|
310
|
+
params[paramName] = coerceWriteValue(value, propDef);
|
|
311
|
+
}
|
|
312
|
+
// `@default` values apply to the ON CREATE branch — a defaulted
|
|
313
|
+
// property not supplied in `create` and not already fixed by the
|
|
314
|
+
// MERGE key gets its default, mirroring create().
|
|
315
|
+
for (const [, propDef] of nodeDef.properties) {
|
|
316
|
+
if (propDef.defaultValue === undefined)
|
|
317
|
+
continue;
|
|
318
|
+
if (propDef.isGenerated || propDef.isCypher)
|
|
319
|
+
continue;
|
|
320
|
+
if (create[propDef.name] !== undefined)
|
|
321
|
+
continue;
|
|
322
|
+
if (mergeKeyNames.includes(propDef.name))
|
|
323
|
+
continue;
|
|
324
|
+
const paramName = `onCreate_${propDef.name}`;
|
|
325
|
+
const bound = resolveDefaultWriteValue(propDef);
|
|
326
|
+
const valueExpr = wrapWriteExpr(`$${paramName}`, bound, propDef);
|
|
327
|
+
createSets.push(`n.${escapeIdentifier(propDef.name)} = ${valueExpr}`);
|
|
328
|
+
params[paramName] = bound;
|
|
304
329
|
}
|
|
305
330
|
if (createSets.length > 0)
|
|
306
331
|
lines.push(`ON CREATE SET ${createSets.join(', ')}`);
|
|
@@ -312,9 +337,11 @@ export class MutationCompiler {
|
|
|
312
337
|
if (nodeDef.relationships.has(key))
|
|
313
338
|
continue;
|
|
314
339
|
assertSafePropertyName(key, 'update property');
|
|
340
|
+
const propDef = nodeDef.properties.get(key);
|
|
315
341
|
const paramName = `onMatch_${key}`;
|
|
316
|
-
|
|
317
|
-
|
|
342
|
+
const valueExpr = wrapWriteExpr(`$${paramName}`, value, propDef);
|
|
343
|
+
updateSets.push(`n.${escapeIdentifier(key)} = ${valueExpr}`);
|
|
344
|
+
params[paramName] = coerceWriteValue(value, propDef);
|
|
318
345
|
}
|
|
319
346
|
if (updateSets.length > 0)
|
|
320
347
|
lines.push(`ON MATCH SET ${updateSets.join(', ')}`);
|
|
@@ -386,12 +413,24 @@ export class MutationCompiler {
|
|
|
386
413
|
for (const [, propDef] of nodeDef.properties)
|
|
387
414
|
if (propDef.isGenerated && !scalarKeys.includes(propDef.name))
|
|
388
415
|
generatedFields.push(propDef.name);
|
|
416
|
+
// Whole-column `@default`s: applied only when NO item supplies the
|
|
417
|
+
// field. Per-item gaps inside a provided column stay null — one
|
|
418
|
+
// createMany batch shares a single Cypher text, and a per-item
|
|
419
|
+
// default would need coalesce(), which would also override explicit
|
|
420
|
+
// nulls.
|
|
421
|
+
const defaultedProps = [];
|
|
422
|
+
for (const [, propDef] of nodeDef.properties)
|
|
423
|
+
if (propDef.defaultValue !== undefined &&
|
|
424
|
+
!propDef.isGenerated &&
|
|
425
|
+
!propDef.isCypher &&
|
|
426
|
+
!scalarKeys.includes(propDef.name))
|
|
427
|
+
defaultedProps.push(propDef);
|
|
389
428
|
// Build sanitized items (strip undefined values)
|
|
390
429
|
const sanitizedItems = data.map((item) => {
|
|
391
430
|
const sanitized = {};
|
|
392
431
|
for (const key of scalarKeys)
|
|
393
432
|
if (item[key] !== undefined)
|
|
394
|
-
sanitized[key] = item[key];
|
|
433
|
+
sanitized[key] = coerceWriteValue(item[key], nodeDef.properties.get(key));
|
|
395
434
|
return sanitized;
|
|
396
435
|
});
|
|
397
436
|
params.items = sanitizedItems;
|
|
@@ -408,16 +447,22 @@ export class MutationCompiler {
|
|
|
408
447
|
throw new OGMError('createMany with skipDuplicates requires at least one @id or @unique field in data');
|
|
409
448
|
// MERGE on unique key(s)
|
|
410
449
|
const mergeProps = mergeKeys
|
|
411
|
-
.map((k) => `${escapeIdentifier(k)}: item.${escapeIdentifier(k)}`)
|
|
450
|
+
.map((k) => `${escapeIdentifier(k)}: ${wrapListItemExpr(`item.${escapeIdentifier(k)}`, data.map((d) => d[k]), nodeDef.properties.get(k))}`)
|
|
412
451
|
.join(', ');
|
|
413
452
|
lines.push(`MERGE (n:${labelStr} { ${mergeProps} })`);
|
|
414
453
|
// ON CREATE SET — remaining fields + generated IDs
|
|
415
454
|
const onCreateSets = [];
|
|
416
455
|
for (const key of scalarKeys)
|
|
417
456
|
if (!mergeKeys.includes(key))
|
|
418
|
-
onCreateSets.push(`n.${escapeIdentifier(key)} = item.${escapeIdentifier(key)}`);
|
|
457
|
+
onCreateSets.push(`n.${escapeIdentifier(key)} = ${wrapListItemExpr(`item.${escapeIdentifier(key)}`, data.map((d) => d[key]), nodeDef.properties.get(key))}`);
|
|
419
458
|
for (const field of generatedFields)
|
|
420
459
|
onCreateSets.push(`n.${escapeIdentifier(field)} = randomUUID()`);
|
|
460
|
+
for (const propDef of defaultedProps) {
|
|
461
|
+
const paramName = `default_${propDef.name}`;
|
|
462
|
+
const bound = resolveDefaultWriteValue(propDef);
|
|
463
|
+
onCreateSets.push(`n.${escapeIdentifier(propDef.name)} = ${wrapWriteExpr(`$${paramName}`, bound, propDef)}`);
|
|
464
|
+
params[paramName] = bound;
|
|
465
|
+
}
|
|
421
466
|
if (onCreateSets.length > 0)
|
|
422
467
|
lines.push(`ON CREATE SET ${onCreateSets.join(', ')}`);
|
|
423
468
|
}
|
|
@@ -427,7 +472,13 @@ export class MutationCompiler {
|
|
|
427
472
|
for (const field of generatedFields)
|
|
428
473
|
propParts.push(`${escapeIdentifier(field)}: randomUUID()`);
|
|
429
474
|
for (const key of scalarKeys)
|
|
430
|
-
propParts.push(`${escapeIdentifier(key)}: item.${escapeIdentifier(key)}`);
|
|
475
|
+
propParts.push(`${escapeIdentifier(key)}: ${wrapListItemExpr(`item.${escapeIdentifier(key)}`, data.map((d) => d[key]), nodeDef.properties.get(key))}`);
|
|
476
|
+
for (const propDef of defaultedProps) {
|
|
477
|
+
const paramName = `default_${propDef.name}`;
|
|
478
|
+
const bound = resolveDefaultWriteValue(propDef);
|
|
479
|
+
propParts.push(`${escapeIdentifier(propDef.name)}: ${wrapWriteExpr(`$${paramName}`, bound, propDef)}`);
|
|
480
|
+
params[paramName] = bound;
|
|
481
|
+
}
|
|
431
482
|
lines.push(`CREATE (n:${labelStr} { ${propParts.join(', ')} })`);
|
|
432
483
|
}
|
|
433
484
|
// Extra labels
|
|
@@ -439,6 +490,19 @@ export class MutationCompiler {
|
|
|
439
490
|
return { cypher: lines.join('\n'), params };
|
|
440
491
|
}
|
|
441
492
|
// ─── Private helpers ──────────────────────────────────────────────
|
|
493
|
+
/**
|
|
494
|
+
* Resolve the PropertyDefinition of an edge (relationship) property so
|
|
495
|
+
* Int/BigInt edge writes get the same Integer coercion as node writes.
|
|
496
|
+
* Returns undefined when the relationship declares no properties type
|
|
497
|
+
* (the coercion then passes the value through untouched).
|
|
498
|
+
*/
|
|
499
|
+
getEdgePropDef(relDef, propName) {
|
|
500
|
+
if (!relDef.properties)
|
|
501
|
+
return undefined;
|
|
502
|
+
return this.schema.relationshipProperties
|
|
503
|
+
.get(relDef.properties)
|
|
504
|
+
?.properties.get(propName);
|
|
505
|
+
}
|
|
442
506
|
buildCreateProperties(input, nodeDef, prefix) {
|
|
443
507
|
const parts = [];
|
|
444
508
|
const propParams = {};
|
|
@@ -453,9 +517,28 @@ export class MutationCompiler {
|
|
|
453
517
|
if (value === undefined)
|
|
454
518
|
continue;
|
|
455
519
|
assertSafePropertyName(key, 'create property');
|
|
520
|
+
const propDef = nodeDef.properties.get(key);
|
|
456
521
|
const paramName = `${prefix}_${key}`;
|
|
457
|
-
|
|
458
|
-
|
|
522
|
+
const valueExpr = wrapWriteExpr(`$${paramName}`, value, propDef);
|
|
523
|
+
parts.push(`${escapeIdentifier(key)}: ${valueExpr}`);
|
|
524
|
+
propParams[paramName] = coerceWriteValue(value, propDef);
|
|
525
|
+
}
|
|
526
|
+
// `@default` values — applied for any property with a default that the
|
|
527
|
+
// input leaves undefined. Explicit null is respected as null: the user
|
|
528
|
+
// asked for null, a default would override intent. Generated (@id) and
|
|
529
|
+
// @cypher (computed, not stored) properties never take defaults.
|
|
530
|
+
for (const [, propDef] of nodeDef.properties) {
|
|
531
|
+
if (propDef.defaultValue === undefined)
|
|
532
|
+
continue;
|
|
533
|
+
if (propDef.isGenerated || propDef.isCypher)
|
|
534
|
+
continue;
|
|
535
|
+
if (input[propDef.name] !== undefined)
|
|
536
|
+
continue;
|
|
537
|
+
const paramName = `${prefix}_${propDef.name}`;
|
|
538
|
+
const bound = resolveDefaultWriteValue(propDef);
|
|
539
|
+
const valueExpr = wrapWriteExpr(`$${paramName}`, bound, propDef);
|
|
540
|
+
parts.push(`${escapeIdentifier(propDef.name)}: ${valueExpr}`);
|
|
541
|
+
propParams[paramName] = bound;
|
|
459
542
|
}
|
|
460
543
|
return { propString: parts.join(', '), propParams };
|
|
461
544
|
}
|
|
@@ -560,9 +643,10 @@ export class MutationCompiler {
|
|
|
560
643
|
if (val === undefined)
|
|
561
644
|
continue;
|
|
562
645
|
assertSafePropertyName(prop, 'edge property');
|
|
646
|
+
const edgePropDef = this.getEdgePropDef(relDef, prop);
|
|
563
647
|
const paramName = `${prefix}_create${ci}_edge_${prop}`;
|
|
564
|
-
setItems.push(`${relVar}.${escapeIdentifier(prop)} =
|
|
565
|
-
params[paramName] = val;
|
|
648
|
+
setItems.push(`${relVar}.${escapeIdentifier(prop)} = ${wrapWriteExpr(`$${paramName}`, val, edgePropDef)}`);
|
|
649
|
+
params[paramName] = coerceWriteValue(val, edgePropDef);
|
|
566
650
|
}
|
|
567
651
|
if (setItems.length > 0)
|
|
568
652
|
lines.push(`SET ${setItems.join(', ')}`);
|
|
@@ -612,9 +696,10 @@ export class MutationCompiler {
|
|
|
612
696
|
if (val === undefined)
|
|
613
697
|
continue;
|
|
614
698
|
assertSafePropertyName(prop, 'edge property');
|
|
699
|
+
const edgePropDef = this.getEdgePropDef(relDef, prop);
|
|
615
700
|
const paramName = `${prefix}_conn${ci}_edge_${prop}`;
|
|
616
|
-
setItems.push(`r_conn_${startCounter + counter - 1}.${escapeIdentifier(prop)} =
|
|
617
|
-
params[paramName] = val;
|
|
701
|
+
setItems.push(`r_conn_${startCounter + counter - 1}.${escapeIdentifier(prop)} = ${wrapWriteExpr(`$${paramName}`, val, edgePropDef)}`);
|
|
702
|
+
params[paramName] = coerceWriteValue(val, edgePropDef);
|
|
618
703
|
}
|
|
619
704
|
if (setItems.length > 0)
|
|
620
705
|
lines.push(`SET ${setItems.join(', ')}`);
|
|
@@ -683,9 +768,10 @@ export class MutationCompiler {
|
|
|
683
768
|
if (val === undefined)
|
|
684
769
|
continue;
|
|
685
770
|
assertSafePropertyName(prop, 'edge property');
|
|
771
|
+
const edgePropDef = this.getEdgePropDef(relDef, prop);
|
|
686
772
|
const paramName = `connect_${fieldName}_${ci}_edge_${prop}`;
|
|
687
|
-
setItems.push(`${relVar}.${escapeIdentifier(prop)} =
|
|
688
|
-
params[paramName] = val;
|
|
773
|
+
setItems.push(`${relVar}.${escapeIdentifier(prop)} = ${wrapWriteExpr(`$${paramName}`, val, edgePropDef)}`);
|
|
774
|
+
params[paramName] = coerceWriteValue(val, edgePropDef);
|
|
689
775
|
}
|
|
690
776
|
if (setItems.length > 0)
|
|
691
777
|
lines.push(`SET ${setItems.join(', ')}`);
|
|
@@ -713,7 +799,7 @@ export class MutationCompiler {
|
|
|
713
799
|
const cleaned = {};
|
|
714
800
|
for (const [k, v] of Object.entries(edge))
|
|
715
801
|
if (v !== undefined)
|
|
716
|
-
cleaned[k] = v;
|
|
802
|
+
cleaned[k] = coerceWriteValue(v, this.getEdgePropDef(relDef, k));
|
|
717
803
|
return { ...item, edge: cleaned };
|
|
718
804
|
});
|
|
719
805
|
lines.push('WITH n');
|
|
@@ -737,7 +823,7 @@ export class MutationCompiler {
|
|
|
737
823
|
`silently dropped. Split into separate connect calls, or normalise the items ` +
|
|
738
824
|
`to share the same shape.`);
|
|
739
825
|
}
|
|
740
|
-
const whereConditions = this.extractConnectWhereConditions(firstItem, 'target', 'connItem');
|
|
826
|
+
const whereConditions = this.extractConnectWhereConditions(firstItem, 'target', 'connItem', targetNodeDef);
|
|
741
827
|
const policyPredicate = this.buildTargetPolicyPredicate(targetNodeDef, 'target', params, policyContext, paramCounter);
|
|
742
828
|
const unwindConditions = policyPredicate
|
|
743
829
|
? [...whereConditions, policyPredicate]
|
|
@@ -762,7 +848,7 @@ export class MutationCompiler {
|
|
|
762
848
|
// Replace MERGE line with one that has a variable
|
|
763
849
|
lines[lines.length - 1] = `MERGE ${mergeWithVar}`;
|
|
764
850
|
// r.prop is relationship property (escape), connItem.edge.prop is parameter map (no escape)
|
|
765
|
-
const setItems = edgeProps.map((p) => `r.${escapeIdentifier(p)} = connItem.edge.${p}`);
|
|
851
|
+
const setItems = edgeProps.map((p) => `r.${escapeIdentifier(p)} = ${wrapListItemExpr(`connItem.edge.${p}`, spec.map((it) => it.edge?.[p]), this.getEdgePropDef(relDef, p))}`);
|
|
766
852
|
lines.push(`SET ${setItems.join(', ')}`);
|
|
767
853
|
}
|
|
768
854
|
}
|
|
@@ -793,9 +879,10 @@ export class MutationCompiler {
|
|
|
793
879
|
for (const [prop, val] of Object.entries(edgeInput)) {
|
|
794
880
|
if (val === undefined)
|
|
795
881
|
continue;
|
|
882
|
+
const edgePropDef = this.getEdgePropDef(relDef, prop);
|
|
796
883
|
const paramName = `connect_${fieldName}_edge_${prop}`;
|
|
797
|
-
setItems.push(`r.${escapeIdentifier(prop)} =
|
|
798
|
-
params[paramName] = val;
|
|
884
|
+
setItems.push(`r.${escapeIdentifier(prop)} = ${wrapWriteExpr(`$${paramName}`, val, edgePropDef)}`);
|
|
885
|
+
params[paramName] = coerceWriteValue(val, edgePropDef);
|
|
799
886
|
}
|
|
800
887
|
if (setItems.length > 0)
|
|
801
888
|
lines.push(`SET ${setItems.join(', ')}`);
|
|
@@ -1108,9 +1195,10 @@ export class MutationCompiler {
|
|
|
1108
1195
|
if (val === undefined)
|
|
1109
1196
|
continue;
|
|
1110
1197
|
assertSafePropertyName(prop, 'edge property');
|
|
1198
|
+
const edgePropDef = this.getEdgePropDef(relDef, prop);
|
|
1111
1199
|
const paramName = `${itemPrefix}_conn${ci}_edge_${prop}`;
|
|
1112
|
-
setItems.push(`r_conn_${key}_${ci}.${escapeIdentifier(prop)} =
|
|
1113
|
-
params[paramName] = val;
|
|
1200
|
+
setItems.push(`r_conn_${key}_${ci}.${escapeIdentifier(prop)} = ${wrapWriteExpr(`$${paramName}`, val, edgePropDef)}`);
|
|
1201
|
+
params[paramName] = coerceWriteValue(val, edgePropDef);
|
|
1114
1202
|
}
|
|
1115
1203
|
if (setItems.length > 0)
|
|
1116
1204
|
lines.push(`SET ${setItems.join(', ')}`);
|
|
@@ -1146,9 +1234,10 @@ export class MutationCompiler {
|
|
|
1146
1234
|
if (val === undefined)
|
|
1147
1235
|
continue;
|
|
1148
1236
|
assertSafePropertyName(prop, 'nested update property');
|
|
1237
|
+
const nodePropDef = targetNodeDef.properties.get(prop);
|
|
1149
1238
|
const paramName = `${itemPrefix}_set_${prop}`;
|
|
1150
|
-
setClauses.push(`${updateVar}.${escapeIdentifier(prop)} =
|
|
1151
|
-
setParams[paramName] = val;
|
|
1239
|
+
setClauses.push(`${updateVar}.${escapeIdentifier(prop)} = ${wrapWriteExpr(`$${paramName}`, val, nodePropDef)}`);
|
|
1240
|
+
setParams[paramName] = coerceWriteValue(val, nodePropDef);
|
|
1152
1241
|
}
|
|
1153
1242
|
// Pre-compute SET clauses for edge (relationship) properties
|
|
1154
1243
|
const edgeClauses = [];
|
|
@@ -1158,9 +1247,10 @@ export class MutationCompiler {
|
|
|
1158
1247
|
if (val === undefined)
|
|
1159
1248
|
continue;
|
|
1160
1249
|
assertSafePropertyName(prop, 'edge update property');
|
|
1250
|
+
const edgePropDef = this.getEdgePropDef(relDef, prop);
|
|
1161
1251
|
const paramName = `${itemPrefix}_edge_${prop}`;
|
|
1162
|
-
edgeClauses.push(`${relVar}.${escapeIdentifier(prop)} =
|
|
1163
|
-
edgeParams[paramName] = val;
|
|
1252
|
+
edgeClauses.push(`${relVar}.${escapeIdentifier(prop)} = ${wrapWriteExpr(`$${paramName}`, val, edgePropDef)}`);
|
|
1253
|
+
edgeParams[paramName] = coerceWriteValue(val, edgePropDef);
|
|
1164
1254
|
}
|
|
1165
1255
|
// Pre-compute nested relationship operations
|
|
1166
1256
|
const nestedParams = {};
|
|
@@ -1246,15 +1336,16 @@ export class MutationCompiler {
|
|
|
1246
1336
|
assertSafePropertyName(baseProp, 'where property');
|
|
1247
1337
|
const paramName = `${prefix}_NOT_${notProp}`;
|
|
1248
1338
|
const escapedProp = escapeIdentifier(baseProp);
|
|
1339
|
+
const paramRef = wrapWhereParam(`$${paramName}`, notVal, targetNodeDef?.properties.get(baseProp), notProp.slice(baseProp.length));
|
|
1249
1340
|
let expr;
|
|
1250
1341
|
if (template === '%v = $%p')
|
|
1251
1342
|
// Simple equality → negate to <>
|
|
1252
|
-
expr = `${targetVar}.${escapedProp} <>
|
|
1343
|
+
expr = `${targetVar}.${escapedProp} <> ${paramRef}`;
|
|
1253
1344
|
else {
|
|
1254
1345
|
// Operator suffix — wrap with NOT if not already negated
|
|
1255
1346
|
const resolved = template
|
|
1256
1347
|
.replace('%v', `${targetVar}.${escapedProp}`)
|
|
1257
|
-
.replace('$%p',
|
|
1348
|
+
.replace('$%p', paramRef);
|
|
1258
1349
|
expr = resolved.startsWith('NOT ') ? resolved : `NOT ${resolved}`;
|
|
1259
1350
|
}
|
|
1260
1351
|
conditions.push(expr);
|
|
@@ -1271,9 +1362,10 @@ export class MutationCompiler {
|
|
|
1271
1362
|
const { baseProp, template } = this.parseOperatorSuffix(prop);
|
|
1272
1363
|
assertSafePropertyName(baseProp, 'where property');
|
|
1273
1364
|
const paramName = `${prefix}_${prop}`;
|
|
1365
|
+
const paramRef = wrapWhereParam(`$${paramName}`, val, targetNodeDef?.properties.get(baseProp), prop.slice(baseProp.length));
|
|
1274
1366
|
conditions.push(template
|
|
1275
1367
|
.replace('%v', `${targetVar}.${escapeIdentifier(baseProp)}`)
|
|
1276
|
-
.replace('$%p',
|
|
1368
|
+
.replace('$%p', paramRef));
|
|
1277
1369
|
params[paramName] = val;
|
|
1278
1370
|
}
|
|
1279
1371
|
return conditions;
|
|
@@ -1438,7 +1530,7 @@ export class MutationCompiler {
|
|
|
1438
1530
|
}
|
|
1439
1531
|
return false;
|
|
1440
1532
|
}
|
|
1441
|
-
extractConnectWhereConditions(item, targetVar, itemVar) {
|
|
1533
|
+
extractConnectWhereConditions(item, targetVar, itemVar, targetNodeDef) {
|
|
1442
1534
|
const conditions = [];
|
|
1443
1535
|
const whereSpec = item.where;
|
|
1444
1536
|
if (!whereSpec)
|
|
@@ -1449,7 +1541,9 @@ export class MutationCompiler {
|
|
|
1449
1541
|
assertSafePropertyName(baseProp, 'connect where property');
|
|
1450
1542
|
// Replace template placeholders with dynamic UNWIND references
|
|
1451
1543
|
// Note: itemVar map access is NOT escaped (parameter map keys, not Cypher identifiers)
|
|
1452
|
-
|
|
1544
|
+
// Temporal wrap keys off the FIRST item's value type — the UNWIND
|
|
1545
|
+
// fast path already requires shape-homogeneous items.
|
|
1546
|
+
const valueRef = wrapWhereParam(`${itemVar}.where.node.${key}`, nodeWhere[key], targetNodeDef?.properties.get(baseProp), key.slice(baseProp.length));
|
|
1453
1547
|
conditions.push(template
|
|
1454
1548
|
.replace('%v', `${targetVar}.${escapeIdentifier(baseProp)}`)
|
|
1455
1549
|
.replace('$%p', valueRef));
|