Skip to content

Conversation

@mod08
Copy link

@mod08 mod08 commented Jan 9, 2026

Fixes #3947

The total visitor count was being filtered by eventType, which I believe is incorrect. I’ve therefore removed the eventType parameter from the parseFilters function.

@vercel
Copy link

vercel bot commented Jan 9, 2026

@mod08 is attempting to deploy a commit to the umami-software Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This PR removes the eventType filter from both the relational and ClickHouse queries in getGoal.ts. This change introduces a critical bug that causes incorrect query results.

Problem

The goal query counts unique sessions that match a specific condition (path or event name) and calculates a conversion rate against total sessions. The eventType filter is essential because:

  1. Path-based goals (type === 'path') should only count pageView events (eventType = 1)
  2. Event-based goals (type !== 'path') should only count customEvent events (eventType = 2)

According to the database schema (prisma/schema.prisma):

  • All events have a url_path (the URL where the event occurred)
  • Only custom events have an event_name field populated
  • The eventType field distinguishes between pageViews (1) and customEvents (2)

Impact

Without the eventType filter:

  • Path-based goals will incorrectly include custom events that occurred on the target path, inflating the goal count
  • Event-based goals may incorrectly include page views (though less likely since pageViews typically have null event_name)
  • The total count (denominator) will incorrectly mix both event types, making conversion rates meaningless

Required Fix

Restore the eventType calculation and pass it to parseFilters:

const eventType = type === 'path' ? EVENT_TYPE.pageView : EVENT_TYPE.customEvent;

Then include it in the parseFilters call (as shown in similar queries like getAttribution.ts).

Confidence Score: 0/5

  • UNSAFE TO MERGE - introduces critical data correctness bug
  • This PR removes essential filtering that prevents mixing of different event types (pageViews vs customEvents). The bug will cause all goal metrics to return incorrect data, mixing event types that should be kept separate. This is a P0 blocking issue that will break the goal tracking feature.
  • src/queries/sql/reports/getGoal.ts requires the eventType filter to be restored in both relationalQuery and clickhouseQuery functions

Important Files Changed

File Analysis

Filename Score Overview
src/queries/sql/reports/getGoal.ts 1/5 Removed eventType filter from goal queries, causing incorrect mixing of pageView and customEvent data types

Sequence Diagram

sequenceDiagram
    participant Client
    participant API as /api/reports/goal
    participant getGoal
    participant parseFilters
    participant DB as Database

    Client->>API: POST with goal parameters
    Note over Client,API: {type: 'path', value: '/checkout'}
    API->>getGoal: getGoal(websiteId, params, filters)
    
    alt Before this PR (correct)
        getGoal->>getGoal: eventType = EVENT_TYPE.pageView
        getGoal->>parseFilters: parseFilters({...filters, eventType})
        parseFilters->>parseFilters: Add "event_type = 1" to filterQuery
        getGoal->>DB: Query with event_type filter
        Note over DB: Only counts pageView events
        DB-->>getGoal: {num: 50, total: 1000}
    else After this PR (incorrect)
        getGoal->>parseFilters: parseFilters({...filters})
        Note over parseFilters: NO event_type filter added
        getGoal->>DB: Query WITHOUT event_type filter
        Note over DB: Counts ALL event types<br/>(pageViews + customEvents)
        DB-->>getGoal: {num: 75, total: 1500}
        Note over getGoal,DB: WRONG - includes custom events<br/>that occurred on /checkout
    end
    
    getGoal-->>API: Result
    API-->>Client: Goal metrics
Loading

