@ruiapp/rapid-core 0.1.61 → 0.1.63
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/dataAccessTypes.d.ts +7 -7
- package/dist/dataAccess/entityManager.d.ts +15 -1
- package/dist/index.js +158 -50
- package/dist/queryBuilder/queryBuilder.d.ts +2 -2
- package/dist/types.d.ts +8 -1
- package/package.json +1 -1
- package/src/dataAccess/dataAccessTypes.ts +36 -13
- package/src/dataAccess/entityManager.ts +216 -61
- package/src/queryBuilder/queryBuilder.ts +15 -15
- package/src/types.ts +60 -6
|
@@ -6,7 +6,7 @@ export type RowFilterExistenceOperators = "exists" | "notExists";
|
|
|
6
6
|
export type RowFilterOperators = RowFilterRelationalOperators | RowFilterSetOperators | RowFilterLogicalOperators | RowFilterUnaryOperators | RowFilterExistenceOperators;
|
|
7
7
|
export type RowFilterOptions = FindRowRelationalFilterOptions | FindRowSetFilterOptions | FindRowLogicalFilterOptions | FindRowUnaryFilterOptions | FindRowExistenceFilterOptions;
|
|
8
8
|
export type RowNonRelationPropertyFilterOptions = FindRowRelationalFilterOptions | FindRowSetFilterOptions | FindRowUnaryFilterOptions;
|
|
9
|
-
export type
|
|
9
|
+
export type ColumnSelectOptions = string | ColumnNameWithTableName;
|
|
10
10
|
export type ColumnNameWithTableName = {
|
|
11
11
|
name: string;
|
|
12
12
|
tableName?: string;
|
|
@@ -15,16 +15,16 @@ export interface FindRowOptions {
|
|
|
15
15
|
filters?: RowFilterOptions[];
|
|
16
16
|
orderBy?: FindRowOrderByOptions[];
|
|
17
17
|
pagination?: FindRowPaginationOptions;
|
|
18
|
-
fields?:
|
|
18
|
+
fields?: ColumnSelectOptions[];
|
|
19
19
|
keepNonPropertyFields?: boolean;
|
|
20
20
|
}
|
|
21
21
|
export interface FindRowRelationalFilterOptions {
|
|
22
|
-
field:
|
|
22
|
+
field: ColumnSelectOptions;
|
|
23
23
|
operator: RowFilterRelationalOperators;
|
|
24
24
|
value: any;
|
|
25
25
|
}
|
|
26
26
|
export interface FindRowSetFilterOptions {
|
|
27
|
-
field:
|
|
27
|
+
field: ColumnSelectOptions;
|
|
28
28
|
operator: RowFilterSetOperators;
|
|
29
29
|
value: any[];
|
|
30
30
|
itemType?: string;
|
|
@@ -34,11 +34,11 @@ export interface FindRowLogicalFilterOptions {
|
|
|
34
34
|
filters: RowFilterOptions[];
|
|
35
35
|
}
|
|
36
36
|
export interface FindRowUnaryFilterOptions {
|
|
37
|
-
field:
|
|
37
|
+
field: ColumnSelectOptions;
|
|
38
38
|
operator: RowFilterUnaryOperators;
|
|
39
39
|
}
|
|
40
40
|
export interface FindRowExistenceFilterOptions {
|
|
41
|
-
field:
|
|
41
|
+
field: ColumnSelectOptions;
|
|
42
42
|
operator: RowFilterExistenceOperators;
|
|
43
43
|
filters: RowFilterOptions[];
|
|
44
44
|
}
|
|
@@ -48,7 +48,7 @@ export interface FindRowPaginationOptions {
|
|
|
48
48
|
withoutTotal?: boolean;
|
|
49
49
|
}
|
|
50
50
|
export interface FindRowOrderByOptions {
|
|
51
|
-
field:
|
|
51
|
+
field: ColumnSelectOptions;
|
|
52
52
|
desc?: boolean;
|
|
53
53
|
}
|
|
54
54
|
export interface CountRowOptions {
|
|
@@ -1,5 +1,19 @@
|
|
|
1
|
-
import { AddEntityRelationsOptions, CountEntityOptions, CountEntityResult, CreateEntityOptions, DeleteEntityByIdOptions, FindEntityByIdOptions, FindEntityOptions, IRpdDataAccessor, RemoveEntityRelationsOptions, RpdDataModel, UpdateEntityByIdOptions } from "../types";
|
|
1
|
+
import { AddEntityRelationsOptions, CountEntityOptions, CountEntityResult, CreateEntityOptions, DeleteEntityByIdOptions, FindEntityByIdOptions, FindEntityOptions, FindEntitySelectRelationOptions, IRpdDataAccessor, RemoveEntityRelationsOptions, RpdDataModel, RpdDataModelProperty, UpdateEntityByIdOptions } from "../types";
|
|
2
2
|
import { IRpdServer, RapidPlugin } from "../core/server";
|
|
3
|
+
export type FindOneRelationEntitiesOptions = {
|
|
4
|
+
server: IRpdServer;
|
|
5
|
+
mainModel: RpdDataModel;
|
|
6
|
+
relationProperty: RpdDataModelProperty;
|
|
7
|
+
relationEntityIds: any[];
|
|
8
|
+
selectRelationOptions?: FindEntitySelectRelationOptions;
|
|
9
|
+
};
|
|
10
|
+
export type FindManyRelationEntitiesOptions = {
|
|
11
|
+
server: IRpdServer;
|
|
12
|
+
mainModel: RpdDataModel;
|
|
13
|
+
relationProperty: RpdDataModelProperty;
|
|
14
|
+
mainEntityIds: any[];
|
|
15
|
+
selectRelationOptions?: FindEntitySelectRelationOptions;
|
|
16
|
+
};
|
|
3
17
|
export default class EntityManager<TEntity = any> {
|
|
4
18
|
#private;
|
|
5
19
|
constructor(server: IRpdServer, dataAccessor: IRpdDataAccessor);
|
package/dist/index.js
CHANGED
|
@@ -277,9 +277,11 @@ class QueryBuilder {
|
|
|
277
277
|
command += `${this.quoteObject(derivedModel.tableName)}.* FROM `;
|
|
278
278
|
}
|
|
279
279
|
else {
|
|
280
|
-
command += columns
|
|
280
|
+
command += columns
|
|
281
|
+
.map((column) => {
|
|
281
282
|
return this.quoteColumn(column, ctx.emitTableAlias);
|
|
282
|
-
})
|
|
283
|
+
})
|
|
284
|
+
.join(", ");
|
|
283
285
|
command += " FROM ";
|
|
284
286
|
}
|
|
285
287
|
command += `${this.quoteTable(derivedModel)} LEFT JOIN ${this.quoteTable(baseModel)} ON ${this.quoteObject(derivedModel.tableName)}.id = ${this.quoteObject(baseModel.tableName)}.id`;
|
|
@@ -2001,16 +2003,18 @@ async function findEntities(server, dataAccessor, options) {
|
|
|
2001
2003
|
singularCode: model.base,
|
|
2002
2004
|
});
|
|
2003
2005
|
}
|
|
2004
|
-
let
|
|
2006
|
+
let propertiesToSelect;
|
|
2007
|
+
let relationOptions = options.relations || {};
|
|
2008
|
+
let relationPropertyCodes = Object.keys(relationOptions) || [];
|
|
2005
2009
|
if (!options.properties || !options.properties.length) {
|
|
2006
|
-
|
|
2010
|
+
propertiesToSelect = getEntityPropertiesIncludingBase(server, model).filter((property) => !isRelationProperty(property) || relationPropertyCodes.includes(property.code));
|
|
2007
2011
|
}
|
|
2008
2012
|
else {
|
|
2009
|
-
|
|
2013
|
+
propertiesToSelect = getEntityPropertiesIncludingBase(server, model).filter((property) => options.properties.includes(property.code) || relationPropertyCodes.includes(property.code));
|
|
2010
2014
|
}
|
|
2011
2015
|
const columnsToSelect = [];
|
|
2012
2016
|
const relationPropertiesToSelect = [];
|
|
2013
|
-
lodash.forEach(
|
|
2017
|
+
lodash.forEach(propertiesToSelect, (property) => {
|
|
2014
2018
|
if (isRelationProperty(property)) {
|
|
2015
2019
|
relationPropertiesToSelect.push(property);
|
|
2016
2020
|
if (property.relation === "one" && !property.linkTableName) {
|
|
@@ -2058,6 +2062,31 @@ async function findEntities(server, dataAccessor, options) {
|
|
|
2058
2062
|
}
|
|
2059
2063
|
});
|
|
2060
2064
|
}
|
|
2065
|
+
if (options.extraColumnsToSelect) {
|
|
2066
|
+
lodash.forEach(options.extraColumnsToSelect, (extraColumnToSelect) => {
|
|
2067
|
+
const columnSelectOptionExists = lodash.find(columnsToSelect, (item) => {
|
|
2068
|
+
if (typeof item === "string") {
|
|
2069
|
+
if (typeof extraColumnToSelect === "string") {
|
|
2070
|
+
return item === extraColumnToSelect;
|
|
2071
|
+
}
|
|
2072
|
+
else {
|
|
2073
|
+
return item == extraColumnToSelect.name;
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
else {
|
|
2077
|
+
if (typeof extraColumnToSelect === "string") {
|
|
2078
|
+
return item.name === extraColumnToSelect;
|
|
2079
|
+
}
|
|
2080
|
+
else {
|
|
2081
|
+
return item.name == extraColumnToSelect.name;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
});
|
|
2085
|
+
if (!columnSelectOptionExists) {
|
|
2086
|
+
columnsToSelect.push(extraColumnToSelect);
|
|
2087
|
+
}
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2061
2090
|
const rowFilters = await convertEntityFiltersToRowFilters(server, model, baseModel, options.filters);
|
|
2062
2091
|
const findRowOptions = {
|
|
2063
2092
|
filters: rowFilters,
|
|
@@ -2074,27 +2103,45 @@ async function findEntities(server, dataAccessor, options) {
|
|
|
2074
2103
|
for (const relationProperty of relationPropertiesToSelect) {
|
|
2075
2104
|
const isManyRelation = relationProperty.relation === "many";
|
|
2076
2105
|
if (relationProperty.linkTableName) {
|
|
2077
|
-
const
|
|
2078
|
-
if (!
|
|
2106
|
+
const relationModel = server.getModel({ singularCode: relationProperty.targetSingularCode });
|
|
2107
|
+
if (!relationModel) {
|
|
2079
2108
|
continue;
|
|
2080
2109
|
}
|
|
2081
2110
|
if (isManyRelation) {
|
|
2082
|
-
const relationLinks = await findManyRelationLinksViaLinkTable(
|
|
2111
|
+
const relationLinks = await findManyRelationLinksViaLinkTable({
|
|
2112
|
+
server,
|
|
2113
|
+
mainModel: relationModel,
|
|
2114
|
+
relationProperty,
|
|
2115
|
+
mainEntityIds: entityIds,
|
|
2116
|
+
selectRelationOptions: relationOptions[relationProperty.code],
|
|
2117
|
+
});
|
|
2083
2118
|
lodash.forEach(rows, (row) => {
|
|
2084
2119
|
row[relationProperty.code] = lodash.filter(relationLinks, (link) => {
|
|
2085
2120
|
return link[relationProperty.selfIdColumnName] == row["id"];
|
|
2086
|
-
}).map((link) => mapDbRowToEntity(server,
|
|
2121
|
+
}).map((link) => mapDbRowToEntity(server, relationModel, link.targetEntity, options.keepNonPropertyFields));
|
|
2087
2122
|
});
|
|
2088
2123
|
}
|
|
2089
2124
|
}
|
|
2090
2125
|
else {
|
|
2091
2126
|
let relatedEntities;
|
|
2092
2127
|
if (isManyRelation) {
|
|
2093
|
-
relatedEntities = await findManyRelatedEntitiesViaIdPropertyCode(
|
|
2128
|
+
relatedEntities = await findManyRelatedEntitiesViaIdPropertyCode({
|
|
2129
|
+
server,
|
|
2130
|
+
mainModel: model,
|
|
2131
|
+
relationProperty,
|
|
2132
|
+
mainEntityIds: entityIds,
|
|
2133
|
+
selectRelationOptions: relationOptions[relationProperty.code],
|
|
2134
|
+
});
|
|
2094
2135
|
}
|
|
2095
2136
|
else {
|
|
2096
2137
|
const targetEntityIds = lodash.uniq(lodash.reject(lodash.map(rows, (entity) => entity[relationProperty.targetIdColumnName]), isNullOrUndefined));
|
|
2097
|
-
relatedEntities = await findOneRelatedEntitiesViaIdPropertyCode(
|
|
2138
|
+
relatedEntities = await findOneRelatedEntitiesViaIdPropertyCode({
|
|
2139
|
+
server,
|
|
2140
|
+
mainModel: model,
|
|
2141
|
+
relationProperty,
|
|
2142
|
+
relationEntityIds: targetEntityIds,
|
|
2143
|
+
selectRelationOptions: relationOptions[relationProperty.code],
|
|
2144
|
+
});
|
|
2098
2145
|
}
|
|
2099
2146
|
const targetModel = server.getModel({
|
|
2100
2147
|
singularCode: relationProperty.targetSingularCode,
|
|
@@ -2295,7 +2342,10 @@ async function convertEntityFiltersToRowFilters(server, model, baseModel, filter
|
|
|
2295
2342
|
],
|
|
2296
2343
|
});
|
|
2297
2344
|
const targetEntityIds = lodash.map(targetEntities, (entity) => entity["id"]);
|
|
2298
|
-
const command = `SELECT * FROM ${server.queryBuilder.quoteTable({
|
|
2345
|
+
const command = `SELECT * FROM ${server.queryBuilder.quoteTable({
|
|
2346
|
+
schema: relationProperty.linkSchema,
|
|
2347
|
+
tableName: relationProperty.linkTableName,
|
|
2348
|
+
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.targetIdColumnName)} = ANY($1::int[])`;
|
|
2299
2349
|
const params = [targetEntityIds];
|
|
2300
2350
|
const links = await server.queryDatabaseObject(command, params);
|
|
2301
2351
|
const selfEntityIds = links.map((link) => link[relationProperty.selfIdColumnName]);
|
|
@@ -2351,65 +2401,99 @@ async function convertEntityFiltersToRowFilters(server, model, baseModel, filter
|
|
|
2351
2401
|
}
|
|
2352
2402
|
return replacedFilters;
|
|
2353
2403
|
}
|
|
2354
|
-
async function findManyRelationLinksViaLinkTable(
|
|
2355
|
-
const
|
|
2356
|
-
const
|
|
2404
|
+
async function findManyRelationLinksViaLinkTable(options) {
|
|
2405
|
+
const { server, relationProperty, mainModel: relationModel, mainEntityIds, selectRelationOptions } = options;
|
|
2406
|
+
const command = `SELECT * FROM ${server.queryBuilder.quoteTable({
|
|
2407
|
+
schema: relationProperty.linkSchema,
|
|
2408
|
+
tableName: relationProperty.linkTableName,
|
|
2409
|
+
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName)} = ANY($1::int[])`;
|
|
2410
|
+
const params = [mainEntityIds];
|
|
2357
2411
|
const links = await server.queryDatabaseObject(command, params);
|
|
2358
2412
|
const targetEntityIds = links.map((link) => link[relationProperty.targetIdColumnName]);
|
|
2413
|
+
const dataAccessor = server.getDataAccessor({
|
|
2414
|
+
namespace: relationModel.namespace,
|
|
2415
|
+
singularCode: relationModel.singularCode,
|
|
2416
|
+
});
|
|
2359
2417
|
const findEntityOptions = {
|
|
2360
2418
|
filters: [
|
|
2361
2419
|
{
|
|
2362
|
-
field:
|
|
2363
|
-
name: "id",
|
|
2364
|
-
},
|
|
2420
|
+
field: "id",
|
|
2365
2421
|
operator: "in",
|
|
2366
2422
|
value: targetEntityIds,
|
|
2367
2423
|
},
|
|
2368
2424
|
],
|
|
2425
|
+
keepNonPropertyFields: true,
|
|
2369
2426
|
};
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2427
|
+
if (selectRelationOptions) {
|
|
2428
|
+
if (typeof selectRelationOptions !== "boolean") {
|
|
2429
|
+
if (selectRelationOptions.properties) {
|
|
2430
|
+
findEntityOptions.properties = ["id", ...selectRelationOptions.properties];
|
|
2431
|
+
}
|
|
2432
|
+
if (selectRelationOptions.relations) {
|
|
2433
|
+
findEntityOptions.relations = selectRelationOptions.relations;
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
const targetEntities = await findEntities(server, dataAccessor, findEntityOptions);
|
|
2375
2438
|
lodash.forEach(links, (link) => {
|
|
2376
2439
|
link.targetEntity = lodash.find(targetEntities, (e) => e["id"] == link[relationProperty.targetIdColumnName]);
|
|
2377
2440
|
});
|
|
2378
2441
|
return links;
|
|
2379
2442
|
}
|
|
2380
|
-
function findManyRelatedEntitiesViaIdPropertyCode(
|
|
2443
|
+
async function findManyRelatedEntitiesViaIdPropertyCode(options) {
|
|
2444
|
+
const { server, relationProperty, mainEntityIds, selectRelationOptions } = options;
|
|
2445
|
+
const dataAccessor = server.getDataAccessor({
|
|
2446
|
+
singularCode: relationProperty.targetSingularCode,
|
|
2447
|
+
});
|
|
2381
2448
|
const findEntityOptions = {
|
|
2382
2449
|
filters: [
|
|
2383
2450
|
{
|
|
2384
|
-
field:
|
|
2385
|
-
name: relationProperty.selfIdColumnName,
|
|
2386
|
-
},
|
|
2451
|
+
field: relationProperty.selfIdColumnName,
|
|
2387
2452
|
operator: "in",
|
|
2388
|
-
value:
|
|
2453
|
+
value: mainEntityIds,
|
|
2389
2454
|
},
|
|
2390
2455
|
],
|
|
2456
|
+
extraColumnsToSelect: [relationProperty.selfIdColumnName],
|
|
2457
|
+
keepNonPropertyFields: true,
|
|
2391
2458
|
};
|
|
2459
|
+
if (selectRelationOptions) {
|
|
2460
|
+
if (typeof selectRelationOptions !== "boolean") {
|
|
2461
|
+
if (selectRelationOptions.properties) {
|
|
2462
|
+
findEntityOptions.properties = ["id", ...selectRelationOptions.properties];
|
|
2463
|
+
}
|
|
2464
|
+
if (selectRelationOptions.relations) {
|
|
2465
|
+
findEntityOptions.relations = selectRelationOptions.relations;
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
return await findEntities(server, dataAccessor, findEntityOptions);
|
|
2470
|
+
}
|
|
2471
|
+
async function findOneRelatedEntitiesViaIdPropertyCode(options) {
|
|
2472
|
+
const { server, relationProperty, relationEntityIds, selectRelationOptions } = options;
|
|
2392
2473
|
const dataAccessor = server.getDataAccessor({
|
|
2393
2474
|
singularCode: relationProperty.targetSingularCode,
|
|
2394
2475
|
});
|
|
2395
|
-
return dataAccessor.find(findEntityOptions);
|
|
2396
|
-
}
|
|
2397
|
-
function findOneRelatedEntitiesViaIdPropertyCode(server, model, relationProperty, targetEntityIds) {
|
|
2398
2476
|
const findEntityOptions = {
|
|
2399
2477
|
filters: [
|
|
2400
2478
|
{
|
|
2401
|
-
field:
|
|
2402
|
-
name: "id",
|
|
2403
|
-
},
|
|
2479
|
+
field: "id",
|
|
2404
2480
|
operator: "in",
|
|
2405
|
-
value:
|
|
2481
|
+
value: relationEntityIds,
|
|
2406
2482
|
},
|
|
2407
2483
|
],
|
|
2484
|
+
keepNonPropertyFields: true,
|
|
2408
2485
|
};
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2486
|
+
if (selectRelationOptions) {
|
|
2487
|
+
if (typeof selectRelationOptions !== "boolean") {
|
|
2488
|
+
if (selectRelationOptions.properties) {
|
|
2489
|
+
findEntityOptions.properties = ["id", ...selectRelationOptions.properties];
|
|
2490
|
+
}
|
|
2491
|
+
if (selectRelationOptions.relations) {
|
|
2492
|
+
findEntityOptions.relations = selectRelationOptions.relations;
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
return await findEntities(server, dataAccessor, findEntityOptions);
|
|
2413
2497
|
}
|
|
2414
2498
|
async function createEntity(server, dataAccessor, options, plugin) {
|
|
2415
2499
|
const model = dataAccessor.getModel();
|
|
@@ -2417,7 +2501,7 @@ async function createEntity(server, dataAccessor, options, plugin) {
|
|
|
2417
2501
|
throw newEntityOperationError("Create base entity directly is not allowed.");
|
|
2418
2502
|
}
|
|
2419
2503
|
const { entity, routeContext } = options;
|
|
2420
|
-
const userId = options.routeContext
|
|
2504
|
+
const userId = options.routeContext?.state?.userId;
|
|
2421
2505
|
if (userId) {
|
|
2422
2506
|
const createdByProperty = getEntityPropertyByCode(server, model, "createdBy");
|
|
2423
2507
|
if (createdByProperty) {
|
|
@@ -2536,7 +2620,10 @@ async function createEntity(server, dataAccessor, options, plugin) {
|
|
|
2536
2620
|
entity: targetEntity,
|
|
2537
2621
|
});
|
|
2538
2622
|
if (property.linkTableName) {
|
|
2539
|
-
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
2623
|
+
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
2624
|
+
schema: property.linkSchema,
|
|
2625
|
+
tableName: property.linkTableName,
|
|
2626
|
+
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
2540
2627
|
const params = [newEntity.id, newTargetEntity.id];
|
|
2541
2628
|
await server.queryDatabaseObject(command, params);
|
|
2542
2629
|
}
|
|
@@ -2549,7 +2636,10 @@ async function createEntity(server, dataAccessor, options, plugin) {
|
|
|
2549
2636
|
throw new Error(`Entity with id '${relatedEntityId}' in field '${property.code}' is not exists.`);
|
|
2550
2637
|
}
|
|
2551
2638
|
if (property.linkTableName) {
|
|
2552
|
-
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
2639
|
+
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
2640
|
+
schema: property.linkSchema,
|
|
2641
|
+
tableName: property.linkTableName,
|
|
2642
|
+
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
2553
2643
|
const params = [newEntity.id, relatedEntityId];
|
|
2554
2644
|
await server.queryDatabaseObject(command, params);
|
|
2555
2645
|
}
|
|
@@ -2568,7 +2658,10 @@ async function createEntity(server, dataAccessor, options, plugin) {
|
|
|
2568
2658
|
throw new Error(`Entity with id '${relatedEntityId}' in field '${property.code}' is not exists.`);
|
|
2569
2659
|
}
|
|
2570
2660
|
if (property.linkTableName) {
|
|
2571
|
-
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
2661
|
+
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
2662
|
+
schema: property.linkSchema,
|
|
2663
|
+
tableName: property.linkTableName,
|
|
2664
|
+
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
2572
2665
|
const params = [newEntity.id, relatedEntityId];
|
|
2573
2666
|
await server.queryDatabaseObject(command, params);
|
|
2574
2667
|
}
|
|
@@ -2612,7 +2705,7 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
|
|
|
2612
2705
|
return entity;
|
|
2613
2706
|
}
|
|
2614
2707
|
entityToSave = changes || {};
|
|
2615
|
-
const userId = options.routeContext
|
|
2708
|
+
const userId = options.routeContext?.state?.userId;
|
|
2616
2709
|
if (userId) {
|
|
2617
2710
|
const updatedByProperty = getEntityPropertyByCode(server, model, "updatedBy");
|
|
2618
2711
|
if (updatedByProperty) {
|
|
@@ -2724,7 +2817,10 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
|
|
|
2724
2817
|
}
|
|
2725
2818
|
if (property.linkTableName) {
|
|
2726
2819
|
// TODO: should support removing relation
|
|
2727
|
-
await server.queryDatabaseObject(`DELETE FROM ${server.queryBuilder.quoteTable({
|
|
2820
|
+
await server.queryDatabaseObject(`DELETE FROM ${server.queryBuilder.quoteTable({
|
|
2821
|
+
schema: property.linkSchema,
|
|
2822
|
+
tableName: property.linkTableName,
|
|
2823
|
+
})} WHERE ${server.queryBuilder.quoteObject(property.selfIdColumnName)} = $1`, [id]);
|
|
2728
2824
|
}
|
|
2729
2825
|
for (const relatedEntityToBeSaved of relatedEntitiesToBeSaved) {
|
|
2730
2826
|
let relatedEntityId;
|
|
@@ -2740,7 +2836,10 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
|
|
|
2740
2836
|
entity: targetEntity,
|
|
2741
2837
|
});
|
|
2742
2838
|
if (property.linkTableName) {
|
|
2743
|
-
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
2839
|
+
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
2840
|
+
schema: property.linkSchema,
|
|
2841
|
+
tableName: property.linkTableName,
|
|
2842
|
+
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
2744
2843
|
const params = [id, newTargetEntity.id];
|
|
2745
2844
|
await server.queryDatabaseObject(command, params);
|
|
2746
2845
|
}
|
|
@@ -2753,7 +2852,10 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
|
|
|
2753
2852
|
throw new Error(`Entity with id '${relatedEntityId}' in field '${property.code}' is not exists.`);
|
|
2754
2853
|
}
|
|
2755
2854
|
if (property.linkTableName) {
|
|
2756
|
-
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
2855
|
+
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
2856
|
+
schema: property.linkSchema,
|
|
2857
|
+
tableName: property.linkTableName,
|
|
2858
|
+
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
2757
2859
|
const params = [id, relatedEntityId];
|
|
2758
2860
|
await server.queryDatabaseObject(command, params);
|
|
2759
2861
|
}
|
|
@@ -2772,7 +2874,10 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
|
|
|
2772
2874
|
throw new Error(`Entity with id '${relatedEntityId}' in field '${property.code}' is not exists.`);
|
|
2773
2875
|
}
|
|
2774
2876
|
if (property.linkTableName) {
|
|
2775
|
-
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
2877
|
+
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
2878
|
+
schema: property.linkSchema,
|
|
2879
|
+
tableName: property.linkTableName,
|
|
2880
|
+
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
2776
2881
|
const params = [id, relatedEntityId];
|
|
2777
2882
|
await server.queryDatabaseObject(command, params);
|
|
2778
2883
|
}
|
|
@@ -2916,7 +3021,10 @@ class EntityManager {
|
|
|
2916
3021
|
const { queryBuilder } = server;
|
|
2917
3022
|
if (relationProperty.linkTableName) {
|
|
2918
3023
|
for (const relation of relations) {
|
|
2919
|
-
const command = `INSERT INTO ${queryBuilder.quoteTable({
|
|
3024
|
+
const command = `INSERT INTO ${queryBuilder.quoteTable({
|
|
3025
|
+
schema: relationProperty.linkSchema,
|
|
3026
|
+
tableName: relationProperty.linkTableName,
|
|
3027
|
+
})} (${queryBuilder.quoteObject(relationProperty.selfIdColumnName)}, ${queryBuilder.quoteObject(relationProperty.targetIdColumnName)})
|
|
2920
3028
|
SELECT $1, $2 WHERE NOT EXISTS (
|
|
2921
3029
|
SELECT ${queryBuilder.quoteObject(relationProperty.selfIdColumnName)}, ${queryBuilder.quoteObject(relationProperty.targetIdColumnName)}
|
|
2922
3030
|
FROM ${queryBuilder.quoteTable({ schema: relationProperty.linkSchema, tableName: relationProperty.linkTableName })}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RpdDataModel, CreateEntityOptions, QuoteTableOptions, DatabaseQuery } from "../types";
|
|
2
|
-
import { CountRowOptions, DeleteRowOptions, FindRowOptions, RowFilterOptions, UpdateRowOptions,
|
|
2
|
+
import { CountRowOptions, DeleteRowOptions, FindRowOptions, RowFilterOptions, UpdateRowOptions, ColumnSelectOptions } from "../dataAccess/dataAccessTypes";
|
|
3
3
|
export interface BuildQueryContext {
|
|
4
4
|
builder: QueryBuilder;
|
|
5
5
|
params: any[];
|
|
@@ -13,7 +13,7 @@ export default class QueryBuilder {
|
|
|
13
13
|
constructor(options: InitQueryBuilderOptions);
|
|
14
14
|
quoteTable(options: QuoteTableOptions): string;
|
|
15
15
|
quoteObject(name: string): string;
|
|
16
|
-
quoteColumn(column:
|
|
16
|
+
quoteColumn(column: ColumnSelectOptions, emitTableAlias: boolean): string;
|
|
17
17
|
select(model: RpdDataModel, options: FindRowOptions): DatabaseQuery;
|
|
18
18
|
selectDerived(derivedModel: RpdDataModel, baseModel: RpdDataModel, options: FindRowOptions): DatabaseQuery;
|
|
19
19
|
count(model: RpdDataModel, options: CountRowOptions): DatabaseQuery;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RouteContext } from "./core/routeContext";
|
|
2
2
|
import { IRpdServer, RapidPlugin } from "./core/server";
|
|
3
|
-
import { CountRowOptions, FindRowOptions } from "./dataAccess/dataAccessTypes";
|
|
3
|
+
import { ColumnSelectOptions, CountRowOptions, FindRowOptions } from "./dataAccess/dataAccessTypes";
|
|
4
4
|
export type RapidServerConfig = {
|
|
5
5
|
baseUrl?: string;
|
|
6
6
|
sessionCookieName: string;
|
|
@@ -355,12 +355,15 @@ export interface FindEntityOptions {
|
|
|
355
355
|
orderBy?: FindEntityOrderByOptions[];
|
|
356
356
|
pagination?: FindEntityPaginationOptions;
|
|
357
357
|
properties?: string[];
|
|
358
|
+
relations?: Record<string, FindEntitySelectRelationOptions>;
|
|
359
|
+
extraColumnsToSelect?: ColumnSelectOptions[];
|
|
358
360
|
keepNonPropertyFields?: boolean;
|
|
359
361
|
}
|
|
360
362
|
export interface FindEntityByIdOptions {
|
|
361
363
|
routeContext?: RouteContext;
|
|
362
364
|
id: any;
|
|
363
365
|
properties?: string[];
|
|
366
|
+
relations?: Record<string, FindEntitySelectRelationOptions>;
|
|
364
367
|
keepNonPropertyFields?: boolean;
|
|
365
368
|
}
|
|
366
369
|
export interface FindEntityRelationalFilterOptions {
|
|
@@ -392,6 +395,10 @@ export interface FindEntityPaginationOptions {
|
|
|
392
395
|
limit: number;
|
|
393
396
|
withoutTotal?: boolean;
|
|
394
397
|
}
|
|
398
|
+
export type FindEntitySelectRelationOptions = true | {
|
|
399
|
+
properties?: string[];
|
|
400
|
+
relations?: Record<string, FindEntitySelectRelationOptions>;
|
|
401
|
+
};
|
|
395
402
|
export interface FindEntityOrderByOptions {
|
|
396
403
|
field: string;
|
|
397
404
|
desc?: boolean;
|
package/package.json
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
|
-
export type RowFilterRelationalOperators =
|
|
1
|
+
export type RowFilterRelationalOperators =
|
|
2
|
+
| "eq"
|
|
3
|
+
| "ne"
|
|
4
|
+
| "lt"
|
|
5
|
+
| "lte"
|
|
6
|
+
| "gt"
|
|
7
|
+
| "gte"
|
|
8
|
+
| "contains"
|
|
9
|
+
| "notContains"
|
|
10
|
+
| "containsCS"
|
|
11
|
+
| "notContainsCS"
|
|
12
|
+
| "startsWith"
|
|
13
|
+
| "notStartsWith"
|
|
14
|
+
| "endsWith"
|
|
15
|
+
| "notEndsWith";
|
|
2
16
|
|
|
3
17
|
export type RowFilterSetOperators = "in" | "notIn";
|
|
4
18
|
|
|
@@ -8,38 +22,47 @@ export type RowFilterUnaryOperators = "null" | "notNull";
|
|
|
8
22
|
|
|
9
23
|
export type RowFilterExistenceOperators = "exists" | "notExists";
|
|
10
24
|
|
|
11
|
-
export type RowFilterOperators =
|
|
25
|
+
export type RowFilterOperators =
|
|
26
|
+
| RowFilterRelationalOperators
|
|
27
|
+
| RowFilterSetOperators
|
|
28
|
+
| RowFilterLogicalOperators
|
|
29
|
+
| RowFilterUnaryOperators
|
|
30
|
+
| RowFilterExistenceOperators;
|
|
12
31
|
|
|
13
|
-
export type RowFilterOptions =
|
|
32
|
+
export type RowFilterOptions =
|
|
33
|
+
| FindRowRelationalFilterOptions
|
|
34
|
+
| FindRowSetFilterOptions
|
|
35
|
+
| FindRowLogicalFilterOptions
|
|
36
|
+
| FindRowUnaryFilterOptions
|
|
37
|
+
| FindRowExistenceFilterOptions;
|
|
14
38
|
|
|
15
39
|
export type RowNonRelationPropertyFilterOptions = FindRowRelationalFilterOptions | FindRowSetFilterOptions | FindRowUnaryFilterOptions;
|
|
16
40
|
|
|
17
|
-
export type
|
|
41
|
+
export type ColumnSelectOptions = string | ColumnNameWithTableName;
|
|
18
42
|
|
|
19
43
|
export type ColumnNameWithTableName = {
|
|
20
44
|
name: string;
|
|
21
45
|
tableName?: string;
|
|
22
|
-
}
|
|
46
|
+
};
|
|
23
47
|
|
|
24
48
|
export interface FindRowOptions {
|
|
25
49
|
filters?: RowFilterOptions[];
|
|
26
50
|
orderBy?: FindRowOrderByOptions[];
|
|
27
51
|
pagination?: FindRowPaginationOptions;
|
|
28
52
|
// TODO: may be `columns` is a better name.
|
|
29
|
-
fields?:
|
|
53
|
+
fields?: ColumnSelectOptions[];
|
|
30
54
|
keepNonPropertyFields?: boolean;
|
|
31
55
|
}
|
|
32
56
|
|
|
33
|
-
|
|
34
57
|
export interface FindRowRelationalFilterOptions {
|
|
35
58
|
// TODO: may be `column` is a better name.
|
|
36
|
-
field:
|
|
59
|
+
field: ColumnSelectOptions;
|
|
37
60
|
operator: RowFilterRelationalOperators;
|
|
38
61
|
value: any;
|
|
39
62
|
}
|
|
40
63
|
|
|
41
64
|
export interface FindRowSetFilterOptions {
|
|
42
|
-
field:
|
|
65
|
+
field: ColumnSelectOptions;
|
|
43
66
|
operator: RowFilterSetOperators;
|
|
44
67
|
value: any[];
|
|
45
68
|
itemType?: string;
|
|
@@ -51,12 +74,12 @@ export interface FindRowLogicalFilterOptions {
|
|
|
51
74
|
}
|
|
52
75
|
|
|
53
76
|
export interface FindRowUnaryFilterOptions {
|
|
54
|
-
field:
|
|
77
|
+
field: ColumnSelectOptions;
|
|
55
78
|
operator: RowFilterUnaryOperators;
|
|
56
79
|
}
|
|
57
80
|
|
|
58
81
|
export interface FindRowExistenceFilterOptions {
|
|
59
|
-
field:
|
|
82
|
+
field: ColumnSelectOptions;
|
|
60
83
|
operator: RowFilterExistenceOperators;
|
|
61
84
|
filters: RowFilterOptions[];
|
|
62
85
|
}
|
|
@@ -68,7 +91,7 @@ export interface FindRowPaginationOptions {
|
|
|
68
91
|
}
|
|
69
92
|
|
|
70
93
|
export interface FindRowOrderByOptions {
|
|
71
|
-
field:
|
|
94
|
+
field: ColumnSelectOptions;
|
|
72
95
|
desc?: boolean;
|
|
73
96
|
}
|
|
74
97
|
|
|
@@ -83,4 +106,4 @@ export interface UpdateRowOptions {
|
|
|
83
106
|
|
|
84
107
|
export interface DeleteRowOptions {
|
|
85
108
|
filters?: RowFilterOptions[];
|
|
86
|
-
}
|
|
109
|
+
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
FindEntityByIdOptions,
|
|
11
11
|
FindEntityOptions,
|
|
12
12
|
FindEntityOrderByOptions,
|
|
13
|
+
FindEntitySelectRelationOptions,
|
|
13
14
|
IRpdDataAccessor,
|
|
14
15
|
RemoveEntityRelationsOptions,
|
|
15
16
|
RpdDataModel,
|
|
@@ -24,10 +25,26 @@ import { IRpdServer, RapidPlugin } from "~/core/server";
|
|
|
24
25
|
import { getEntityPartChanges } from "~/helpers/entityHelpers";
|
|
25
26
|
import { filter, find, first, forEach, isArray, isObject, keys, map, reject, uniq } from "lodash";
|
|
26
27
|
import { getEntityPropertiesIncludingBase, getEntityProperty, getEntityPropertyByCode } from "./metaHelper";
|
|
27
|
-
import {
|
|
28
|
+
import { ColumnSelectOptions, CountRowOptions, FindRowOptions, FindRowOrderByOptions, RowFilterOptions } from "./dataAccessTypes";
|
|
28
29
|
import { newEntityOperationError } from "~/utilities/errorUtility";
|
|
29
30
|
import { getNowStringWithTimezone } from "~/utilities/timeUtility";
|
|
30
31
|
|
|
32
|
+
export type FindOneRelationEntitiesOptions = {
|
|
33
|
+
server: IRpdServer;
|
|
34
|
+
mainModel: RpdDataModel;
|
|
35
|
+
relationProperty: RpdDataModelProperty;
|
|
36
|
+
relationEntityIds: any[];
|
|
37
|
+
selectRelationOptions?: FindEntitySelectRelationOptions;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type FindManyRelationEntitiesOptions = {
|
|
41
|
+
server: IRpdServer;
|
|
42
|
+
mainModel: RpdDataModel;
|
|
43
|
+
relationProperty: RpdDataModelProperty;
|
|
44
|
+
mainEntityIds: any[];
|
|
45
|
+
selectRelationOptions?: FindEntitySelectRelationOptions;
|
|
46
|
+
};
|
|
47
|
+
|
|
31
48
|
function convertEntityOrderByToRowOrderBy(server: IRpdServer, model: RpdDataModel, baseModel?: RpdDataModel, orderByList?: FindEntityOrderByOptions[]) {
|
|
32
49
|
if (!orderByList) {
|
|
33
50
|
return null;
|
|
@@ -66,17 +83,23 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
66
83
|
});
|
|
67
84
|
}
|
|
68
85
|
|
|
69
|
-
let
|
|
86
|
+
let propertiesToSelect: RpdDataModelProperty[];
|
|
87
|
+
let relationOptions = options.relations || {};
|
|
88
|
+
let relationPropertyCodes = Object.keys(relationOptions) || [];
|
|
70
89
|
if (!options.properties || !options.properties.length) {
|
|
71
|
-
|
|
90
|
+
propertiesToSelect = getEntityPropertiesIncludingBase(server, model).filter(
|
|
91
|
+
(property) => !isRelationProperty(property) || relationPropertyCodes.includes(property.code),
|
|
92
|
+
);
|
|
72
93
|
} else {
|
|
73
|
-
|
|
94
|
+
propertiesToSelect = getEntityPropertiesIncludingBase(server, model).filter(
|
|
95
|
+
(property) => options.properties.includes(property.code) || relationPropertyCodes.includes(property.code),
|
|
96
|
+
);
|
|
74
97
|
}
|
|
75
98
|
|
|
76
|
-
const columnsToSelect:
|
|
99
|
+
const columnsToSelect: ColumnSelectOptions[] = [];
|
|
77
100
|
|
|
78
101
|
const relationPropertiesToSelect: RpdDataModelProperty[] = [];
|
|
79
|
-
forEach(
|
|
102
|
+
forEach(propertiesToSelect, (property) => {
|
|
80
103
|
if (isRelationProperty(property)) {
|
|
81
104
|
relationPropertiesToSelect.push(property);
|
|
82
105
|
|
|
@@ -114,7 +137,9 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
114
137
|
|
|
115
138
|
// if `keepNonPropertyFields` is true and `properties` are not specified, then select relation columns automatically.
|
|
116
139
|
if (options.keepNonPropertyFields && (!options.properties || !options.properties.length)) {
|
|
117
|
-
const oneRelationPropertiesWithNoLinkTable = getEntityPropertiesIncludingBase(server, model).filter(
|
|
140
|
+
const oneRelationPropertiesWithNoLinkTable = getEntityPropertiesIncludingBase(server, model).filter(
|
|
141
|
+
(property) => property.relation === "one" && !property.linkTableName,
|
|
142
|
+
);
|
|
118
143
|
oneRelationPropertiesWithNoLinkTable.forEach((property) => {
|
|
119
144
|
if (property.targetIdColumnName) {
|
|
120
145
|
columnsToSelect.push({
|
|
@@ -125,6 +150,30 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
125
150
|
});
|
|
126
151
|
}
|
|
127
152
|
|
|
153
|
+
if (options.extraColumnsToSelect) {
|
|
154
|
+
forEach(options.extraColumnsToSelect, (extraColumnToSelect: ColumnSelectOptions) => {
|
|
155
|
+
const columnSelectOptionExists = find(columnsToSelect, (item: ColumnSelectOptions) => {
|
|
156
|
+
if (typeof item === "string") {
|
|
157
|
+
if (typeof extraColumnToSelect === "string") {
|
|
158
|
+
return item === extraColumnToSelect;
|
|
159
|
+
} else {
|
|
160
|
+
return item == extraColumnToSelect.name;
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
if (typeof extraColumnToSelect === "string") {
|
|
164
|
+
return item.name === extraColumnToSelect;
|
|
165
|
+
} else {
|
|
166
|
+
return item.name == extraColumnToSelect.name;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if (!columnSelectOptionExists) {
|
|
172
|
+
columnsToSelect.push(extraColumnToSelect);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
128
177
|
const rowFilters = await convertEntityFiltersToRowFilters(server, model, baseModel, options.filters);
|
|
129
178
|
const findRowOptions: FindRowOptions = {
|
|
130
179
|
filters: rowFilters,
|
|
@@ -143,24 +192,36 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
143
192
|
const isManyRelation = relationProperty.relation === "many";
|
|
144
193
|
|
|
145
194
|
if (relationProperty.linkTableName) {
|
|
146
|
-
const
|
|
147
|
-
if (!
|
|
195
|
+
const relationModel = server.getModel({ singularCode: relationProperty.targetSingularCode! });
|
|
196
|
+
if (!relationModel) {
|
|
148
197
|
continue;
|
|
149
198
|
}
|
|
150
199
|
|
|
151
200
|
if (isManyRelation) {
|
|
152
|
-
const relationLinks = await findManyRelationLinksViaLinkTable(
|
|
201
|
+
const relationLinks = await findManyRelationLinksViaLinkTable({
|
|
202
|
+
server,
|
|
203
|
+
mainModel: relationModel,
|
|
204
|
+
relationProperty,
|
|
205
|
+
mainEntityIds: entityIds,
|
|
206
|
+
selectRelationOptions: relationOptions[relationProperty.code],
|
|
207
|
+
});
|
|
153
208
|
|
|
154
209
|
forEach(rows, (row: any) => {
|
|
155
210
|
row[relationProperty.code] = filter(relationLinks, (link: any) => {
|
|
156
211
|
return link[relationProperty.selfIdColumnName!] == row["id"];
|
|
157
|
-
}).map((link) => mapDbRowToEntity(server,
|
|
212
|
+
}).map((link) => mapDbRowToEntity(server, relationModel, link.targetEntity, options.keepNonPropertyFields));
|
|
158
213
|
});
|
|
159
214
|
}
|
|
160
215
|
} else {
|
|
161
216
|
let relatedEntities: any[];
|
|
162
217
|
if (isManyRelation) {
|
|
163
|
-
relatedEntities = await findManyRelatedEntitiesViaIdPropertyCode(
|
|
218
|
+
relatedEntities = await findManyRelatedEntitiesViaIdPropertyCode({
|
|
219
|
+
server,
|
|
220
|
+
mainModel: model,
|
|
221
|
+
relationProperty,
|
|
222
|
+
mainEntityIds: entityIds,
|
|
223
|
+
selectRelationOptions: relationOptions[relationProperty.code],
|
|
224
|
+
});
|
|
164
225
|
} else {
|
|
165
226
|
const targetEntityIds = uniq(
|
|
166
227
|
reject(
|
|
@@ -168,7 +229,13 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
168
229
|
isNullOrUndefined,
|
|
169
230
|
),
|
|
170
231
|
);
|
|
171
|
-
relatedEntities = await findOneRelatedEntitiesViaIdPropertyCode(
|
|
232
|
+
relatedEntities = await findOneRelatedEntitiesViaIdPropertyCode({
|
|
233
|
+
server,
|
|
234
|
+
mainModel: model,
|
|
235
|
+
relationProperty,
|
|
236
|
+
relationEntityIds: targetEntityIds,
|
|
237
|
+
selectRelationOptions: relationOptions[relationProperty.code],
|
|
238
|
+
});
|
|
172
239
|
}
|
|
173
240
|
|
|
174
241
|
const targetModel = server.getModel({
|
|
@@ -232,7 +299,12 @@ async function findById(server: IRpdServer, dataAccessor: IRpdDataAccessor, opti
|
|
|
232
299
|
});
|
|
233
300
|
}
|
|
234
301
|
|
|
235
|
-
async function convertEntityFiltersToRowFilters(
|
|
302
|
+
async function convertEntityFiltersToRowFilters(
|
|
303
|
+
server: IRpdServer,
|
|
304
|
+
model: RpdDataModel,
|
|
305
|
+
baseModel: RpdDataModel,
|
|
306
|
+
filters: EntityFilterOptions[] | undefined,
|
|
307
|
+
): Promise<RowFilterOptions[]> {
|
|
236
308
|
if (!filters || !filters.length) {
|
|
237
309
|
return [];
|
|
238
310
|
}
|
|
@@ -251,7 +323,9 @@ async function convertEntityFiltersToRowFilters(server: IRpdServer, model: RpdDa
|
|
|
251
323
|
throw new Error(`Invalid filters. Property '${filter.field}' was not found in model '${model.namespace}.${model.singularCode}'`);
|
|
252
324
|
}
|
|
253
325
|
if (!isRelationProperty(relationProperty)) {
|
|
254
|
-
throw new Error(
|
|
326
|
+
throw new Error(
|
|
327
|
+
`Invalid filters. Filter with 'existence' operator on property '${filter.field}' is not allowed. You can only use it on an relation property.`,
|
|
328
|
+
);
|
|
255
329
|
}
|
|
256
330
|
|
|
257
331
|
const relatedEntityFilters = filter.filters;
|
|
@@ -382,7 +456,10 @@ async function convertEntityFiltersToRowFilters(server: IRpdServer, model: RpdDa
|
|
|
382
456
|
});
|
|
383
457
|
const targetEntityIds = map(targetEntities, (entity: any) => entity["id"]);
|
|
384
458
|
|
|
385
|
-
const command = `SELECT * FROM ${server.queryBuilder.quoteTable({
|
|
459
|
+
const command = `SELECT * FROM ${server.queryBuilder.quoteTable({
|
|
460
|
+
schema: relationProperty.linkSchema,
|
|
461
|
+
tableName: relationProperty.linkTableName!,
|
|
462
|
+
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.targetIdColumnName!)} = ANY($1::int[])`;
|
|
386
463
|
const params = [targetEntityIds];
|
|
387
464
|
const links = await server.queryDatabaseObject(command, params);
|
|
388
465
|
const selfEntityIds = links.map((link) => link[relationProperty.selfIdColumnName!]);
|
|
@@ -439,27 +516,44 @@ async function convertEntityFiltersToRowFilters(server: IRpdServer, model: RpdDa
|
|
|
439
516
|
return replacedFilters;
|
|
440
517
|
}
|
|
441
518
|
|
|
442
|
-
async function findManyRelationLinksViaLinkTable(
|
|
443
|
-
const
|
|
444
|
-
const
|
|
519
|
+
async function findManyRelationLinksViaLinkTable(options: FindManyRelationEntitiesOptions) {
|
|
520
|
+
const { server, relationProperty, mainModel: relationModel, mainEntityIds, selectRelationOptions } = options;
|
|
521
|
+
const command = `SELECT * FROM ${server.queryBuilder.quoteTable({
|
|
522
|
+
schema: relationProperty.linkSchema,
|
|
523
|
+
tableName: relationProperty.linkTableName!,
|
|
524
|
+
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName!)} = ANY($1::int[])`;
|
|
525
|
+
const params = [mainEntityIds];
|
|
445
526
|
const links = await server.queryDatabaseObject(command, params);
|
|
446
527
|
const targetEntityIds = links.map((link) => link[relationProperty.targetIdColumnName!]);
|
|
447
|
-
|
|
528
|
+
|
|
529
|
+
const dataAccessor = server.getDataAccessor({
|
|
530
|
+
namespace: relationModel.namespace,
|
|
531
|
+
singularCode: relationModel.singularCode,
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
const findEntityOptions: FindEntityOptions = {
|
|
448
535
|
filters: [
|
|
449
536
|
{
|
|
450
|
-
field:
|
|
451
|
-
name: "id",
|
|
452
|
-
},
|
|
537
|
+
field: "id",
|
|
453
538
|
operator: "in",
|
|
454
539
|
value: targetEntityIds,
|
|
455
540
|
},
|
|
456
541
|
],
|
|
542
|
+
keepNonPropertyFields: true,
|
|
457
543
|
};
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
544
|
+
|
|
545
|
+
if (selectRelationOptions) {
|
|
546
|
+
if (typeof selectRelationOptions !== "boolean") {
|
|
547
|
+
if (selectRelationOptions.properties) {
|
|
548
|
+
findEntityOptions.properties = ["id", ...selectRelationOptions.properties];
|
|
549
|
+
}
|
|
550
|
+
if (selectRelationOptions.relations) {
|
|
551
|
+
findEntityOptions.relations = selectRelationOptions.relations;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const targetEntities = await findEntities(server, dataAccessor, findEntityOptions);
|
|
463
557
|
|
|
464
558
|
forEach(links, (link: any) => {
|
|
465
559
|
link.targetEntity = find(targetEntities, (e: any) => e["id"] == link[relationProperty.targetIdColumnName!]);
|
|
@@ -468,42 +562,68 @@ async function findManyRelationLinksViaLinkTable(server: IRpdServer, targetModel
|
|
|
468
562
|
return links;
|
|
469
563
|
}
|
|
470
564
|
|
|
471
|
-
function findManyRelatedEntitiesViaIdPropertyCode(
|
|
472
|
-
const
|
|
565
|
+
async function findManyRelatedEntitiesViaIdPropertyCode(options: FindManyRelationEntitiesOptions) {
|
|
566
|
+
const { server, relationProperty, mainEntityIds, selectRelationOptions } = options;
|
|
567
|
+
const dataAccessor = server.getDataAccessor({
|
|
568
|
+
singularCode: relationProperty.targetSingularCode as string,
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
const findEntityOptions: FindEntityOptions = {
|
|
473
572
|
filters: [
|
|
474
573
|
{
|
|
475
|
-
field:
|
|
476
|
-
name: relationProperty.selfIdColumnName,
|
|
477
|
-
},
|
|
574
|
+
field: relationProperty.selfIdColumnName,
|
|
478
575
|
operator: "in",
|
|
479
|
-
value:
|
|
576
|
+
value: mainEntityIds,
|
|
480
577
|
},
|
|
481
578
|
],
|
|
579
|
+
extraColumnsToSelect: [relationProperty.selfIdColumnName],
|
|
580
|
+
keepNonPropertyFields: true,
|
|
482
581
|
};
|
|
582
|
+
|
|
583
|
+
if (selectRelationOptions) {
|
|
584
|
+
if (typeof selectRelationOptions !== "boolean") {
|
|
585
|
+
if (selectRelationOptions.properties) {
|
|
586
|
+
findEntityOptions.properties = ["id", ...selectRelationOptions.properties];
|
|
587
|
+
}
|
|
588
|
+
if (selectRelationOptions.relations) {
|
|
589
|
+
findEntityOptions.relations = selectRelationOptions.relations;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return await findEntities(server, dataAccessor, findEntityOptions);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
async function findOneRelatedEntitiesViaIdPropertyCode(options: FindOneRelationEntitiesOptions) {
|
|
598
|
+
const { server, relationProperty, relationEntityIds, selectRelationOptions } = options;
|
|
599
|
+
|
|
483
600
|
const dataAccessor = server.getDataAccessor({
|
|
484
601
|
singularCode: relationProperty.targetSingularCode as string,
|
|
485
602
|
});
|
|
486
603
|
|
|
487
|
-
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
function findOneRelatedEntitiesViaIdPropertyCode(server: IRpdServer, model: RpdDataModel, relationProperty: RpdDataModelProperty, targetEntityIds: any[]) {
|
|
491
|
-
const findEntityOptions: FindRowOptions = {
|
|
604
|
+
const findEntityOptions: FindEntityOptions = {
|
|
492
605
|
filters: [
|
|
493
606
|
{
|
|
494
|
-
field:
|
|
495
|
-
name: "id",
|
|
496
|
-
},
|
|
607
|
+
field: "id",
|
|
497
608
|
operator: "in",
|
|
498
|
-
value:
|
|
609
|
+
value: relationEntityIds,
|
|
499
610
|
},
|
|
500
611
|
],
|
|
612
|
+
keepNonPropertyFields: true,
|
|
501
613
|
};
|
|
502
|
-
const dataAccessor = server.getDataAccessor({
|
|
503
|
-
singularCode: relationProperty.targetSingularCode as string,
|
|
504
|
-
});
|
|
505
614
|
|
|
506
|
-
|
|
615
|
+
if (selectRelationOptions) {
|
|
616
|
+
if (typeof selectRelationOptions !== "boolean") {
|
|
617
|
+
if (selectRelationOptions.properties) {
|
|
618
|
+
findEntityOptions.properties = ["id", ...selectRelationOptions.properties];
|
|
619
|
+
}
|
|
620
|
+
if (selectRelationOptions.relations) {
|
|
621
|
+
findEntityOptions.relations = selectRelationOptions.relations;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
return await findEntities(server, dataAccessor, findEntityOptions);
|
|
507
627
|
}
|
|
508
628
|
|
|
509
629
|
async function createEntity(server: IRpdServer, dataAccessor: IRpdDataAccessor, options: CreateEntityOptions, plugin?: RapidPlugin) {
|
|
@@ -514,7 +634,7 @@ async function createEntity(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
514
634
|
|
|
515
635
|
const { entity, routeContext } = options;
|
|
516
636
|
|
|
517
|
-
const userId = options.routeContext
|
|
637
|
+
const userId = options.routeContext?.state?.userId;
|
|
518
638
|
if (userId) {
|
|
519
639
|
const createdByProperty = getEntityPropertyByCode(server, model, "createdBy");
|
|
520
640
|
if (createdByProperty) {
|
|
@@ -583,7 +703,9 @@ async function createEntity(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
583
703
|
routeContext,
|
|
584
704
|
});
|
|
585
705
|
if (!targetEntity) {
|
|
586
|
-
throw newEntityOperationError(
|
|
706
|
+
throw newEntityOperationError(
|
|
707
|
+
`Create ${model.singularCode} entity failed. Property '${property.code}' was invalid. Related ${property.targetSingularCode} entity with id '${targetEntityId}' was not found.`,
|
|
708
|
+
);
|
|
587
709
|
}
|
|
588
710
|
newEntityOneRelationProps[property.code] = targetEntity;
|
|
589
711
|
targetRow[property.targetIdColumnName!] = targetEntityId;
|
|
@@ -596,7 +718,9 @@ async function createEntity(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
596
718
|
routeContext,
|
|
597
719
|
});
|
|
598
720
|
if (!targetEntity) {
|
|
599
|
-
throw newEntityOperationError(
|
|
721
|
+
throw newEntityOperationError(
|
|
722
|
+
`Create ${model.singularCode} entity failed. Property '${property.code}' was invalid. Related ${property.targetSingularCode} entity with id '${targetEntityId}' was not found.`,
|
|
723
|
+
);
|
|
600
724
|
}
|
|
601
725
|
newEntityOneRelationProps[property.code] = targetEntity;
|
|
602
726
|
targetRow[property.targetIdColumnName!] = targetEntityId;
|
|
@@ -643,7 +767,10 @@ async function createEntity(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
643
767
|
});
|
|
644
768
|
|
|
645
769
|
if (property.linkTableName) {
|
|
646
|
-
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
770
|
+
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
771
|
+
schema: property.linkSchema,
|
|
772
|
+
tableName: property.linkTableName,
|
|
773
|
+
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName!)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
647
774
|
const params = [newEntity.id, newTargetEntity.id];
|
|
648
775
|
await server.queryDatabaseObject(command, params);
|
|
649
776
|
}
|
|
@@ -657,7 +784,10 @@ async function createEntity(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
657
784
|
}
|
|
658
785
|
|
|
659
786
|
if (property.linkTableName) {
|
|
660
|
-
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
787
|
+
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
788
|
+
schema: property.linkSchema,
|
|
789
|
+
tableName: property.linkTableName,
|
|
790
|
+
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName!)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
661
791
|
const params = [newEntity.id, relatedEntityId];
|
|
662
792
|
await server.queryDatabaseObject(command, params);
|
|
663
793
|
} else {
|
|
@@ -675,7 +805,10 @@ async function createEntity(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
675
805
|
}
|
|
676
806
|
|
|
677
807
|
if (property.linkTableName) {
|
|
678
|
-
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
808
|
+
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
809
|
+
schema: property.linkSchema,
|
|
810
|
+
tableName: property.linkTableName,
|
|
811
|
+
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName!)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
679
812
|
const params = [newEntity.id, relatedEntityId];
|
|
680
813
|
await server.queryDatabaseObject(command, params);
|
|
681
814
|
} else {
|
|
@@ -726,7 +859,7 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
|
|
|
726
859
|
|
|
727
860
|
entityToSave = changes || {};
|
|
728
861
|
|
|
729
|
-
const userId = options.routeContext
|
|
862
|
+
const userId = options.routeContext?.state?.userId;
|
|
730
863
|
if (userId) {
|
|
731
864
|
const updatedByProperty = getEntityPropertyByCode(server, model, "updatedBy");
|
|
732
865
|
if (updatedByProperty) {
|
|
@@ -799,7 +932,9 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
|
|
|
799
932
|
routeContext,
|
|
800
933
|
});
|
|
801
934
|
if (!targetEntity) {
|
|
802
|
-
throw newEntityOperationError(
|
|
935
|
+
throw newEntityOperationError(
|
|
936
|
+
`Create ${model.singularCode} entity failed. Property '${property.code}' was invalid. Related ${property.targetSingularCode} entity with id '${targetEntityId}' was not found.`,
|
|
937
|
+
);
|
|
803
938
|
}
|
|
804
939
|
updatedEntityOneRelationProps[property.code] = targetEntity;
|
|
805
940
|
targetRow[property.targetIdColumnName!] = targetEntityId;
|
|
@@ -812,7 +947,9 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
|
|
|
812
947
|
routeContext,
|
|
813
948
|
});
|
|
814
949
|
if (!targetEntity) {
|
|
815
|
-
throw newEntityOperationError(
|
|
950
|
+
throw newEntityOperationError(
|
|
951
|
+
`Create ${model.singularCode} entity failed. Property '${property.code}' was invalid. Related ${property.targetSingularCode} entity with id '${targetEntityId}' was not found.`,
|
|
952
|
+
);
|
|
816
953
|
}
|
|
817
954
|
updatedEntityOneRelationProps[property.code] = targetEntity;
|
|
818
955
|
targetRow[property.targetIdColumnName!] = targetEntityId;
|
|
@@ -848,7 +985,13 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
|
|
|
848
985
|
|
|
849
986
|
if (property.linkTableName) {
|
|
850
987
|
// TODO: should support removing relation
|
|
851
|
-
await server.queryDatabaseObject(
|
|
988
|
+
await server.queryDatabaseObject(
|
|
989
|
+
`DELETE FROM ${server.queryBuilder.quoteTable({
|
|
990
|
+
schema: property.linkSchema,
|
|
991
|
+
tableName: property.linkTableName,
|
|
992
|
+
})} WHERE ${server.queryBuilder.quoteObject(property.selfIdColumnName!)} = $1`,
|
|
993
|
+
[id],
|
|
994
|
+
);
|
|
852
995
|
}
|
|
853
996
|
|
|
854
997
|
for (const relatedEntityToBeSaved of relatedEntitiesToBeSaved) {
|
|
@@ -866,7 +1009,10 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
|
|
|
866
1009
|
});
|
|
867
1010
|
|
|
868
1011
|
if (property.linkTableName) {
|
|
869
|
-
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
1012
|
+
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
1013
|
+
schema: property.linkSchema,
|
|
1014
|
+
tableName: property.linkTableName,
|
|
1015
|
+
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName!)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
870
1016
|
const params = [id, newTargetEntity.id];
|
|
871
1017
|
await server.queryDatabaseObject(command, params);
|
|
872
1018
|
}
|
|
@@ -880,7 +1026,10 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
|
|
|
880
1026
|
}
|
|
881
1027
|
|
|
882
1028
|
if (property.linkTableName) {
|
|
883
|
-
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
1029
|
+
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
1030
|
+
schema: property.linkSchema,
|
|
1031
|
+
tableName: property.linkTableName,
|
|
1032
|
+
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName!)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
884
1033
|
const params = [id, relatedEntityId];
|
|
885
1034
|
await server.queryDatabaseObject(command, params);
|
|
886
1035
|
} else {
|
|
@@ -898,7 +1047,10 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
|
|
|
898
1047
|
}
|
|
899
1048
|
|
|
900
1049
|
if (property.linkTableName) {
|
|
901
|
-
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
1050
|
+
const command = `INSERT INTO ${server.queryBuilder.quoteTable({
|
|
1051
|
+
schema: property.linkSchema,
|
|
1052
|
+
tableName: property.linkTableName,
|
|
1053
|
+
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName!)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
902
1054
|
const params = [id, relatedEntityId];
|
|
903
1055
|
await server.queryDatabaseObject(command, params);
|
|
904
1056
|
} else {
|
|
@@ -1065,7 +1217,10 @@ export default class EntityManager<TEntity = any> {
|
|
|
1065
1217
|
const { queryBuilder } = server;
|
|
1066
1218
|
if (relationProperty.linkTableName) {
|
|
1067
1219
|
for (const relation of relations) {
|
|
1068
|
-
const command = `INSERT INTO ${queryBuilder.quoteTable({
|
|
1220
|
+
const command = `INSERT INTO ${queryBuilder.quoteTable({
|
|
1221
|
+
schema: relationProperty.linkSchema,
|
|
1222
|
+
tableName: relationProperty.linkTableName,
|
|
1223
|
+
})} (${queryBuilder.quoteObject(relationProperty.selfIdColumnName!)}, ${queryBuilder.quoteObject(relationProperty.targetIdColumnName!)})
|
|
1069
1224
|
SELECT $1, $2 WHERE NOT EXISTS (
|
|
1070
1225
|
SELECT ${queryBuilder.quoteObject(relationProperty.selfIdColumnName!)}, ${queryBuilder.quoteObject(relationProperty.targetIdColumnName!)}
|
|
1071
1226
|
FROM ${queryBuilder.quoteTable({ schema: relationProperty.linkSchema, tableName: relationProperty.linkTableName })}
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import { find } from "lodash";
|
|
2
|
-
import {
|
|
3
|
-
RpdDataModel,
|
|
4
|
-
RpdDataModelProperty,
|
|
5
|
-
CreateEntityOptions,
|
|
6
|
-
QuoteTableOptions,
|
|
7
|
-
DatabaseQuery,
|
|
8
|
-
} from "../types";
|
|
2
|
+
import { RpdDataModel, RpdDataModelProperty, CreateEntityOptions, QuoteTableOptions, DatabaseQuery } from "../types";
|
|
9
3
|
import {
|
|
10
4
|
CountRowOptions,
|
|
11
5
|
DeleteRowOptions,
|
|
@@ -17,7 +11,7 @@ import {
|
|
|
17
11
|
RowFilterOptions,
|
|
18
12
|
RowFilterRelationalOperators,
|
|
19
13
|
UpdateRowOptions,
|
|
20
|
-
|
|
14
|
+
ColumnSelectOptions,
|
|
21
15
|
} from "~/dataAccess/dataAccessTypes";
|
|
22
16
|
|
|
23
17
|
const objLeftQuoteChar = '"';
|
|
@@ -64,7 +58,7 @@ export default class QueryBuilder {
|
|
|
64
58
|
return `${objLeftQuoteChar}${name}${objRightQuoteChar}`;
|
|
65
59
|
}
|
|
66
60
|
|
|
67
|
-
quoteColumn(column:
|
|
61
|
+
quoteColumn(column: ColumnSelectOptions, emitTableAlias: boolean) {
|
|
68
62
|
if (typeof column === "string") {
|
|
69
63
|
return `${objLeftQuoteChar}${column}${objRightQuoteChar}`;
|
|
70
64
|
} else if (emitTableAlias && column.tableName) {
|
|
@@ -100,7 +94,7 @@ export default class QueryBuilder {
|
|
|
100
94
|
command += " ORDER BY ";
|
|
101
95
|
command += orderBy
|
|
102
96
|
.map((item) => {
|
|
103
|
-
const quotedName = this.quoteColumn(item.field,
|
|
97
|
+
const quotedName = this.quoteColumn(item.field, ctx.emitTableAlias);
|
|
104
98
|
return item.desc ? quotedName + " DESC" : quotedName;
|
|
105
99
|
})
|
|
106
100
|
.join(", ");
|
|
@@ -133,13 +127,17 @@ export default class QueryBuilder {
|
|
|
133
127
|
if (!columns || !columns.length) {
|
|
134
128
|
command += `${this.quoteObject(derivedModel.tableName)}.* FROM `;
|
|
135
129
|
} else {
|
|
136
|
-
command += columns
|
|
137
|
-
|
|
138
|
-
|
|
130
|
+
command += columns
|
|
131
|
+
.map((column) => {
|
|
132
|
+
return this.quoteColumn(column, ctx.emitTableAlias);
|
|
133
|
+
})
|
|
134
|
+
.join(", ");
|
|
139
135
|
command += " FROM ";
|
|
140
136
|
}
|
|
141
137
|
|
|
142
|
-
command += `${this.quoteTable(derivedModel)} LEFT JOIN ${this.quoteTable(baseModel)} ON ${this.quoteObject(derivedModel.tableName)}.id = ${this.quoteObject(
|
|
138
|
+
command += `${this.quoteTable(derivedModel)} LEFT JOIN ${this.quoteTable(baseModel)} ON ${this.quoteObject(derivedModel.tableName)}.id = ${this.quoteObject(
|
|
139
|
+
baseModel.tableName,
|
|
140
|
+
)}.id`;
|
|
143
141
|
|
|
144
142
|
if (filters && filters.length) {
|
|
145
143
|
command += " WHERE ";
|
|
@@ -203,7 +201,9 @@ export default class QueryBuilder {
|
|
|
203
201
|
let { filters } = options;
|
|
204
202
|
let command = 'SELECT COUNT(*)::int as "count" FROM ';
|
|
205
203
|
|
|
206
|
-
command += `${this.quoteTable(derivedModel)} LEFT JOIN ${this.quoteTable(baseModel)} ON ${this.quoteObject(derivedModel.tableName)}.id = ${this.quoteObject(
|
|
204
|
+
command += `${this.quoteTable(derivedModel)} LEFT JOIN ${this.quoteTable(baseModel)} ON ${this.quoteObject(derivedModel.tableName)}.id = ${this.quoteObject(
|
|
205
|
+
baseModel.tableName,
|
|
206
|
+
)}.id`;
|
|
207
207
|
|
|
208
208
|
if (filters && filters.length) {
|
|
209
209
|
command += " WHERE ";
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RouteContext } from "./core/routeContext";
|
|
2
2
|
import { IRpdServer, RapidPlugin } from "./core/server";
|
|
3
|
-
import { CountRowOptions, FindRowOptions } from "./dataAccess/dataAccessTypes";
|
|
3
|
+
import { ColumnSelectOptions, CountRowOptions, FindRowOptions } from "./dataAccess/dataAccessTypes";
|
|
4
4
|
|
|
5
5
|
export type RapidServerConfig = {
|
|
6
6
|
baseUrl?: string;
|
|
@@ -296,7 +296,21 @@ export interface RpdDataModelProperty {
|
|
|
296
296
|
linkSchema?: string;
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
-
export type RpdDataPropertyTypes =
|
|
299
|
+
export type RpdDataPropertyTypes =
|
|
300
|
+
| "integer"
|
|
301
|
+
| "long"
|
|
302
|
+
| "float"
|
|
303
|
+
| "double"
|
|
304
|
+
| "decimal"
|
|
305
|
+
| "text"
|
|
306
|
+
| "boolean"
|
|
307
|
+
| "date"
|
|
308
|
+
| "time"
|
|
309
|
+
| "datetime"
|
|
310
|
+
| "json"
|
|
311
|
+
| "relation"
|
|
312
|
+
| "relation[]"
|
|
313
|
+
| "option";
|
|
300
314
|
|
|
301
315
|
/**
|
|
302
316
|
* 数据字典
|
|
@@ -388,7 +402,21 @@ export interface IRpdDataAccessor<T = any> {
|
|
|
388
402
|
deleteById(id: any): Promise<void>;
|
|
389
403
|
}
|
|
390
404
|
|
|
391
|
-
export type EntityFilterRelationalOperators =
|
|
405
|
+
export type EntityFilterRelationalOperators =
|
|
406
|
+
| "eq"
|
|
407
|
+
| "ne"
|
|
408
|
+
| "lt"
|
|
409
|
+
| "lte"
|
|
410
|
+
| "gt"
|
|
411
|
+
| "gte"
|
|
412
|
+
| "contains"
|
|
413
|
+
| "notContains"
|
|
414
|
+
| "containsCS"
|
|
415
|
+
| "notContainsCS"
|
|
416
|
+
| "startsWith"
|
|
417
|
+
| "notStartsWith"
|
|
418
|
+
| "endsWith"
|
|
419
|
+
| "notEndsWith";
|
|
392
420
|
|
|
393
421
|
export type EntityFilterSetOperators = "in" | "notIn";
|
|
394
422
|
|
|
@@ -398,9 +426,19 @@ export type EntityFilterUnaryOperators = "null" | "notNull";
|
|
|
398
426
|
|
|
399
427
|
export type EntityFilterExistenceOperators = "exists" | "notExists";
|
|
400
428
|
|
|
401
|
-
export type EntityFilterOperators =
|
|
429
|
+
export type EntityFilterOperators =
|
|
430
|
+
| EntityFilterRelationalOperators
|
|
431
|
+
| EntityFilterSetOperators
|
|
432
|
+
| EntityFilterLogicalOperators
|
|
433
|
+
| EntityFilterUnaryOperators
|
|
434
|
+
| EntityFilterExistenceOperators;
|
|
402
435
|
|
|
403
|
-
export type EntityFilterOptions =
|
|
436
|
+
export type EntityFilterOptions =
|
|
437
|
+
| FindEntityRelationalFilterOptions
|
|
438
|
+
| FindEntitySetFilterOptions
|
|
439
|
+
| FindEntityLogicalFilterOptions
|
|
440
|
+
| FindEntityUnaryFilterOptions
|
|
441
|
+
| FindEntityExistenceFilterOptions;
|
|
404
442
|
|
|
405
443
|
export type EntityNonRelationPropertyFilterOptions = FindEntityRelationalFilterOptions | FindEntitySetFilterOptions | FindEntityUnaryFilterOptions;
|
|
406
444
|
|
|
@@ -410,6 +448,8 @@ export interface FindEntityOptions {
|
|
|
410
448
|
orderBy?: FindEntityOrderByOptions[];
|
|
411
449
|
pagination?: FindEntityPaginationOptions;
|
|
412
450
|
properties?: string[];
|
|
451
|
+
relations?: Record<string, FindEntitySelectRelationOptions>;
|
|
452
|
+
extraColumnsToSelect?: ColumnSelectOptions[];
|
|
413
453
|
keepNonPropertyFields?: boolean;
|
|
414
454
|
}
|
|
415
455
|
|
|
@@ -417,6 +457,7 @@ export interface FindEntityByIdOptions {
|
|
|
417
457
|
routeContext?: RouteContext;
|
|
418
458
|
id: any;
|
|
419
459
|
properties?: string[];
|
|
460
|
+
relations?: Record<string, FindEntitySelectRelationOptions>;
|
|
420
461
|
keepNonPropertyFields?: boolean;
|
|
421
462
|
}
|
|
422
463
|
|
|
@@ -455,6 +496,13 @@ export interface FindEntityPaginationOptions {
|
|
|
455
496
|
withoutTotal?: boolean;
|
|
456
497
|
}
|
|
457
498
|
|
|
499
|
+
export type FindEntitySelectRelationOptions =
|
|
500
|
+
| true
|
|
501
|
+
| {
|
|
502
|
+
properties?: string[];
|
|
503
|
+
relations?: Record<string, FindEntitySelectRelationOptions>;
|
|
504
|
+
};
|
|
505
|
+
|
|
458
506
|
export interface FindEntityOrderByOptions {
|
|
459
507
|
field: string;
|
|
460
508
|
desc?: boolean;
|
|
@@ -514,7 +562,13 @@ export interface RemoveEntityRelationsOptions {
|
|
|
514
562
|
relations: { id?: number; [k: string]: any }[];
|
|
515
563
|
}
|
|
516
564
|
|
|
517
|
-
export type EntityWatcherType =
|
|
565
|
+
export type EntityWatcherType =
|
|
566
|
+
| EntityWatcher<"entity.create">
|
|
567
|
+
| EntityWatcher<"entity.update">
|
|
568
|
+
| EntityWatcher<"entity.delete">
|
|
569
|
+
| EntityWatcher<"entity.addRelations">
|
|
570
|
+
| EntityWatcher<"entity.removeRelations">
|
|
571
|
+
| EntityWatcher<any>;
|
|
518
572
|
|
|
519
573
|
export interface EntityWatcher<TEventName extends keyof RpdServerEventTypes = any> {
|
|
520
574
|
eventName: TEventName;
|