Skip to content

Commit 5b6b49e

Browse files
committed
Add 'search-flow-by-filters-strategy' implementation
Nested strategies to find flowIDs from different filters Merge 5de9ca3
1 parent bd3c376 commit 5b6b49e

5 files changed

+553
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { type Database } from '@unocha/hpc-api-core/src/db';
2+
import { type ShortcutCategoryFilter } from '../../categories/model';
3+
import { type FlowObjectFilterGrouped } from '../../flow-object/model';
4+
import { type FlowCategory, type NestedFlowFilters } from '../graphql/args';
5+
import { type UniqueFlowEntity } from '../model';
6+
7+
export interface FlowIdSearchStrategyResponse {
8+
flows: UniqueFlowEntity[];
9+
}
10+
11+
export interface FlowIdSearchStrategyArgs {
12+
models: Database;
13+
flowObjectFilterGrouped?: FlowObjectFilterGrouped;
14+
flowCategoryConditions?: FlowCategory[];
15+
nestedFlowFilters?: NestedFlowFilters;
16+
shortcutFilters?: ShortcutCategoryFilter[] | null;
17+
}
18+
19+
export interface FlowIDSearchStrategy {
20+
search(args: FlowIdSearchStrategyArgs): Promise<FlowIdSearchStrategyResponse>;
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { type Database } from '@unocha/hpc-api-core/src/db';
2+
import { type CategoryId } from '@unocha/hpc-api-core/src/db/models/category';
3+
import {
4+
Op,
5+
type Condition,
6+
} from '@unocha/hpc-api-core/src/db/util/conditions';
7+
import { type InstanceOfModel } from '@unocha/hpc-api-core/src/db/util/types';
8+
import { createBrandedValue } from '@unocha/hpc-api-core/src/util/types';
9+
import { Service } from 'typedi';
10+
import { FlowService } from '../../flow-service';
11+
import type { UniqueFlowEntity } from '../../model';
12+
import {
13+
type FlowIDSearchStrategy,
14+
type FlowIdSearchStrategyArgs,
15+
type FlowIdSearchStrategyResponse,
16+
} from '../flowID-search-strategy';
17+
import { mapFlowCategoryConditionsToWhereClause } from './utils';
18+
19+
@Service()
20+
export class GetFlowIdsFromCategoryConditionsStrategyImpl
21+
implements FlowIDSearchStrategy
22+
{
23+
constructor(private readonly flowService: FlowService) {}
24+
25+
async search(
26+
args: FlowIdSearchStrategyArgs
27+
): Promise<FlowIdSearchStrategyResponse> {
28+
const { models, flowCategoryConditions, shortcutFilters } = args;
29+
30+
let categoriesIds: CategoryId[] = [];
31+
32+
let whereClause = null;
33+
if (flowCategoryConditions) {
34+
whereClause = mapFlowCategoryConditionsToWhereClause(
35+
flowCategoryConditions
36+
);
37+
}
38+
if (whereClause) {
39+
const categories = await models.category.find({
40+
where: whereClause,
41+
});
42+
43+
categoriesIds = categories.map((category) => category.id);
44+
}
45+
46+
// Add category IDs from shortcut filter
47+
// to the list of category IDs IN or NOT_IN
48+
const categoriesIdsFromShortcutFilterIN: CategoryId[] = [];
49+
const categoriesIdsFromShortcutFilterNOTIN: CategoryId[] = [];
50+
51+
if (shortcutFilters) {
52+
for (const shortcut of shortcutFilters) {
53+
if (shortcut.operation === Op.IN) {
54+
categoriesIdsFromShortcutFilterIN.push(
55+
createBrandedValue(shortcut.id)
56+
);
57+
} else {
58+
categoriesIdsFromShortcutFilterNOTIN.push(
59+
createBrandedValue(shortcut.id)
60+
);
61+
}
62+
}
63+
}
64+
65+
// Search categoriesRef with categoriesID IN and categoriesIdsFromShortcutFilterIN
66+
// and categoriesIdsFromShortcutFilterNOTIN
67+
const where: Condition<InstanceOfModel<Database['categoryRef']>> = {
68+
objectType: 'flow',
69+
};
70+
71+
if (categoriesIdsFromShortcutFilterNOTIN.length > 0) {
72+
where['categoryID'] = {
73+
[Op.NOT_IN]: categoriesIdsFromShortcutFilterNOTIN,
74+
};
75+
}
76+
77+
const categoriesIDsIN = [
78+
...categoriesIds,
79+
...categoriesIdsFromShortcutFilterIN,
80+
];
81+
82+
if (categoriesIDsIN.length > 0) {
83+
where['categoryID'] = { [Op.IN]: categoriesIDsIN };
84+
}
85+
86+
const categoriesRef = await models.categoryRef.find({
87+
where,
88+
distinct: ['objectID', 'versionID'],
89+
});
90+
91+
// Map categoryRef to UniqueFlowEntity (flowId and versionID)
92+
const flowIDsFromCategoryRef: UniqueFlowEntity[] = categoriesRef.map(
93+
(catRef) => ({
94+
id: createBrandedValue(catRef.objectID),
95+
versionID: catRef.versionID,
96+
})
97+
);
98+
return { flows: flowIDsFromCategoryRef };
99+
}
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { Service } from 'typedi';
2+
import { ExternalReferenceService } from '../../../external-reference/external-reference-service';
3+
import { LegacyService } from '../../../legacy/legacy-service';
4+
import { ReportDetailService } from '../../../report-details/report-detail-service';
5+
import { FlowService } from '../../flow-service';
6+
import type { UniqueFlowEntity } from '../../model';
7+
import {
8+
type FlowIDSearchStrategy,
9+
type FlowIdSearchStrategyArgs,
10+
type FlowIdSearchStrategyResponse,
11+
} from '../flowID-search-strategy';
12+
import { intersectUniqueFlowEntities } from './utils';
13+
14+
@Service()
15+
export class GetFlowIdsFromNestedFlowFiltersStrategyImpl
16+
implements FlowIDSearchStrategy
17+
{
18+
constructor(
19+
private readonly reportDetailService: ReportDetailService,
20+
private readonly legacyService: LegacyService,
21+
private readonly externalRefenceService: ExternalReferenceService,
22+
private readonly flowService: FlowService
23+
) {}
24+
25+
async search(
26+
args: FlowIdSearchStrategyArgs
27+
): Promise<FlowIdSearchStrategyResponse> {
28+
const { models, nestedFlowFilters } = args;
29+
30+
let flowsReporterReferenceCode: UniqueFlowEntity[] = [];
31+
let flowsSourceSystemId: UniqueFlowEntity[] = [];
32+
let flowsSystemId: UniqueFlowEntity[] = [];
33+
const flowsLegacyId: UniqueFlowEntity[] = [];
34+
35+
// Get the flowIDs using 'reporterReferenceCode'
36+
if (nestedFlowFilters?.reporterRefCode) {
37+
flowsReporterReferenceCode =
38+
await this.reportDetailService.getUniqueFlowIDsFromReportDetailsByReporterReferenceCode(
39+
models,
40+
nestedFlowFilters.reporterRefCode
41+
);
42+
}
43+
44+
// Get the flowIDs using 'sourceSystemID' from 'reportDetail'
45+
if (nestedFlowFilters?.sourceSystemID) {
46+
flowsSourceSystemId =
47+
await this.reportDetailService.getUniqueFlowIDsFromReportDetailsBySourceSystemID(
48+
models,
49+
nestedFlowFilters.sourceSystemID
50+
);
51+
}
52+
53+
// Get the flowIDs using 'systemID' from 'externalRefecence'
54+
if (nestedFlowFilters?.systemID) {
55+
flowsSystemId =
56+
await this.externalRefenceService.getUniqueFlowIDsBySystemID(
57+
models,
58+
nestedFlowFilters.systemID
59+
);
60+
}
61+
62+
// Get the flowIDs using 'legacyID'
63+
if (nestedFlowFilters?.legacyID) {
64+
const flowID = await this.legacyService.getFlowIdFromLegacyId(
65+
models,
66+
nestedFlowFilters.legacyID
67+
);
68+
69+
if (flowID) {
70+
flowsLegacyId.push({
71+
id: flowID,
72+
versionID: 1,
73+
});
74+
}
75+
}
76+
77+
// Intersect the flowIDs from the nestedFlowFilters
78+
const flowIDsFromNestedFlowFilters: UniqueFlowEntity[] =
79+
intersectUniqueFlowEntities(
80+
flowsReporterReferenceCode,
81+
flowsSourceSystemId,
82+
flowsSystemId,
83+
flowsLegacyId
84+
);
85+
86+
if (flowIDsFromNestedFlowFilters.length === 0) {
87+
return { flows: [] };
88+
}
89+
// Once gathered and disjoined the flowIDs from the nestedFlowFilters
90+
// Look after this uniqueFlows in the flow table
91+
const flows = await this.flowService.progresiveSearch(
92+
models,
93+
flowIDsFromNestedFlowFilters,
94+
1000,
95+
0,
96+
false, // Stop when we have the limit
97+
[]
98+
);
99+
100+
return { flows };
101+
}
102+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { Op } from '@unocha/hpc-api-core/src/db/util/conditions';
2+
import { Service } from 'typedi';
3+
import { type UniqueFlowEntity } from '../../model';
4+
import {
5+
type FlowIDSearchStrategy,
6+
type FlowIdSearchStrategyArgs,
7+
type FlowIdSearchStrategyResponse,
8+
} from '../flowID-search-strategy';
9+
import { intersectUniqueFlowEntities } from './utils';
10+
11+
@Service()
12+
export class GetFlowIdsFromObjectConditionsStrategyImpl
13+
implements FlowIDSearchStrategy
14+
{
15+
constructor() {}
16+
17+
async search(
18+
args: FlowIdSearchStrategyArgs
19+
): Promise<FlowIdSearchStrategyResponse> {
20+
const { flowObjectFilterGrouped, models } = args;
21+
22+
if (!flowObjectFilterGrouped) {
23+
return { flows: [] };
24+
}
25+
26+
let intersectedFlows: UniqueFlowEntity[] = [];
27+
28+
for (const [flowObjectType, group] of flowObjectFilterGrouped.entries()) {
29+
for (const [direction, ids] of group.entries()) {
30+
const condition = {
31+
objectType: flowObjectType,
32+
refDirection: direction,
33+
objectID: { [Op.IN]: ids },
34+
};
35+
const flowObjectsFound = await models.flowObject.find({
36+
where: condition,
37+
});
38+
39+
const uniqueFlowObjectsEntities: UniqueFlowEntity[] =
40+
flowObjectsFound.map(
41+
(flowObject) =>
42+
({
43+
id: flowObject.flowID,
44+
versionID: flowObject.versionID,
45+
}) satisfies UniqueFlowEntity
46+
);
47+
48+
intersectedFlows = intersectUniqueFlowEntities(
49+
intersectedFlows,
50+
uniqueFlowObjectsEntities
51+
);
52+
}
53+
}
54+
55+
return { flows: intersectedFlows };
56+
}
57+
}

0 commit comments

Comments
 (0)