Multiple and() conditions in where clause for couchbase lite Android client - android

In my Android app we use couchbase lite database version 2.8.6
I run three database queries.
One item in where clause.
QueryBuilder.select(
SelectResult.property("id"),
SelectResult.property("timestamp"),
SelectResult.property("rating"))
.from(DataSource.database(database))
.where(Expression.property("type").equalTo(Expression.string(DOC_TYPE)))
In the result I see three items from database printed to a console. As expected. Format here ans below is id|timestamp|rating
4e39f79c-9e11-4aba-9fb6-95d910f46cd9|0|-2147483648
e95646ee-ba3a-4978-b2a8-5383f31be2f1|0|-2147483648
e02d0eb3-6c9b-4942-b43c-a752eefc77a8|1630525956184|2147483647
I add and() condition to where() to get all items where type = 'type' AND rating < 1
QueryBuilder.select(
SelectResult.property("id"),
SelectResult.property("timestamp"),
SelectResult.property("rating"))
.from(DataSource.database(database))
.where(Expression.property("type").equalTo(Expression.string(DOC_TYPE))
.and(Expression.property("rating").lessThan(Expression.intValue(1))
)
Result is as expected as we search everything with rating < 1, third item is filtered out.
4e39f79c-9e11-4aba-9fb6-95d910f46cd9|0|-2147483648
e95646ee-ba3a-4978-b2a8-5383f31be2f1|0|-2147483648
Finally, I want to see records where type = 'type' AND rating < 1 AND timestamp <= 1
QueryBuilder.select(
SelectResult.property("id"),
SelectResult.property("timestamp"),
SelectResult.property("rating"))
.from(DataSource.database(database))
.where(Expression.property("type").equalTo(Expression.string(DOC_TYPE))
.and(Expression.property("rating").lessThan(Expression.intValue(1))
.and(Expression.property("timestamp")).lessThanOrEqualTo (Expression.longValue(1))
)
)
And now the result is really strange as I receive three items form the database. And the third one has timestamp much greater than 1 put into the query.
4e39f79c-9e11-4aba-9fb6-95d910f46cd9|0|-2147483648
e95646ee-ba3a-4978-b2a8-5383f31be2f1|0|-2147483648
e02d0eb3-6c9b-4942-b43c-a752eefc77a8|1630525956184|2147483647
Any ideas how to make it work with multiple and()? If I try to remove the second and() and keep the third one everything works as expected.

After deep investigations I found several problems in my code:
.and() should be called at the end of the "child" condition and not at the "parent" level.
For the conditions like this
val condition1 = Expression.property("type").equalTo(Expression.string(DOC_TYPE))
val condition2 = Expression.property("rating").lessThan(Expression.intValue(1))
val condition3 = Expression.property("timestamp")).lessThanOrEqualTo (Expression.longValue(1))
The correct way is
.where(condition1.and(
condition2.and(
condition3.and(
...
)
)
)
but NOT like I've tried
.where(condition1.and(condition2)
.and(condition3)
.and(...)
)
For the creation of the object I used a code that converts an object to Map<String, Any> before saving to couchbase. Everything seemed to be OK till I realized that Long was converted to Double.
The last point, I have much complicated queries and inside one of them I accidentally used .add() instead of .and(). No comments.
Hope this will help to somebody to save some time.

I would expect that the query would be as you did originally:
.where(condition1.and(condition2)
.and(condition3)
.and(...))
I think there is an additional parenthesis here that seems to be wrong:
.and(Expression.property("timestamp"))

Related

firestore getDocuments().size() != getDocumentChanges().size()

I query my firestore db with a complex query
.whereIn(gender_node, getInterestedIn())
.whereGreaterThanOrEqualTo(birthdate_year_node, getShowAgeMax() + 1)
.whereLessThanOrEqualTo(birthdate_year_node, getShowAgeMin())
but I run into an IndexOutOfBoundException, because getDocuments().size() is not equal to getDocumentChanges().size().
I can't find anything in the documentation, does anybody know when or why getDocuments().size() != getDocumentChanges().size() ?
I assumed there would be a DocumentChange for every Document.
It doesn't happen every time, which makes it hard to debug.
There are 7 test documents and getDocuments().size() is always 7, but getDocumentChanges().size() is sometimes 5. Always seems to be the number 5, as far as I have seen.
getDocument().size() and getDocumentChanges().size() are two different methods.
The getDocumentChange().size() returns the list of documents that
changed since the last snapshot.
The getDocument().size() returns
the list of documents.
The details about these methods can be found out in the documentation section.
Suppose, if you have 7 documents and there are changes on 2 documents since last snapshot, then getDocument().size() would be 7( including the newly added or modified documents) here and getDocumentChanges().size() would always refer to the number of changed documents, which in this case is 2.
And hence is the condition,
getDocument().size() ! = getDocumentChanges().size()
true.
Is it possible that you have an extra paranthese closed in this line? (just after birthdate_year_node)
.whereLessThanOrEqualTo(birthdate_year_node),getShowAgeMin())
Otherwise, can you run your code with each one of the 3 "where" clauses seperately so we know which one of the three is creating the issue.

