Skip to content

Commit

Permalink
Change how flowObject filters intersect results
Browse files Browse the repository at this point in the history
  • Loading branch information
manelcecs committed Mar 1, 2024
1 parent 3f27c7c commit 04c165e
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 94 deletions.
35 changes: 34 additions & 1 deletion src/domain-services/flow-object/flow-object-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { type Database } from '@unocha/hpc-api-core/src/db';
import { type FlowId } from '@unocha/hpc-api-core/src/db/models/flow';
import { Op } from '@unocha/hpc-api-core/src/db/util/conditions';
import { createBrandedValue } from '@unocha/hpc-api-core/src/util/types';
import type Knex from 'knex';
import { Service } from 'typedi';
import { UniqueFlowEntity } from '../flows/model';
import { type UniqueFlowEntity } from '../flows/model';

@Service()
export class FlowObjectService {
Expand Down Expand Up @@ -47,4 +48,36 @@ export class FlowObjectService {
},
});
}

async getFlowObjectsByFlowObjectConditions(
databaseConnection: Knex,
flowObjectConditions: any
) {
// 1. Map flowObjectConditions to selectQuery
let selectQuery = databaseConnection?.queryBuilder();

// 1.1. Map first element of flowObjectConditions to selectQuery
// We know there is at least one element in flowObjectConditions
const firstFlowObjectCondition = flowObjectConditions[0];
selectQuery = selectQuery
?.select('flowID', 'versionID')
.from('flowObject')
.where('objectID', firstFlowObjectCondition.objectID)
.andWhere('objectType', firstFlowObjectCondition.objectType)
.andWhere('refDirection', firstFlowObjectCondition.direction);

// 1.2 Map the rest of the elements of flowObjectConditions to selectQuery
for (let i = 1; i < flowObjectConditions.length; i++) {
const flowObjectCondition = flowObjectConditions[i];
selectQuery = selectQuery?.intersect(function () {
this.select('flowID', 'versionID')
.from('flowObject')
.where('objectID', flowObjectCondition.objectID)
.andWhere('objectType', flowObjectCondition.objectType)
.andWhere('refDirection', flowObjectCondition.direction);
});
}

return await selectQuery;
}
}
4 changes: 2 additions & 2 deletions src/domain-services/flows/strategy/flowID-search-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ export interface FlowIdSearchStrategyResponse {
}

export interface FlowIdSearchStrategyArgs {
databaseConnection?: Knex;
databaseConnection: Knex;
models: Database;
flowObjectsConditions?: Map<string, Map<string, number[]>>;
flowObjectsConditions?: any; // TODO: use proper type
flowCategoryConditions?: FlowCategory[];
nestedFlowFilters?: NestedFlowFilters;
shortcutFilter?: any[] | null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
type FlowIdSearchStrategyArgs,
type FlowIdSearchStrategyResponse,
} from '../flowID-search-strategy';
import { mapFlowObjectConditionsToWhereClause } from './utils';

