Connecting Redis to ElasticSearch for custom scoring with nativescripts
Posted by Kelvin on 14 Jun 2012 at 12:24 am | Tagged as: Lucene / Solr / Elasticsearch / Nutch
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); } }