@@ -195,8 +195,11 @@ export async function queryMembersAdvanced(
195195 // Initialize cache
196196 const cache = new MemberQueryCache ( redis )
197197
198- // Build cache key — countOnly is excluded so count and full-result caches share the same key.
199- // Normalize empty search to null so "" and null produce the same key (buildSearchCTE treats them identically).
198+ // Normalize search once: trim whitespace, lowercase (buildSearchCTE lowercases anyway),
199+ // convert empty string to null so "" and null hash identically.
200+ const normalizedSearch = search ?. trim ( ) . toLowerCase ( ) || null
201+
202+ // Full result key — includes pagination and projection so page 1 and page 2 are separate entries.
200203 const cacheKey = cache . buildCacheKey ( {
201204 fields,
202205 filter,
@@ -205,18 +208,26 @@ export async function queryMembersAdvanced(
205208 limit,
206209 offset,
207210 orderBy,
208- search : search ?. trim ( ) || null ,
211+ search : normalizedSearch ,
212+ segmentId,
213+ } )
214+
215+ // Count key — excludes pagination/projection and include flags since the count query
216+ // only depends on filter, search, and segmentId (buildCountQuery hardcodes includeMemberOrgs=false).
217+ const countCacheKey = cache . buildCountCacheKey ( {
218+ filter,
219+ search : normalizedSearch ,
209220 segmentId,
210221 } )
211222
212223 // Try to get from cache first
213224 const cachedResult = countOnly ? null : await cache . get ( cacheKey )
214- const cachedCount = countOnly ? await cache . getCount ( cacheKey ) : null
225+ const cachedCount = countOnly ? await cache . getCount ( countCacheKey ) : null
215226
216227 if ( cachedResult ) {
217228 refreshCacheInBackground ( bgQx , redis , cacheKey , {
218229 filter,
219- search,
230+ search : normalizedSearch ,
220231 limit,
221232 offset,
222233 orderBy,
@@ -233,16 +244,16 @@ export async function queryMembersAdvanced(
233244 }
234245
235246 if ( countOnly && cachedCount !== null ) {
236- refreshCountCacheInBackground ( bgQx , redis , cacheKey , {
247+ refreshCountCacheInBackground ( bgQx , redis , countCacheKey , {
237248 filter,
238- search,
249+ search : normalizedSearch ,
239250 segmentId,
240251 include,
241252 includeAllAttributes,
242253 attributeSettings,
243254 } )
244255
245- log . debug ( `Members advanced count query cache hit: ${ cacheKey } ` )
256+ log . debug ( `Members advanced count query cache hit: ${ countCacheKey } ` )
246257 return {
247258 rows : [ ] ,
248259 count : cachedCount ,
@@ -253,7 +264,7 @@ export async function queryMembersAdvanced(
253264
254265 return await executeQuery ( qx , redis , cacheKey , {
255266 filter,
256- search,
267+ search : normalizedSearch ,
257268 limit,
258269 offset,
259270 orderBy,
@@ -299,6 +310,11 @@ export async function executeQuery(
299310 } : IQueryMembersAdvancedParams ,
300311) : Promise < PageData < IDbMemberData > > {
301312 const cache = new MemberQueryCache ( redis )
313+ const countCacheKey = cache . buildCountCacheKey ( {
314+ filter,
315+ search : search ?? null ,
316+ segmentId,
317+ } )
302318 const withAggregates = ! ! segmentId
303319 const searchConfig = buildSearchCTE ( search )
304320
@@ -352,8 +368,7 @@ export async function executeQuery(
352368 const result = await qx . selectOne ( countQuery , params )
353369 const count = parseInt ( result . count , 10 )
354370
355- // Cache the count
356- await cache . setCount ( cacheKey , count , 21600 ) // 6 hours TTL
371+ await cache . setCount ( countCacheKey , count , 21600 )
357372
358373 return {
359374 rows : [ ] ,
@@ -540,8 +555,8 @@ export async function executeQuery(
540555 const result = { rows, count, limit, offset }
541556
542557 await Promise . all ( [
543- cache . set ( cacheKey , result , 21600 ) , // 6 hours TTL
544- cache . setCount ( cacheKey , count , 21600 ) , // also warm count cache so countOnly=true hits next time
558+ cache . set ( cacheKey , result , 21600 ) ,
559+ cache . setCount ( countCacheKey , count , 21600 ) ,
545560 ] )
546561
547562 return result
0 commit comments