-
-
Notifications
You must be signed in to change notification settings - Fork 166
Firestore Where Clause extensions #449
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Firestore Where Clause extensions #449
Conversation
Is there an underlying limitation to the implementation of Query.where via named arguments? Because if not I suggest we add the missing functionality and fix the issues without making breaking changes to the API. If is it indeed an underlying limitation to expressing where clauses as named arguments and we do have to make breaking change to the API I would suggest we match the native android SDK API instead of creating a new API for everybody to learn. It's much easiest to point people towards the android docs than to write and maintain separate documentation for this project. In fact when this project started, the Firebase Android SDK was java first and, for example, did not support consuming document snapshots as flows. It's nice to see that feature has now been added (as part of their new Kolin-first commitment) and its API is very similar to ours. If they had already supported this then we would have matched that API just like we did for the ktx extensions such as FIrebase.firestore (which did exist at the time). My point being that now the android sdk is kotlin first we should aim to match the API in any new changes and only deviate in cases when is it not possible due to platform differences. |
The API doesnt change that much compared to the one this project had before, though I did introduce some convenience methods. Android (and by extension JS and iOS, though named differently) can do things like This SDK offers:
This is already a bit inconsistent with the Android API in that lessThan/greaterThan are grouped together which they arent in the Android SDK. So we're already adding convenience methods. This just takes it one step further by using sealed classes to make parameters a bit more concise (and by grouping the lessThan and inArray where methods since there's not really a reason they cant be) |
I believe the grouping is inconsequential (a workaround for method signature clashes) and the API is designed to use a single named arg per method call and supports chaining just like the native APIs. It doesn't matter so much if the API changes a little or a lot its more the case of does old code still compile or not, breaking changes such as these should only be introduced when unavoidable and as far as I can tell this does not appear to be the case here. |
If only one argument at a time is used, these aren't breaking changes though. You can still call Nevertheless I can ungroup the lessThan and inArray calls if you prefer |
Ok thats good news, do we need the WhereClause class in that case then? Or if it is necessary can we make it internal? |
I can make it internal yeah, if that's preferred. We don't "need" it, I just prefer folding a list of sealed classes over a huge nested if statement. It's more readable and more Kotlin like. |
Definitely more readable and more Kotlin like but less performant, I don't remember the original issue with the method signatures but maybe its a legacy one in an older Kotlin version. I wonder if it's now possible to simply use one method per clause and avoid the clashes with @JsName/@JvmName/@ObjCName |
I've made the WhereClause internal. On performance: Ive tested using value classes as instead of data classes, but the performance impact of that was slightly worse. Nevertheless, the time to process is a fraction of a millisecond and considering nobody's gonna be creating 1000s of where clauses per second, I doubt you'd see any noticeable performance impact from using sealed interfaces here. I would consider this the most stable and maintainable kotlin like implementation. See this example to see that the performance difference is minimal: https://pl.kotl.in/jJN_X6wqu On method signatures: Kotlin does not support writing the same method with different parameter names. Since named arguments are not obligatory, the language considers them the same signature. This is regardless of platform so JvmName etc wont fix that. You'll see this is why Ive added the whereNot method for a notEquals (this cannot be grouped because null is meaningful for equals/notEquals as they take nullable properties on the platforms). So either we deprecate |
Yes I am not particularly worried about performance tbh. Looking at the docs seems like there is now support for or queries that introduce a new Filter class: If you ask me the syntax doesn't look great and certainly not Kotlin first! How would you handle those with your WhereClause sealed class? |
Honestly, Id make it proper kotlin. Something like this: So you can do: query.where {
"path" equalTo 0 and ( "otherPath" lessThan 8 or (field notIn listOf(1, 2, 3)))
} |
If you like, I can change my PR to implement it like this |
that looks great! Is it capable of supporting all the queries possible with the Filter class? |
In theory it should. One way to find out |
we could add this and keep but deprecate the old functions |
Yeah Ill keep the existing functions, make them shortcut to this and deprecate them |
Small note: your Java SDK doesnt seem to track this change yet. So For now Ill split sources between JVM and Android and on JVM I wont support the OR constraints |
@nbransby Want to add a few more tests, but in principle its implemented :) EDIT: Tests added |
@Deprecated("Deprecated in favor of using a [FilterBuilder]", replaceWith = ReplaceWith("where { }", "dev.gitlive.firebase.firestore")) | ||
fun Query.where(field: String, inArray: List<Any>? = null, arrayContainsAny: List<Any>? = null) = where { | ||
val filters = listOfNotNull( | ||
inArray?.let { field `in` it }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you overload the in operator to avoid the quotes?
https://kotlinlang.org/docs/operator-overloading.html#in-operator
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I cant overload it since the method to overload is contains which has a different signature. Ill rename to inArray
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For similar reasons I cant make operator overloads for < > etc. They all go to the compareTo method and that isnt suitable for overloading in this case
Would you like to add a paragraph to the the readme on your new kotlin-style where clauses in this PR? |
Done. I removed the section on named arguments cause I couldnt find another example where its needed. |
This is a great contribution to the library, I'm sure people will enjoy using the new syntax, and, who knows, you might even inspire the official android sdk! |
The implementation of Query.where is somewhat limited currently: Some functionality is missing and iOS does not handle equals null correctly.
This PR fixes this by introducing a WhereClause that can easily be mixed and matched.