@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.
@@ -1,4 +1,4 @@
1
- import { AddEntityRelationsOptions, CountEntityOptions, CountEntityResult, CreateEntityOptions, DeleteEntityByIdOptions, FindEntityByIdOptions, FindEntityOptions, FindEntitySelectRelationOptions, IRpdDataAccessor, RemoveEntityRelationsOptions, RpdDataModel, RpdDataModelIndex, RpdDataModelProperty, UpdateEntityByIdOptions } from "../types";
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?: FindEntitySelectRelationOptions;
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?: FindEntitySelectRelationOptions;
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
- ddl += ` WHERE ${queryBuilder.buildFiltersExpression(model, index.conditions)}`;
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, FindEntitySelectRelationOptions>;
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, FindEntitySelectRelationOptions>;
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 FindEntitySelectRelationOptions = true | {
440
+ export type FindEntityFindRelationEntitiesOptions = FindEntityFindOneRelationEntitiesOptions | FindEntityFindManyRelationEntitiesOptions;
441
+ export type FindEntityFindOneRelationEntitiesOptions = true | {
441
442
  properties?: string[];
442
- relations?: Record<string, FindEntitySelectRelationOptions>;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruiapp/rapid-core",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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?: FindEntitySelectRelationOptions;
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?: FindEntitySelectRelationOptions;
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 { EntityFilterOptions, FindEntityExistenceFilterOptions, FindEntityLogicalFilterOptions } from "~/types";
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
- ddl += ` WHERE ${queryBuilder.buildFiltersExpression(model, index.conditions)}`;
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, FindEntitySelectRelationOptions>;
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, FindEntitySelectRelationOptions>;
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 FindEntitySelectRelationOptions =
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, FindEntitySelectRelationOptions>;
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 {