From 29cc6f441502e1f0a25a04c443f5985e9d9776f4 Mon Sep 17 00:00:00 2001 From: souravbhowmik1999 Date: Wed, 13 Aug 2025 15:12:23 +0530 Subject: [PATCH 1/3] set tenant type --- src/adapters/postgres/user-adapter.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/adapters/postgres/user-adapter.ts b/src/adapters/postgres/user-adapter.ts index cfa80f6c..432ae6f0 100644 --- a/src/adapters/postgres/user-adapter.ts +++ b/src/adapters/postgres/user-adapter.ts @@ -791,6 +791,7 @@ export class PostgresUserService implements IServicelocator { T."channelId", T.name AS tenantName, T.params, + T."type", UTM."Id" AS userTenantMappingId FROM public."UserTenantMapping" UTM @@ -832,6 +833,7 @@ export class PostgresUserService implements IServicelocator { params: data.params, roleId: roleId, roleName: roleName, + tenantType: data.type, // privileges: privileges, }); } From 43581567bca15828bfcb68560a2fdd87fd25522f Mon Sep 17 00:00:00 2001 From: Shubham Date: Fri, 22 Aug 2025 14:06:15 +0530 Subject: [PATCH 2/3] Added default Tenant Id in header for Searh user --- src/adapters/postgres/user-adapter.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/adapters/postgres/user-adapter.ts b/src/adapters/postgres/user-adapter.ts index cfa80f6c..b3846b54 100644 --- a/src/adapters/postgres/user-adapter.ts +++ b/src/adapters/postgres/user-adapter.ts @@ -358,7 +358,7 @@ export class PostgresUserService implements IServicelocator { ) { const apiId = APIID.USER_LIST; try { - const findData = await this.findAllUserDetails(userSearchDto); + const findData = await this.findAllUserDetails(userSearchDto, tenantId); if (findData === false) { LoggerUtil.error( @@ -401,7 +401,7 @@ export class PostgresUserService implements IServicelocator { } - async findAllUserDetails(userSearchDto) { + async findAllUserDetails(userSearchDto, tenantId?: string) { let { limit, offset, filters, exclude, sort } = userSearchDto; let excludeCohortIdes; let excludeUserIdes; @@ -554,6 +554,18 @@ export class PostgresUserService implements IServicelocator { whereCondition = ""; } + // Apply tenant filtering conditionally if tenantId is provided from headers + if (tenantId && tenantId.trim() !== '') { + if (index === 0 && whereCondition === "") { + whereCondition = `WHERE UTM."tenantId" = '${tenantId}'`; + } else { + whereCondition += ` AND UTM."tenantId" = '${tenantId}'`; + } + LoggerUtil.log(`Applying tenant filter for tenantId: ${tenantId}`, APIID.USER_LIST); + } else { + LoggerUtil.warn(`No tenantId provided - returning users from all tenants`, APIID.USER_LIST); + } + //Get user core fields data const query = `SELECT U."userId",U."enrollmentId", U."username",U."email", U."firstName",UTM."tenantId", U."middleName", U."lastName", U."gender", U."dob", R."name" AS role, U."mobile", U."createdBy",U."updatedBy", U."createdAt", U."updatedAt", U."status", COUNT(*) OVER() AS total_count FROM public."Users" U From a30cb265480efe16d34b1cd4d8578d36cb531cee Mon Sep 17 00:00:00 2001 From: Shubham Date: Fri, 22 Aug 2025 15:42:10 +0530 Subject: [PATCH 3/3] Optimzation of Field Value Queries --- src/adapters/postgres/cohort-adapter.ts | 2 +- src/adapters/postgres/fields-adapter.ts | 65 +++++++++++++++++++++++++ src/adapters/postgres/user-adapter.ts | 2 +- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/adapters/postgres/cohort-adapter.ts b/src/adapters/postgres/cohort-adapter.ts index 2f7b2860..3a158da1 100644 --- a/src/adapters/postgres/cohort-adapter.ts +++ b/src/adapters/postgres/cohort-adapter.ts @@ -858,7 +858,7 @@ export class PostgresCohortService { if (Object.keys(searchCustomFields).length > 0) { const context = "COHORT"; getCohortIdUsingCustomFields = - await this.fieldsService.filterUserUsingCustomFields( + await this.fieldsService.filterUserUsingCustomFieldsOptimized( context, searchCustomFields ); diff --git a/src/adapters/postgres/fields-adapter.ts b/src/adapters/postgres/fields-adapter.ts index 5d975391..2b0bf050 100644 --- a/src/adapters/postgres/fields-adapter.ts +++ b/src/adapters/postgres/fields-adapter.ts @@ -1538,6 +1538,71 @@ export class PostgresFieldsService implements IServicelocatorfields { return result; } + // OPTIMIZED VERSION - Much faster alternative to avoid JSON aggregation + async filterUserUsingCustomFieldsOptimized(context: string, stateDistBlockData: any) { + let joinCond = ""; + let targetTable = ""; + + if (context === "COHORT") { + joinCond = `JOIN "Cohort" u ON fv."itemId" = u."cohortId"`; + targetTable = "Cohort"; + } else if (context === "USERS") { + joinCond = `JOIN "Users" u ON fv."itemId" = u."userId"`; + targetTable = "Users"; + } else { + // Generic case - no specific table join + targetTable = "FieldValues"; + } + + // Build EXISTS conditions for each field filter + const conditions = []; + let paramIndex = 1; + const queryParams = []; + + for (const [fieldName, fieldValues] of Object.entries(stateDistBlockData)) { + const values = Array.isArray(fieldValues) ? fieldValues : [fieldValues]; + + // Create placeholders for parameterized query + const valuePlaceholders = values.map(() => `$${paramIndex++}`); + queryParams.push(...values); + + const condition = ` + EXISTS ( + SELECT 1 + FROM "FieldValues" fv_inner + JOIN "Fields" f_inner ON fv_inner."fieldId" = f_inner."fieldId" + WHERE fv_inner."itemId" = ${context === 'COHORT' ? 'c."cohortId"' : 'u."userId"'} + AND f_inner."name" = $${paramIndex} + AND (f_inner.context IN($${paramIndex + 1}, 'NULL', 'null', '') OR f_inner.context IS NULL) + AND fv_inner."value" && ARRAY[${valuePlaceholders.join(',')}] + )`; + + queryParams.push(fieldName, context); + paramIndex += 2; + conditions.push(condition); + } + + let query; + if (context === "COHORT") { + query = ` + SELECT DISTINCT c."cohortId" as "itemId" + FROM "Cohort" c + WHERE ${conditions.join(' AND ')}`; + } else if (context === "USERS") { + query = ` + SELECT DISTINCT u."userId" as "itemId" + FROM "Users" u + WHERE ${conditions.join(' AND ')}`; + } else { + // Fallback to original logic for unknown context + return this.filterUserUsingCustomFields(context, stateDistBlockData); + } + + const queryData = await this.fieldsValuesRepository.query(query, queryParams); + const result = queryData.length > 0 ? queryData.map((item) => item.itemId) : null; + return result; + } + async filterUserUsingCustomFields(context: string, stateDistBlockData: any) { const searchKey = []; let whereCondition = ` WHERE `; diff --git a/src/adapters/postgres/user-adapter.ts b/src/adapters/postgres/user-adapter.ts index 2e1676e5..19ea3170 100644 --- a/src/adapters/postgres/user-adapter.ts +++ b/src/adapters/postgres/user-adapter.ts @@ -513,7 +513,7 @@ export class PostgresUserService implements IServicelocator { const context = "USERS"; getUserIdUsingCustomFields = - await this.fieldsService.filterUserUsingCustomFields( + await this.fieldsService.filterUserUsingCustomFieldsOptimized( context, searchCustomFields );