Over a million developers have joined DZone.

Connecting Redis to ElasticSearch For Custom Scoring With Nativescripts

· Java Zone

Discover how AppDynamics steps in to upgrade your performance game and prevent your enterprise from these top 10 Java performance problems, brought to you in partnership with AppDynamics.

After connecting Redis and MongoDB to Solr, I figured it'd be interesting to do the same with ElasticSearch. Here's the result of my experiments:

We'll be implementing this using AbstractSearchScript, which is roughly ElasticSearch's version of Solr's FunctionQuery.

ES' NativeScriptFactory corresponds loosely to Solr's ValueSourceParser, and AbstractSearchScript to ValueSource.

public class RedisNativeScriptFactory implements NativeScriptFactory {
  @Override public ExecutableScript newScript(@Nullable Map<String, Object> params) {
    return new RedisScript(params);
  }
}
public class RedisScript extends AbstractFloatSearchScript {
  private String idField;
  private String redisKey;
  private String redisValue;
  private final Jedis jedis;
  private JSONObject obj;

  public RedisScript(Map<String, Object> params) {
    this.idField = (String) params.get("idField");
    this.redisKey = (String) params.get("redisKey");
    this.redisValue = (String) params.get("redisValue");
    jedis = new Jedis("localhost");
    String v = jedis.hget(redisKey, redisValue);
    if (v != null) {
      obj = (JSONObject) JSONValue.parse(v);
    } else {
      obj = new JSONObject();
    }
  }

  @Override public float runAsFloat() {
    String id = doc().field(idField).stringValue();
    Object v = obj.get(id);
    if (v != null) {
      try {
        return Float.parseFloat(v.toString());
      } catch (NumberFormatException e) {
        return 0;
      }
    }
    return 0;
  }
}

Now in config/elasticsearch.yml, add this:

script.native:
  redis.type: org.supermind.es.redis.RedisNativeScriptFactory

Change redis to whatever you want the script name to be, and change the class name accordingly too.

Now, to use this:

curl -XGET 'http://localhost:9200/electronics/product/_search' -d '{
  "query" :{
     "custom_score": {
       "query" : { "match_all": {}},
       "script" : "redis",
       "params" :{
          "idField": "id",
          "redisKey": "bar",
          "redisValue" : "500"
       },
       "lang": "native"
     }
  }
}'

PS: My implementation of RedisScript assumes a Redis hash has been populated with a json object corresponding to an idField. Here's a class populating the redis hash. JSON objects are created with the json-smart package, but you can plugin your favourite json lib:

public static void main(String[] args) {
    Jedis jedis = new Jedis("localhost");
    int num = 100000;
    Random r = new Random();
    for(int i=0;i< num;++i) {
      JSONObject o = new JSONObject();
      int numberOfEntries = r.nextInt(100);
      for(int j=0;j< numberOfEntries;++j) {
        o.put("es" + j, r.nextInt(100));
      }
      String json = o.toJSONString(JSONStyle.MAX_COMPRESS);
      jedis.hset("bar", Integer.toString(i), json);
    }
  }

The Java Zone is brought to you in partnership with AppDynamics. AppDynamics helps you gain the fundamentals behind application performance, and implement best practices so you can proactively analyze and act on performance problems as they arise, and more specifically with your Java applications. Start a Free Trial.

Topics:

Published at DZone with permission of Kelvin Tan .

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}