From 04c165ef7bc1893aee45a35fc757defa5a153dbb Mon Sep 17 00:00:00 2001 From: manelcecs Date: Fri, 1 Mar 2024 11:14:52 +0100 Subject: [PATCH] Change how flowObject filters intersect results --- .../flow-object/flow-object-service.ts | 35 ++++++++- .../flows/strategy/flowID-search-strategy.ts | 4 +- ...ds-flow-object-conditions-strategy-impl.ts | 32 ++++---- .../search-flow-by-filters-strategy-impl.ts | 25 ++++++- .../flows/strategy/impl/utils.ts | 75 +------------------ 5 files changed, 77 insertions(+), 94 deletions(-) diff --git a/src/domain-services/flow-object/flow-object-service.ts b/src/domain-services/flow-object/flow-object-service.ts index 0baf427b..3fe5fb64 100644 --- a/src/domain-services/flow-object/flow-object-service.ts +++ b/src/domain-services/flow-object/flow-object-service.ts @@ -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 { @@ -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; + } } diff --git a/src/domain-services/flows/strategy/flowID-search-strategy.ts b/src/domain-services/flows/strategy/flowID-search-strategy.ts index 2770715e..efb895b8 100644 --- a/src/domain-services/flows/strategy/flowID-search-strategy.ts +++ b/src/domain-services/flows/strategy/flowID-search-strategy.ts @@ -8,9 +8,9 @@ export interface FlowIdSearchStrategyResponse { } export interface FlowIdSearchStrategyArgs { - databaseConnection?: Knex; + databaseConnection: Knex; models: Database; - flowObjectsConditions?: Map>; + flowObjectsConditions?: any; // TODO: use proper type flowCategoryConditions?: FlowCategory[]; nestedFlowFilters?: NestedFlowFilters; shortcutFilter?: any[] | null; diff --git a/src/domain-services/flows/strategy/impl/get-flowIds-flow-object-conditions-strategy-impl.ts b/src/domain-services/flows/strategy/impl/get-flowIds-flow-object-conditions-strategy-impl.ts index 61a0c7bd..6b00e9ea 100644 --- a/src/domain-services/flows/strategy/impl/get-flowIds-flow-object-conditions-strategy-impl.ts +++ b/src/domain-services/flows/strategy/impl/get-flowIds-flow-object-conditions-strategy-impl.ts @@ -6,7 +6,6 @@ import { type FlowIdSearchStrategyArgs, type FlowIdSearchStrategyResponse, } from '../flowID-search-strategy'; -import { mapFlowObjectConditionsToWhereClause } from './utils'; @Service() export class GetFlowIdsFromObjectConditionsStrategyImpl @@ -17,21 +16,28 @@ export class GetFlowIdsFromObjectConditionsStrategyImpl async search( args: FlowIdSearchStrategyArgs ): Promise { - 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 }; } diff --git a/src/domain-services/flows/strategy/impl/search-flow-by-filters-strategy-impl.ts b/src/domain-services/flows/strategy/impl/search-flow-by-filters-strategy-impl.ts index d1c6bde8..c5195a6c 100644 --- a/src/domain-services/flows/strategy/impl/search-flow-by-filters-strategy-impl.ts +++ b/src/domain-services/flows/strategy/impl/search-flow-by-filters-strategy-impl.ts @@ -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, @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/src/domain-services/flows/strategy/impl/utils.ts b/src/domain-services/flows/strategy/impl/utils.ts index bdc8db79..20cd9b70 100644 --- a/src/domain-services/flows/strategy/impl/utils.ts +++ b/src/domain-services/flows/strategy/impl/utils.ts @@ -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> -): 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[] ) { @@ -200,40 +161,6 @@ export function mapCountResultToCountObject(countRes: any[]) { return countObject; } -export function mapFlowObjectConditions( - flowObjectFilters: FlowObjectFilters[] = [] -): Map> { - const flowObjectsConditions: Map> = new Map< - string, - Map - >(); - - for (const flowObjectFilter of flowObjectFilters) { - const { objectType, direction, objectID } = flowObjectFilter; - - if (!flowObjectsConditions.has(objectType)) { - flowObjectsConditions.set(objectType, new Map()); - } - - 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[]