Skip to content

fix: include empty or null values#4124

Open
ElfenB wants to merge 1 commit intoumami-software:masterfrom
ElfenB:fix-4123
Open

fix: include empty or null values#4124
ElfenB wants to merge 1 commit intoumami-software:masterfrom
ElfenB:fix-4123

Conversation

@ElfenB
Copy link
Copy Markdown

@ElfenB ElfenB commented Apr 1, 2026

fixes #4123

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 1, 2026

@ElfenB is attempting to deploy a commit to the Umami Software Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 1, 2026

Greptile Summary

This PR fixes filter queries in both the ClickHouse and Prisma (PostgreSQL) database layers so that rows with absent/empty values are correctly included when applying negative filter operators (notEquals, doesNotContain).

Key changes:

  • prisma.ts: Adds OR column IS NULL to both notEquals and doesNotContain clauses. This is a necessary fix because PostgreSQL's three-valued logic causes NULL != value and NULL NOT ILIKE value to evaluate to NULL (falsy), silently dropping rows with NULL column values from negative-filter results.
  • clickhouse.ts: Adds OR column = '' to the notEquals clause only. ClickHouse website_event string columns are declared as non-nullable (String, not Nullable(String)), so '' is the correct "no value" sentinel rather than NULL. The doesNotContain case intentionally requires no fix: positionCaseInsensitive('', value) already returns 0, so empty-string rows naturally pass the = 0 check.

Minor concern: In ClickHouse, if a user ever provides an empty string '' as the filter value for notEquals, the generated SQL (column != '' OR column = '') is a tautology that matches every row. This is a pre-existing edge case amplified slightly by the fix, but unlikely to surface in practice given how filters are populated from UI inputs.

Confidence Score: 5/5

Safe to merge — both fixes are logically correct for their respective databases and address the reported bug.

The Prisma fix is straightforward and correct: adding OR IS NULL is the standard SQL pattern for including NULL rows in negative comparisons. The ClickHouse fix correctly uses OR = '' to match the non-nullable String column design. The only remaining concern (tautology when filtering by empty string in ClickHouse) is a P2 edge case that does not affect normal usage.

No files require special attention, though src/lib/clickhouse.ts has a minor edge-case noted above.

Important Files Changed

Filename Overview
src/lib/clickhouse.ts Fixed notEquals to include empty-string rows (OR column = ''). The doesNotContain case needs no fix because positionCaseInsensitive('', value) = 0 is already TRUE for empty strings. Minor edge case: filtering "not equals empty string" now produces a tautology that matches all rows.
src/lib/prisma.ts Fixed notEquals and doesNotContain to include NULL rows via OR column IS NULL. Correct and necessary: in SQL, NULL != value and NULL NOT ILIKE value both evaluate to NULL (falsy), silently excluding NULL rows from these negative filters.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[mapFilter called] --> B{operator}
    B --> |equals| C["column = value"]
    B --> |notEquals - Prisma| D["(column != value OR column IS NULL)"]
    B --> |notEquals - ClickHouse| E["(column != value OR column = '')"]
    B --> |contains| F["column ilike value / positionCaseInsensitive > 0"]
    B --> |doesNotContain - Prisma| G["(column NOT ilike value OR column IS NULL)"]
    B --> |doesNotContain - ClickHouse| H["positionCaseInsensitive = 0"]

    note1["ClickHouse String columns are non-nullable\n→ empty string '' is the 'no value' sentinel\n→ positionCaseInsensitive('', v) = 0 already TRUE\n   so doesNotContain needs no fix"]
    note2["Postgres columns are nullable\n→ NULL rows silently dropped by != and NOT ILIKE\n→ OR IS NULL fix required for both operators"]

    E -.-> note1
    D -.-> note2
    G -.-> note2
Loading

Reviews (1): Last reviewed commit: "fix: include empty or null values" | Re-trigger Greptile

Comment thread src/lib/clickhouse.ts
return `${column} = ${value}`;
case OPERATORS.notEquals:
return `${column} != ${value}`;
return `(${column} != ${value} OR ${column} = '')`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Tautology when filtering by empty string

The guard OR ${column} = '' is the ClickHouse equivalent of IS NULL for Postgres, but it introduces a tautology when the filter value itself is an empty string ''. In that case the generated SQL becomes:

(column != '' OR column = '')

which is always TRUE for every row — so a "not equals empty-string" filter would return the entire dataset instead of excluding nothing. In practice users are unlikely to filter on an exact empty-string value, but it is worth being aware of this edge case. The Prisma fix avoids this because IS NULL only catches NULL, not the '' value itself.

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.

Tag filter "is not" does not seem to work

1 participant