Javari
Javari

Reputation: 118

node-redis/search query elements by geospatial position and additional indexes

I want to be able to query elements in a redis cache based on 3 different indexes. Those indexes would be:

  1. A MAC address stored as a String.
  2. A number.
  3. A latitude and longitude(to be able to query spatially).

I have seen that Redis has support for multi indexing using redis search and native geospatial api.

so using nodejs and node-redis I have written the following index:


client.ft.create(
            'idx:cits',
            {
                mid: {
                    type: SchemaFieldTypes.TEXT
                },
                timestamp: {
                    type: SchemaFieldTypes.NUMERIC,
                    sortable: true
                },
                position: {
                    type: SchemaFieldTypes.GEO
                }
            },
            {
                ON: 'HASH',
                PREFIX: 'CITS'
            }
        )

Now, i would like to insert records on the database that include those 3 parameters plus an additional String that stores some payload. I have tried using

await client.hSet('CITS:19123123:0:0:00:00:5e:00:53:af', {
        timestamp: 19123123,
        position: {latitude:0, longitude:0},
        mid: '00:00:5e:00:53:af',
        message: 'payload'
    })

But I get the following error:

throw new TypeError('Invalid argument type');
                  ^

TypeError: Invalid argument type

So, i can't add the latitude and longitude that way, I also tried using the module ngeohash and computing an 11 character wide geohash like so:

await client.hSet('CITS:19123123:0:0:00:00:5e:00:53:af', {
        timestamp: 19123123,
        position: geohash.encode(0, 0, 11),
        mid: '00:00:5e:00:53:af',
        message: 'payload'
    })

And it does not give any error but when using redis search querys It does not find points near it.

Is it even possible what I am trying to do? If so, how would you input the data to the redis database?

Here is a minimal reproducible example (Im using "ngeohash": "^0.6.3" and "redis": "^4.5.0"):

const { createClient, SchemaFieldTypes } = require('redis')
const geohash = require('ngeohash')

const client = createClient()

async function start(client) {
    await client.connect()
    try {
        // We only want to sort by these 3 values
        await client.ft.create(
            'idx:cits',
            {
                mid: {
                    type: SchemaFieldTypes.TEXT
                },
                timestamp: {
                    type: SchemaFieldTypes.NUMERIC,
                    sortable: true
                },
                position: {
                    type: SchemaFieldTypes.GEO
                }
            },
            {
                ON: 'HASH',
                PREFIX: 'CITS'
            }
        )
    } catch (e) {
        if (e.message === 'Index already exists') {
            console.log('Skipping index creation as it already exists.')
        } else {
            console.error(e)
            process.exit(1)
        }
    }

    await client.hSet('CITS:19123123:0:0:00:00:5e:00:53:af', {
        timestamp: 19123123,
        position: geohash.encode(0, 0, 11),
        mid: '00:00:5e:00:53:af',
        message: 'payload'
    })
    await client.hSet('CITS:19123123:0.001:0.001:ff:ff:ff:ff:ff:ff', {
        timestamp: 19123123,
        position: geohash.encode(0.001, 0.001, 11),
        mid: 'ff:ff:ff:ff:ff:ff',
        message: 'payload'
    })

    const results = await client.ft.search(
        'idx:cits',
        '@position:[0 0 10000 km]'
    )
    console.log(results)

    await client.quit()
}

start(client)

Additionally, I would like to ask if there is maybe another type of database that better suits my needs. I have chosen redis because it offers low latency, and that is the biggest constraint in my environment(I will probably do more writes than reads per second). I only want it to act as a inmediate cache, as persistent data will be stored in another database that does not need to be fast.

Thank you.

Upvotes: 1

Views: 993

Answers (1)

Leibale Eidelman
Leibale Eidelman

Reputation: 3194

You get the Invalid argument type error because Redis does not support nested fields in hashes.

"GEO allows geographic range queries against the value in this attribute. The value of the attribute must be a string containing a longitude (first) and latitude separated by a comma" (https://redis.io/commands/ft.create/)

Upvotes: 2

Related Questions