@ruiapp/rapid-core 0.2.1 → 0.2.3
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/entityManager.d.ts +3 -3
- package/dist/helpers/filterHelper.d.ts +4 -1
- package/dist/helpers/metaHelper.d.ts +2 -0
- package/dist/index.js +156 -40
- package/dist/types.d.ts +14 -4
- package/package.json +1 -1
- package/src/dataAccess/entityManager.ts +37 -6
- package/src/helpers/filterHelper.ts +89 -1
- package/src/helpers/metaHelper.ts +9 -0
- package/src/plugins/metaManage/MetaManagePlugin.ts +4 -1
- package/src/types.ts +18 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AddEntityRelationsOptions, CountEntityOptions, CountEntityResult, CreateEntityOptions, DeleteEntityByIdOptions, FindEntityByIdOptions, FindEntityOptions,
|
|
1
|
+
import { AddEntityRelationsOptions, CountEntityOptions, CountEntityResult, CreateEntityOptions, DeleteEntityByIdOptions, FindEntityByIdOptions, FindEntityOptions, IRpdDataAccessor, RemoveEntityRelationsOptions, RpdDataModel, RpdDataModelIndex, RpdDataModelProperty, UpdateEntityByIdOptions, FindEntityFindOneRelationEntitiesOptions, FindEntityFindManyRelationEntitiesOptions } from "../types";
|
|
2
2
|
import { IRpdServer, RapidPlugin } from "../core/server";
|
|
3
3
|
import { RouteContext } from "../core/routeContext";
|
|
4
4
|
export type FindOneRelationEntitiesOptions = {
|
|
@@ -6,14 +6,14 @@ export type FindOneRelationEntitiesOptions = {
|
|
|
6
6
|
mainModel: RpdDataModel;
|
|
7
7
|
relationProperty: RpdDataModelProperty;
|
|
8
8
|
relationEntityIds: any[];
|
|
9
|
-
selectRelationOptions?:
|
|
9
|
+
selectRelationOptions?: FindEntityFindOneRelationEntitiesOptions;
|
|
10
10
|
};
|
|
11
11
|
export type FindManyRelationEntitiesOptions = {
|
|
12
12
|
server: IRpdServer;
|
|
13
13
|
mainModel: RpdDataModel;
|
|
14
14
|
relationProperty: RpdDataModelProperty;
|
|
15
15
|
mainEntityIds: any[];
|
|
16
|
-
selectRelationOptions?:
|
|
16
|
+
selectRelationOptions?: FindEntityFindManyRelationEntitiesOptions;
|
|
17
17
|
};
|
|
18
18
|
export type CheckEntityDuplicatedOptions = {
|
|
19
19
|
routeContext?: RouteContext;
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
import { EntityFilterOptions } from "../types";
|
|
1
|
+
import { EntityFilterOptions, RpdDataModel, RpdDataModelIndexOptions } from "../types";
|
|
2
|
+
import { RowFilterOptions } from "../dataAccess/dataAccessTypes";
|
|
2
3
|
export declare function removeFiltersWithNullValue(filters?: EntityFilterOptions[]): EntityFilterOptions[];
|
|
4
|
+
export declare function convertModelIndexConditionsToRowFilterOptions(model: RpdDataModel, filters: RpdDataModelIndexOptions[]): RowFilterOptions[];
|
|
5
|
+
export declare function replaceModelIndexConditionEntityPropertyWithTableColumn(model: RpdDataModel, filter: RpdDataModelIndexOptions): RowFilterOptions;
|
|
@@ -7,4 +7,6 @@ export declare function getEntityProperties(server: IRpdServer, model: RpdDataMo
|
|
|
7
7
|
export declare function getEntityPropertiesIncludingBase(server: IRpdServer, model: RpdDataModel): RpdDataModelProperty[];
|
|
8
8
|
export declare function getEntityPropertyByCode(server: IRpdServer, model: RpdDataModel, propertyCode: string): RpdDataModelProperty | undefined;
|
|
9
9
|
export declare function getEntityProperty(server: IRpdServer, model: RpdDataModel, predicate: (item: RpdDataModelProperty) => boolean): RpdDataModelProperty | undefined;
|
|
10
|
+
export declare function getEntityOwnPropertyByCode(model: RpdDataModel, propertyCode: string): RpdDataModelProperty | undefined;
|
|
11
|
+
export declare function getEntityOwnProperty(model: RpdDataModel, predicate: (item: RpdDataModelProperty) => boolean): RpdDataModelProperty | undefined;
|
|
10
12
|
export declare function getEntityPropertyByFieldName(server: IRpdServer, model: RpdDataModel, fieldName: string): RpdDataModelProperty | undefined;
|
package/dist/index.js
CHANGED
|
@@ -2064,6 +2064,13 @@ function getEntityProperty(server, model, predicate) {
|
|
|
2064
2064
|
}
|
|
2065
2065
|
return property;
|
|
2066
2066
|
}
|
|
2067
|
+
function getEntityOwnPropertyByCode(model, propertyCode) {
|
|
2068
|
+
return getEntityOwnProperty(model, (e) => e.code === propertyCode);
|
|
2069
|
+
}
|
|
2070
|
+
function getEntityOwnProperty(model, predicate) {
|
|
2071
|
+
let property = model.properties.find(predicate);
|
|
2072
|
+
return property;
|
|
2073
|
+
}
|
|
2067
2074
|
function getEntityPropertyByFieldName(server, model, fieldName) {
|
|
2068
2075
|
let property = getEntityPropertyByCode(server, model, fieldName);
|
|
2069
2076
|
if (!property) {
|
|
@@ -2748,7 +2755,9 @@ async function findManyRelationLinksViaLinkTable(options) {
|
|
|
2748
2755
|
const command = `SELECT * FROM ${server.queryBuilder.quoteTable({
|
|
2749
2756
|
schema: relationProperty.linkSchema,
|
|
2750
2757
|
tableName: relationProperty.linkTableName,
|
|
2751
|
-
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName)} = ANY($1::int[])
|
|
2758
|
+
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName)} = ANY($1::int[])
|
|
2759
|
+
ORDER BY id
|
|
2760
|
+
`;
|
|
2752
2761
|
const params = [mainEntityIds];
|
|
2753
2762
|
const links = await server.queryDatabaseObject(command, params);
|
|
2754
2763
|
const targetEntityIds = links.map((link) => link[relationProperty.targetIdColumnName]);
|
|
@@ -2774,6 +2783,18 @@ async function findManyRelationLinksViaLinkTable(options) {
|
|
|
2774
2783
|
if (selectRelationOptions.relations) {
|
|
2775
2784
|
findEntityOptions.relations = selectRelationOptions.relations;
|
|
2776
2785
|
}
|
|
2786
|
+
if (selectRelationOptions.orderBy) {
|
|
2787
|
+
findEntityOptions.orderBy = selectRelationOptions.orderBy;
|
|
2788
|
+
}
|
|
2789
|
+
if (selectRelationOptions.pagination) {
|
|
2790
|
+
findEntityOptions.pagination = selectRelationOptions.pagination;
|
|
2791
|
+
}
|
|
2792
|
+
if (selectRelationOptions.filters) {
|
|
2793
|
+
findEntityOptions.filters = [...findEntityOptions.filters, ...selectRelationOptions.filters];
|
|
2794
|
+
}
|
|
2795
|
+
if (!lodash.isUndefined(selectRelationOptions.keepNonPropertyFields)) {
|
|
2796
|
+
findEntityOptions.keepNonPropertyFields = selectRelationOptions.keepNonPropertyFields;
|
|
2797
|
+
}
|
|
2777
2798
|
}
|
|
2778
2799
|
}
|
|
2779
2800
|
const targetEntities = await findEntities(server, dataAccessor, findEntityOptions);
|
|
@@ -2795,6 +2816,11 @@ async function findManyRelatedEntitiesViaIdPropertyCode(options) {
|
|
|
2795
2816
|
value: mainEntityIds,
|
|
2796
2817
|
},
|
|
2797
2818
|
],
|
|
2819
|
+
orderBy: [
|
|
2820
|
+
{
|
|
2821
|
+
field: "id",
|
|
2822
|
+
},
|
|
2823
|
+
],
|
|
2798
2824
|
extraColumnsToSelect: [relationProperty.selfIdColumnName],
|
|
2799
2825
|
keepNonPropertyFields: true,
|
|
2800
2826
|
};
|
|
@@ -2806,6 +2832,18 @@ async function findManyRelatedEntitiesViaIdPropertyCode(options) {
|
|
|
2806
2832
|
if (selectRelationOptions.relations) {
|
|
2807
2833
|
findEntityOptions.relations = selectRelationOptions.relations;
|
|
2808
2834
|
}
|
|
2835
|
+
if (selectRelationOptions.orderBy) {
|
|
2836
|
+
findEntityOptions.orderBy = selectRelationOptions.orderBy;
|
|
2837
|
+
}
|
|
2838
|
+
if (selectRelationOptions.pagination) {
|
|
2839
|
+
findEntityOptions.pagination = selectRelationOptions.pagination;
|
|
2840
|
+
}
|
|
2841
|
+
if (selectRelationOptions.filters) {
|
|
2842
|
+
findEntityOptions.filters = [...findEntityOptions.filters, ...selectRelationOptions.filters];
|
|
2843
|
+
}
|
|
2844
|
+
if (!lodash.isUndefined(selectRelationOptions.keepNonPropertyFields)) {
|
|
2845
|
+
findEntityOptions.keepNonPropertyFields = selectRelationOptions.keepNonPropertyFields;
|
|
2846
|
+
}
|
|
2809
2847
|
}
|
|
2810
2848
|
}
|
|
2811
2849
|
return await findEntities(server, dataAccessor, findEntityOptions);
|
|
@@ -4119,6 +4157,120 @@ var getMetaModelDetail = /*#__PURE__*/Object.freeze({
|
|
|
4119
4157
|
handler: handler$r
|
|
4120
4158
|
});
|
|
4121
4159
|
|
|
4160
|
+
function removeFiltersWithNullValue(filters) {
|
|
4161
|
+
const result = [];
|
|
4162
|
+
if (!filters) {
|
|
4163
|
+
return result;
|
|
4164
|
+
}
|
|
4165
|
+
for (const filter of filters) {
|
|
4166
|
+
const { operator } = filter;
|
|
4167
|
+
switch (operator) {
|
|
4168
|
+
case "null":
|
|
4169
|
+
case "notNull":
|
|
4170
|
+
result.push(filter);
|
|
4171
|
+
break;
|
|
4172
|
+
case "exists":
|
|
4173
|
+
case "notExists":
|
|
4174
|
+
case "and":
|
|
4175
|
+
case "or":
|
|
4176
|
+
const transformedFilter = transformFilterWithSubFilters(filter);
|
|
4177
|
+
if (transformedFilter !== null) {
|
|
4178
|
+
result.push(transformedFilter);
|
|
4179
|
+
}
|
|
4180
|
+
break;
|
|
4181
|
+
default:
|
|
4182
|
+
if (!isNullOrUndefined(filter.value)) {
|
|
4183
|
+
result.push(filter);
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
}
|
|
4187
|
+
return result;
|
|
4188
|
+
}
|
|
4189
|
+
function transformFilterWithSubFilters(filter) {
|
|
4190
|
+
const subFilters = removeFiltersWithNullValue(filter.filters);
|
|
4191
|
+
if (!subFilters.length) {
|
|
4192
|
+
return null;
|
|
4193
|
+
}
|
|
4194
|
+
filter.filters = subFilters;
|
|
4195
|
+
return filter;
|
|
4196
|
+
}
|
|
4197
|
+
function convertModelIndexConditionsToRowFilterOptions(model, filters) {
|
|
4198
|
+
if (!filters || !filters.length) {
|
|
4199
|
+
return [];
|
|
4200
|
+
}
|
|
4201
|
+
const replacedFilters = [];
|
|
4202
|
+
for (const filter of filters) {
|
|
4203
|
+
const { operator } = filter;
|
|
4204
|
+
if (operator === "and" || operator === "or") {
|
|
4205
|
+
replacedFilters.push({
|
|
4206
|
+
operator: operator,
|
|
4207
|
+
filters: convertModelIndexConditionsToRowFilterOptions(model, filter.filters),
|
|
4208
|
+
});
|
|
4209
|
+
}
|
|
4210
|
+
else {
|
|
4211
|
+
replacedFilters.push(replaceModelIndexConditionEntityPropertyWithTableColumn(model, filter));
|
|
4212
|
+
}
|
|
4213
|
+
}
|
|
4214
|
+
return replacedFilters;
|
|
4215
|
+
}
|
|
4216
|
+
function replaceModelIndexConditionEntityPropertyWithTableColumn(model, filter) {
|
|
4217
|
+
const { operator } = filter;
|
|
4218
|
+
const filterField = filter.field;
|
|
4219
|
+
let property = getEntityOwnPropertyByCode(model, filterField);
|
|
4220
|
+
let filterValue = filter.value;
|
|
4221
|
+
let columnName = "";
|
|
4222
|
+
if (property) {
|
|
4223
|
+
if (isOneRelationProperty(property)) {
|
|
4224
|
+
columnName = property.targetIdColumnName;
|
|
4225
|
+
if (lodash.isPlainObject(filterValue)) {
|
|
4226
|
+
filterValue = filterValue.id;
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
else if (isManyRelationProperty(property)) {
|
|
4230
|
+
throw new Error(`Condition on many-relation property "${property.code}" is not supported.`);
|
|
4231
|
+
}
|
|
4232
|
+
else {
|
|
4233
|
+
columnName = property.columnName || property.code;
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
else if (operator === "exists" || operator === "notExists") {
|
|
4237
|
+
throw new Error(`"exists" and "notExists" operators are not supported in index conditions.`);
|
|
4238
|
+
}
|
|
4239
|
+
else {
|
|
4240
|
+
property = getEntityOwnProperty(model, (property) => {
|
|
4241
|
+
return property.columnName === filterField;
|
|
4242
|
+
});
|
|
4243
|
+
if (property) {
|
|
4244
|
+
columnName = property.columnName;
|
|
4245
|
+
}
|
|
4246
|
+
else {
|
|
4247
|
+
// may be relation property.
|
|
4248
|
+
property = getEntityOwnProperty(model, (property) => {
|
|
4249
|
+
return property.targetIdColumnName === filterField;
|
|
4250
|
+
});
|
|
4251
|
+
if (property) {
|
|
4252
|
+
if (isManyRelationProperty(property)) {
|
|
4253
|
+
throw new Error(`Condition on many-relation property "${property.code}" is not supported.`);
|
|
4254
|
+
}
|
|
4255
|
+
columnName = property.targetIdColumnName;
|
|
4256
|
+
if (lodash.isPlainObject(filterValue)) {
|
|
4257
|
+
filterValue = filterValue.id;
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
else {
|
|
4261
|
+
throw new Error(`Unknown field "${filterField}" in index conditions.`);
|
|
4262
|
+
}
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
// TODO: do not use `any` here
|
|
4266
|
+
return {
|
|
4267
|
+
operator: filter.operator,
|
|
4268
|
+
field: columnName,
|
|
4269
|
+
value: filterValue,
|
|
4270
|
+
itemType: filter.itemType,
|
|
4271
|
+
};
|
|
4272
|
+
}
|
|
4273
|
+
|
|
4122
4274
|
/**
|
|
4123
4275
|
* Meta manager plugin
|
|
4124
4276
|
*/
|
|
@@ -4400,6 +4552,7 @@ async function syncDatabaseSchema(server, applicationConfig) {
|
|
|
4400
4552
|
if (!model.indexes || !model.indexes.length) {
|
|
4401
4553
|
continue;
|
|
4402
4554
|
}
|
|
4555
|
+
logger.debug(`Creating indexes of table ${queryBuilder.quoteTable(model)}`);
|
|
4403
4556
|
for (const index of model.indexes) {
|
|
4404
4557
|
const sqlCreateIndex = generateTableIndexDDL(queryBuilder, server, model, index);
|
|
4405
4558
|
await server.tryQueryDatabaseObject(sqlCreateIndex, []);
|
|
@@ -4477,7 +4630,8 @@ function generateTableIndexDDL(queryBuilder, server, model, index) {
|
|
|
4477
4630
|
tableName: model.tableName,
|
|
4478
4631
|
})} (${indexColumns.join(", ")})`;
|
|
4479
4632
|
if (index.conditions) {
|
|
4480
|
-
|
|
4633
|
+
const rowFilterOptions = convertModelIndexConditionsToRowFilterOptions(model, index.conditions);
|
|
4634
|
+
ddl += ` WHERE ${queryBuilder.buildFiltersExpression(model, rowFilterOptions)}`;
|
|
4481
4635
|
}
|
|
4482
4636
|
return ddl;
|
|
4483
4637
|
}
|
|
@@ -4506,44 +4660,6 @@ async function runCollectionEntityActionHandler(ctx, options, code, handleEntity
|
|
|
4506
4660
|
}
|
|
4507
4661
|
}
|
|
4508
4662
|
|
|
4509
|
-
function removeFiltersWithNullValue(filters) {
|
|
4510
|
-
const result = [];
|
|
4511
|
-
if (!filters) {
|
|
4512
|
-
return result;
|
|
4513
|
-
}
|
|
4514
|
-
for (const filter of filters) {
|
|
4515
|
-
const { operator } = filter;
|
|
4516
|
-
switch (operator) {
|
|
4517
|
-
case "null":
|
|
4518
|
-
case "notNull":
|
|
4519
|
-
result.push(filter);
|
|
4520
|
-
break;
|
|
4521
|
-
case "exists":
|
|
4522
|
-
case "notExists":
|
|
4523
|
-
case "and":
|
|
4524
|
-
case "or":
|
|
4525
|
-
const transformedFilter = transformFilterWithSubFilters(filter);
|
|
4526
|
-
if (transformedFilter !== null) {
|
|
4527
|
-
result.push(transformedFilter);
|
|
4528
|
-
}
|
|
4529
|
-
break;
|
|
4530
|
-
default:
|
|
4531
|
-
if (!isNullOrUndefined(filter.value)) {
|
|
4532
|
-
result.push(filter);
|
|
4533
|
-
}
|
|
4534
|
-
}
|
|
4535
|
-
}
|
|
4536
|
-
return result;
|
|
4537
|
-
}
|
|
4538
|
-
function transformFilterWithSubFilters(filter) {
|
|
4539
|
-
const subFilters = removeFiltersWithNullValue(filter.filters);
|
|
4540
|
-
if (!subFilters.length) {
|
|
4541
|
-
return null;
|
|
4542
|
-
}
|
|
4543
|
-
filter.filters = subFilters;
|
|
4544
|
-
return filter;
|
|
4545
|
-
}
|
|
4546
|
-
|
|
4547
4663
|
const code$q = "findCollectionEntities";
|
|
4548
4664
|
async function handler$q(plugin, ctx, options) {
|
|
4549
4665
|
await runCollectionEntityActionHandler(ctx, options, code$q, async (entityManager, input) => {
|
package/dist/types.d.ts
CHANGED
|
@@ -391,7 +391,7 @@ export interface FindEntityOptions {
|
|
|
391
391
|
orderBy?: FindEntityOrderByOptions[];
|
|
392
392
|
pagination?: FindEntityPaginationOptions;
|
|
393
393
|
properties?: string[];
|
|
394
|
-
relations?: Record<string,
|
|
394
|
+
relations?: Record<string, FindEntityFindRelationEntitiesOptions>;
|
|
395
395
|
extraColumnsToSelect?: ColumnSelectOptions[];
|
|
396
396
|
keepNonPropertyFields?: boolean;
|
|
397
397
|
}
|
|
@@ -399,7 +399,7 @@ export interface FindEntityByIdOptions {
|
|
|
399
399
|
routeContext?: RouteContext;
|
|
400
400
|
id: any;
|
|
401
401
|
properties?: string[];
|
|
402
|
-
relations?: Record<string,
|
|
402
|
+
relations?: Record<string, FindEntityFindRelationEntitiesOptions>;
|
|
403
403
|
keepNonPropertyFields?: boolean;
|
|
404
404
|
}
|
|
405
405
|
export interface FindEntityRelationalFilterOptions {
|
|
@@ -437,9 +437,19 @@ export interface FindEntityPaginationOptions {
|
|
|
437
437
|
limit: number;
|
|
438
438
|
withoutTotal?: boolean;
|
|
439
439
|
}
|
|
440
|
-
export type
|
|
440
|
+
export type FindEntityFindRelationEntitiesOptions = FindEntityFindOneRelationEntitiesOptions | FindEntityFindManyRelationEntitiesOptions;
|
|
441
|
+
export type FindEntityFindOneRelationEntitiesOptions = true | {
|
|
441
442
|
properties?: string[];
|
|
442
|
-
relations?: Record<string,
|
|
443
|
+
relations?: Record<string, FindEntityFindRelationEntitiesOptions>;
|
|
444
|
+
keepNonPropertyFields?: boolean;
|
|
445
|
+
};
|
|
446
|
+
export type FindEntityFindManyRelationEntitiesOptions = true | {
|
|
447
|
+
properties?: string[];
|
|
448
|
+
relations?: Record<string, FindEntityFindRelationEntitiesOptions>;
|
|
449
|
+
filters?: EntityFilterOptions[];
|
|
450
|
+
orderBy?: FindEntityOrderByOptions[];
|
|
451
|
+
pagination?: FindEntityPaginationOptions;
|
|
452
|
+
keepNonPropertyFields?: boolean;
|
|
443
453
|
};
|
|
444
454
|
export interface FindEntityOrderByOptions {
|
|
445
455
|
field: string;
|
package/package.json
CHANGED
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
FindEntityByIdOptions,
|
|
11
11
|
FindEntityOptions,
|
|
12
12
|
FindEntityOrderByOptions,
|
|
13
|
-
FindEntitySelectRelationOptions,
|
|
14
13
|
IRpdDataAccessor,
|
|
15
14
|
RemoveEntityRelationsOptions,
|
|
16
15
|
RpdDataModel,
|
|
@@ -18,13 +17,15 @@ import {
|
|
|
18
17
|
RpdDataModelIndexOptions,
|
|
19
18
|
RpdDataModelProperty,
|
|
20
19
|
UpdateEntityByIdOptions,
|
|
20
|
+
FindEntityFindOneRelationEntitiesOptions,
|
|
21
|
+
FindEntityFindManyRelationEntitiesOptions,
|
|
21
22
|
} from "~/types";
|
|
22
23
|
import { isNullOrUndefined } from "~/utilities/typeUtility";
|
|
23
24
|
import { mapDbRowToEntity, mapEntityToDbRow } from "./entityMapper";
|
|
24
25
|
import { mapPropertyNameToColumnName } from "./propertyMapper";
|
|
25
26
|
import { IRpdServer, RapidPlugin } from "~/core/server";
|
|
26
27
|
import { getEntityPartChanges } from "~/helpers/entityHelpers";
|
|
27
|
-
import { cloneDeep, filter, find, first, forEach, isArray, isNumber, isObject, isPlainObject, isString, keys, map, reject, uniq } from "lodash";
|
|
28
|
+
import { cloneDeep, filter, find, first, forEach, isArray, isNumber, isObject, isPlainObject, isString, isUndefined, keys, map, reject, uniq } from "lodash";
|
|
28
29
|
import {
|
|
29
30
|
getEntityPropertiesIncludingBase,
|
|
30
31
|
getEntityProperty,
|
|
@@ -37,7 +38,6 @@ import {
|
|
|
37
38
|
import { ColumnSelectOptions, CountRowOptions, FindRowOptions, FindRowOrderByOptions, RowFilterOptions } from "./dataAccessTypes";
|
|
38
39
|
import { newEntityOperationError } from "~/utilities/errorUtility";
|
|
39
40
|
import { getNowStringWithTimezone } from "~/utilities/timeUtility";
|
|
40
|
-
import { or } from "xstate";
|
|
41
41
|
import { RouteContext } from "~/core/routeContext";
|
|
42
42
|
|
|
43
43
|
export type FindOneRelationEntitiesOptions = {
|
|
@@ -45,7 +45,7 @@ export type FindOneRelationEntitiesOptions = {
|
|
|
45
45
|
mainModel: RpdDataModel;
|
|
46
46
|
relationProperty: RpdDataModelProperty;
|
|
47
47
|
relationEntityIds: any[];
|
|
48
|
-
selectRelationOptions?:
|
|
48
|
+
selectRelationOptions?: FindEntityFindOneRelationEntitiesOptions;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
51
|
export type FindManyRelationEntitiesOptions = {
|
|
@@ -53,7 +53,7 @@ export type FindManyRelationEntitiesOptions = {
|
|
|
53
53
|
mainModel: RpdDataModel;
|
|
54
54
|
relationProperty: RpdDataModelProperty;
|
|
55
55
|
mainEntityIds: any[];
|
|
56
|
-
selectRelationOptions?:
|
|
56
|
+
selectRelationOptions?: FindEntityFindManyRelationEntitiesOptions;
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
function convertEntityOrderByToRowOrderBy(server: IRpdServer, model: RpdDataModel, baseModel?: RpdDataModel, orderByList?: FindEntityOrderByOptions[]) {
|
|
@@ -583,7 +583,9 @@ async function findManyRelationLinksViaLinkTable(options: FindManyRelationEntiti
|
|
|
583
583
|
const command = `SELECT * FROM ${server.queryBuilder.quoteTable({
|
|
584
584
|
schema: relationProperty.linkSchema,
|
|
585
585
|
tableName: relationProperty.linkTableName!,
|
|
586
|
-
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName!)} = ANY($1::int[])
|
|
586
|
+
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName!)} = ANY($1::int[])
|
|
587
|
+
ORDER BY id
|
|
588
|
+
`;
|
|
587
589
|
const params = [mainEntityIds];
|
|
588
590
|
const links = await server.queryDatabaseObject(command, params);
|
|
589
591
|
const targetEntityIds = links.map((link) => link[relationProperty.targetIdColumnName!]);
|
|
@@ -612,6 +614,18 @@ async function findManyRelationLinksViaLinkTable(options: FindManyRelationEntiti
|
|
|
612
614
|
if (selectRelationOptions.relations) {
|
|
613
615
|
findEntityOptions.relations = selectRelationOptions.relations;
|
|
614
616
|
}
|
|
617
|
+
if (selectRelationOptions.orderBy) {
|
|
618
|
+
findEntityOptions.orderBy = selectRelationOptions.orderBy;
|
|
619
|
+
}
|
|
620
|
+
if (selectRelationOptions.pagination) {
|
|
621
|
+
findEntityOptions.pagination = selectRelationOptions.pagination;
|
|
622
|
+
}
|
|
623
|
+
if (selectRelationOptions.filters) {
|
|
624
|
+
findEntityOptions.filters = [...findEntityOptions.filters, ...selectRelationOptions.filters];
|
|
625
|
+
}
|
|
626
|
+
if (!isUndefined(selectRelationOptions.keepNonPropertyFields)) {
|
|
627
|
+
findEntityOptions.keepNonPropertyFields = selectRelationOptions.keepNonPropertyFields;
|
|
628
|
+
}
|
|
615
629
|
}
|
|
616
630
|
}
|
|
617
631
|
|
|
@@ -638,6 +652,11 @@ async function findManyRelatedEntitiesViaIdPropertyCode(options: FindManyRelatio
|
|
|
638
652
|
value: mainEntityIds,
|
|
639
653
|
},
|
|
640
654
|
],
|
|
655
|
+
orderBy: [
|
|
656
|
+
{
|
|
657
|
+
field: "id",
|
|
658
|
+
},
|
|
659
|
+
],
|
|
641
660
|
extraColumnsToSelect: [relationProperty.selfIdColumnName],
|
|
642
661
|
keepNonPropertyFields: true,
|
|
643
662
|
};
|
|
@@ -650,6 +669,18 @@ async function findManyRelatedEntitiesViaIdPropertyCode(options: FindManyRelatio
|
|
|
650
669
|
if (selectRelationOptions.relations) {
|
|
651
670
|
findEntityOptions.relations = selectRelationOptions.relations;
|
|
652
671
|
}
|
|
672
|
+
if (selectRelationOptions.orderBy) {
|
|
673
|
+
findEntityOptions.orderBy = selectRelationOptions.orderBy;
|
|
674
|
+
}
|
|
675
|
+
if (selectRelationOptions.pagination) {
|
|
676
|
+
findEntityOptions.pagination = selectRelationOptions.pagination;
|
|
677
|
+
}
|
|
678
|
+
if (selectRelationOptions.filters) {
|
|
679
|
+
findEntityOptions.filters = [...findEntityOptions.filters, ...selectRelationOptions.filters];
|
|
680
|
+
}
|
|
681
|
+
if (!isUndefined(selectRelationOptions.keepNonPropertyFields)) {
|
|
682
|
+
findEntityOptions.keepNonPropertyFields = selectRelationOptions.keepNonPropertyFields;
|
|
683
|
+
}
|
|
653
684
|
}
|
|
654
685
|
}
|
|
655
686
|
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
EntityFilterOptions,
|
|
3
|
+
EntityNonRelationPropertyFilterOptions,
|
|
4
|
+
FindEntityExistenceFilterOptions,
|
|
5
|
+
FindEntityLogicalFilterOptions,
|
|
6
|
+
RpdDataModel,
|
|
7
|
+
RpdDataModelIndexOptions,
|
|
8
|
+
RpdDataModelProperty,
|
|
9
|
+
} from "~/types";
|
|
2
10
|
import { isNullOrUndefined } from "~/utilities/typeUtility";
|
|
11
|
+
import { getEntityOwnProperty, getEntityOwnPropertyByCode, isManyRelationProperty, isOneRelationProperty } from "./metaHelper";
|
|
12
|
+
import { IRpdServer } from "~/core/server";
|
|
13
|
+
import { isPlainObject } from "lodash";
|
|
14
|
+
import { RowFilterOptions } from "~/dataAccess/dataAccessTypes";
|
|
3
15
|
|
|
4
16
|
export function removeFiltersWithNullValue(filters?: EntityFilterOptions[]) {
|
|
5
17
|
const result: EntityFilterOptions[] = [];
|
|
@@ -45,3 +57,79 @@ function transformFilterWithSubFilters(
|
|
|
45
57
|
filter.filters = subFilters;
|
|
46
58
|
return filter;
|
|
47
59
|
}
|
|
60
|
+
|
|
61
|
+
export function convertModelIndexConditionsToRowFilterOptions(model: RpdDataModel, filters: RpdDataModelIndexOptions[]) {
|
|
62
|
+
if (!filters || !filters.length) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const replacedFilters: RowFilterOptions[] = [];
|
|
67
|
+
for (const filter of filters) {
|
|
68
|
+
const { operator } = filter;
|
|
69
|
+
if (operator === "and" || operator === "or") {
|
|
70
|
+
replacedFilters.push({
|
|
71
|
+
operator: operator,
|
|
72
|
+
filters: convertModelIndexConditionsToRowFilterOptions(model, filter.filters),
|
|
73
|
+
});
|
|
74
|
+
} else {
|
|
75
|
+
replacedFilters.push(replaceModelIndexConditionEntityPropertyWithTableColumn(model, filter));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return replacedFilters;
|
|
79
|
+
}
|
|
80
|
+
export function replaceModelIndexConditionEntityPropertyWithTableColumn(model: RpdDataModel, filter: RpdDataModelIndexOptions): RowFilterOptions {
|
|
81
|
+
const { operator } = filter;
|
|
82
|
+
const filterField = (filter as EntityNonRelationPropertyFilterOptions).field;
|
|
83
|
+
let property: RpdDataModelProperty = getEntityOwnPropertyByCode(model, filterField);
|
|
84
|
+
|
|
85
|
+
let filterValue = (filter as any).value;
|
|
86
|
+
|
|
87
|
+
let columnName = "";
|
|
88
|
+
if (property) {
|
|
89
|
+
if (isOneRelationProperty(property)) {
|
|
90
|
+
columnName = property.targetIdColumnName;
|
|
91
|
+
if (isPlainObject(filterValue)) {
|
|
92
|
+
filterValue = filterValue.id;
|
|
93
|
+
}
|
|
94
|
+
} else if (isManyRelationProperty(property)) {
|
|
95
|
+
throw new Error(`Condition on many-relation property "${property.code}" is not supported.`);
|
|
96
|
+
} else {
|
|
97
|
+
columnName = property.columnName || property.code;
|
|
98
|
+
}
|
|
99
|
+
} else if ((operator as any) === "exists" || (operator as any) === "notExists") {
|
|
100
|
+
throw new Error(`"exists" and "notExists" operators are not supported in index conditions.`);
|
|
101
|
+
} else {
|
|
102
|
+
property = getEntityOwnProperty(model, (property) => {
|
|
103
|
+
return property.columnName === filterField;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (property) {
|
|
107
|
+
columnName = property.columnName;
|
|
108
|
+
} else {
|
|
109
|
+
// may be relation property.
|
|
110
|
+
property = getEntityOwnProperty(model, (property) => {
|
|
111
|
+
return property.targetIdColumnName === filterField;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (property) {
|
|
115
|
+
if (isManyRelationProperty(property)) {
|
|
116
|
+
throw new Error(`Condition on many-relation property "${property.code}" is not supported.`);
|
|
117
|
+
}
|
|
118
|
+
columnName = property.targetIdColumnName;
|
|
119
|
+
if (isPlainObject(filterValue)) {
|
|
120
|
+
filterValue = filterValue.id;
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
throw new Error(`Unknown field "${filterField}" in index conditions.`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// TODO: do not use `any` here
|
|
129
|
+
return {
|
|
130
|
+
operator: filter.operator,
|
|
131
|
+
field: columnName,
|
|
132
|
+
value: filterValue,
|
|
133
|
+
itemType: (filter as any).itemType,
|
|
134
|
+
} as RowFilterOptions;
|
|
135
|
+
}
|
|
@@ -66,6 +66,15 @@ export function getEntityProperty(
|
|
|
66
66
|
return property;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
export function getEntityOwnPropertyByCode(model: RpdDataModel, propertyCode: string): RpdDataModelProperty | undefined {
|
|
70
|
+
return getEntityOwnProperty(model, (e) => e.code === propertyCode);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function getEntityOwnProperty(model: RpdDataModel, predicate: (item: RpdDataModelProperty) => boolean): RpdDataModelProperty | undefined {
|
|
74
|
+
let property = model.properties.find(predicate);
|
|
75
|
+
return property;
|
|
76
|
+
}
|
|
77
|
+
|
|
69
78
|
export function getEntityPropertyByFieldName(server: IRpdServer, model: RpdDataModel, fieldName: string): RpdDataModelProperty | undefined {
|
|
70
79
|
let property = getEntityPropertyByCode(server, model, fieldName);
|
|
71
80
|
if (!property) {
|
|
@@ -30,6 +30,7 @@ import { find, isString, map } from "lodash";
|
|
|
30
30
|
import { getEntityPropertiesIncludingBase, getEntityPropertyByCode, isOneRelationProperty, isRelationProperty } from "~/helpers/metaHelper";
|
|
31
31
|
import { DataAccessPgColumnTypes } from "~/dataAccess/dataAccessTypes";
|
|
32
32
|
import { pgPropertyTypeColumnMap } from "~/dataAccess/columnTypeMapper";
|
|
33
|
+
import { convertModelIndexConditionsToRowFilterOptions } from "~/helpers/filterHelper";
|
|
33
34
|
|
|
34
35
|
class MetaManager implements RapidPlugin {
|
|
35
36
|
get code(): string {
|
|
@@ -388,6 +389,7 @@ async function syncDatabaseSchema(server: IRpdServer, applicationConfig: RpdAppl
|
|
|
388
389
|
continue;
|
|
389
390
|
}
|
|
390
391
|
|
|
392
|
+
logger.debug(`Creating indexes of table ${queryBuilder.quoteTable(model)}`);
|
|
391
393
|
for (const index of model.indexes) {
|
|
392
394
|
const sqlCreateIndex = generateTableIndexDDL(queryBuilder, server, model, index);
|
|
393
395
|
await server.tryQueryDatabaseObject(sqlCreateIndex, []);
|
|
@@ -493,7 +495,8 @@ function generateTableIndexDDL(queryBuilder: IQueryBuilder, server: IRpdServer,
|
|
|
493
495
|
})} (${indexColumns.join(", ")})`;
|
|
494
496
|
|
|
495
497
|
if (index.conditions) {
|
|
496
|
-
|
|
498
|
+
const rowFilterOptions = convertModelIndexConditionsToRowFilterOptions(model, index.conditions);
|
|
499
|
+
ddl += ` WHERE ${queryBuilder.buildFiltersExpression(model, rowFilterOptions)}`;
|
|
497
500
|
}
|
|
498
501
|
|
|
499
502
|
return ddl;
|
package/src/types.ts
CHANGED
|
@@ -509,7 +509,7 @@ export interface FindEntityOptions {
|
|
|
509
509
|
orderBy?: FindEntityOrderByOptions[];
|
|
510
510
|
pagination?: FindEntityPaginationOptions;
|
|
511
511
|
properties?: string[];
|
|
512
|
-
relations?: Record<string,
|
|
512
|
+
relations?: Record<string, FindEntityFindRelationEntitiesOptions>;
|
|
513
513
|
extraColumnsToSelect?: ColumnSelectOptions[];
|
|
514
514
|
keepNonPropertyFields?: boolean;
|
|
515
515
|
}
|
|
@@ -518,7 +518,7 @@ export interface FindEntityByIdOptions {
|
|
|
518
518
|
routeContext?: RouteContext;
|
|
519
519
|
id: any;
|
|
520
520
|
properties?: string[];
|
|
521
|
-
relations?: Record<string,
|
|
521
|
+
relations?: Record<string, FindEntityFindRelationEntitiesOptions>;
|
|
522
522
|
keepNonPropertyFields?: boolean;
|
|
523
523
|
}
|
|
524
524
|
|
|
@@ -564,11 +564,25 @@ export interface FindEntityPaginationOptions {
|
|
|
564
564
|
withoutTotal?: boolean;
|
|
565
565
|
}
|
|
566
566
|
|
|
567
|
-
export type
|
|
567
|
+
export type FindEntityFindRelationEntitiesOptions = FindEntityFindOneRelationEntitiesOptions | FindEntityFindManyRelationEntitiesOptions;
|
|
568
|
+
|
|
569
|
+
export type FindEntityFindOneRelationEntitiesOptions =
|
|
570
|
+
| true
|
|
571
|
+
| {
|
|
572
|
+
properties?: string[];
|
|
573
|
+
relations?: Record<string, FindEntityFindRelationEntitiesOptions>;
|
|
574
|
+
keepNonPropertyFields?: boolean;
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
export type FindEntityFindManyRelationEntitiesOptions =
|
|
568
578
|
| true
|
|
569
579
|
| {
|
|
570
580
|
properties?: string[];
|
|
571
|
-
relations?: Record<string,
|
|
581
|
+
relations?: Record<string, FindEntityFindRelationEntitiesOptions>;
|
|
582
|
+
filters?: EntityFilterOptions[];
|
|
583
|
+
orderBy?: FindEntityOrderByOptions[];
|
|
584
|
+
pagination?: FindEntityPaginationOptions;
|
|
585
|
+
keepNonPropertyFields?: boolean;
|
|
572
586
|
};
|
|
573
587
|
|
|
574
588
|
export interface FindEntityOrderByOptions {
|