Skip to content

Commit 0cd20c0

Browse files
committed
fix: revert direct path
1 parent 40a86fb commit 0cd20c0

1 file changed

Lines changed: 59 additions & 7 deletions

File tree

services/libs/data-access-layer/src/members/queryBuilder.ts

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,6 @@ export const buildQuery = ({
155155
const fallbackDir: OrderDirection = orderDirection || 'DESC'
156156
const { field: sortField, direction } = parseOrderBy(orderBy, fallbackDir)
157157

158-
console.log(`filterString in buildQuery: ${filterString}`)
159-
160158
// Detect alias usage in filters (to decide joins/CTEs needed outside)
161159
const filterHasMo = filterString.includes('mo.')
162160
const filterHasMe = filterString.includes('me.')
@@ -178,18 +176,72 @@ export const buildQuery = ({
178176

179177
log.info(`useDirectIdPath=${useDirectIdPath}`)
180178
if (useDirectIdPath) {
181-
// ...existing direct path code...
179+
// Direct path: start from memberSegmentsAgg keyed by (memberId, segmentId)
180+
const ctes: string[] = []
181+
if (needsMemberOrgs) ctes.push(buildMemberOrgsCTE(true).trim())
182+
183+
const withClause = ctes.length ? `WITH ${ctes.join(',\n')}` : ''
184+
185+
const memberOrgsJoin = needsMemberOrgs ? `LEFT JOIN member_orgs mo ON mo."memberId" = m.id` : ''
186+
187+
return `
188+
${withClause}
189+
SELECT ${fields}
190+
FROM "memberSegmentsAgg" msa
191+
JOIN members m
192+
ON m.id = msa."memberId"
193+
${memberOrgsJoin}
194+
LEFT JOIN "memberEnrichments" me
195+
ON me."memberId" = m.id
196+
WHERE
197+
msa."segmentId" = $(segmentId)
198+
AND (${filterString})
199+
ORDER BY ${orderClause} NULLS LAST
200+
LIMIT ${limit}
201+
OFFSET ${offset}
202+
`.trim()
182203
}
183204

205+
// Check if filters are safe for activityCount optimization
206+
const hasSafeFilters = (() => {
207+
if (!filterString || filterString.trim() === '' || filterString.match(/^\s*1\s*=\s*1\s*$/)) {
208+
return true // No filters or trivial filters
209+
}
210+
211+
// Allow filters that are likely to have good correlation with activity or are very selective
212+
const safePatterns = [
213+
/\bm\.id\s*[=IN]/i, // ID filters are always safe
214+
/\(1\s*=\s*1\)/, // Trivial conditions
215+
/AND\s*\(1\s*=\s*1\)/, // Trivial AND conditions
216+
/\(1\s*=\s*1\)\s*AND/, // Trivial conditions at start
217+
]
218+
219+
// Check if filter contains only safe patterns and basic logical operators
220+
let cleanFilter = filterString
221+
safePatterns.forEach((pattern) => {
222+
cleanFilter = cleanFilter.replace(pattern, '')
223+
})
224+
225+
// Remove whitespace, parentheses, and basic logical operators
226+
cleanFilter = cleanFilter.replace(/[\s\(\)]/g, '').replace(/\b(and|or)\b/gi, '')
227+
228+
// If nothing significant remains, it's safe
229+
return cleanFilter.length === 0
230+
})()
231+
184232
// Use activityCount optimization if:
185233
// 1. We have aggregates and are sorting by activityCount
186-
// 2. No expensive joins needed (me.*, mo.* filters)
187-
// 3. Only m.* filters (we can handle these in the CTE)
234+
// 2. No unsafe joins needed (me.*, mo.* filters)
235+
// 3. Filters are safe (mostly ID-based or trivial)
188236
const useActivityCountOptimized =
189-
withAggregates && (!sortField || sortField === 'activityCount') && !filterHasMe && !filterHasMo
237+
withAggregates &&
238+
(!sortField || sortField === 'activityCount') &&
239+
!filterHasMe &&
240+
!filterHasMo &&
241+
hasSafeFilters
190242

191243
log.info(
192-
`useActivityCountOptimized=${useActivityCountOptimized}, filterHasMe=${filterHasMe}, filterHasMo=${filterHasMo}, filterHasM=${filterHasM}`,
244+
`useActivityCountOptimized=${useActivityCountOptimized}, filterHasMe=${filterHasMe}, filterHasMo=${filterHasMo}, filterHasM=${filterHasM}, hasSafeFilters=${hasSafeFilters}`,
193245
)
194246

195247
if (useActivityCountOptimized) {

0 commit comments

Comments
 (0)