Skip to content

Commit 3d30f90

Browse files
committed
Merge pull request #15 from lstrojny/feature/eval
EVAL/EVALSHA support
2 parents 4ef771c + 8c04e6c commit 3d30f90

30 files changed

+460
-75
lines changed

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Configure the query parser plugin in `solrconfig.xml`. Add the following to the
3232

3333
### Allowed parameters for the RedisQParserPlugin:
3434

35-
* **command** - Redis command. Currently allowed: `SMEMBERS`, `SRANDMEMBER`, `SDIFF`, `SINTER`, `SUNION`, `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE`, `ZREVRANGEBYSCORE`, `HKEYS`, `HMGET`, `HVALS`, `LRANGE`, `LINDEX`, `GET`, `MGET`, `KEYS`, `SORT` (required)
35+
* **command** - Redis command. Currently allowed: `SMEMBERS`, `SRANDMEMBER`, `SDIFF`, `SINTER`, `SUNION`, `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE`, `ZREVRANGEBYSCORE`, `HKEYS`, `HMGET`, `HVALS`, `LRANGE`, `LINDEX`, `GET`, `MGET`, `KEYS`, `SORT`, `EVAL`, `EVALSHA` (required)
3636
* **key** - Key used to fetch data from Redis (required)
3737
* **operator** - Operator which connects terms taken from Redis. Allowed values are AND/OR (optional - default is OR)
3838
* **useAnalyzer** - Turns on and off query time analyzer true/false (optional - default is true)
@@ -75,6 +75,18 @@ SORT specific parameters:
7575
* **by** - Specify a different sort keys
7676
* **get\*** - Anything that starts with the prefix **get** is assumed to a redis SORT GET parameter
7777

78+
EVAL specific parameters:
79+
* **script** - Specify the LUA script to run
80+
* **returns_hash** - Specify if a LUA table like the following should be treated as a hash: `{1.2, 'str1', 2.2, 'str2'}`
81+
* **key\*** - Anything that starts with the prefix **key** is assumed to be a key
82+
* **arg\*** - Anything that starts with the prefix **arg** is assumed to be an arg
83+
84+
EVALSHA specific parameters:
85+
* **sha1** - Specify the SHA1 hash of the stored script
86+
* **returns_hash** - Specify if a LUA table like the following should be treated as a hash: `{1.2, 'str1', 2.2, 'str2'}`
87+
* **key\*** - Anything that starts with the prefix **key** is assumed to be a key
88+
* **arg\*** - Anything that starts with the prefix **arg** is assumed to be an arg
89+
7890
Examples of usage:
7991
* `q=*:*&fq={!redis command=SMEMBERS key=some_key}field`
8092
* `q=*:*&fq={!redis command=SRANDMEMBER key=some_key count=2}field`

src/main/java/com/sematext/solr/redis/RedisQParser.java

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.sematext.solr.redis;
22

33
import com.sematext.solr.redis.command.Command;
4+
import com.sematext.solr.redis.command.Eval;
5+
import com.sematext.solr.redis.command.EvalSha;
46
import com.sematext.solr.redis.command.Get;
57
import com.sematext.solr.redis.command.HGet;
68
import com.sematext.solr.redis.command.HKeys;
@@ -75,13 +77,15 @@ final class RedisQParser extends QParser {
7577
commands.put("KEYS", new Keys());
7678

7779
commands.put("SORT", new Sort());
80+
81+
commands.put("EVAL", new Eval());
82+
commands.put("EVALSHA", new EvalSha());
7883
}
7984

8085
private final JedisPool jedisPool;
8186
private Map<String, Float> results;
8287
private BooleanClause.Occur operator = BooleanClause.Occur.SHOULD;
8388
private final String redisCommand;
84-
private final String redisKey;
8589
private final boolean useQueryTimeAnalyzer;
8690
private final int maxJedisRetries;
8791

