objection 3.1.5 → 3.3.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/README.md +16 -16
- package/lib/model/AjvValidator.js +173 -18
- package/lib/model/Model.js +3 -1
- package/lib/model/graph/ModelGraphNode.js +12 -0
- package/lib/model/modelToJson.js +2 -1
- package/lib/queryBuilder/QueryBuilder.js +339 -47
- package/lib/queryBuilder/QueryBuilderOperationSupport.js +5 -0
- package/lib/queryBuilder/ReferenceBuilder.js +5 -3
- package/lib/queryBuilder/RelationExpression.js +21 -9
- package/lib/queryBuilder/StaticHookArguments.js +12 -1
- package/lib/queryBuilder/graph/GraphFetcher.js +3 -1
- package/lib/queryBuilder/graph/GraphOptions.js +21 -2
- package/lib/queryBuilder/graph/GraphUpsert.js +52 -0
- package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +20 -14
- package/lib/queryBuilder/graph/insert/DeferredRelationGraphInsertAction.js +57 -0
- package/lib/queryBuilder/graph/insert/GraphInsert.js +113 -8
- package/lib/queryBuilder/graph/insert/GraphInsertAction.js +10 -1
- package/lib/queryBuilder/graph/patch/GraphPatchAction.js +15 -3
- package/lib/queryBuilder/join/JoinResultParser.js +22 -0
- package/lib/queryBuilder/join/RelationJoiner.js +71 -5
- package/lib/queryBuilder/operations/DelegateOperation.js +4 -0
- package/lib/queryBuilder/operations/InsertAndFetchOperation.js +7 -1
- package/lib/queryBuilder/operations/InsertGraphOperation.js +2 -2
- package/lib/queryBuilder/operations/InsertOperation.js +121 -12
- package/lib/queryBuilder/operations/NoneOperation.js +37 -0
- package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +5 -0
- package/lib/queryBuilder/operations/QueryBuilderOperation.js +4 -0
- package/lib/queryBuilder/operations/ReturningOperation.js +28 -3
- package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +20 -0
- package/lib/queryBuilder/operations/UpdateOperation.js +3 -2
- package/lib/queryBuilder/operations/UpsertGraphOperation.js +5 -0
- package/lib/queryBuilder/operations/WhereGroupOperation.js +41 -0
- package/lib/queryBuilder/operations/eager/EagerOperation.js +7 -5
- package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +4 -2
- package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +9 -20
- package/lib/queryBuilder/operations/jsonApi/WhereJsonHasPostgresOperation.js +3 -3
- package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectPostgresOperation.js +1 -1
- package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +45 -32
- package/lib/queryBuilder/operations/select/Selection.js +95 -4
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +6 -0
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +5 -1
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +9 -0
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +81 -43
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.pegjs +5 -4
- package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +9 -0
- package/lib/queryBuilder/whereGroups.js +125 -0
- package/lib/relations/Relation.js +5 -2
- package/lib/relations/RelationProperty.js +7 -3
- package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +1 -0
- package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +5 -0
- package/lib/relations/hasMany/HasManyRelateOperation.js +6 -2
- package/lib/relations/hasMany/HasManyUnrelateOperation.js +5 -2
- package/lib/relations/manyToMany/ManyToManyModifyMixin.js +26 -3
- package/lib/relations/manyToMany/ManyToManyMySqlModifyMixin.js +66 -0
- package/lib/relations/manyToMany/ManyToManyPostgresModifyMixin.js +27 -0
- package/lib/relations/manyToMany/ManyToManyRelation.js +70 -48
- package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +6 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateMySqlOperation.js +16 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +5 -1
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelatePostgresOperation.js +16 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateMySqlOperation.js +12 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +8 -2
- package/lib/relations/manyToMany/update/ManyToManyUpdatePostgresOperation.js +12 -0
- package/lib/utils/deprecate.js +6 -0
- package/lib/utils/identifierMapping.js +23 -3
- package/lib/utils/objectUtils.js +9 -5
- package/lib/utils/parseFieldExpression.js +28 -0
- package/package.json +22 -14
- package/typings/objection/index.d.ts +339 -68
package/README.md
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
[](https://stand-with-ukraine.pp.ua)
|
|
2
2
|
|
|
3
|
-
[](https://github.com/ditojs/objection/actions)
|
|
4
4
|
[](https://gitter.im/Vincit/objection.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
5
5
|
|
|
6
|
-
# [Objection.js](https://
|
|
6
|
+
# [Objection.js](https://ditojs.github.io/objection/)
|
|
7
7
|
|
|
8
8
|
Objection.js is an [ORM](https://en.wikipedia.org/wiki/Object-relational_mapping) for [Node.js](https://nodejs.org/) that aims to stay out of your way and make it as easy as possible to use the full power of SQL and the underlying database engine while still making the common stuff easy and enjoyable.
|
|
9
9
|
|
|
10
10
|
Even though ORM is the best commonly known acronym to describe objection, a more accurate description is to call it **a relational query builder**. You get all the benefits of an SQL query builder but also a powerful set of tools for working with relations.
|
|
11
11
|
|
|
12
|
-
Objection.js is built on an SQL query builder called [knex](http://knexjs.org). All databases supported by knex are supported by objection.js. **SQLite3**, **Postgres** and **MySQL** are [thoroughly tested](https://github.com/
|
|
12
|
+
Objection.js is built on an SQL query builder called [knex](http://knexjs.org). All databases supported by knex are supported by objection.js. **SQLite3**, **Postgres** and **MySQL** are [thoroughly tested](https://github.com/ditojs/objection/actions).
|
|
13
13
|
|
|
14
14
|
What objection.js gives you:
|
|
15
15
|
|
|
16
|
-
- **An easy declarative way of [defining models](https://
|
|
17
|
-
- **Simple and fun way to [fetch, insert, update and delete](https://
|
|
18
|
-
- **Powerful mechanisms for [eager loading](https://
|
|
19
|
-
- **Easy to use [transactions](https://
|
|
20
|
-
- **Official [TypeScript](https://github.com/
|
|
21
|
-
- **Optional [JSON schema](https://
|
|
22
|
-
- **A way to [store complex documents](https://
|
|
16
|
+
- **An easy declarative way of [defining models](https://ditojs.github.io/objection/guide/models.html) and relationships between them**
|
|
17
|
+
- **Simple and fun way to [fetch, insert, update and delete](https://ditojs.github.io/objection/guide/query-examples.html) objects using the full power of SQL**
|
|
18
|
+
- **Powerful mechanisms for [eager loading](https://ditojs.github.io/objection/guide/query-examples.html#eager-loading), [inserting](https://ditojs.github.io/objection/guide/query-examples.html#graph-inserts) and [upserting](https://ditojs.github.io/objection/guide/query-examples.html#graph-upserts) object graphs**
|
|
19
|
+
- **Easy to use [transactions](https://ditojs.github.io/objection/guide/transactions.html)**
|
|
20
|
+
- **Official [TypeScript](https://github.com/ditojs/objection/blob/main/typings/objection/index.d.ts) support**
|
|
21
|
+
- **Optional [JSON schema](https://ditojs.github.io/objection/guide/validation.html) validation**
|
|
22
|
+
- **A way to [store complex documents](https://ditojs.github.io/objection/guide/documents.html) as single rows**
|
|
23
23
|
|
|
24
24
|
What objection.js **doesn't** give you:
|
|
25
25
|
|
|
@@ -31,7 +31,7 @@ What objection.js **doesn't** give you:
|
|
|
31
31
|
- **A custom query DSL. SQL is used as a query language.**
|
|
32
32
|
This doesn't mean you have to write SQL strings though. A query builder based on [knex](http://knexjs.org) is
|
|
33
33
|
used to build the SQL. However, if the query builder fails you for some reason, raw SQL strings can be easily
|
|
34
|
-
written using the [raw](https://
|
|
34
|
+
written using the [raw](https://ditojs.github.io/objection/api/objection/#raw) helper function.
|
|
35
35
|
- **Automatic database schema creation and migration from model definitions.**
|
|
36
36
|
For simple things it is useful that the database schema is automatically generated from the model definitions,
|
|
37
37
|
but usually just gets in your way when doing anything non-trivial. Objection.js leaves the schema related things
|
|
@@ -45,9 +45,9 @@ Check out [this issue](https://github.com/Vincit/objection.js/issues/1069) to se
|
|
|
45
45
|
Shortcuts:
|
|
46
46
|
|
|
47
47
|
- [Who uses objection.js](https://github.com/Vincit/objection.js/discussions/2464)
|
|
48
|
-
- [API reference](https://
|
|
48
|
+
- [API reference](https://ditojs.github.io/objection/api/query-builder/)
|
|
49
49
|
- [Example projects](https://github.com/Vincit/objection.js/tree/main/examples)
|
|
50
|
-
- [Changelog](https://
|
|
51
|
-
- [v1 -> v2 -> v3 migration guide](https://
|
|
52
|
-
- [Contribution guide](https://
|
|
53
|
-
- [Plugins](https://
|
|
50
|
+
- [Changelog](https://ditojs.github.io/objection/release-notes/changelog.html)
|
|
51
|
+
- [v1 -> v2 -> v3 migration guide](https://ditojs.github.io/objection/release-notes/migration.html)
|
|
52
|
+
- [Contribution guide](https://ditojs.github.io/objection/guide/contributing.html)
|
|
53
|
+
- [Plugins](https://ditojs.github.io/objection/guide/plugins.html)
|
|
@@ -5,6 +5,7 @@ const addFormats = require('ajv-formats');
|
|
|
5
5
|
const { Validator } = require('./Validator');
|
|
6
6
|
const { ValidationErrorType } = require('../model/ValidationError');
|
|
7
7
|
const { isObject, once, cloneDeep: lodashCloneDeep, omit } = require('../utils/objectUtils');
|
|
8
|
+
const { parseFieldExpression } = require('../utils/parseFieldExpression');
|
|
8
9
|
|
|
9
10
|
class AjvValidator extends Validator {
|
|
10
11
|
static init(self, conf) {
|
|
@@ -35,6 +36,9 @@ class AjvValidator extends Validator {
|
|
|
35
36
|
// A cache for the compiled validator functions.
|
|
36
37
|
self.cache = new Map();
|
|
37
38
|
|
|
39
|
+
// The cache keys of the unmodified model class schemas.
|
|
40
|
+
self.modelCacheKeys = new WeakMap();
|
|
41
|
+
|
|
38
42
|
const setupAjv = (ajv) => {
|
|
39
43
|
if (conf.onCreateAjv) {
|
|
40
44
|
conf.onCreateAjv(ajv);
|
|
@@ -78,13 +82,24 @@ class AjvValidator extends Validator {
|
|
|
78
82
|
json = cloneDeep(json);
|
|
79
83
|
}
|
|
80
84
|
|
|
81
|
-
|
|
85
|
+
// Patch objects can contain field expression keys like `'meta:b'` that update
|
|
86
|
+
// a single property inside a json column. Validate them against the nested
|
|
87
|
+
// schema by converting them into nested objects like `{ meta: { b: value } }`.
|
|
88
|
+
const fieldExpressions = options.patch ? nestFieldExpressions(json) : null;
|
|
89
|
+
const validationJson = fieldExpressions ? fieldExpressions.json : json;
|
|
90
|
+
|
|
91
|
+
validator.call(model, validationJson);
|
|
82
92
|
const error = parseValidationError(validator.errors, modelClass, options, this.ajvOptions);
|
|
83
93
|
|
|
84
94
|
if (error) {
|
|
85
95
|
throw error;
|
|
86
96
|
}
|
|
87
97
|
|
|
98
|
+
if (fieldExpressions) {
|
|
99
|
+
// Copy back the changes made by Ajv options like `coerceTypes` and `removeAdditional`.
|
|
100
|
+
unnestFieldExpressions(json, fieldExpressions);
|
|
101
|
+
}
|
|
102
|
+
|
|
88
103
|
return json;
|
|
89
104
|
}
|
|
90
105
|
|
|
@@ -92,12 +107,23 @@ class AjvValidator extends Validator {
|
|
|
92
107
|
// Use the Ajv custom serializer if provided.
|
|
93
108
|
const createCacheKey = this.ajvOptions.serialize || JSON.stringify;
|
|
94
109
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
jsonSchema
|
|
99
|
-
|
|
100
|
-
|
|
110
|
+
let cacheKey;
|
|
111
|
+
|
|
112
|
+
if (jsonSchema === modelClass.getJsonSchema()) {
|
|
113
|
+
// Optimization for the common case where jsonSchema is never modified.
|
|
114
|
+
// The key is only computed once per model class. It includes the schema
|
|
115
|
+
// contents so that model classes with the same `uniqueTag()` but
|
|
116
|
+
// different schemas don't share validators, while bound model classes
|
|
117
|
+
// with equal schemas still do.
|
|
118
|
+
cacheKey = this.modelCacheKeys.get(modelClass);
|
|
119
|
+
|
|
120
|
+
if (cacheKey === undefined) {
|
|
121
|
+
cacheKey = `${modelClass.uniqueTag()}:${createCacheKey(jsonSchema)}`;
|
|
122
|
+
this.modelCacheKeys.set(modelClass, cacheKey);
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
cacheKey = createCacheKey(jsonSchema);
|
|
126
|
+
}
|
|
101
127
|
|
|
102
128
|
let validators = this.cache.get(cacheKey);
|
|
103
129
|
let validator = null;
|
|
@@ -209,6 +235,116 @@ function parseValidationError(errors, modelClass, options, ajvOptions) {
|
|
|
209
235
|
});
|
|
210
236
|
}
|
|
211
237
|
|
|
238
|
+
function nestFieldExpressions(json) {
|
|
239
|
+
const keys = Object.keys(json);
|
|
240
|
+
|
|
241
|
+
if (!keys.some(isFieldExpression)) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const nested = Object.create(null);
|
|
246
|
+
const paths = new Map();
|
|
247
|
+
// The objects created here for the nested paths. Other objects are values
|
|
248
|
+
// of the input json and must not be modified.
|
|
249
|
+
const createdObjects = new Set();
|
|
250
|
+
|
|
251
|
+
for (const key of keys) {
|
|
252
|
+
if (!isFieldExpression(key)) {
|
|
253
|
+
nested[key] = json[key];
|
|
254
|
+
paths.set(key, [key]);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
for (const key of keys) {
|
|
259
|
+
if (isFieldExpression(key)) {
|
|
260
|
+
const path = fieldExpressionPath(key);
|
|
261
|
+
|
|
262
|
+
// Field expressions with array access, and those that conflict with other
|
|
263
|
+
// keys of the patch, can't be validated without the current value of the
|
|
264
|
+
// json column. Leave them out of the validation.
|
|
265
|
+
if (path && setPath(nested, path, json[key], createdObjects)) {
|
|
266
|
+
paths.set(key, path);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return { json: nested, paths };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function unnestFieldExpressions(json, { json: nested, paths }) {
|
|
275
|
+
for (const [key, path] of paths) {
|
|
276
|
+
let value = nested;
|
|
277
|
+
let found = true;
|
|
278
|
+
|
|
279
|
+
for (const prop of path) {
|
|
280
|
+
found = isObject(value) && hasOwn(value, prop);
|
|
281
|
+
|
|
282
|
+
if (!found) {
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
value = value[prop];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (!found) {
|
|
290
|
+
// Removed by `removeAdditional`.
|
|
291
|
+
delete json[key];
|
|
292
|
+
} else if (json[key] !== value) {
|
|
293
|
+
json[key] = value;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function isFieldExpression(key) {
|
|
299
|
+
return key.indexOf(':') !== -1;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function fieldExpressionPath(key) {
|
|
303
|
+
let parsed;
|
|
304
|
+
|
|
305
|
+
try {
|
|
306
|
+
parsed = parseFieldExpression(key);
|
|
307
|
+
} catch (err) {
|
|
308
|
+
// Not a valid field expression, validate it as a top-level property.
|
|
309
|
+
return [key];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (parsed.access.some((access) => access.type !== 'object')) {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return [parsed.column, ...parsed.access.map((access) => String(access.ref))];
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function setPath(obj, path, value, createdObjects) {
|
|
320
|
+
const last = path.length - 1;
|
|
321
|
+
|
|
322
|
+
for (let i = 0; i < last; ++i) {
|
|
323
|
+
const prop = path[i];
|
|
324
|
+
|
|
325
|
+
if (!hasOwn(obj, prop)) {
|
|
326
|
+
const child = Object.create(null);
|
|
327
|
+
createdObjects.add(child);
|
|
328
|
+
obj[prop] = child;
|
|
329
|
+
} else if (!createdObjects.has(obj[prop])) {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
obj = obj[prop];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (hasOwn(obj, path[last])) {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
obj[path[last]] = value;
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function hasOwn(obj, prop) {
|
|
345
|
+
return Object.prototype.hasOwnProperty.call(obj, prop);
|
|
346
|
+
}
|
|
347
|
+
|
|
212
348
|
function cloneDeep(obj) {
|
|
213
349
|
if (isObject(obj) && obj.$isObjectionModel) {
|
|
214
350
|
return obj.$clone();
|
|
@@ -262,25 +398,41 @@ function objectHasDefaults(obj) {
|
|
|
262
398
|
}
|
|
263
399
|
|
|
264
400
|
function jsonSchemaWithoutRequired(jsonSchema) {
|
|
265
|
-
|
|
401
|
+
// `not` is intentionally left untouched: Stripping `required` inside it
|
|
402
|
+
// would turn it into a schema that rejects everything (e.g.
|
|
403
|
+
// `not: { required: ['a'] }` -> `not: {}`). Kept as is, it still holds for
|
|
404
|
+
// patches: Any property present in the patch is also present in the
|
|
405
|
+
// resulting object.
|
|
406
|
+
const subSchemaProps = ['anyOf', 'oneOf', 'allOf', 'then', 'else', 'properties'];
|
|
407
|
+
// `definitions` was renamed to `$defs` in draft 2019-09. Support both.
|
|
408
|
+
const definitionProps = ['definitions', '$defs'];
|
|
266
409
|
return Object.assign(
|
|
267
410
|
omit(jsonSchema, ['required', ...subSchemaProps]),
|
|
268
411
|
...subSchemaProps.map((prop) => subSchemaWithoutRequired(jsonSchema, prop)),
|
|
269
|
-
|
|
270
|
-
? {
|
|
271
|
-
definitions: Object.assign(
|
|
272
|
-
...Object.keys(jsonSchema.definitions).map((prop) => ({
|
|
273
|
-
[prop]: jsonSchemaWithoutRequired(jsonSchema.definitions[prop]),
|
|
274
|
-
})),
|
|
275
|
-
),
|
|
276
|
-
}
|
|
277
|
-
: {},
|
|
412
|
+
...definitionProps.map((prop) => definitionsWithoutRequired(jsonSchema, prop)),
|
|
278
413
|
jsonSchema.discriminator && jsonSchema.discriminator.propertyName
|
|
279
414
|
? { required: [jsonSchema.discriminator.propertyName] }
|
|
280
415
|
: {},
|
|
281
416
|
);
|
|
282
417
|
}
|
|
283
418
|
|
|
419
|
+
function definitionsWithoutRequired(jsonSchema, prop) {
|
|
420
|
+
const definitions = jsonSchema && jsonSchema[prop];
|
|
421
|
+
|
|
422
|
+
if (definitions && Object.keys(definitions).length > 0) {
|
|
423
|
+
return {
|
|
424
|
+
[prop]: Object.fromEntries(
|
|
425
|
+
Object.entries(definitions).map(([key, schema]) => [
|
|
426
|
+
key,
|
|
427
|
+
jsonSchemaWithoutRequired(schema),
|
|
428
|
+
]),
|
|
429
|
+
),
|
|
430
|
+
};
|
|
431
|
+
} else {
|
|
432
|
+
return {};
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
284
436
|
function subSchemaWithoutRequired(jsonSchema, prop) {
|
|
285
437
|
if (jsonSchema[prop]) {
|
|
286
438
|
if (Array.isArray(jsonSchema[prop])) {
|
|
@@ -293,7 +445,10 @@ function subSchemaWithoutRequired(jsonSchema, prop) {
|
|
|
293
445
|
} else {
|
|
294
446
|
return {};
|
|
295
447
|
}
|
|
296
|
-
} else if (
|
|
448
|
+
} else if (prop === 'properties') {
|
|
449
|
+
// `properties` is a map of property names to schemas, regardless of the
|
|
450
|
+
// schema's `type` (which may also be e.g. `['object', 'null']`, or be
|
|
451
|
+
// missing entirely).
|
|
297
452
|
return {
|
|
298
453
|
[prop]: Object.fromEntries(
|
|
299
454
|
Object.entries(jsonSchema[prop]).map(([key, schema]) => [
|
package/lib/model/Model.js
CHANGED
|
@@ -120,7 +120,7 @@ class Model {
|
|
|
120
120
|
return json;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
$formatJson(json) {
|
|
123
|
+
$formatJson(json, options) {
|
|
124
124
|
return json;
|
|
125
125
|
}
|
|
126
126
|
|
|
@@ -721,6 +721,8 @@ Model.uidProp = '#id';
|
|
|
721
721
|
Model.uidRefProp = '#ref';
|
|
722
722
|
Model.dbRefProp = '#dbRef';
|
|
723
723
|
Model.propRefRegex = /#ref{([^\.]+)\.([^}]+)}/g;
|
|
724
|
+
Model.graphUnrelateProp = '#unrelate';
|
|
725
|
+
Model.graphDeleteProp = '#delete';
|
|
724
726
|
Model.jsonAttributes = null;
|
|
725
727
|
Model.cloneObjectAttributes = true;
|
|
726
728
|
Model.virtualAttributes = null;
|
|
@@ -46,6 +46,18 @@ class ModelGraphNode {
|
|
|
46
46
|
return this.obj[this.modelClass.dbRefProp];
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
get toBeUnrelated() {
|
|
50
|
+
return this.obj[this.modelClass.graphUnrelateProp] === true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get toBeDeleted() {
|
|
54
|
+
return this.obj[this.modelClass.graphDeleteProp] === true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get toBeUnrelatedOrDeleted() {
|
|
58
|
+
return this.toBeUnrelated || this.toBeDeleted;
|
|
59
|
+
}
|
|
60
|
+
|
|
49
61
|
get parentNode() {
|
|
50
62
|
if (this.parentEdge) {
|
|
51
63
|
return this.parentEdge.ownerNode;
|
package/lib/model/modelToJson.js
CHANGED
|
@@ -15,10 +15,11 @@ function toJson(model, optIn) {
|
|
|
15
15
|
pick: null,
|
|
16
16
|
omitFromJson: model.$omitFromJson() || null,
|
|
17
17
|
cloneObjects: modelClass.cloneObjectAttributes,
|
|
18
|
+
format: optIn && optIn.format,
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
let json = toExternalJsonImpl(model, opt);
|
|
21
|
-
json = model.$formatJson(json);
|
|
22
|
+
json = model.$formatJson(json, opt.format);
|
|
22
23
|
|
|
23
24
|
return json;
|
|
24
25
|
}
|