@ruiapp/rapid-core 0.1.74 → 0.1.76
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/dataAccess/columnTypeMapper.d.ts +3 -0
- package/dist/dataAccess/dataAccessTypes.d.ts +11 -3
- package/dist/index.js +75 -34
- package/dist/types.d.ts +11 -4
- package/package.json +1 -1
- package/src/dataAccess/columnTypeMapper.ts +22 -0
- package/src/dataAccess/dataAccessTypes.ts +30 -1
- package/src/dataAccess/entityManager.ts +9 -4
- package/src/plugins/metaManage/MetaManagePlugin.ts +2 -15
- package/src/queryBuilder/queryBuilder.ts +56 -17
- package/src/types.ts +22 -2
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
export type DataAccessPgColumnTypes = "int4" | "int8" | "float4" | "float8" | "decimal" | "text" | "text[]" | "bool" | "date" | "time" | "timestamptz" | "jsonb";
|
|
1
2
|
export type RowFilterRelationalOperators = "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "contains" | "notContains" | "containsCS" | "notContainsCS" | "startsWith" | "notStartsWith" | "endsWith" | "notEndsWith";
|
|
3
|
+
export type RowFilterArrayOperators = "arrayContains" | "arrayOverlap";
|
|
2
4
|
export type RowFilterSetOperators = "in" | "notIn";
|
|
3
5
|
export type RowFilterLogicalOperators = "or" | "and";
|
|
4
6
|
export type RowFilterUnaryOperators = "null" | "notNull";
|
|
5
7
|
export type RowFilterExistenceOperators = "exists" | "notExists";
|
|
6
|
-
export type RowFilterOperators = RowFilterRelationalOperators | RowFilterSetOperators | RowFilterLogicalOperators | RowFilterUnaryOperators | RowFilterExistenceOperators;
|
|
7
|
-
export type RowFilterOptions = FindRowRelationalFilterOptions | FindRowSetFilterOptions | FindRowLogicalFilterOptions | FindRowUnaryFilterOptions | FindRowExistenceFilterOptions;
|
|
8
|
-
export type RowNonRelationPropertyFilterOptions = FindRowRelationalFilterOptions | FindRowSetFilterOptions | FindRowUnaryFilterOptions;
|
|
8
|
+
export type RowFilterOperators = RowFilterRelationalOperators | RowFilterArrayOperators | RowFilterSetOperators | RowFilterLogicalOperators | RowFilterUnaryOperators | RowFilterExistenceOperators;
|
|
9
|
+
export type RowFilterOptions = FindRowRelationalFilterOptions | FindRowArrayFilterOptions | FindRowSetFilterOptions | FindRowLogicalFilterOptions | FindRowUnaryFilterOptions | FindRowExistenceFilterOptions;
|
|
10
|
+
export type RowNonRelationPropertyFilterOptions = FindRowRelationalFilterOptions | FindRowArrayFilterOptions | FindRowSetFilterOptions | FindRowUnaryFilterOptions;
|
|
9
11
|
export type ColumnSelectOptions = string | ColumnNameWithTableName;
|
|
10
12
|
export type ColumnNameWithTableName = {
|
|
11
13
|
name: string;
|
|
@@ -24,6 +26,12 @@ export interface FindRowRelationalFilterOptions {
|
|
|
24
26
|
operator: RowFilterRelationalOperators;
|
|
25
27
|
value: any;
|
|
26
28
|
}
|
|
29
|
+
export interface FindRowArrayFilterOptions {
|
|
30
|
+
field: ColumnSelectOptions;
|
|
31
|
+
operator: RowFilterArrayOperators;
|
|
32
|
+
value: any[];
|
|
33
|
+
itemType?: string;
|
|
34
|
+
}
|
|
27
35
|
export interface FindRowSetFilterOptions {
|
|
28
36
|
field: ColumnSelectOptions;
|
|
29
37
|
operator: RowFilterSetOperators;
|
package/dist/index.js
CHANGED
|
@@ -182,6 +182,26 @@ class DataAccessor {
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
const pgPropertyTypeColumnMap = {
|
|
186
|
+
integer: "int4",
|
|
187
|
+
long: "int8",
|
|
188
|
+
float: "float4",
|
|
189
|
+
double: "float8",
|
|
190
|
+
decimal: "decimal",
|
|
191
|
+
text: "text",
|
|
192
|
+
boolean: "bool",
|
|
193
|
+
date: "date",
|
|
194
|
+
time: "time",
|
|
195
|
+
datetime: "timestamptz",
|
|
196
|
+
json: "jsonb",
|
|
197
|
+
option: "text",
|
|
198
|
+
"option[]": "text[]",
|
|
199
|
+
file: "jsonb",
|
|
200
|
+
"file[]": "jsonb",
|
|
201
|
+
image: "jsonb",
|
|
202
|
+
"image[]": "jsonb",
|
|
203
|
+
};
|
|
204
|
+
|
|
185
205
|
const objLeftQuoteChar = '"';
|
|
186
206
|
const objRightQuoteChar = '"';
|
|
187
207
|
const relationalOperatorsMap = new Map([
|
|
@@ -392,26 +412,27 @@ class QueryBuilder {
|
|
|
392
412
|
const { entity } = options;
|
|
393
413
|
let command = "INSERT INTO ";
|
|
394
414
|
command += this.quoteTable(model);
|
|
395
|
-
const
|
|
415
|
+
const columnNames = Object.keys(entity);
|
|
396
416
|
let values = "";
|
|
397
|
-
|
|
417
|
+
columnNames.forEach((columnName, index) => {
|
|
398
418
|
if (index) {
|
|
399
419
|
values += ", ";
|
|
400
420
|
}
|
|
401
421
|
let property = null;
|
|
402
422
|
if (model) {
|
|
403
|
-
property = lodash.find(model.properties, (e) => e.code ===
|
|
423
|
+
property = lodash.find(model.properties, (e) => (e.columnName || e.code) === columnName);
|
|
404
424
|
}
|
|
405
|
-
|
|
406
|
-
|
|
425
|
+
const columnType = property ? pgPropertyTypeColumnMap[property.type] : null;
|
|
426
|
+
if (columnType === "jsonb") {
|
|
427
|
+
params.push(JSON.stringify(entity[columnName]));
|
|
407
428
|
values += `$${params.length}::jsonb`;
|
|
408
429
|
}
|
|
409
430
|
else {
|
|
410
|
-
params.push(entity[
|
|
431
|
+
params.push(entity[columnName]);
|
|
411
432
|
values += `$${params.length}`;
|
|
412
433
|
}
|
|
413
434
|
});
|
|
414
|
-
command += ` (${
|
|
435
|
+
command += ` (${columnNames.map(this.quoteObject).join(", ")})`;
|
|
415
436
|
command += ` VALUES (${values}) RETURNING *`;
|
|
416
437
|
return {
|
|
417
438
|
command,
|
|
@@ -431,22 +452,24 @@ class QueryBuilder {
|
|
|
431
452
|
let command = "UPDATE ";
|
|
432
453
|
command += this.quoteTable(model);
|
|
433
454
|
command += " SET ";
|
|
434
|
-
const
|
|
435
|
-
|
|
455
|
+
const columnNames = Object.keys(entity);
|
|
456
|
+
columnNames.forEach((columnName, index) => {
|
|
436
457
|
if (index) {
|
|
437
458
|
command += ", ";
|
|
438
459
|
}
|
|
460
|
+
command += `${this.quoteObject(columnName)}=`;
|
|
439
461
|
let property = null;
|
|
440
462
|
if (model) {
|
|
441
|
-
property = lodash.find(model.properties, (e) => (e.columnName || e.code) ===
|
|
463
|
+
property = lodash.find(model.properties, (e) => (e.columnName || e.code) === columnName);
|
|
442
464
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
465
|
+
const columnType = property ? pgPropertyTypeColumnMap[property.type] : null;
|
|
466
|
+
if (columnType === "jsonb") {
|
|
467
|
+
params.push(JSON.stringify(entity[columnName]));
|
|
468
|
+
command += `$${params.length}::jsonb`;
|
|
446
469
|
}
|
|
447
470
|
else {
|
|
448
|
-
params.push(entity[
|
|
449
|
-
command +=
|
|
471
|
+
params.push(entity[columnName]);
|
|
472
|
+
command += `$${params.length}`;
|
|
450
473
|
}
|
|
451
474
|
});
|
|
452
475
|
if (filters && filters.length) {
|
|
@@ -530,6 +553,12 @@ function buildFilterQuery(level, ctx, filter) {
|
|
|
530
553
|
else if (operator === "notEndsWith") {
|
|
531
554
|
return buildNotEndsWithFilterQuery(ctx, filter);
|
|
532
555
|
}
|
|
556
|
+
else if (operator === "arrayContains") {
|
|
557
|
+
return buildArrayContainsFilterQuery(ctx, filter);
|
|
558
|
+
}
|
|
559
|
+
else if (operator === "arrayOverlap") {
|
|
560
|
+
return buildArrayOverlapFilterQuery(ctx, filter);
|
|
561
|
+
}
|
|
533
562
|
else {
|
|
534
563
|
throw new Error(`Filter operator '${operator}' is not supported.`);
|
|
535
564
|
}
|
|
@@ -645,6 +674,26 @@ function buildRelationalFilterQuery(ctx, filter) {
|
|
|
645
674
|
}
|
|
646
675
|
return command;
|
|
647
676
|
}
|
|
677
|
+
function buildArrayContainsFilterQuery(ctx, filter) {
|
|
678
|
+
let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
|
|
679
|
+
command += " @> ";
|
|
680
|
+
if (ctx.paramToLiteral) ;
|
|
681
|
+
else {
|
|
682
|
+
ctx.params.push(filter.value);
|
|
683
|
+
command += "$" + ctx.params.length;
|
|
684
|
+
}
|
|
685
|
+
return command;
|
|
686
|
+
}
|
|
687
|
+
function buildArrayOverlapFilterQuery(ctx, filter) {
|
|
688
|
+
let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
|
|
689
|
+
command += " && ";
|
|
690
|
+
if (ctx.paramToLiteral) ;
|
|
691
|
+
else {
|
|
692
|
+
ctx.params.push(filter.value);
|
|
693
|
+
command += "$" + ctx.params.length;
|
|
694
|
+
}
|
|
695
|
+
return command;
|
|
696
|
+
}
|
|
648
697
|
function formatValueToSqlLiteral(value) {
|
|
649
698
|
if (lodash.isNull(value) || lodash.isUndefined(value)) {
|
|
650
699
|
return "null";
|
|
@@ -2618,12 +2667,18 @@ async function convertEntityFiltersToRowFilters(server, model, baseModel, filter
|
|
|
2618
2667
|
}
|
|
2619
2668
|
else {
|
|
2620
2669
|
const filterField = filter.field;
|
|
2621
|
-
let property =
|
|
2622
|
-
return property.code === filterField;
|
|
2623
|
-
});
|
|
2670
|
+
let property = getEntityPropertyByCode(server, model, filterField);
|
|
2624
2671
|
let columnName = "";
|
|
2625
2672
|
if (property) {
|
|
2626
|
-
|
|
2673
|
+
if (isOneRelationProperty(property)) {
|
|
2674
|
+
columnName = property.targetIdColumnName;
|
|
2675
|
+
}
|
|
2676
|
+
else if (isManyRelationProperty(property)) {
|
|
2677
|
+
throw new Error(`Operator "${operator}" is not supported on many-relation property "${property.code}"`);
|
|
2678
|
+
}
|
|
2679
|
+
else {
|
|
2680
|
+
columnName = property.columnName || property.code;
|
|
2681
|
+
}
|
|
2627
2682
|
}
|
|
2628
2683
|
else {
|
|
2629
2684
|
property = getEntityProperty(server, model, (property) => {
|
|
@@ -4336,21 +4391,7 @@ function generateTableIndexDDL(queryBuilder, server, model, index) {
|
|
|
4336
4391
|
ddl += ` WHERE ${queryBuilder.buildFiltersExpression(model, index.conditions)}`;
|
|
4337
4392
|
}
|
|
4338
4393
|
return ddl;
|
|
4339
|
-
}
|
|
4340
|
-
const pgPropertyTypeColumnMap = {
|
|
4341
|
-
integer: "int4",
|
|
4342
|
-
long: "int8",
|
|
4343
|
-
float: "float4",
|
|
4344
|
-
double: "float8",
|
|
4345
|
-
decimal: "decimal",
|
|
4346
|
-
text: "text",
|
|
4347
|
-
boolean: "bool",
|
|
4348
|
-
date: "date",
|
|
4349
|
-
time: "time",
|
|
4350
|
-
datetime: "timestamptz",
|
|
4351
|
-
json: "jsonb",
|
|
4352
|
-
option: "text",
|
|
4353
|
-
};
|
|
4394
|
+
}
|
|
4354
4395
|
|
|
4355
4396
|
function mergeInput(defaultInput, input, fixedInput) {
|
|
4356
4397
|
return lodash.mergeWith({}, defaultInput, input, fixedInput, doNotMergeArray);
|
package/dist/types.d.ts
CHANGED
|
@@ -271,7 +271,7 @@ export interface RpdDataModelProperty {
|
|
|
271
271
|
*/
|
|
272
272
|
linkSchema?: string;
|
|
273
273
|
}
|
|
274
|
-
export type RpdDataPropertyTypes = "integer" | "long" | "float" | "double" | "decimal" | "text" | "boolean" | "date" | "time" | "datetime" | "json" | "relation" | "relation[]" | "option";
|
|
274
|
+
export type RpdDataPropertyTypes = "integer" | "long" | "float" | "double" | "decimal" | "text" | "boolean" | "date" | "time" | "datetime" | "json" | "relation" | "relation[]" | "option" | "option[]" | "file" | "file[]" | "image" | "image[]";
|
|
275
275
|
/**
|
|
276
276
|
* 数据字典
|
|
277
277
|
*/
|
|
@@ -359,13 +359,14 @@ export interface IRpdDataAccessor<T = any> {
|
|
|
359
359
|
deleteById(id: any): Promise<void>;
|
|
360
360
|
}
|
|
361
361
|
export type EntityFilterRelationalOperators = "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "contains" | "notContains" | "containsCS" | "notContainsCS" | "startsWith" | "notStartsWith" | "endsWith" | "notEndsWith";
|
|
362
|
+
export type EntityFilterArrayOperators = "arrayContains" | "arrayOverlap";
|
|
362
363
|
export type EntityFilterSetOperators = "in" | "notIn";
|
|
363
364
|
export type EntityFilterLogicalOperators = "or" | "and";
|
|
364
365
|
export type EntityFilterUnaryOperators = "null" | "notNull";
|
|
365
366
|
export type EntityFilterExistenceOperators = "exists" | "notExists";
|
|
366
|
-
export type EntityFilterOperators = EntityFilterRelationalOperators | EntityFilterSetOperators | EntityFilterLogicalOperators | EntityFilterUnaryOperators | EntityFilterExistenceOperators;
|
|
367
|
-
export type EntityFilterOptions = FindEntityRelationalFilterOptions | FindEntitySetFilterOptions | FindEntityLogicalFilterOptions | FindEntityUnaryFilterOptions | FindEntityExistenceFilterOptions;
|
|
368
|
-
export type EntityNonRelationPropertyFilterOptions = FindEntityRelationalFilterOptions | FindEntitySetFilterOptions | FindEntityUnaryFilterOptions;
|
|
367
|
+
export type EntityFilterOperators = EntityFilterRelationalOperators | EntityFilterArrayOperators | EntityFilterSetOperators | EntityFilterLogicalOperators | EntityFilterUnaryOperators | EntityFilterExistenceOperators;
|
|
368
|
+
export type EntityFilterOptions = FindEntityRelationalFilterOptions | FindEntityArrayFilterOptions | FindEntitySetFilterOptions | FindEntityLogicalFilterOptions | FindEntityUnaryFilterOptions | FindEntityExistenceFilterOptions;
|
|
369
|
+
export type EntityNonRelationPropertyFilterOptions = FindEntityRelationalFilterOptions | FindEntityArrayFilterOptions | FindEntitySetFilterOptions | FindEntityUnaryFilterOptions;
|
|
369
370
|
export interface FindEntityOptions {
|
|
370
371
|
routeContext?: RouteContext;
|
|
371
372
|
filters?: EntityFilterOptions[];
|
|
@@ -388,6 +389,12 @@ export interface FindEntityRelationalFilterOptions {
|
|
|
388
389
|
operator: EntityFilterRelationalOperators;
|
|
389
390
|
value: any;
|
|
390
391
|
}
|
|
392
|
+
export interface FindEntityArrayFilterOptions {
|
|
393
|
+
field: string;
|
|
394
|
+
operator: EntityFilterArrayOperators;
|
|
395
|
+
value: any[];
|
|
396
|
+
itemType?: string;
|
|
397
|
+
}
|
|
391
398
|
export interface FindEntitySetFilterOptions {
|
|
392
399
|
field: string;
|
|
393
400
|
operator: EntityFilterSetOperators;
|
package/package.json
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { RpdDataPropertyTypes } from "~/types";
|
|
2
|
+
import { DataAccessPgColumnTypes } from "./dataAccessTypes";
|
|
3
|
+
|
|
4
|
+
export const pgPropertyTypeColumnMap: Record<Exclude<RpdDataPropertyTypes, "relation" | "relation[]">, DataAccessPgColumnTypes> = {
|
|
5
|
+
integer: "int4",
|
|
6
|
+
long: "int8",
|
|
7
|
+
float: "float4",
|
|
8
|
+
double: "float8",
|
|
9
|
+
decimal: "decimal",
|
|
10
|
+
text: "text",
|
|
11
|
+
boolean: "bool",
|
|
12
|
+
date: "date",
|
|
13
|
+
time: "time",
|
|
14
|
+
datetime: "timestamptz",
|
|
15
|
+
json: "jsonb",
|
|
16
|
+
option: "text",
|
|
17
|
+
"option[]": "text[]",
|
|
18
|
+
file: "jsonb",
|
|
19
|
+
"file[]": "jsonb",
|
|
20
|
+
image: "jsonb",
|
|
21
|
+
"image[]": "jsonb",
|
|
22
|
+
};
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
export type DataAccessPgColumnTypes =
|
|
2
|
+
| "int4"
|
|
3
|
+
| "int8"
|
|
4
|
+
| "float4"
|
|
5
|
+
| "float8"
|
|
6
|
+
| "decimal"
|
|
7
|
+
| "text"
|
|
8
|
+
| "text[]"
|
|
9
|
+
| "bool"
|
|
10
|
+
| "date"
|
|
11
|
+
| "time"
|
|
12
|
+
| "timestamptz"
|
|
13
|
+
| "jsonb";
|
|
14
|
+
|
|
1
15
|
export type RowFilterRelationalOperators =
|
|
2
16
|
| "eq"
|
|
3
17
|
| "ne"
|
|
@@ -14,6 +28,8 @@ export type RowFilterRelationalOperators =
|
|
|
14
28
|
| "endsWith"
|
|
15
29
|
| "notEndsWith";
|
|
16
30
|
|
|
31
|
+
export type RowFilterArrayOperators = "arrayContains" | "arrayOverlap";
|
|
32
|
+
|
|
17
33
|
export type RowFilterSetOperators = "in" | "notIn";
|
|
18
34
|
|
|
19
35
|
export type RowFilterLogicalOperators = "or" | "and";
|
|
@@ -24,6 +40,7 @@ export type RowFilterExistenceOperators = "exists" | "notExists";
|
|
|
24
40
|
|
|
25
41
|
export type RowFilterOperators =
|
|
26
42
|
| RowFilterRelationalOperators
|
|
43
|
+
| RowFilterArrayOperators
|
|
27
44
|
| RowFilterSetOperators
|
|
28
45
|
| RowFilterLogicalOperators
|
|
29
46
|
| RowFilterUnaryOperators
|
|
@@ -31,12 +48,17 @@ export type RowFilterOperators =
|
|
|
31
48
|
|
|
32
49
|
export type RowFilterOptions =
|
|
33
50
|
| FindRowRelationalFilterOptions
|
|
51
|
+
| FindRowArrayFilterOptions
|
|
34
52
|
| FindRowSetFilterOptions
|
|
35
53
|
| FindRowLogicalFilterOptions
|
|
36
54
|
| FindRowUnaryFilterOptions
|
|
37
55
|
| FindRowExistenceFilterOptions;
|
|
38
56
|
|
|
39
|
-
export type RowNonRelationPropertyFilterOptions =
|
|
57
|
+
export type RowNonRelationPropertyFilterOptions =
|
|
58
|
+
| FindRowRelationalFilterOptions
|
|
59
|
+
| FindRowArrayFilterOptions
|
|
60
|
+
| FindRowSetFilterOptions
|
|
61
|
+
| FindRowUnaryFilterOptions;
|
|
40
62
|
|
|
41
63
|
export type ColumnSelectOptions = string | ColumnNameWithTableName;
|
|
42
64
|
|
|
@@ -62,6 +84,13 @@ export interface FindRowRelationalFilterOptions {
|
|
|
62
84
|
value: any;
|
|
63
85
|
}
|
|
64
86
|
|
|
87
|
+
export interface FindRowArrayFilterOptions {
|
|
88
|
+
field: ColumnSelectOptions;
|
|
89
|
+
operator: RowFilterArrayOperators;
|
|
90
|
+
value: any[];
|
|
91
|
+
itemType?: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
65
94
|
export interface FindRowSetFilterOptions {
|
|
66
95
|
field: ColumnSelectOptions;
|
|
67
96
|
operator: RowFilterSetOperators;
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
getEntityPropertyByCode,
|
|
32
32
|
getEntityPropertyByFieldName,
|
|
33
33
|
isManyRelationProperty,
|
|
34
|
+
isOneRelationProperty,
|
|
34
35
|
isRelationProperty,
|
|
35
36
|
} from "../helpers/metaHelper";
|
|
36
37
|
import { ColumnSelectOptions, CountRowOptions, FindRowOptions, FindRowOrderByOptions, RowFilterOptions } from "./dataAccessTypes";
|
|
@@ -520,13 +521,17 @@ async function convertEntityFiltersToRowFilters(
|
|
|
520
521
|
}
|
|
521
522
|
} else {
|
|
522
523
|
const filterField = (filter as EntityNonRelationPropertyFilterOptions).field;
|
|
523
|
-
let property: RpdDataModelProperty =
|
|
524
|
-
return property.code === filterField;
|
|
525
|
-
});
|
|
524
|
+
let property: RpdDataModelProperty = getEntityPropertyByCode(server, model, filterField);
|
|
526
525
|
|
|
527
526
|
let columnName = "";
|
|
528
527
|
if (property) {
|
|
529
|
-
|
|
528
|
+
if (isOneRelationProperty(property)) {
|
|
529
|
+
columnName = property.targetIdColumnName;
|
|
530
|
+
} else if (isManyRelationProperty(property)) {
|
|
531
|
+
throw new Error(`Operator "${operator}" is not supported on many-relation property "${property.code}"`);
|
|
532
|
+
} else {
|
|
533
|
+
columnName = property.columnName || property.code;
|
|
534
|
+
}
|
|
530
535
|
} else {
|
|
531
536
|
property = getEntityProperty(server, model, (property) => {
|
|
532
537
|
return property.columnName === filterField;
|
|
@@ -27,6 +27,8 @@ import * as listMetaRoutes from "./actionHandlers/listMetaRoutes";
|
|
|
27
27
|
import * as getMetaModelDetail from "./actionHandlers/getMetaModelDetail";
|
|
28
28
|
import { find, isString, map } from "lodash";
|
|
29
29
|
import { getEntityPropertiesIncludingBase, getEntityPropertyByCode, isOneRelationProperty, isRelationProperty } from "~/helpers/metaHelper";
|
|
30
|
+
import { DataAccessPgColumnTypes } from "~/dataAccess/dataAccessTypes";
|
|
31
|
+
import { pgPropertyTypeColumnMap } from "~/dataAccess/columnTypeMapper";
|
|
30
32
|
|
|
31
33
|
class MetaManager implements RapidPlugin {
|
|
32
34
|
get code(): string {
|
|
@@ -484,18 +486,3 @@ function generateTableIndexDDL(queryBuilder: IQueryBuilder, server: IRpdServer,
|
|
|
484
486
|
|
|
485
487
|
return ddl;
|
|
486
488
|
}
|
|
487
|
-
|
|
488
|
-
const pgPropertyTypeColumnMap: Partial<Record<RpdDataPropertyTypes, string>> = {
|
|
489
|
-
integer: "int4",
|
|
490
|
-
long: "int8",
|
|
491
|
-
float: "float4",
|
|
492
|
-
double: "float8",
|
|
493
|
-
decimal: "decimal",
|
|
494
|
-
text: "text",
|
|
495
|
-
boolean: "bool",
|
|
496
|
-
date: "date",
|
|
497
|
-
time: "time",
|
|
498
|
-
datetime: "timestamptz",
|
|
499
|
-
json: "jsonb",
|
|
500
|
-
option: "text",
|
|
501
|
-
};
|
|
@@ -13,7 +13,10 @@ import {
|
|
|
13
13
|
UpdateRowOptions,
|
|
14
14
|
ColumnSelectOptions,
|
|
15
15
|
ColumnNameWithTableName,
|
|
16
|
+
DataAccessPgColumnTypes,
|
|
17
|
+
FindRowArrayFilterOptions,
|
|
16
18
|
} from "~/dataAccess/dataAccessTypes";
|
|
19
|
+
import { pgPropertyTypeColumnMap } from "~/dataAccess/columnTypeMapper";
|
|
17
20
|
|
|
18
21
|
const objLeftQuoteChar = '"';
|
|
19
22
|
const objRightQuoteChar = '"';
|
|
@@ -274,28 +277,28 @@ export default class QueryBuilder {
|
|
|
274
277
|
|
|
275
278
|
command += this.quoteTable(model);
|
|
276
279
|
|
|
277
|
-
const
|
|
280
|
+
const columnNames: string[] = Object.keys(entity);
|
|
278
281
|
let values = "";
|
|
279
|
-
|
|
282
|
+
columnNames.forEach((columnName, index) => {
|
|
280
283
|
if (index) {
|
|
281
284
|
values += ", ";
|
|
282
285
|
}
|
|
283
286
|
|
|
284
287
|
let property: RpdDataModelProperty | null = null;
|
|
285
288
|
if (model) {
|
|
286
|
-
property = find(model.properties, (e: RpdDataModelProperty) => e.code ===
|
|
289
|
+
property = find(model.properties, (e: RpdDataModelProperty) => (e.columnName || e.code) === columnName);
|
|
287
290
|
}
|
|
288
|
-
|
|
289
|
-
if (
|
|
290
|
-
params.push(JSON.stringify(entity[
|
|
291
|
+
const columnType: DataAccessPgColumnTypes | null = property ? pgPropertyTypeColumnMap[property.type] : null;
|
|
292
|
+
if (columnType === "jsonb") {
|
|
293
|
+
params.push(JSON.stringify(entity[columnName]));
|
|
291
294
|
values += `$${params.length}::jsonb`;
|
|
292
295
|
} else {
|
|
293
|
-
params.push(entity[
|
|
296
|
+
params.push(entity[columnName]);
|
|
294
297
|
values += `$${params.length}`;
|
|
295
298
|
}
|
|
296
299
|
});
|
|
297
300
|
|
|
298
|
-
command += ` (${
|
|
301
|
+
command += ` (${columnNames.map(this.quoteObject).join(", ")})`;
|
|
299
302
|
command += ` VALUES (${values}) RETURNING *`;
|
|
300
303
|
|
|
301
304
|
return {
|
|
@@ -319,23 +322,25 @@ export default class QueryBuilder {
|
|
|
319
322
|
command += this.quoteTable(model);
|
|
320
323
|
|
|
321
324
|
command += " SET ";
|
|
322
|
-
const
|
|
323
|
-
|
|
325
|
+
const columnNames: string[] = Object.keys(entity);
|
|
326
|
+
columnNames.forEach((columnName, index) => {
|
|
324
327
|
if (index) {
|
|
325
328
|
command += ", ";
|
|
326
329
|
}
|
|
327
330
|
|
|
331
|
+
command += `${this.quoteObject(columnName)}=`;
|
|
332
|
+
|
|
328
333
|
let property: RpdDataModelProperty | null = null;
|
|
329
334
|
if (model) {
|
|
330
|
-
property = find(model.properties, (e: RpdDataModelProperty) => (e.columnName || e.code) ===
|
|
335
|
+
property = find(model.properties, (e: RpdDataModelProperty) => (e.columnName || e.code) === columnName);
|
|
331
336
|
}
|
|
332
|
-
|
|
333
|
-
if (
|
|
334
|
-
params.push(JSON.stringify(entity[
|
|
335
|
-
command +=
|
|
337
|
+
const columnType: DataAccessPgColumnTypes | null = property ? pgPropertyTypeColumnMap[property.type] : null;
|
|
338
|
+
if (columnType === "jsonb") {
|
|
339
|
+
params.push(JSON.stringify(entity[columnName]));
|
|
340
|
+
command += `$${params.length}::jsonb`;
|
|
336
341
|
} else {
|
|
337
|
-
params.push(entity[
|
|
338
|
-
command +=
|
|
342
|
+
params.push(entity[columnName]);
|
|
343
|
+
command += `$${params.length}`;
|
|
339
344
|
}
|
|
340
345
|
});
|
|
341
346
|
|
|
@@ -420,6 +425,10 @@ function buildFilterQuery(level: number, ctx: BuildQueryContext, filter: RowFilt
|
|
|
420
425
|
return buildEndsWithFilterQuery(ctx, filter);
|
|
421
426
|
} else if (operator === "notEndsWith") {
|
|
422
427
|
return buildNotEndsWithFilterQuery(ctx, filter);
|
|
428
|
+
} else if (operator === "arrayContains") {
|
|
429
|
+
return buildArrayContainsFilterQuery(ctx, filter);
|
|
430
|
+
} else if (operator === "arrayOverlap") {
|
|
431
|
+
return buildArrayOverlapFilterQuery(ctx, filter);
|
|
423
432
|
} else {
|
|
424
433
|
throw new Error(`Filter operator '${operator}' is not supported.`);
|
|
425
434
|
}
|
|
@@ -573,6 +582,36 @@ function buildRelationalFilterQuery(ctx: BuildQueryContext, filter: FindRowRelat
|
|
|
573
582
|
return command;
|
|
574
583
|
}
|
|
575
584
|
|
|
585
|
+
function buildArrayContainsFilterQuery(ctx: BuildQueryContext, filter: FindRowArrayFilterOptions) {
|
|
586
|
+
let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
|
|
587
|
+
|
|
588
|
+
command += " @> ";
|
|
589
|
+
|
|
590
|
+
if (ctx.paramToLiteral) {
|
|
591
|
+
// TODO: implement it
|
|
592
|
+
} else {
|
|
593
|
+
ctx.params.push(filter.value);
|
|
594
|
+
command += "$" + ctx.params.length;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
return command;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function buildArrayOverlapFilterQuery(ctx: BuildQueryContext, filter: FindRowArrayFilterOptions) {
|
|
601
|
+
let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
|
|
602
|
+
|
|
603
|
+
command += " && ";
|
|
604
|
+
|
|
605
|
+
if (ctx.paramToLiteral) {
|
|
606
|
+
// TODO: implement it
|
|
607
|
+
} else {
|
|
608
|
+
ctx.params.push(filter.value);
|
|
609
|
+
command += "$" + ctx.params.length;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
return command;
|
|
613
|
+
}
|
|
614
|
+
|
|
576
615
|
function formatValueToSqlLiteral(value: any) {
|
|
577
616
|
if (isNull(value) || isUndefined(value)) {
|
|
578
617
|
return "null";
|
package/src/types.ts
CHANGED
|
@@ -316,7 +316,12 @@ export type RpdDataPropertyTypes =
|
|
|
316
316
|
| "json"
|
|
317
317
|
| "relation"
|
|
318
318
|
| "relation[]"
|
|
319
|
-
| "option"
|
|
319
|
+
| "option"
|
|
320
|
+
| "option[]"
|
|
321
|
+
| "file"
|
|
322
|
+
| "file[]"
|
|
323
|
+
| "image"
|
|
324
|
+
| "image[]";
|
|
320
325
|
|
|
321
326
|
/**
|
|
322
327
|
* 数据字典
|
|
@@ -444,6 +449,8 @@ export type EntityFilterRelationalOperators =
|
|
|
444
449
|
| "endsWith"
|
|
445
450
|
| "notEndsWith";
|
|
446
451
|
|
|
452
|
+
export type EntityFilterArrayOperators = "arrayContains" | "arrayOverlap";
|
|
453
|
+
|
|
447
454
|
export type EntityFilterSetOperators = "in" | "notIn";
|
|
448
455
|
|
|
449
456
|
export type EntityFilterLogicalOperators = "or" | "and";
|
|
@@ -454,6 +461,7 @@ export type EntityFilterExistenceOperators = "exists" | "notExists";
|
|
|
454
461
|
|
|
455
462
|
export type EntityFilterOperators =
|
|
456
463
|
| EntityFilterRelationalOperators
|
|
464
|
+
| EntityFilterArrayOperators
|
|
457
465
|
| EntityFilterSetOperators
|
|
458
466
|
| EntityFilterLogicalOperators
|
|
459
467
|
| EntityFilterUnaryOperators
|
|
@@ -461,12 +469,17 @@ export type EntityFilterOperators =
|
|
|
461
469
|
|
|
462
470
|
export type EntityFilterOptions =
|
|
463
471
|
| FindEntityRelationalFilterOptions
|
|
472
|
+
| FindEntityArrayFilterOptions
|
|
464
473
|
| FindEntitySetFilterOptions
|
|
465
474
|
| FindEntityLogicalFilterOptions
|
|
466
475
|
| FindEntityUnaryFilterOptions
|
|
467
476
|
| FindEntityExistenceFilterOptions;
|
|
468
477
|
|
|
469
|
-
export type EntityNonRelationPropertyFilterOptions =
|
|
478
|
+
export type EntityNonRelationPropertyFilterOptions =
|
|
479
|
+
| FindEntityRelationalFilterOptions
|
|
480
|
+
| FindEntityArrayFilterOptions
|
|
481
|
+
| FindEntitySetFilterOptions
|
|
482
|
+
| FindEntityUnaryFilterOptions;
|
|
470
483
|
|
|
471
484
|
export interface FindEntityOptions {
|
|
472
485
|
routeContext?: RouteContext;
|
|
@@ -493,6 +506,13 @@ export interface FindEntityRelationalFilterOptions {
|
|
|
493
506
|
value: any;
|
|
494
507
|
}
|
|
495
508
|
|
|
509
|
+
export interface FindEntityArrayFilterOptions {
|
|
510
|
+
field: string;
|
|
511
|
+
operator: EntityFilterArrayOperators;
|
|
512
|
+
value: any[];
|
|
513
|
+
itemType?: string;
|
|
514
|
+
}
|
|
515
|
+
|
|
496
516
|
export interface FindEntitySetFilterOptions {
|
|
497
517
|
field: string;
|
|
498
518
|
operator: EntityFilterSetOperators;
|