@@ -96,7 +100,6 @@ final class RedisQParser extends QParser {
96100
this.jedisPool = jedisPool;
97101

98102
redisCommand = localParams.get("command") == null ? null : localParams.get("command").toUpperCase();
99-
redisKey = localParams.get("key");
100103
final String operatorString = localParams.get("operator");
101104

102105
if (redisCommand == null) {
@@ -107,11 +110,6 @@ final class RedisQParser extends QParser {
107110
throw new IllegalArgumentException(String.format("Wrong Redis command '%s'.", redisCommand));
108111
}
109112

110-
if (redisKey == null || redisKey.isEmpty()) {
111-
log.error("No key argument passed to RedisQParser");
112-
throw new IllegalArgumentException("No key argument passed to RedisQParser");
113-
}
114-
115113
operator = "AND".equalsIgnoreCase(operatorString) ? BooleanClause.Occur.MUST : BooleanClause.Occur.SHOULD;
116114

117115
useQueryTimeAnalyzer = localParams.getBool("useAnalyzer", true);
@@ -125,7 +123,7 @@ public Query parse() throws SyntaxError {
125123
final BooleanQuery booleanQuery = new BooleanQuery(true);
126124
int booleanClausesTotal = 0;
127125

128-
fetchDataFromRedis(redisCommand, redisKey, maxJedisRetries);
126+
fetchDataFromRedis(redisCommand, maxJedisRetries);
129127

130128
if (results != null) {
131129
log.debug("Preparing a query for {} redis objects for field: {}", results.size(), fieldName);
@@ -178,20 +176,20 @@ public Query parse() throws SyntaxError {
178176
return booleanQuery;
179177
}
180178

181-
private void fetchDataFromRedis(final String redisCommand, final String redisKey, final int maxJedisRetries) {
179+
private void fetchDataFromRedis(final String redisCommand, final int maxRetries) {
182180
int retries = 0;
183181
final Command command = commands.get(redisCommand);
184182

185-
while (results == null && retries++ < maxJedisRetries + 1) {
183+
while (results == null && retries++ < maxRetries + 1) {
186184
Jedis jedis = null;
187185
try {
188186
jedis = jedisPool.getResource();
189-
results = command.execute(jedis, redisKey, localParams);
187+
results = command.execute(jedis, localParams);
190188
jedisPool.returnResource(jedis);
191189
} catch (final JedisException ex) {
192190
jedisPool.returnBrokenResource(jedis);
193191
log.debug("There was an error fetching data from redis. Retrying", ex);
194-
if (retries >= maxJedisRetries + 1) {
192+
if (retries >= maxRetries + 1) {
195193
throw ex;
196194
}
197195
}

src/main/java/com/sematext/solr/redis/RedisQParserPlugin.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ public QParser createParser(final String qstr, final SolrParams localParams, fin
4747
@Override
4848
public void init(final NamedList args) {
4949
final GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
50-
poolConfig.setMaxTotal(getInteger(args, MAX_CONNECTIONS_FIELD, DEFAULT_MAX_CONNECTIONS));
50+
poolConfig.setMaxTotal(getInt(args, MAX_CONNECTIONS_FIELD, DEFAULT_MAX_CONNECTIONS));
5151

5252
final String host = getString(args, HOST_FIELD, HostAndPort.LOCALHOST_STR);
53-
final Integer timeout = getInteger(args, TIMEOUT_FIELD, Protocol.DEFAULT_TIMEOUT);
53+
final int timeout = getInt(args, TIMEOUT_FIELD, Protocol.DEFAULT_TIMEOUT);
5454
final String password = getString(args, PASSWORD_FIELD, null);
55-
final Integer database = getInteger(args, DATABASE_FIELD, Protocol.DEFAULT_DATABASE);
56-
retries = getInteger(args, RETRIES_FIELD, DEFAULT_RETRIES);
55+
final int database = getInt(args, DATABASE_FIELD, Protocol.DEFAULT_DATABASE);
56+
retries = getInt(args, RETRIES_FIELD, DEFAULT_RETRIES);
5757

5858
log.info("Initializing RedisQParserPlugin with host: " + host);
5959
final String[] hostAndPort = host.split(":");
@@ -72,7 +72,7 @@ JedisPool createPool(final GenericObjectPoolConfig poolConfig, final String host
7272
return new JedisPool(poolConfig, host, port, timeout, password, database);
7373
}
7474

75-
private Integer getInteger(final NamedList args, final String key, final Integer def) {
75+
private int getInt(final NamedList args, final String key, final int def) {
7676
final Object value = args != null ? args.get(key) : null;
7777
return value instanceof String ? Integer.parseInt((String) value) : def;
7878
}

src/main/java/com/sematext/solr/redis/command/Command.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
import java.util.Map;
55

66
public interface Command<T> {
7-
Map<String, Float> execute(T client, String key, SolrParams params);
7+
Map<String, Float> execute(T client, SolrParams params);
88
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.sematext.solr.redis.command;
2+
3+
import org.apache.solr.common.params.SolrParams;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import redis.clients.jedis.ScriptingCommands;
7+
import java.util.Map;
8+
9+
public class Eval extends ScriptingCommand {
10+
private static final Logger log = LoggerFactory.getLogger(Eval.class);
11+
12+
@Override
13+
protected Map<String, Float> invokeCommand(final ScriptingCommands client, final SolrParams params,
14+
final int keyLength, final String[] args) {
15+
final String script = ParamUtil.assertGetStringByName(params, "script");
16+
17+
log.debug("Fetching EVAL from Redis for script: {}", script);
18+
19+
return createReturnValue(params, client.eval(script, keyLength, args));
20+
}
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.sematext.solr.redis.command;
2+
3+
import org.apache.solr.common.params.SolrParams;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import redis.clients.jedis.ScriptingCommands;
7+
import java.util.Map;
8+
9+
public class EvalSha extends ScriptingCommand {
10+
private static final Logger log = LoggerFactory.getLogger(EvalSha.class);
11+
12+
@Override
13+
protected Map<String, Float> invokeCommand(final ScriptingCommands client, final SolrParams params,
14+
final int keyLength, final String[] args) {
15+
final String sha1 = ParamUtil.assertGetStringByName(params, "sha1");
16+
17+
log.debug("Fetching EVALSHA from Redis for SHA1 sum: {}", sha1);
18+
19+
return createReturnValue(params, client.evalsha(sha1, keyLength, args));
20+
}
21+
}

src/main/java/com/sematext/solr/redis/command/Get.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ public final class Get implements Command<JedisCommands> {
1111
private static final Logger log = LoggerFactory.getLogger(Get.class);
1212

1313
@Override
14-
public Map<String, Float> execute(final JedisCommands client, final String key, final SolrParams params) {
14+
public Map<String, Float> execute(final JedisCommands client, final SolrParams params) {
15+
final String key = ParamUtil.assertGetStringByName(params, "key");
16+
1517
log.debug("Fetching GET from Redis for key: {}", key);
1618

1719
return ResultUtil.stringIteratorToMap(Arrays.asList(client.get(key)));

src/main/java/com/sematext/solr/redis/command/HGet.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,9 @@ public final class HGet implements Command<JedisCommands> {
1111
private static final Logger log = LoggerFactory.getLogger(HGet.class);
1212

1313
@Override
14-
public Map<String, Float> execute(final JedisCommands client, final String key, final SolrParams params) {
15-
final String field = ParamUtil.getStringByName(params, "field", null);
16-
17-
if (field == null) {
18-
throw new IllegalArgumentException("Required parameter \"field\" missing");
19-
}
14+
public Map<String, Float> execute(final JedisCommands client, final SolrParams params) {
15+
final String key = ParamUtil.assertGetStringByName(params, "key");
16+
final String field = ParamUtil.assertGetStringByName(params, "field");
2017

2118
log.debug("Fetching HGET from Redis for key: {} ({})", key, field);
2219

src/main/java/com/sematext/solr/redis/command/HKeys.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ public final class HKeys implements Command<JedisCommands> {
1010
private static final Logger log = LoggerFactory.getLogger(HKeys.class);
1111

1212
@Override
13-
public Map<String, Float> execute(final JedisCommands client, final String key, final SolrParams params) {
13+
public Map<String, Float> execute(final JedisCommands client, final SolrParams params) {
14+
final String key = ParamUtil.assertGetStringByName(params, "key");
15+
1416
log.debug("Fetching HKEYS from Redis for key: {}", key);
1517

1618
return ResultUtil.stringIteratorToMap(client.hkeys(key));

src/main/java/com/sematext/solr/redis/command/HMGet.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ public final class HMGet implements Command<JedisCommands> {
1010
private static final Logger log = LoggerFactory.getLogger(HMGet.class);
1111

1212
@Override
13-
public Map<String, Float> execute(final JedisCommands client, final String key, final SolrParams params) {
13+
public Map<String, Float> execute(final JedisCommands client, final SolrParams params) {
14+
final String key = ParamUtil.assertGetStringByName(params, "key");
1415
final String[] fields = ParamUtil.getStringByPrefix(params, "field");
1516

1617
log.debug("Fetching HMGET from Redis for key: {} ({})", key, fields);

0 commit comments

Comments
 (0)