Comment on lines 32 to 39
const column = type === 'path' ? 'url_path' : 'event_name';
const { filterQuery, dateQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters({
...filters,
websiteId,
value,
startDate,
endDate,
eventType,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P0] Removing the eventType filter causes incorrect query results. When type === 'path', the query should filter by eventType: EVENT_TYPE.pageView (1), and when type !== 'path', it should filter by eventType: EVENT_TYPE.customEvent (2). Without this filter, path-based goals will incorrectly include custom events that occurred on that path, and event-based goals will incorrectly include page views. The eventType parameter must be added back to parseFilters.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/queries/sql/reports/getGoal.ts
Line: 32:39

Comment:
[P0] Removing the `eventType` filter causes incorrect query results. When `type === 'path'`, the query should filter by `eventType: EVENT_TYPE.pageView` (1), and when `type !== 'path'`, it should filter by `eventType: EVENT_TYPE.customEvent` (2). Without this filter, path-based goals will incorrectly include custom events that occurred on that path, and event-based goals will incorrectly include page views. The eventType parameter must be added back to parseFilters.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 72 to 79
const column = type === 'path' ? 'url_path' : 'event_name';
const { filterQuery, dateQuery, cohortQuery, queryParams } = parseFilters({
...filters,
websiteId,
value,
startDate,
endDate,
eventType,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P0] Same issue as in the relational query - the eventType filter must be added back to parseFilters to prevent incorrect mixing of pageView and customEvent types. Without it, both the numerator (goal count) and denominator (total count) will include events of the wrong type.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/queries/sql/reports/getGoal.ts
Line: 72:79

Comment:
[P0] Same issue as in the relational query - the `eventType` filter must be added back to parseFilters to prevent incorrect mixing of pageView and customEvent types. Without it, both the numerator (goal count) and denominator (total count) will include events of the wrong type.

How can I resolve this? If you propose a fix, please make it concise.

@mod08
Copy link
Author

mod08 commented Jan 13, 2026

@greptileai

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Jan 13, 2026

Greptile Overview

Greptile Summary

This PR addresses issue #3947 by separating the filter logic for goal queries. The implementation splits parseFilters into two calls:

  1. Base filters (without eventType): Used to calculate the total session count
  2. Goal filters (with eventType): Used to calculate sessions that achieved the specific goal

The approach correctly applies the explicit eventType (pageView or customEvent) only to the goal-specific count, while intending to exclude it from the total count. Both relational (PostgreSQL) and ClickHouse query implementations follow the same pattern.

However, there is a critical logic error: If users include eventType in their filter parameters (which is possible via getRequestFilters in src/lib/request.ts), it will still be spread into the base filters on lines 42-47 and 101-106. This means the total count will still be filtered by eventType when users explicitly provide it, defeating the purpose of the fix.

The fix requires explicitly excluding eventType from the filters object before passing it to the first parseFilters call using destructuring: const { eventType: _, ...filtersWithoutEventType } = filters;

Positive aspects:

  • Consistent implementation across both database backends
  • Well-commented code explaining the intent
  • Query structure is sound and efficient
  • Type safety is maintained

Areas needing attention:

Confidence Score: 2/5

  • This PR has a critical logic error that prevents it from fully achieving its stated goal
  • The score reflects a significant logic bug where user-provided eventType filters will still be applied to the total count, defeating the purpose of this fix. While the approach is sound and the code is well-structured, the implementation fails to handle the edge case where eventType is included in the filters parameter. This is not a syntax error or style issue, but a functional bug that will cause incorrect behavior when users filter by eventType.
  • The single file changed (src/queries/sql/reports/getGoal.ts) requires attention at lines 42-47 and 101-106 to properly exclude eventType from the base filters

Important Files Changed

File Analysis

Filename Score Overview
src/queries/sql/reports/getGoal.ts 2/5 Separates eventType filtering for total vs goal counts, but fails to exclude user-provided eventType from total count calculation

Sequence Diagram

sequenceDiagram
    participant Client
    participant API as /api/reports/goal
    participant getGoal
    participant parseFilters
    participant DB as Database

    Client->>API: POST request with filters & parameters
    API->>API: getQueryFilters(filters)
    Note over API: Extracts eventType from filters if present
    API->>getGoal: getGoal(websiteId, parameters, filters)
    
    Note over getGoal: Calculate explicit eventType from type
    getGoal->>getGoal: eventType = type === 'path' ? 1 : 2
    
    Note over getGoal: First parseFilters (base filters)
    getGoal->>parseFilters: parseFilters({...filters, websiteId, startDate, endDate})
    Note over parseFilters: BUG: filters may include eventType
    parseFilters-->>getGoal: {baseFilterQuery, dateQuery, cohortQuery, baseQueryParams}
    
    Note over getGoal: Second parseFilters (goal filters)
    getGoal->>parseFilters: parseFilters({...filters, websiteId, value, startDate, endDate, eventType})
    parseFilters-->>getGoal: {goalFilterQuery, goalQueryParams}
    
    Note over getGoal: Merge parameters
    getGoal->>getGoal: queryParams = {...baseQueryParams, ...goalQueryParams}
    Note over getGoal: goalQueryParams.eventType overwrites baseQueryParams.eventType
    
    Note over getGoal: Execute SQL query
    getGoal->>DB: SELECT num, total FROM website_event
    Note over DB: Subquery uses baseFilterQuery<br/>(may incorrectly filter by eventType)
    Note over DB: Main query uses goalFilterQuery<br/>(correctly filters by eventType)
    DB-->>getGoal: {num, total}
    
    getGoal-->>API: Query result
    API-->>Client: JSON response
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +42 to +47
} = parseFilters({
...filters,
websiteId,
startDate,
endDate,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filters object is spread into the first parseFilters call, which means if the user includes eventType in their filters (e.g., via query parameters), it will still be applied to the total count calculation. This defeats the purpose of this fix, which is to exclude eventType filtering from the total count.

The getRequestFilters function in src/lib/request.ts (lines 70-81) extracts all filter parameters including eventType from the request, so users can pass eventType as a filter. When this happens:

  1. baseFilterQuery will include and event_type = {{eventType}}
  2. queryParams (after merging on line 59) will contain the explicit eventType value from line 56
  3. The total count subquery will be incorrectly filtered by event_type

To fix this, eventType should be explicitly excluded from the first parseFilters call:

Suggested change
} = parseFilters({
...filters,
websiteId,
startDate,
endDate,
});
// Filters WITHOUT eventType for total count (all sessions)
const { eventType: _, ...filtersWithoutEventType } = filters;
const {
filterQuery: baseFilterQuery,
dateQuery,
joinSessionQuery,
cohortQuery,
queryParams: baseQueryParams,
} = parseFilters({
...filtersWithoutEventType,
websiteId,
startDate,
endDate,
});

This ensures the total count is never filtered by eventType, regardless of whether it's provided in the user's filters.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/queries/sql/reports/getGoal.ts
Line: 42:47

Comment:
The `filters` object is spread into the first parseFilters call, which means if the user includes `eventType` in their filters (e.g., via query parameters), it will still be applied to the total count calculation. This defeats the purpose of this fix, which is to exclude eventType filtering from the total count.

The `getRequestFilters` function in `src/lib/request.ts` (lines 70-81) extracts all filter parameters including `eventType` from the request, so users can pass eventType as a filter. When this happens:

1. `baseFilterQuery` will include `and event_type = {{eventType}}`
2. `queryParams` (after merging on line 59) will contain the explicit `eventType` value from line 56
3. The total count subquery will be incorrectly filtered by event_type

To fix this, `eventType` should be explicitly excluded from the first parseFilters call:

```suggestion
  // Filters WITHOUT eventType for total count (all sessions)
  const { eventType: _, ...filtersWithoutEventType } = filters;
  const {
    filterQuery: baseFilterQuery,
    dateQuery,
    joinSessionQuery,
    cohortQuery,
    queryParams: baseQueryParams,
  } = parseFilters({
    ...filtersWithoutEventType,
    websiteId,
    startDate,
    endDate,
  });
```

This ensures the total count is never filtered by eventType, regardless of whether it's provided in the user's filters.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +101 to +106
} = parseFilters({
...filters,
websiteId,
startDate,
endDate,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as in the relational query: if eventType is included in the user's filters, it will be applied to the total count calculation, defeating the purpose of this fix.

The eventType should be explicitly excluded from the filters passed to this parseFilters call:

Suggested change
} = parseFilters({
...filters,
websiteId,
startDate,
endDate,
});
// Filters WITHOUT eventType for total count (all sessions)
const { eventType: _, ...filtersWithoutEventType } = filters;
const {
filterQuery: baseFilterQuery,
dateQuery,
cohortQuery,
queryParams: baseQueryParams,
} = parseFilters({
...filtersWithoutEventType,
websiteId,
startDate,
endDate,
});
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/queries/sql/reports/getGoal.ts
Line: 101:106

Comment:
Same issue as in the relational query: if `eventType` is included in the user's filters, it will be applied to the total count calculation, defeating the purpose of this fix.

The `eventType` should be explicitly excluded from the filters passed to this parseFilters call:

```suggestion
  // Filters WITHOUT eventType for total count (all sessions)
  const { eventType: _, ...filtersWithoutEventType } = filters;
  const {
    filterQuery: baseFilterQuery,
    dateQuery,
    cohortQuery,
    queryParams: baseQueryParams,
  } = parseFilters({
    ...filtersWithoutEventType,
    websiteId,
    startDate,
    endDate,
  });
```

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wrong Visitors / Visits Count in Goals

1 participant