From 09529cf97f9f816af7ad01dfd7b30e04a9d00d61 Mon Sep 17 00:00:00 2001 From: Ivan Vasilov Date: Sat, 5 Sep 2026 23:44:10 +0200 Subject: [PATCH] fix: merge duplicate PostgREST IN filters --- src/functions.ts | 33 ++++++++++++++++++++++++++++++++- tests/index.test.ts | 24 ++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/functions.ts b/src/functions.ts index aa6a547..a7c8c09 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -13,6 +13,37 @@ import { } from "@tanstack/db" import type { QueryClient, QueryMeta } from "@tanstack/query-core" +const mergeInFilters = (filters: SimpleComparison[]) => { + const mergedFilters: SimpleComparison[] = [] + const filtersByField = new Map() + + for (const filter of filters) { + const field = filter.field?.join(".") + if (filter.operator !== "in" || !field) { + mergedFilters.push(filter) + continue + } + + const values = Array.isArray(filter.value) ? filter.value : [filter.value] + const existingFilter = filtersByField.get(field) + if (existingFilter) { + existingFilter.value = Array.from( + new Set([...(existingFilter.value as unknown[]), ...values]) + ) + continue + } + + const mergedFilter = { + ...filter, + value: Array.from(new Set(values)), + } + mergedFilters.push(mergedFilter) + filtersByField.set(field, mergedFilter) + } + + return mergedFilters +} + const buildQuery = ( baseQuery: PostgrestFilterBuilder, filter: SimpleComparison @@ -145,7 +176,7 @@ export const supabaseQueryFn = async ( } if (parsed.filters) { - ;[...parsed.filters, ...cursorFilters].forEach((filter) => { + mergeInFilters([...parsed.filters, ...cursorFilters]).forEach((filter) => { buildQuery(baseQuery, filter) }) } diff --git a/tests/index.test.ts b/tests/index.test.ts index 633f429..7a9f9a7 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -121,6 +121,30 @@ describe("PostgREST query generation", () => { expectFetchUrls(mockFetch, ["/rest/v1/users?select=*&id=in.(1,2,3)"]) }) + test("merges IN filters for the same column", async () => { + await queryResult((q) => + q + .from({ user: usersCollection }) + .where(({ user }) => + and(inArray(user.id, [1, 2]), inArray(user.id, [2, 3])) + ) + ) + expectFetchUrls(mockFetch, ["/rest/v1/users?select=*&id=in.(1,2,3)"]) + }) + + test("keeps IN filters for different columns separate", async () => { + await queryResult((q) => + q + .from({ user: usersCollection }) + .where(({ user }) => + and(inArray(user.id, [1, 2]), inArray(user.name, ["Alice", "Bob"])) + ) + ) + expectFetchUrls(mockFetch, [ + "/rest/v1/users?select=*&id=in.(1,2)&name=in.(Alice,Bob)", + ]) + }) + test("WHERE NOT(active = false)", async () => { await queryResult((q) => q