`Rxify` — Maintaining Order in Auto-Complete Search
Recently I gave a talk titled “Rxify — a simple spell for complex RxJava operators” at DroidconIN Bangalore 2016, on how to master the
Rxify spell and why we all should be using
RxJava in our apps. Video of the talk has been uploaded here and slides here.
During the Q&A session, one fine chap asked me the question about “Maintaining Order in Auto-Complete Search”. Let me explain the question first.
Auto-Search : Referred as the problem of continuously hitting the server to provide users with suggestions while they are typing.
Simple Solution : A wide-known solution to optimize the network traffic here is to use the Debounce-y spell (
debounce() operator), also mentioned in my previous blog post here and even in Kaushik Gopal’s examples here.
Common Problem (with this solution) :
A common problem to this approach is maintaining order of results shown in the auto-complete list. If we searched for India, then even after using debounce, depending upon the speed of the person typing, we might hit the server for query strings like :
I, Ind, India.
In this case we hit the API three times Call1(‘I’), Call2(‘Ind’) and Call3(‘India’). Many times I have noticed that the result for the calls can interleave such that we might get the response in order :
Response1(‘I’), Response3(‘India’) and then Response2(‘Ind’).
In the above case if we simply use the
flatmap() operator to populate the data, then our list will momentarily show the correct data and then again replace it with old data corresponding to Response2(‘Ind’) as shown below :
Not so cool!
Easier Approach :
One of the approaches that can be taken to get the latest or best possible result is to cancel / unsubscribe to the response of previous call. The limitation of following such a solution is that, if we keep cancelling or unsubscribing from previous calls, then we will have no data to show if there is a delay in execution of the final call or even if the final call fails. I think in this case, we should instead follow “Old Data is better than No-Data” approach.
Concat-Map-ium (concat-Map() operator)
“Simpler version of the
flatMapoperator, but it concatenates rather than merges the resulting Observables”
The difference between
flatMap() is same as the difference between
merge() explained in my earlier post here. ConcatMap() doesn’t interleave items while combining, whereas flatMap() will combine items in the order they arrive.
ConcatMap() to the rescue!!
Let’s see how we can apply a concatMap() to achieve what we desire :
Demo Source code is uploaded here.
via-Hedwig : In order to understand the following snippet. You need to have a brief look at the concept of Subjects too. Simply, a subject can act both as an Observable and an Observer (a pipe).
via-Hedwig : I wrote the following code after watching the inspiring talk of Kaushik Gopal at oredev. It’s implementation is inspired by his pagination example.
As you can see in the gif above,
concatMap() has solved the issue of ordering. Once again,
RxJava rocks and the power of it’s spells (operators) never cease to amaze me :)
Everything looks great! BUTTT!
Glitch! As you can see that the list is getting frequently updated and Updating the whole list that frequently isn’t very good in our world.
A friend of ours has suggested to improve upon this solution. Let’s see what he has to say :
via-Hedwig : It’s better to use switchMap as you don’t care about old results.
Switch-Map-ium (switchMap() operator)
Whenever a new item is emitted by the source Observable, it will unsubscribe to and stop mirroring the Observable that was generated from the previously-emitted item, and begin only mirroring the current one.
concatMap() also preserves order and will not unnecessarily emit the previously cached results if we have our final result! This sounds like just what we need here. Let’s see how our Auto-Complete Search looks like after using the magical spell Switch-Map-ium!
via-Hedwig : In the magical world of Rxify, nothing is right or wrong. I am still a beginner learning about RxJava and sharing my findings. There might be a better way of doing the things that I mention. If you happen to know a better way, I will be happy to discuss it :)
If you want to read some Harry-Potter themed walk-throughs of RxJava, you can have a look my multi-part
Rxify series here.
Good Luck :)