tidwall / tile38

Real-time Geospatial and Geofencing
https://tile38.com
MIT License
9.15k stars 570 forks source link

Nearby command example with any client #190

Open sayden opened 7 years ago

sayden commented 7 years ago

Hi,

We're trying to use Tile38 with any Redis library available in the examples section. Unfortunately, we haven't managed to make NEARBY query works with Java or Go client libraries. In the examples it's not clear how to make queries like NEARBY so a small example will be nice.

NEARBY command has worked using CLI.

It's related with https://github.com/tidwall/tile38/issues/189

tidwall commented 7 years ago

Hi,

I just looked into the issue and I think that the client you are using may be having problems.

I posted a solution to #189.

Could you let me know which Go and Java clients you tested with so I can make a note of the issue?

sayden commented 7 years ago

The Java example here https://github.com/tidwall/tile38/wiki/Java-example-(lettuce) shows an example of using GET and SET.

Most of the clients also use only GET and SET examples (Node also shows a nearby). In the Java example, you can see that using GET you can retrieve a single point. If you add a .add("NEARBY") at the beginning it doesn't work.

import com.lambdaworks.redis.RedisClient;
import com.lambdaworks.redis.api.StatefulRedisConnection;
import com.lambdaworks.redis.codec.StringCodec;
import com.lambdaworks.redis.output.StatusOutput;
import com.lambdaworks.redis.protocol.CommandArgs;
import com.lambdaworks.redis.protocol.CommandType;
import com.lambdaworks.redis.api.sync.RedisCommands;

/**
 * Example for using Tile38 in Java using lettuce version 5.0.1.Beta1
 */
public class Example {

    public static void main(String[] args) {
        RedisClient client = RedisClient.create("redis://localhost:9851");
        StatefulRedisConnection<String, String> connection = client.connect();
        RedisCommands<String, String> sync = connection.sync();

        StringCodec codec = StringCodec.UTF8;
        sync.dispatch(CommandType.SET,
                new StatusOutput<>(codec), new CommandArgs<>(codec)
                        .add("fleet")
                        .add("truck1")
                        .add("POINT")
                        .add(33L)
                        .add(-115L));

        String result = sync.dispatch(CommandType.GET,
                new StatusOutput<>(codec), new CommandArgs<>(codec)
                        .add("NEARBY")    // <- We have tried this
                        .add("fleet")
                        .add("truck1"));

        System.out.println(result);
    }
}

It seems that lettuce needs to know the exact Redis command you are using in advance and don't provide direct way to execute a string command.

tidwall commented 7 years ago

Thanks for notifying me of this. The example should have at least one NEARBY query.

Lettuce recently added a dynamic API which is intended to work with custom commands.

https://paluch.biz/blog/168-command-interfaces-approaching-redis-with-dynamic-apis-in-java.html

The article states that it will work with Tile38.

Lars-Meijer commented 7 years ago

You need a enum class that extends CommandType, that includes "NEARBY", then you can replace sync.dispatch(CommandType.GET) by sync.dispatch(YourClass.NEARBY).

Looks like the Dynamic command API might also be a good option to use here.

sayden commented 7 years ago

CommandType is a enum that you cannot inherit from. We have tried with ProtocolKeyword which is the class that CommandType inherits. Ok we have tried this:


    val result = sync.dispatch(
      Nearby.NEARBY,
      new StatusOutput(codec),
      new CommandArgs(codec)
        .add("fleet")
        .add("LIMIT")
        .add("5")
        .add("POINT")
        .add("40")
        .add("-3")
        .add("100")

With this implementation of Nearby:

package com.routing;

import com.lambdaworks.redis.protocol.LettuceCharsets;
import com.lambdaworks.redis.protocol.ProtocolKeyword;

public enum Nearby implements ProtocolKeyword
{
    NEARBY("NEARBY");

    private final byte[] name;

    Nearby(String commandName) {
        name = commandName.getBytes(LettuceCharsets.ASCII);
    }

    public byte[] getBytes() {
        return name;
    }
}

Unfortunately we couldn't manage to make it work either. I'll be happy to make a PR once the example is working :)

I don't want to bother you much, maybe I should move to Lettuce Github.

mp911de commented 7 years ago

I spiked at Tile38 support with Lettuce a while ago to provide a dedicated API. The major effort lies in supporting all the possible data types, especially GeoJSON types. I didn't continue on that end because I didn't have much time left to support another client library. Dynamic API in Lettuce 5 is a decent abstraction for custom commands and support for custom response types.

If someone decides to maintain the data type part I'm happy to support on the Lettuce integration part to provide a Tile38-specific Java client.

skz363 commented 6 years ago

could anyone solve this issue? or have got solution to the same???

kimiaoi commented 6 years ago

I made it work by using previous comment, I'm not a pro and I'm just starting so it might have a lot of mistakes but it could help someone.

import com.lambdaworks.redis.RedisClient;
import com.lambdaworks.redis.api.StatefulRedisConnection;
import com.lambdaworks.redis.codec.StringCodec;
import com.lambdaworks.redis.output.NestedMultiOutput;
import com.lambdaworks.redis.output.StatusOutput;
import com.lambdaworks.redis.protocol.CommandArgs;
import com.lambdaworks.redis.protocol.CommandType;
import com.lambdaworks.redis.api.sync.RedisCommands;
import com.lambdaworks.redis.protocol.LettuceCharsets;
import com.lambdaworks.redis.protocol.ProtocolKeyword;

import java.util.List;

enum Nearby implements ProtocolKeyword
{
    NEARBY("NEARBY");

    private final byte[] name;

    Nearby(String commandName) {
        name = commandName.getBytes(LettuceCharsets.ASCII);
    }

    public byte[] getBytes() {
        return name;
    }
}

/**
 * Example for using Tile38 in Java using lettuce version 5.0.1.Beta1
 */
public class Example {

    public static void main(String[] args) {

        RedisClient client = RedisClient.create("redis://localhost:9851");
        StatefulRedisConnection<String, String> connection = client.connect();
        RedisCommands<String, String> sync = connection.sync();

        StringCodec codec = StringCodec.UTF8;

        //scan example
        CommandArgs<String, String> argScan = new CommandArgs<>(codec).add("fleet");
        List<Object> responseScan = sync.dispatch(CommandType.SCAN, new NestedMultiOutput<>(codec), argScan);
        System.out.println(responseScan);

        //set example
        CommandArgs<String, String> argSet = new CommandArgs<>(codec).add("fleet").add("truck1").add("POINT").add("33.462").add("-112.268");
        String responseSet = sync.dispatch(CommandType.SET, new StatusOutput<>(codec), argSet);
        System.out.println(responseSet);

        //get example
        CommandArgs<String, String> argGet = new CommandArgs<>(codec).add("fleet").add("truck1");
        String responseGet = sync.dispatch(CommandType.GET, new StatusOutput<>(codec), argGet );
        System.out.println(responseGet);

        //nearby example
        CommandArgs<String, String> argNearby = new CommandArgs<>(codec).add("fleet").add("POINT").add("33.462").add("-112.268").add("6000");
        List<Object> responseNearby = sync.dispatch(Nearby.NEARBY, new NestedMultiOutput<>(codec), argNearby);
        System.out.println(responseNearby);

    }
}