@@ -103,30 +103,22 @@ const getOrderClause = (
103103 direction : OrderDirection ,
104104 withAggregates : boolean ,
105105) : string => {
106- // Default sort:
107- // - when aggregates are included → sort by activityCount (from msa)
108- // - otherwise → sort by joinedAt (from members)
109106 const defaultOrder = withAggregates ? 'msa."activityCount" DESC' : 'm."joinedAt" DESC'
110107
111- // If no specific order field is provided, use the default one
112108 if ( ! parsedField ) return defaultOrder
113109
114110 const fieldExpr = ORDER_FIELD_MAP [ parsedField ]
115111
116- // If the requested field is not mapped, fall back to default order
117112 if ( ! fieldExpr ) return defaultOrder
118113
119- // Safety check:
120- // If the order field refers to msa.* but aggregates are not included,
121- // fallback to the default safe order instead of generating invalid SQL.
122114 if ( ! withAggregates && fieldExpr . includes ( 'msa.' ) ) {
123115 return defaultOrder
124116 }
125117
126- // Return the valid ORDER BY clause
127118 return `${ fieldExpr } ${ direction } `
128119}
129120
121+ // TODO: rework
130122const detectPinnedMemberId = ( filterString : string ) : { pinned : boolean ; smallList : boolean } => {
131123 if ( ! filterString ) return { pinned : false , smallList : false }
132124
@@ -211,7 +203,7 @@ export const buildQuery = ({
211203 const useActivityCountOptimized =
212204 withAggregates && ( ! sortField || sortField === 'activityCount' ) && ! filterHasMe
213205 // (we do allow mo.* now, but only outside the CTE; see below)
214-
206+ log . info ( `useActivityCountOptimized= ${ useActivityCountOptimized } ` )
215207 if ( useActivityCountOptimized ) {
216208 const ctes : string [ ] = [ ]
217209
@@ -225,9 +217,8 @@ export const buildQuery = ({
225217 ? `\n INNER JOIN member_search ms ON ms."memberId" = msa."memberId"`
226218 : ''
227219
228- // Oversample to keep page filled after outer filters; tune multiplier if needed
229- const oversampleMultiplier = 5
230- const prefetch = Math . max ( limit * oversampleMultiplier + offset , limit + offset )
220+ // Fix pagination: fetch enough rows to handle the requested page correctly
221+ const totalNeeded = limit + offset
231222
232223 ctes . push (
233224 `
@@ -241,15 +232,15 @@ export const buildQuery = ({
241232 msa."segmentId" = $(segmentId)
242233 ORDER BY
243234 msa."activityCount" ${ direction } NULLS LAST
244- LIMIT ${ prefetch }
235+ LIMIT ${ totalNeeded }
245236 )
246237 ` . trim ( ) ,
247238 )
248239
249240 const withClause = `WITH ${ ctes . join ( ',\n' ) } `
250241 const memberOrgsJoin = needsMemberOrgs ? `LEFT JOIN member_orgs mo ON mo."memberId" = m.id` : ''
251242
252- // Outer filters (including mo./me.) applied here; index handles the CTE ranking
243+ // Outer filters (including mo./me.) applied here; remove OFFSET/LIMIT since top_members already handles it
253244 return `
254245 ${ withClause }
255246 SELECT ${ fields }
@@ -303,6 +294,7 @@ export const buildCountQuery = ({
303294 includeMemberOrgs = false ,
304295} : BuildCountQueryArgs ) : string => {
305296 const filterHasMo = filterString . includes ( 'mo.' )
297+ const filterHasMe = filterString . includes ( 'me.' )
306298 const needsMemberOrgs = includeMemberOrgs || filterHasMo
307299
308300 const ctes = [ needsMemberOrgs ? buildMemberOrgsCTE ( true ) : '' , searchConfig . cte ] . filter ( Boolean )
@@ -312,6 +304,7 @@ export const buildCountQuery = ({
312304 ? `INNER JOIN "memberSegmentsAgg" msa ON msa."memberId" = m.id AND msa."segmentId" = $(segmentId)`
313305 : '' ,
314306 needsMemberOrgs ? `LEFT JOIN member_orgs mo ON mo."memberId" = m.id` : '' ,
307+ filterHasMe ? `LEFT JOIN "memberEnrichments" me ON me."memberId" = m.id` : '' ,
315308 searchConfig . join ,
316309 ] . filter ( Boolean )
317310
0 commit comments