Skip to content

Commit 693fcd1

Browse files
committed
core(filters): add range, ilike-set and notInList operators
Closes the remaining gaps against the PostgREST operator surface (all additive): rangeGt/rangeGte/rangeLt/rangeLte/rangeAdjacent (sr/nxl/sl/nxr/ adj), ilikeAllOf/ilikeAnyOf, and notInList. Operator names and token mappings mirror the supabase-js client.
1 parent e4ff23b commit 693fcd1

5 files changed

Lines changed: 82 additions & 1 deletion

File tree

supabase-core/api/android/supabase-core.api

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ public final class io/github/androidpoet/supabase/core/models/WhereBuilder {
137137
public final fun greater--PLe0oE (Ljava/lang/String;Ljava/lang/Comparable;)V
138138
public final fun greaterEq--PLe0oE (Ljava/lang/String;Ljava/lang/Comparable;)V
139139
public final fun ilike--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
140+
public final fun ilikeAllOf--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
141+
public final fun ilikeAnyOf--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
140142
public final fun imatches--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
141143
public final fun inList--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
142144
public final fun isDistinctFrom--PLe0oE (Ljava/lang/String;Ljava/lang/Object;)V
@@ -151,9 +153,15 @@ public final class io/github/androidpoet/supabase/core/models/WhereBuilder {
151153
public final fun matches--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
152154
public final fun neq--PLe0oE (Ljava/lang/String;Ljava/lang/Object;)V
153155
public final fun not (Lkotlin/jvm/functions/Function1;)V
156+
public final fun notInList--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
154157
public final fun or (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
155158
public static synthetic fun or$default (Lio/github/androidpoet/supabase/core/models/WhereBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
156159
public final fun overlaps--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
160+
public final fun rangeAdjacent--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
161+
public final fun rangeGt--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
162+
public final fun rangeGte--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
163+
public final fun rangeLt--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
164+
public final fun rangeLte--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
157165
public final fun raw-elUkyCk (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
158166
public final fun textSearch-7Jr2Q9A (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/androidpoet/supabase/core/models/TextSearchType;)V
159167
public static synthetic fun textSearch-7Jr2Q9A$default (Lio/github/androidpoet/supabase/core/models/WhereBuilder;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/androidpoet/supabase/core/models/TextSearchType;ILjava/lang/Object;)V

supabase-core/api/jvm/supabase-core.api

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ public final class io/github/androidpoet/supabase/core/models/WhereBuilder {
137137
public final fun greater--PLe0oE (Ljava/lang/String;Ljava/lang/Comparable;)V
138138
public final fun greaterEq--PLe0oE (Ljava/lang/String;Ljava/lang/Comparable;)V
139139
public final fun ilike--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
140+
public final fun ilikeAllOf--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
141+
public final fun ilikeAnyOf--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
140142
public final fun imatches--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
141143
public final fun inList--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
142144
public final fun isDistinctFrom--PLe0oE (Ljava/lang/String;Ljava/lang/Object;)V
@@ -151,9 +153,15 @@ public final class io/github/androidpoet/supabase/core/models/WhereBuilder {
151153
public final fun matches--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
152154
public final fun neq--PLe0oE (Ljava/lang/String;Ljava/lang/Object;)V
153155
public final fun not (Lkotlin/jvm/functions/Function1;)V
156+
public final fun notInList--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
154157
public final fun or (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
155158
public static synthetic fun or$default (Lio/github/androidpoet/supabase/core/models/WhereBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
156159
public final fun overlaps--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
160+
public final fun rangeAdjacent--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
161+
public final fun rangeGt--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
162+
public final fun rangeGte--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
163+
public final fun rangeLt--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
164+
public final fun rangeLte--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
157165
public final fun raw-elUkyCk (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
158166
public final fun textSearch-7Jr2Q9A (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/androidpoet/supabase/core/models/TextSearchType;)V
159167
public static synthetic fun textSearch-7Jr2Q9A$default (Lio/github/androidpoet/supabase/core/models/WhereBuilder;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/androidpoet/supabase/core/models/TextSearchType;ILjava/lang/Object;)V

supabase-core/api/supabase-core.klib.api

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,15 @@ final class io.github.androidpoet.supabase.core.models/WhereBuilder { // io.gith
209209

210210
final fun (io.github.androidpoet.supabase.core.models/Column<*>).isNotNull() // io.github.androidpoet.supabase.core.models/WhereBuilder.isNotNull|[email protected]<*>(){}[0]
211211
final fun (io.github.androidpoet.supabase.core.models/Column<*>).isNull() // io.github.androidpoet.supabase.core.models/WhereBuilder.isNull|[email protected]<*>(){}[0]
212+
final fun (io.github.androidpoet.supabase.core.models/Column<*>).rangeAdjacent(kotlin/String) // io.github.androidpoet.supabase.core.models/WhereBuilder.rangeAdjacent|[email protected]<*>(kotlin.String){}[0]
213+
final fun (io.github.androidpoet.supabase.core.models/Column<*>).rangeGt(kotlin/String) // io.github.androidpoet.supabase.core.models/WhereBuilder.rangeGt|[email protected]<*>(kotlin.String){}[0]
214+
final fun (io.github.androidpoet.supabase.core.models/Column<*>).rangeGte(kotlin/String) // io.github.androidpoet.supabase.core.models/WhereBuilder.rangeGte|[email protected]<*>(kotlin.String){}[0]
215+
final fun (io.github.androidpoet.supabase.core.models/Column<*>).rangeLt(kotlin/String) // io.github.androidpoet.supabase.core.models/WhereBuilder.rangeLt|[email protected]<*>(kotlin.String){}[0]
216+
final fun (io.github.androidpoet.supabase.core.models/Column<*>).rangeLte(kotlin/String) // io.github.androidpoet.supabase.core.models/WhereBuilder.rangeLte|[email protected]<*>(kotlin.String){}[0]
212217
final fun (io.github.androidpoet.supabase.core.models/Column<kotlin/Boolean>).isExactly(kotlin/Boolean) // io.github.androidpoet.supabase.core.models/WhereBuilder.isExactly|[email protected]<kotlin.Boolean>(kotlin.Boolean){}[0]
213218
final fun (io.github.androidpoet.supabase.core.models/Column<kotlin/String>).ilike(kotlin/String) // io.github.androidpoet.supabase.core.models/WhereBuilder.ilike|[email protected]<kotlin.String>(kotlin.String){}[0]
219+
final fun (io.github.androidpoet.supabase.core.models/Column<kotlin/String>).ilikeAllOf(kotlin.collections/List<kotlin/String>) // io.github.androidpoet.supabase.core.models/WhereBuilder.ilikeAllOf|[email protected]<kotlin.String>(kotlin.collections.List<kotlin.String>){}[0]
220+
final fun (io.github.androidpoet.supabase.core.models/Column<kotlin/String>).ilikeAnyOf(kotlin.collections/List<kotlin/String>) // io.github.androidpoet.supabase.core.models/WhereBuilder.ilikeAnyOf|[email protected]<kotlin.String>(kotlin.collections.List<kotlin.String>){}[0]
214221
final fun (io.github.androidpoet.supabase.core.models/Column<kotlin/String>).imatches(kotlin/String) // io.github.androidpoet.supabase.core.models/WhereBuilder.imatches|[email protected]<kotlin.String>(kotlin.String){}[0]
215222
final fun (io.github.androidpoet.supabase.core.models/Column<kotlin/String>).like(kotlin/String) // io.github.androidpoet.supabase.core.models/WhereBuilder.like|[email protected]<kotlin.String>(kotlin.String){}[0]
216223
final fun (io.github.androidpoet.supabase.core.models/Column<kotlin/String>).likeAllOf(kotlin.collections/List<kotlin/String>) // io.github.androidpoet.supabase.core.models/WhereBuilder.likeAllOf|[email protected]<kotlin.String>(kotlin.collections.List<kotlin.String>){}[0]
@@ -221,6 +228,7 @@ final class io.github.androidpoet.supabase.core.models/WhereBuilder { // io.gith
221228
final fun <#A1: kotlin/Any?> (io.github.androidpoet.supabase.core.models/Column<#A1>).inList(kotlin.collections/List<#A1>) // io.github.androidpoet.supabase.core.models/WhereBuilder.inList|[email protected]<0:0>(kotlin.collections.List<0:0>){0§<kotlin.Any?>}[0]
222229
final fun <#A1: kotlin/Any?> (io.github.androidpoet.supabase.core.models/Column<#A1>).isDistinctFrom(#A1) // io.github.androidpoet.supabase.core.models/WhereBuilder.isDistinctFrom|[email protected]<0:0>(0:0){0§<kotlin.Any?>}[0]
223230
final fun <#A1: kotlin/Any?> (io.github.androidpoet.supabase.core.models/Column<#A1>).neq(#A1) // io.github.androidpoet.supabase.core.models/WhereBuilder.neq|[email protected]<0:0>(0:0){0§<kotlin.Any?>}[0]
231+
final fun <#A1: kotlin/Any?> (io.github.androidpoet.supabase.core.models/Column<#A1>).notInList(kotlin.collections/List<#A1>) // io.github.androidpoet.supabase.core.models/WhereBuilder.notInList|[email protected]<0:0>(kotlin.collections.List<0:0>){0§<kotlin.Any?>}[0]
224232
final fun <#A1: kotlin/Any?> (io.github.androidpoet.supabase.core.models/Column<kotlin.collections/List<#A1>>).containedBy(kotlin.collections/List<#A1>) // io.github.androidpoet.supabase.core.models/WhereBuilder.containedBy|[email protected]<kotlin.collections.List<0:0>>(kotlin.collections.List<0:0>){0§<kotlin.Any?>}[0]
225233
final fun <#A1: kotlin/Any?> (io.github.androidpoet.supabase.core.models/Column<kotlin.collections/List<#A1>>).contains(kotlin.collections/List<#A1>) // io.github.androidpoet.supabase.core.models/WhereBuilder.contains|[email protected]<kotlin.collections.List<0:0>>(kotlin.collections.List<0:0>){0§<kotlin.Any?>}[0]
226234
final fun <#A1: kotlin/Any?> (io.github.androidpoet.supabase.core.models/Column<kotlin.collections/List<#A1>>).overlaps(kotlin.collections/List<#A1>) // io.github.androidpoet.supabase.core.models/WhereBuilder.overlaps|[email protected]<kotlin.collections.List<0:0>>(kotlin.collections.List<0:0>){0§<kotlin.Any?>}[0]

supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/models/Filters.kt

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,13 +235,25 @@ public class WhereBuilder {
235235
public infix fun Column<String>.likeAnyOf(patterns: List<String>): Unit =
236236
add(name, "like(any).{${patterns.joinToString(",") { encodeValue(it) }}}")
237237

238+
/** Case-insensitively matches every pattern in [patterns] (`ILIKE ALL`). */
239+
public infix fun Column<String>.ilikeAllOf(patterns: List<String>): Unit =
240+
add(name, "ilike(all).{${patterns.joinToString(",") { encodeValue(it) }}}")
241+
242+
/** Case-insensitively matches at least one pattern in [patterns] (`ILIKE ANY`). */
243+
public infix fun Column<String>.ilikeAnyOf(patterns: List<String>): Unit =
244+
add(name, "ilike(any).{${patterns.joinToString(",") { encodeValue(it) }}}")
245+
238246
// ── Membership ──────────────────────────────────────────────────────────────
239247

240248
/** `column IN (values)` (SQL `IN`). */
241249
public infix fun <T> Column<T>.inList(values: List<T>): Unit =
242250
add(name, "in.(${values.joinToString(",") { renderValue(it) }})")
243251

244-
// ── Array / range ─────────────────────────────────────────────────────────
252+
/** `column NOT IN (values)` (SQL `NOT IN`). */
253+
public infix fun <T> Column<T>.notInList(values: List<T>): Unit =
254+
add(name, "not.in.(${values.joinToString(",") { renderValue(it) }})")
255+
256+
// ── Array ─────────────────────────────────────────────────────────────────
245257

246258
/** Array/range column contains every element of [values] (SQL `@>`). */
247259
public infix fun <T> Column<List<T>>.contains(values: List<T>): Unit = add(name, "cs.${arrayLiteral(values)}")
@@ -252,6 +264,27 @@ public class WhereBuilder {
252264
/** Array/range column shares any element with [values] (SQL `&&`). */
253265
public infix fun <T> Column<List<T>>.overlaps(values: List<T>): Unit = add(name, "ov.${arrayLiteral(values)}")
254266

267+
// ── Range columns ───────────────────────────────────────────────────────────
268+
// Operators for Postgres range types (int4range, tstzrange, …). [range] is a range
269+
// literal whose bounds-brackets and comma are part of the value, e.g. "[1,10)" or
270+
// "[2000-01-01,2000-01-15)" — passed through verbatim (PostgREST quotes nothing here).
271+
// Names mirror the supabase-js client; the SQL operator and PostgREST token are noted.
272+
273+
/** Range strictly right of [range] (SQL `>>`, PostgREST `sr`). */
274+
public infix fun Column<*>.rangeGt(range: String): Unit = add(name, "sr.$range")
275+
276+
/** Range does not extend to the left of [range] (SQL `&>`, PostgREST `nxl`). */
277+
public infix fun Column<*>.rangeGte(range: String): Unit = add(name, "nxl.$range")
278+
279+
/** Range strictly left of [range] (SQL `<<`, PostgREST `sl`). */
280+
public infix fun Column<*>.rangeLt(range: String): Unit = add(name, "sl.$range")
281+
282+
/** Range does not extend to the right of [range] (SQL `&<`, PostgREST `nxr`). */
283+
public infix fun Column<*>.rangeLte(range: String): Unit = add(name, "nxr.$range")
284+
285+
/** Range is adjacent to [range] (SQL `-|-`, PostgREST `adj`). */
286+
public infix fun Column<*>.rangeAdjacent(range: String): Unit = add(name, "adj.$range")
287+
255288
// ── Full-text search ──────────────────────────────────────────────────────
256289

257290
/** Full-text search on [this] column against [query] using parse mode [type]. */

supabase-core/src/commonTest/kotlin/io/github/androidpoet/supabase/core/FiltersTest.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ private val status = Column<String>("status")
1515
private val active = Column<Boolean>("active")
1616
private val deletedAt = Column<String>("deleted_at")
1717
private val tags = Column<List<String>>("tags")
18+
private val period = Column<String>("period")
1819

1920
class FiltersTest {
2021
@Test
@@ -127,6 +128,29 @@ class FiltersTest {
127128
assertEquals(listOf("tags" to "cs.{a,b}"), where { tags contains listOf("a", "b") })
128129
}
129130

131+
@Test
132+
fun notInListNegatesMembership() {
133+
assertEquals(listOf("status" to "not.in.(a,b)"), where { status notInList listOf("a", "b") })
134+
assertEquals(listOf("id" to "not.in.(1,2)"), where { id notInList listOf(1L, 2L) })
135+
}
136+
137+
@Test
138+
fun ilikeAllAndAnyOf() {
139+
assertEquals(listOf("name" to "ilike(all).{a%,%b}"), where { name ilikeAllOf listOf("a%", "%b") })
140+
assertEquals(listOf("name" to "ilike(any).{a%,%b}"), where { name ilikeAnyOf listOf("a%", "%b") })
141+
}
142+
143+
@Test
144+
fun rangeOperatorsMatchSupabaseJsTokens() {
145+
// Mapping verified against postgrest-js: rangeGte→nxl and rangeLte→nxr are the
146+
// non-obvious ones. Range literals (brackets/comma) pass through verbatim.
147+
assertEquals(listOf("period" to "sr.[1,10)"), where { period rangeGt "[1,10)" })
148+
assertEquals(listOf("period" to "nxl.[1,10)"), where { period rangeGte "[1,10)" })
149+
assertEquals(listOf("period" to "sl.[1,10)"), where { period rangeLt "[1,10)" })
150+
assertEquals(listOf("period" to "nxr.[1,10)"), where { period rangeLte "[1,10)" })
151+
assertEquals(listOf("period" to "adj.[1,10)"), where { period rangeAdjacent "[1,10)" })
152+
}
153+
130154
@Test
131155
fun queryAddsModifiersAfterFilters() {
132156
assertEquals(

0 commit comments

Comments
 (0)