Connecting Redis to ElasticSearch For Custom Scoring With Nativescripts
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);
}
}
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)




