fix(db): qualify event-level properties access in funnel & conversion queries#404
Conversation
… queries
When a funnel mixes a profile.* filter with an event-level properties.*
filter on different steps, or a conversion has a profile.* breakdown
alongside an event-level properties.* filter, ClickHouse fails with:
JOIN ... ambiguous identifier 'properties'
Root cause: the profile JOIN selects the 'properties' Map from
profiles. The windowFunnel step conditions then have two columns
named 'properties' in scope — events.properties and profile.properties
— and any unqualified properties['key'] reference becomes ambiguous.
Fix: pass eventsAlias='events' to getEventFiltersWhereClause in
funnel.service.ts:getFunnelConditions and conversion.service.ts where-
clause builders. This reuses the existing eventsAlias plumbing in
getSelectPropertyKey (lines 322-328, already used by other call sites
at chart.service.ts:448 and :866 with 'e' alias), so event-level
properties[...] becomes events.properties[...] — explicitly bound to
the events table, no ambiguity with profile.properties[...].
📝 WalkthroughWalkthroughConversion and funnel query builders now pass an ChangesEvent filter qualification
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/db/src/services/conversion.service.ts`:
- Around line 121-131: The conversion query still fails for profile-filter-only
cases because profile joins are gated only by breakdowns. Update the profile
join condition in conversion.service’s conversion query builder so it also
enables the profiles join when either event’s filters reference profile fields,
matching the funnel logic. Use the existing getEventFiltersWhereClause /
profileJoin / breakdown handling in the conversion service to detect profile.*
filters and ensure the query joins profiles whenever profile-based filters or
breakdowns are present, not just when a profile breakdown is selected.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 63b94a57-e5e3-4221-a632-ccb6ad206e9a
📒 Files selected for processing (2)
packages/db/src/services/conversion.service.tspackages/db/src/services/funnel.service.ts
| // Qualify with 'events' so event-level `properties[...]` becomes | ||
| // `events.properties[...]` — required when the conversion query also | ||
| // joins the profiles table (which exposes a `properties` column). | ||
| // Without the qualifier ClickHouse fails with "ambiguous identifier | ||
| // 'properties'" whenever a step filters on properties.X while a | ||
| // breakdown is on profile.properties.Y. | ||
| const whereA = Object.values( | ||
| getEventFiltersWhereClause(eventA.filters, projectId), | ||
| getEventFiltersWhereClause(eventA.filters, projectId, 'events'), | ||
| ).join(' AND '); | ||
| const whereB = Object.values( | ||
| getEventFiltersWhereClause(eventB.filters, projectId), | ||
| getEventFiltersWhereClause(eventB.filters, projectId, 'events'), |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
This still misses profile-filter-only conversion queries.
Line 127 fixes ambiguous events.properties[...], but profileJoin is still only enabled for profile breakdowns at Lines 61-98. In the reported case where an event filter includes profile.* and another includes properties.*, this path now emits profile.properties[...] plus events.properties[...], and the query still 500s unless a profile breakdown also happens to be present. Funnel already handles this by joining profiles when either filters or breakdowns reference profile.; conversion needs the same gating here.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/db/src/services/conversion.service.ts` around lines 121 - 131, The
conversion query still fails for profile-filter-only cases because profile joins
are gated only by breakdowns. Update the profile join condition in
conversion.service’s conversion query builder so it also enables the profiles
join when either event’s filters reference profile fields, matching the funnel
logic. Use the existing getEventFiltersWhereClause / profileJoin / breakdown
handling in the conversion service to detect profile.* filters and ensure the
query joins profiles whenever profile-based filters or breakdowns are present,
not just when a profile breakdown is selected.
Summary
Fixes a 500 error when a funnel mixes
profile.*filters with event-levelproperties.*filters on different steps (and the equivalent for conversion charts).Repro (funnel)
A funnel with 4 steps:
$identifyfiltered byprofile.properties.quotaPlan = 'FRAMEO_BASIC'homePageViewed(no filter)pricingModalViewedfiltered byproperties.$current_url contains 'canvas'paymentSuccessProduces:
Removing either the
$current_urlfilter OR theprofile.properties.quotaPlanfilter makes the query work.Root cause
The funnel CTE adds a profile JOIN that selects
properties(the whole Map) whenever any step referencesprofile.properties.X. ClickHouse then has TWOpropertiescolumns in scope —events.propertiesandprofile.properties— and any unqualifiedproperties['key']in a step condition is ambiguous.The conversion service has the equivalent issue when a
profile.properties.Xbreakdown is combined with event-levelproperties.Xfilters.Fix
getEventFiltersWhereClausealready accepts aneventsAliasparameter (line 1125, documented to handle exactly this case). It's used atchart.service.ts:448and:866with'e'for the regular chart paths. The funnel and conversion services just weren't passing it.Pass
eventsAlias='events'(matching the unaliased table name used in those services):funnel.service.ts:getFunnelConditions→ propagates towindowFunnelstep conditionsconversion.service.ts→ propagates to the twowhereA/whereBstep conditionsAfter the fix, the SQL becomes:
events.properties[...]is explicitly bound to the events table — no ambiguity withprofile.properties[...].Why this approach
The codebase already has the
eventsAliasplumbing for exactly this purpose. Other chart paths (regularchartandchart breakdown) already use it. Funnel + conversion were the two paths still missing it — likely an oversight from wheneventsAliaswas added.This keeps the patch minimal (15 lines, 2 files) and consistent with the existing pattern instead of introducing a different mechanism.
Test plan
profile.*filter only — unchangedproperties.*filter only — unchangedSummary by CodeRabbit
propertiesreferences when filters are used alongside profile-related data.