@Service()
export class GetFlowIdsFromObjectConditionsStrategyImpl
Expand All @@ -17,21 +16,28 @@ export class GetFlowIdsFromObjectConditionsStrategyImpl
async search(
args: FlowIdSearchStrategyArgs
): Promise<FlowIdSearchStrategyResponse> {
const { models, flowObjectsConditions } = args;
const flowObjectWhere = mapFlowObjectConditionsToWhereClause(
flowObjectsConditions!
);
const { flowObjectsConditions, databaseConnection } = args;

// 1. Obtain flowIDs from flowObjects
const flowsFromFilteredFlowObjects: UniqueFlowEntity[] = [];
const tempFlowIDs: UniqueFlowEntity[][] = await Promise.all(
flowObjectWhere.map((whereClause) =>
this.flowObjectService.getFlowFromFlowObjects(models, whereClause)
)
);
const flowObjects =
await this.flowObjectService.getFlowObjectsByFlowObjectConditions(
databaseConnection,
flowObjectsConditions
);

// Flatten array of arrays keeping only values present in all arrays
const flowIDs = tempFlowIDs.flat();
flowsFromFilteredFlowObjects.push(...new Set(flowIDs));
// 1.1. Check if flowObjects is undefined
if (!flowObjects) {
return { flows: [] };
}

// 2. Map flowObjects to UniqueFlowEntity and store in flowsFromFilteredFlowObjects
for (const flowObject of flowObjects) {
flowsFromFilteredFlowObjects.push({
id: flowObject.flowID,
versionID: flowObject.versionID,
});
}

return { flows: flowsFromFilteredFlowObjects };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { GetFlowIdsFromNestedFlowFiltersStrategyImpl } from './get-flowIds-flow-
import { GetFlowIdsFromObjectConditionsStrategyImpl } from './get-flowIds-flow-object-conditions-strategy-impl';
import {
intersectUniqueFlowEntities,
mapFlowObjectConditions,
mapFlowOrderBy,
prepareFlowConditions,
sortEntitiesByReferenceList,
Expand Down Expand Up @@ -100,10 +99,15 @@ export class SearchFlowByFiltersStrategy implements FlowSearchStrategy {
if (isFilterByNestedFilters) {
const { flows }: FlowIdSearchStrategyResponse =
await this.getFlowIdsFromNestedFlowFilters.search({
databaseConnection,
models,
nestedFlowFilters,
});

//If after this filter we have no flows, we can return an empty array
if (flows.length === 0) {
return { flows: [], count: 0 };
}
// Since there can be many flowIDs returned
// This can cause 'Maximum call stack size exceeded' error
// When using the spread operator - a workaround is to use push fot each element
Expand Down Expand Up @@ -133,6 +137,11 @@ export class SearchFlowByFiltersStrategy implements FlowSearchStrategy {
flowObjectsConditions: undefined,
});

//If after this filter we have no flows, we can return an empty array
if (flows.length === 0) {
return { flows: [], count: 0 };
}

// Since there can be many flowIDs returned
// This can cause 'Maximum call stack size exceeded' error
// When using the spread operator - a workaround is to use push fot each element
Expand All @@ -147,15 +156,18 @@ export class SearchFlowByFiltersStrategy implements FlowSearchStrategy {

const flowsFromObjectFilters: UniqueFlowEntity[] = [];
if (isFilterByFlowObjects) {
const flowObjectConditionsMap =
mapFlowObjectConditions(flowObjectFilters);
const { flows }: FlowIdSearchStrategyResponse =
await this.getFlowIdsFromObjectConditions.search({
databaseConnection,
models,
flowObjectsConditions: flowObjectConditionsMap,
flowObjectsConditions: flowObjectFilters,
});

//If after this filter we have no flows, we can return an empty array
if (flows.length === 0) {
return { flows: [], count: 0 };
}

// Since there can be many flowIDs returned
// This can cause 'Maximum call stack size exceeded' error
// When using the spread operator - a workaround is to use push fot each element
Expand All @@ -178,6 +190,11 @@ export class SearchFlowByFiltersStrategy implements FlowSearchStrategy {
orderBy: { column: orderBy.column, order: orderBy.order },
});

//If after this filter we have no flows, we can return an empty array
if (flows.length === 0) {
return { flows: [], count: 0 };
}

// Since there can be many flowIDs returned
// This can cause 'Maximum call stack size exceeded' error
// When using the spread operator - a workaround is to use push fot each element
Expand Down
75 changes: 1 addition & 74 deletions src/domain-services/flows/strategy/impl/utils.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,9 @@
import { type FlowId } from '@unocha/hpc-api-core/src/db/models/flow';
import { Cond, Op } from '@unocha/hpc-api-core/src/db/util/conditions';
import type Knex from 'knex';
import {
type FlowCategory,
type FlowObjectFilters,
type SearchFlowsFilters,
} from '../../graphql/args';
import { type FlowCategory, type SearchFlowsFilters } from '../../graphql/args';
import { type UniqueFlowEntity } from '../../model';

/*
* Map structure:
* {
* KEY = objectType: string,
* VALUE = {
* KEY = refDirection: string,
* VALUE = [objectID: number]
* }
* }
*/
export function mapFlowObjectConditionsToWhereClause(
flowObjectConditions: Map<string, Map<string, number[]>>
): any[] {
const whereClauses: any = [];
for (const [objectType, refDirectionMap] of flowObjectConditions) {
for (const [refDirection, objectIDs] of refDirectionMap) {
const whereClause = {
objectID: {
[Op.IN]: objectIDs,
},
refDirection: {
[Op.LIKE]: refDirection,
},
objectType: {
[Op.LIKE]: objectType,
},
};

whereClauses.push(whereClause);
}
}

return whereClauses;
}

export function mapFlowCategoryConditionsToWhereClause(
flowCategoryConditions: FlowCategory[]
) {
Expand Down Expand Up @@ -200,40 +161,6 @@ export function mapCountResultToCountObject(countRes: any[]) {
return countObject;
}

export function mapFlowObjectConditions(
flowObjectFilters: FlowObjectFilters[] = []
): Map<string, Map<string, number[]>> {
const flowObjectsConditions: Map<string, Map<string, number[]>> = new Map<
string,
Map<string, number[]>
>();

for (const flowObjectFilter of flowObjectFilters) {
const { objectType, direction, objectID } = flowObjectFilter;

if (!flowObjectsConditions.has(objectType)) {
flowObjectsConditions.set(objectType, new Map<string, number[]>());
}

const refDirectionMap = flowObjectsConditions.get(objectType);
if (!refDirectionMap!.has(direction)) {
refDirectionMap!.set(direction, []);
}

const objectIDsArray = refDirectionMap!.get(direction);

if (objectIDsArray!.includes(objectID)) {
throw new Error(
`Duplicate flow object filter: ${objectType} ${direction} ${objectID}`
);
}

objectIDsArray!.push(objectID);
}

return flowObjectsConditions;
}

export function mergeUniqueEntities(
listA: UniqueFlowEntity[],
listB: UniqueFlowEntity[]
Expand Down

0 comments on commit 04c165e

Please sign in to comment.