Redisearch Geo sorting

Hello,

I’ve just discovered the support in RediSearch for Geo filters. This is looks like a nice feature, but I am wondering if it somehow also could be used for sorting?

Let say you want to build a store locator and allow the user to sort by nearest shops.

The difficulty I see here it that the sorting mechanism needs an input: the users location, so I don’t see right now how this can be accomplished. Maybe someone knows a way to get this working.

Theoretical aggregation (https://oss.redislabs.com/redisearch/Aggregations.html) should allow you to do this, but I do not think there is currently enough functionality to achieve it. You need to calculate for each results the distance from the user and then sort by the distance. One option is to get the results (locations) and sort them on the client side. Another option is to use RedisGears (https://oss.redislabs.com/redisgears/). Using RedisGears you can run a python script on the server side that perform the search query and calculate for each result, the distance from the user, then it sort the results by the distance and returns them.

If you do choose to use RedisGears, let me know and I can help you do it.

Using Gears, the script will look something like this:

import math

USER_LON = 19

USER_LAT = 47

def CreateTouple(t):

lon = float(t[1].split(’,’)[0])

lat = float(t[1].split(’,’)[1])

docid = t[0]

return (docid, lon, lat)

‘’’

  1. Perform the redisearch query

  2. Remove the first results which is the number of results

  3. Put the docid and the location inside a touple

  4. Create a touple of (docid, lon, lat)

  5. Create a touple of (distance, docid)

  6. Sorting by distance

  7. Run the exection

‘’’

GB().\

map(lambda x: execute(‘FT.SEARCH’, ‘idx’, ‘@test:[19,47,100,km]’)).\

map(lambda x: x[1:]).\

flatmap(lambda x: [(x[i - 1], x[i][1]) for i in range(len(x)) if i % 2 == 1]).\

map(CreateTouple).\

map(lambda x: (math.sqrt((x[1] - USER_LON)**2 + (x[2] - USER_LAT)**2), x[0])).\

sort().\

run(‘idx:idx’)

``

And when running it on this data:
127.0.0.1:6379> FT.CREATE idx SCHEMA test GEO

OK

127.0.0.1:6379> FT.ADD idx doc1 1.0 FIELDS test “19.05,47.497”

OK

127.0.0.1:6379> FT.ADD idx doc2 1.0 FIELDS test “19.06,47.498”

OK

127.0.0.1:6379> FT.ADD idx doc3 1.0 FIELDS test “19.07,47.499”

OK

127.0.0.1:6379> FT.ADD idx doc4 1.0 FIELDS test “19.02,47.492”

OK

``

You will get something like this:
python gears.py ClientExample.py

[["(0.4924063362711708, ‘doc4’)", “(0.4995087586819674, ‘doc1’)”, “(0.5016014354046422, ‘doc2’)”, “(0.5038858997828798, ‘doc3’)”], []]

``

You can see that inside the Geas script I use RediSearch query to extract the data, then I calculating the distance from the “user” and then I sorting the results.

Hi Meir! I’ve been looking for the same functionality. We are doing a POC and this feature is crucial for us. I was hoping to be able to run a query similar to GEORADIUS WITHDIST:

GEORADIUS vendor-geo -111 33 50 km WITHDIST

``

to get back the full records and a sortable distance with a search query like:

FT.SEARCH vendors “@geolocation:[-111.9729275 33.4322451 50 km] WITHDIST” SORTBY distance ASC LIMIT 0 5

``

+1 - we would also love to have this functionality for the same use case (sort results by distance for the user).

Ok we got the message :), created an issue for this and we will try to push it (https://github.com/RediSearch/RediSearch/issues/916).

Until then I came up with another way to do it but it will require to put the LON and the LAT as NUMERIC fields inside the index (there is not need to actually index them), something like this:

127.0.0.1:6379> FT.CREATE idx SCHEMA geo GEO lon NUMERIC NOINDEX lat NUMERIC NOINDEX

OK

127.0.0.1:6379> FT.ADD idx doc1 1.0 FIELDS geo “19.05,47.497” lon 19.05 lat 47.497

OK

127.0.0.1:6379> FT.ADD idx doc2 1.0 FIELDS geo “19.06,47.498” lon 19.06 lat 47.498

OK

127.0.0.1:6379> FT.ADD idx doc3 1.0 FIELDS geo “19.07,47.499” lon 19.07 lat 47.499

OK

127.0.0.1:6379> FT.ADD idx doc4 1.0 FIELDS geo “19.02,47.492” lon 19.02 lat 47.492

OK

``

Then the query will look like this:

127.0.0.1:6379> FT.AGGREGATE idx @geo:[19,47,100,km] LOAD 2 @lon @lat APPLY “sqrt((@lon - 19)^2 + (@lat - 47)^2)” as dist sortby 1 @dist

  1. (integer) 4

    1. lon
  2. “19.02”

  3. lat

  4. “47.492”

  5. dist

  6. “0.492406336271”

    1. lon
  7. “19.05”

  8. lat

  9. “47.497”

  10. dist

  11. “0.499508758682”

    1. lon
  12. “19.06”

  13. lat

  14. “47.498”

  15. dist

  16. “0.501601435405”

    1. lon
  17. “19.07”

  18. lat

  19. “47.499”

  20. dist

  21. “0.503885899783”

``

What’s your timeline on this PoC?

We added it to 2.0 scope for now and will try to tackle this once 1.6 is out

+1 - thanks @Meir - that’s a brilliant and easy solution for now,

@Pieter - I’m running the POC now with our reps. The workaround @Meir provided will get me through the POC. This feature will be a nice competitive advantage since I haven’t found a means using Elastic or other search platforms to provide this in a single query at the speed of Redis. Thanks for taking it on and looking at it!