Skip to content

Commit bd145c4

Browse files
committed
IMprovements to Where queries
1 parent eb56044 commit bd145c4

File tree

4 files changed

+167
-108
lines changed
  • firebase-firestore/src

4 files changed

+167
-108
lines changed

firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt

Lines changed: 75 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
@file:JvmName("android")
66
package dev.gitlive.firebase.firestore
77

8-
import com.google.android.gms.tasks.Task
98
import com.google.firebase.firestore.*
109
import dev.gitlive.firebase.*
1110
import kotlinx.coroutines.channels.awaitClose
@@ -17,6 +16,9 @@ import kotlinx.serialization.DeserializationStrategy
1716
import kotlinx.serialization.Serializable
1817
import kotlinx.serialization.SerializationStrategy
1918

19+
import com.google.firebase.firestore.Query as AndroidQuery
20+
import com.google.firebase.firestore.FieldPath as AndroidFieldPath
21+
2022
actual val Firebase.firestore get() =
2123
FirebaseFirestore(com.google.firebase.firestore.FirebaseFirestore.getInstance())
2224

@@ -283,7 +285,7 @@ actual class DocumentReference actual constructor(internal actual val nativeValu
283285
override fun toString(): String = nativeValue.toString()
284286
}
285287

286-
actual open class Query(open val android: com.google.firebase.firestore.Query) {
288+
actual open class Query(open val android: AndroidQuery) {
287289

288290
actual suspend fun get() = QuerySnapshot(android.get().await())
289291

@@ -306,39 +308,85 @@ actual open class Query(open val android: com.google.firebase.firestore.Query) {
306308
awaitClose { listener.remove() }
307309
}
308310

309-
internal actual fun _where(field: String, equalTo: Any?) = Query(android.whereEqualTo(field, equalTo))
310-
internal actual fun _where(path: FieldPath, equalTo: Any?) = Query(android.whereEqualTo(path.android, equalTo))
311-
312-
internal actual fun _where(field: String, equalTo: DocumentReference) = Query(android.whereEqualTo(field, equalTo.android))
313-
internal actual fun _where(path: FieldPath, equalTo: DocumentReference) = Query(android.whereEqualTo(path.android, equalTo.android))
314-
315-
internal actual fun _where(field: String, lessThan: Any?, greaterThan: Any?, arrayContains: Any?) = Query(
316-
(lessThan?.let { android.whereLessThan(field, it) } ?: android).let { android2 ->
317-
(greaterThan?.let { android2.whereGreaterThan(field, it) } ?: android2).let { android3 ->
318-
arrayContains?.let { android3.whereArrayContains(field, it) } ?: android3
311+
internal actual fun where(field: String, vararg clauses: WhereClause) = Query(
312+
clauses.fold(android) { query, clause ->
313+
when (clause) {
314+
is WhereClause.ForNullableObject -> {
315+
val modifier: AndroidQuery.(String, Any?) -> AndroidQuery = when (clause) {
316+
is WhereClause.EqualTo -> AndroidQuery::whereEqualTo
317+
is WhereClause.NotEqualTo -> AndroidQuery::whereNotEqualTo
318+
}
319+
modifier.invoke(query, field, clause.safeValue)
320+
}
321+
is WhereClause.ForObject -> {
322+
val modifier: AndroidQuery.(String, Any) -> AndroidQuery = when (clause) {
323+
is WhereClause.LessThan -> AndroidQuery::whereLessThan
324+
is WhereClause.GreaterThan -> AndroidQuery::whereGreaterThan
325+
is WhereClause.LessThanOrEqualTo -> AndroidQuery::whereLessThanOrEqualTo
326+
is WhereClause.GreaterThanOrEqualTo -> AndroidQuery::whereGreaterThanOrEqualTo
327+
is WhereClause.ArrayContains -> AndroidQuery::whereArrayContains
328+
}
329+
modifier.invoke(query, field, clause.safeValue)
330+
}
331+
is WhereClause.ForArray -> {
332+
val modifier: AndroidQuery.(String, List<Any>) -> AndroidQuery = when (clause) {
333+
is WhereClause.InArray -> AndroidQuery::whereIn
334+
is WhereClause.ArrayContainsAny -> AndroidQuery::whereArrayContainsAny
335+
is WhereClause.NotInArray -> AndroidQuery::whereNotIn
336+
}
337+
modifier.invoke(query, field, clause.safeValues)
338+
}
319339
}
320340
}
321341
)
322342

323-
internal actual fun _where(path: FieldPath, lessThan: Any?, greaterThan: Any?, arrayContains: Any?) = Query(
324-
(lessThan?.let { android.whereLessThan(path.android, it) } ?: android).let { android2 ->
325-
(greaterThan?.let { android2.whereGreaterThan(path.android, it) } ?: android2).let { android3 ->
326-
arrayContains?.let { android3.whereArrayContains(path.android, it) } ?: android3
343+
internal actual fun where(path: FieldPath, vararg clauses: WhereClause) = Query(
344+
clauses.fold(android) { query, clause ->
345+
when (clause) {
346+
is WhereClause.ForNullableObject -> {
347+
val modifier: AndroidQuery.(AndroidFieldPath, Any?) -> AndroidQuery = when (clause) {
348+
is WhereClause.EqualTo -> AndroidQuery::whereEqualTo
349+
is WhereClause.NotEqualTo -> AndroidQuery::whereNotEqualTo
350+
}
351+
modifier.invoke(query, path.android, clause.safeValue)
352+
}
353+
is WhereClause.ForObject -> {
354+
val modifier: AndroidQuery.(AndroidFieldPath, Any) -> AndroidQuery = when (clause) {
355+
is WhereClause.LessThan -> AndroidQuery::whereLessThan
356+
is WhereClause.GreaterThan -> AndroidQuery::whereGreaterThan
357+
is WhereClause.LessThanOrEqualTo -> AndroidQuery::whereLessThanOrEqualTo
358+
is WhereClause.GreaterThanOrEqualTo -> AndroidQuery::whereGreaterThanOrEqualTo
359+
is WhereClause.ArrayContains -> AndroidQuery::whereArrayContains
360+
}
361+
modifier.invoke(query, path.android, clause.safeValue)
362+
}
363+
is WhereClause.ForArray -> {
364+
val modifier: AndroidQuery.(AndroidFieldPath, List<Any>) -> AndroidQuery = when (clause) {
365+
is WhereClause.InArray -> AndroidQuery::whereIn
366+
is WhereClause.ArrayContainsAny -> AndroidQuery::whereArrayContainsAny
367+
is WhereClause.NotInArray -> AndroidQuery::whereNotIn
368+
}
369+
modifier.invoke(query, path.android, clause.safeValues)
370+
}
327371
}
328372
}
329373
)
330374

331-
internal actual fun _where(field: String, inArray: List<Any>?, arrayContainsAny: List<Any>?) = Query(
332-
(inArray?.let { android.whereIn(field, it) } ?: android).let { android2 ->
333-
arrayContainsAny?.let { android2.whereArrayContainsAny(field, it) } ?: android2
334-
}
335-
)
336-
337-
internal actual fun _where(path: FieldPath, inArray: List<Any>?, arrayContainsAny: List<Any>?) = Query(
338-
(inArray?.let { android.whereIn(path.android, it) } ?: android).let { android2 ->
339-
arrayContainsAny?.let { android2.whereArrayContainsAny(path.android, it) } ?: android2
340-
}
341-
)
375+
private fun <T : Any> AndroidQuery.whereField(
376+
field: String,
377+
nullable: T?,
378+
modified: AndroidQuery.(String, T) -> AndroidQuery
379+
) : AndroidQuery = nullable?.let {
380+
modified(field, it)
381+
} ?: this
382+
383+
private fun <T : Any> AndroidQuery.wherePath(
384+
path: FieldPath,
385+
nullable: T?,
386+
modified: AndroidQuery.(com.google.firebase.firestore.FieldPath, T) -> AndroidQuery
387+
) : AndroidQuery = nullable?.let {
388+
modified(path.android, it)
389+
} ?: this
342390

343391
internal actual fun _orderBy(field: String, direction: Direction) = Query(android.orderBy(field, direction))
344392
internal actual fun _orderBy(field: FieldPath, direction: Direction) = Query(android.orderBy(field.android, direction))

firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,42 @@ expect class Transaction {
5252
suspend fun get(documentRef: DocumentReference): DocumentSnapshot
5353
}
5454

55+
sealed class WhereClause {
56+
57+
sealed class ForNullableObject : WhereClause() {
58+
abstract val value: Any?
59+
val safeValue get() = value?.safeValue
60+
}
61+
62+
sealed class ForObject : WhereClause() {
63+
abstract val value: Any
64+
val safeValue get() = value.safeValue
65+
}
66+
sealed class ForArray : WhereClause() {
67+
abstract val values: List<Any>
68+
val safeValues get() = values.map { it.safeValue }
69+
}
70+
71+
data class EqualTo(override val value: Any) : ForNullableObject()
72+
data class NotEqualTo(override val value: Any) : ForNullableObject()
73+
data class LessThan(override val value: Any) : ForObject()
74+
data class GreaterThan(override val value: Any) : ForObject()
75+
data class LessThanOrEqualTo(override val value: Any) : ForObject()
76+
data class GreaterThanOrEqualTo(override val value: Any) : ForObject()
77+
data class ArrayContains(override val value: Any) : ForObject()
78+
data class InArray(override val values: List<Any>) : ForArray()
79+
data class ArrayContainsAny(override val values: List<Any>) : ForArray()
80+
data class NotInArray(override val values: List<Any>) : ForArray()
81+
}
82+
5583
expect open class Query {
5684
fun limit(limit: Number): Query
5785
val snapshots: Flow<QuerySnapshot>
5886
fun snapshots(includeMetadataChanges: Boolean = false): Flow<QuerySnapshot>
5987
suspend fun get(): QuerySnapshot
60-
internal fun _where(field: String, equalTo: Any?): Query
61-
internal fun _where(path: FieldPath, equalTo: Any?): Query
62-
internal fun _where(field: String, equalTo: DocumentReference): Query
63-
internal fun _where(path: FieldPath, equalTo: DocumentReference): Query
64-
internal fun _where(field: String, lessThan: Any? = null, greaterThan: Any? = null, arrayContains: Any? = null): Query
65-
internal fun _where(path: FieldPath, lessThan: Any? = null, greaterThan: Any? = null, arrayContains: Any? = null): Query
66-
internal fun _where(field: String, inArray: List<Any>? = null, arrayContainsAny: List<Any>? = null): Query
67-
internal fun _where(path: FieldPath, inArray: List<Any>? = null, arrayContainsAny: List<Any>? = null): Query
88+
89+
internal fun where(field: String, vararg clauses: WhereClause): Query
90+
internal fun where(path: FieldPath, vararg clauses: WhereClause): Query
6891

6992
internal fun _orderBy(field: String, direction: Direction): Query
7093
internal fun _orderBy(field: FieldPath, direction: Direction): Query
@@ -80,35 +103,27 @@ expect open class Query {
80103
internal fun _endAt(vararg fieldValues: Any): Query
81104
}
82105

83-
/** @return a native value of a wrapper or self. */
84-
private val Any.value get() = when (this) {
106+
private val Any.safeValue: Any get() = when (this) {
85107
is Timestamp -> nativeValue
86108
is GeoPoint -> nativeValue
87109
is DocumentReference -> nativeValue
110+
is Map<*, *> -> this.mapNotNull { (key, value) -> key?.let { it.safeValue to value?.safeValue } }
111+
is Collection<*> -> this.mapNotNull { it?.safeValue }
88112
else -> this
89113
}
90114

91-
fun Query.where(field: String, equalTo: Any?) = _where(field, equalTo?.value)
92-
fun Query.where(path: FieldPath, equalTo: Any?) = _where(path, equalTo?.value)
93-
fun Query.where(field: String, equalTo: DocumentReference) = _where(field, equalTo.value)
94-
fun Query.where(path: FieldPath, equalTo: DocumentReference) = _where(path, equalTo.value)
95-
fun Query.where(field: String, lessThan: Any? = null, greaterThan: Any? = null, arrayContains: Any? = null) = _where(field, lessThan?.value, greaterThan?.value, arrayContains?.value)
96-
fun Query.where(path: FieldPath, lessThan: Any? = null, greaterThan: Any? = null, arrayContains: Any? = null) = _where(path, lessThan?.value, greaterThan?.value, arrayContains?.value)
97-
fun Query.where(field: String, inArray: List<Any>? = null, arrayContainsAny: List<Any>? = null) = _where(field, inArray?.value, arrayContainsAny?.value)
98-
fun Query.where(path: FieldPath, inArray: List<Any>? = null, arrayContainsAny: List<Any>? = null) = _where(path, inArray?.value, arrayContainsAny?.value)
99-
100115
fun Query.orderBy(field: String, direction: Direction = Direction.ASCENDING) = _orderBy(field, direction)
101116
fun Query.orderBy(field: FieldPath, direction: Direction = Direction.ASCENDING) = _orderBy(field, direction)
102117

103118
fun Query.startAfter(document: DocumentSnapshot) = _startAfter(document)
104-
fun Query.startAfter(vararg fieldValues: Any) = _startAfter(*(fieldValues.map { it.value }.toTypedArray()))
119+
fun Query.startAfter(vararg fieldValues: Any) = _startAfter(*(fieldValues.mapNotNull { it.safeValue }.toTypedArray()))
105120
fun Query.startAt(document: DocumentSnapshot) = _startAt(document)
106-
fun Query.startAt(vararg fieldValues: Any) = _startAt(*(fieldValues.map { it.value }.toTypedArray()))
121+
fun Query.startAt(vararg fieldValues: Any) = _startAt(*(fieldValues.mapNotNull { it.safeValue }.toTypedArray()))
107122

108123
fun Query.endBefore(document: DocumentSnapshot) = _endBefore(document)
109-
fun Query.endBefore(vararg fieldValues: Any) = _endBefore(*(fieldValues.map { it.value }.toTypedArray()))
124+
fun Query.endBefore(vararg fieldValues: Any) = _endBefore(*(fieldValues.mapNotNull { it.safeValue }.toTypedArray()))
110125
fun Query.endAt(document: DocumentSnapshot) = _endAt(document)
111-
fun Query.endAt(vararg fieldValues: Any) = _endAt(*(fieldValues.map { it.value }.toTypedArray()))
126+
fun Query.endAt(vararg fieldValues: Any) = _endAt(*(fieldValues.mapNotNull { it.safeValue }.toTypedArray()))
112127

113128
expect class WriteBatch {
114129
inline fun <reified T> set(documentRef: DocumentReference, data: T, encodeDefaults: Boolean = true, merge: Boolean = false): WriteBatch

firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -272,40 +272,40 @@ actual open class Query(open val ios: FIRQuery) {
272272
awaitClose { listener.remove() }
273273
}
274274

275-
internal actual fun _where(field: String, equalTo: Any?) = Query(ios.queryWhereField(field, isEqualTo = equalTo!!))
276-
internal actual fun _where(path: FieldPath, equalTo: Any?) = Query(ios.queryWhereFieldPath(path.ios, isEqualTo = equalTo!!))
277-
278-
internal actual fun _where(field: String, equalTo: DocumentReference) = Query(ios.queryWhereField(field, isEqualTo = equalTo.ios))
279-
internal actual fun _where(path: FieldPath, equalTo: DocumentReference) = Query(ios.queryWhereFieldPath(path.ios, isEqualTo = equalTo.ios))
280-
281-
internal actual fun _where(field: String, lessThan: Any?, greaterThan: Any?, arrayContains: Any?) = Query(
282-
(lessThan?.let { ios.queryWhereField(field, isLessThan = it) } ?: ios).let { ios2 ->
283-
(greaterThan?.let { ios2.queryWhereField(field, isGreaterThan = it) } ?: ios2).let { ios3 ->
284-
arrayContains?.let { ios3.queryWhereField(field, arrayContains = it) } ?: ios3
275+
internal actual fun where(field: String, vararg clauses: WhereClause) = Query(
276+
clauses.fold(ios) { query, clause ->
277+
when (clause) {
278+
is WhereClause.EqualTo -> query.queryWhereField(field, isEqualTo = clause.safeValue ?: NSNull)
279+
is WhereClause.NotEqualTo -> query.queryWhereField(field, isNotEqualTo = clause.safeValue ?: NSNull)
280+
is WhereClause.LessThan -> query.queryWhereField(field, isLessThan = clause.safeValue)
281+
is WhereClause.GreaterThan -> query.queryWhereField(field, isGreaterThan = clause.safeValue)
282+
is WhereClause.LessThanOrEqualTo -> query.queryWhereField(field, isLessThanOrEqualTo = clause.safeValue)
283+
is WhereClause.GreaterThanOrEqualTo -> query.queryWhereField(field, isGreaterThanOrEqualTo = clause.safeValue)
284+
is WhereClause.ArrayContains -> query.queryWhereField(field, arrayContains = clause.safeValue)
285+
is WhereClause.InArray -> query.queryWhereField(field, `in` = clause.safeValues)
286+
is WhereClause.ArrayContainsAny -> query.queryWhereField(field, arrayContainsAny = clause.safeValues)
287+
is WhereClause.NotInArray -> query.queryWhereField(field, notIn = clause.safeValues)
285288
}
286289
}
287290
)
288291

289-
internal actual fun _where(path: FieldPath, lessThan: Any?, greaterThan: Any?, arrayContains: Any?) = Query(
290-
(lessThan?.let { ios.queryWhereFieldPath(path.ios, isLessThan = it) } ?: ios).let { ios2 ->
291-
(greaterThan?.let { ios2.queryWhereFieldPath(path.ios, isGreaterThan = it) } ?: ios2).let { ios3 ->
292-
arrayContains?.let { ios3.queryWhereFieldPath(path.ios, arrayContains = it) } ?: ios3
292+
internal actual fun where(path: FieldPath, vararg clauses: WhereClause) = Query(
293+
clauses.fold(ios) { query, clause ->
294+
when (clause) {
295+
is WhereClause.EqualTo -> query.queryWhereFieldPath(path.ios, isEqualTo = clause.safeValue ?: NSNull)
296+
is WhereClause.NotEqualTo -> query.queryWhereFieldPath(path.ios, isNotEqualTo = clause.safeValue ?: NSNull)
297+
is WhereClause.LessThan -> query.queryWhereFieldPath(path.ios, isLessThan = clause.safeValue)
298+
is WhereClause.GreaterThan -> query.queryWhereFieldPath(path.ios, isGreaterThan = clause.safeValue)
299+
is WhereClause.LessThanOrEqualTo -> query.queryWhereFieldPath(path.ios, isLessThanOrEqualTo = clause.safeValue)
300+
is WhereClause.GreaterThanOrEqualTo -> query.queryWhereFieldPath(path.ios, isGreaterThanOrEqualTo = clause.safeValue)
301+
is WhereClause.ArrayContains -> query.queryWhereFieldPath(path.ios, arrayContains = clause.safeValue)
302+
is WhereClause.InArray -> query.queryWhereFieldPath(path.ios, `in` = clause.safeValues)
303+
is WhereClause.ArrayContainsAny -> query.queryWhereFieldPath(path.ios, arrayContainsAny = clause.safeValues)
304+
is WhereClause.NotInArray -> query.queryWhereFieldPath(path.ios, notIn = clause.safeValues)
293305
}
294306
}
295307
)
296308

297-
internal actual fun _where(field: String, inArray: List<Any>?, arrayContainsAny: List<Any>?) = Query(
298-
(inArray?.let { ios.queryWhereField(field, `in` = it) } ?: ios).let { ios2 ->
299-
arrayContainsAny?.let { ios2.queryWhereField(field, arrayContainsAny = arrayContainsAny) } ?: ios2
300-
}
301-
)
302-
303-
internal actual fun _where(path: FieldPath, inArray: List<Any>?, arrayContainsAny: List<Any>?) = Query(
304-
(inArray?.let { ios.queryWhereFieldPath(path.ios, `in` = it) } ?: ios).let { ios2 ->
305-
arrayContainsAny?.let { ios2.queryWhereFieldPath(path.ios, arrayContainsAny = arrayContainsAny) } ?: ios2
306-
}
307-
)
308-
309309
internal actual fun _orderBy(field: String, direction: Direction) = Query(ios.queryOrderedByField(field, direction == Direction.DESCENDING))
310310
internal actual fun _orderBy(field: FieldPath, direction: Direction) = Query(ios.queryOrderedByFieldPath(field.ios, direction == Direction.DESCENDING))
311311

0 commit comments

Comments
 (0)