Query GeoHashes in Firetore in range Android

What I'm trying is to get the documents that are in range of a point.
Following the videos and taking a look to the geo-hash library for android, I'm able to get the bounding box and get the necessary geohashes to query in firebase.
As example:
Point -> LatLng(40.4378698,-3.8196205) (Madrid,Spain)
Radius -> 5000meters (5 km)
The boundary box I get is:
[GeoHashQuery{startValue='ezjnh', endValue='ezjns'}, GeoHashQuery{startValue='ezjjs', endValue='ezjj~'}, GeoHashQuery{startValue='ezjq0', endValue='ezjq8'}, GeoHashQuery{startValue='ezjm8', endValue='ezjmh'}]
One I have this list, I call Firebase to retrieve the documents that "match" this criteria:
fun getUpTos(queries: MutableSet<GeoHashQuery>, onSuccessListener: OnSuccessListener<QuerySnapshot>, onFailureListener: OnFailureListener) {
var reference = Firebase.firestore.collection("pois")
queries.forEach { entry ->
reference
.whereGreaterThanOrEqualTo("geohash", entry.startValue)
.whereLessThanOrEqualTo("geohash", entry.endValue)
}
reference.get()
.addOnSuccessListener(onSuccessListener)
.addOnFailureListener(onFailureListener)
}
At this moment I have around 20 Poi's in firebase to start doing the test. All Poi's are in Barcelona and 1 in Madrid.
After doing the query, I'm gettin ALL the poi's, when it was supposed to just return the Madrid poi.
How can I get only the pois that fit the query? It seems is not working properly (or I'm doing obviously something wrong)
Is possible to achieve this type of querys?
Is possible to achieve this type of querys?
Yes, it is possible.
When you are iterating through your queries MutableSet, at every iteration you are creating a new Query object. So you cannot simply call get() outside the loop only once and expect to have all those queries working. What can you do instead, is to add the get() call to every query inside the loop. The type of the object that results is Task<QuerySnapshot>. Add all those Task objects to a List<Task<QuerySnapshot>>. In the end, pass that list of tasks to Tasks's whenAllSuccess(Collection> tasks) method as explained in my answer from the following post:
Android Firestore convert array of document references to List<Pojo>

Optimize search in list (Kotlin/Android)

Android, Kotlin 1.2.
I have code like
data class Item(val id:Int,val field1:String, val field3:Array<String>,val field2:String)
val items:List<Item> = ....
var str:String='string_to_search'
I need to filter items list by str so I get sublist which contains onyly items in which str is case-insensitive substring of either field1 or field2 or both (it doesn't matter which one was matched or exact offset).
Right now I use code like this:
filteredItems=items
.filter { filtertFunc(it,filter) }
...
fun filtertFunc(item: Item, filter:String):Boolean {
if (item.field1.contains(filter, ignoreCase = true)) {
return true
}
if (item.field2.contains(filter, ignoreCase = true)) {
return true
}
return false
}
list of items usually contains 2-3k items. field1 and field2 are usually 30-300 usually. Search string is 0-50 chars.
Search string changes several times per second (usually new chars added - so abc changes to abcd ). List of items changes rarely.
My code have performance on some devices (filter took several seconds which is bad).
I can't use .parallelStream (I need to support Android 5.x but devices have usually at least 2 cores).
Using parallel map (from Parallel operations on Kotlin collections?) + special flag field in Item + filter does not help too much.
I thought about adding hash map with artificial keys (field1+field3) (or sorting list and using binarySearch) but this wont help because search string is not necessarily occurs at start of field1/field3.
How to better solve this performance issue? Any (sane) amount of pre-processing on list is ok.
Search string changes several times per second (usually new chars
added - so abc changes to abcd ). List of items changes rarely.
You may filter the result of previous filtration after the search string growing.
If search string reduced you may return to previous result.

Cloud Function to sort the Firebase Child from inner child

I need to have one problem sorted out. I want a cloud function, where I need to loop through every Posts that has uniqueID and I want to sort those uniqueID's according to the value of TotalReactions. To me more vivid, suppose, the first post has totalReactions = 5, and second one has totalReactions = 6, then I need to have another RealTimeDatabase "PostArranged" which sorts them in descending order, so second post which has Total Reaction would be in first and then follows the one which has lesser TotalReaction...
Here is how my firebase realtime database looks like:
Posts
-L29TD-nsUYRu3wYcCQl
Caption: "First Screenshot"
CurrentUserReaction: "notreacted"
Image: "https://firebasestorage.googleapis.com/v0/b/nep..."
ReactingUser
<user-id>:"1"
<user-id>:"2"
Time: "13:40:54"
TotalReactions: "2"
Unique: "-L29TD-nsUYRu3wYcCQl"
UserPhoto: "https://firebasestorage.googleapis.com/v0/b/nep..."
Username: "Me"
Any help would be appreciated ..
This is not a good way to do what you want to do. In this method the database has to be sorted to another "ArrangedDatabase" every time a reaction is added which will happen in a high frequency. So it will be very expensive to do this with Cloud Functions.
Instead use orderByChild(), orderByKey(), orderByValue() to sort data when you're querying data.
Sorting data - Firebase documentation this show it quite well.
For anyone who want to do this in your RecyclerViewAdapter provided by Firebase, it is easily done... When you pass in those 4 parameters. Do not pass the database Reference, instead, make a new query, and make sure to sort that thing either by OrderByChild(), OrderByValue() or anything you want to order by... i.e.
In my case, I will do something like this:
q = mDatabaseReference.orderByChild("TotalReactions");
and pass q as parameter inside FirebaseAdapter, like this:
FirebaseRecyclerAdapter<Posts,PostViewHolder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Posts, PostViewHolder>(
Posts.class, R.layout.each_post_layout , PostViewHolder.class, q
) {
.
}

SugarORM offset not working

I'm using SugarORM for my Android app. I want to load messages from the SQLite DB but need an offset.
So I load message 5-15 like for pagination.
Unfortunately the offset function doesn't seem to work as expected.
I wrote:
query = Select.from(Message.class)
.where(Condition.prop("MESSAGES").eq(Long.toString(getId())))
.orderBy("timestamp DESC")
.limit("5")
.offset("10");
query.list();
But this only gives me the limit of 5 elements but doesn't use any offset.
Same if I parameterize the query call like this:
List<Message> messages = getAllMessagesQuery.offset("5").list();
Offset isn't working.
Am I doing something wrong? I use the current SugarORM version 1.5
( compile 'com.github.satyan:sugar:1.5')
stating the working offset functionality. So this shouldn't be the issue.
Any ideas how to make this work?
Well, not sure this is the best way to do but at least it's working this way.
If I add the short version to the limit things work fine.
So using the query like this:
List<Message> messages = getAllMessagesQuery.limit("5,10").list();
gives me 10 messages with a 5 message offset.
If that is too dirty, a raw query is also working fine. So you write:
List<Message> messages = Chat.findWithQuery(Message.class, "Select * from Message where Chat = ? ORDER BY timestamp DESC LIMIT 10 OFFSET 5", getId().toString());
Still no idea how to use the offset parameter directly but at least it works this way.

Categories

Resources