Skip to content

Commit

Permalink
Escape utils for RediSearch queries (#3544)
Browse files Browse the repository at this point in the history
* Escape utils for RediSearch queries

* Escape strings in Map
  • Loading branch information
sazzad16 committed Sep 19, 2023
1 parent de38bda commit 443a377
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 2 deletions.
52 changes: 50 additions & 2 deletions src/main/java/redis/clients/jedis/search/RediSearchUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import redis.clients.jedis.util.SafeEncoder;

Expand All @@ -18,6 +21,18 @@ public class RediSearchUtil {
* @return map with string value
*/
public static Map<String, String> toStringMap(Map<String, Object> input) {
return toStringMap(input, false);
}

/**
* Jedis' {@code hset} methods do not support {@link Object}s as values. This method eases process
* of converting a {@link Map} with Objects as values so that the returning Map can be set to a
* {@code hset} method.
* @param input map with object value
* @param stringEscape whether to escape the String objects
* @return map with string value
*/
public static Map<String, String> toStringMap(Map<String, Object> input, boolean stringEscape) {
Map<String, String> output = new HashMap<>(input.size());
for (Map.Entry<String, Object> entry : input.entrySet()) {
String key = entry.getKey();
Expand All @@ -32,9 +47,9 @@ public static Map<String, String> toStringMap(Map<String, Object> input) {
redis.clients.jedis.GeoCoordinate geo = (redis.clients.jedis.GeoCoordinate) obj;
str = geo.getLongitude() + "," + geo.getLatitude();
} else if (obj instanceof String) {
str = (String) obj;
str = stringEscape ? escape((String) obj) : (String) obj;
} else {
str = obj.toString();
str = String.valueOf(obj);
}
output.put(key, str);
}
Expand All @@ -54,6 +69,39 @@ public static byte[] ToByteArray(float[] input) {
return bytes;
}

private static final Set<Character> ESCAPE_CHARS = new HashSet<>(Arrays.asList(//
',', '.', '<', '>', '{', '}', '[', //
']', '"', '\'', ':', ';', '!', '@', //
'#', '$', '%', '^', '&', '*', '(', //
')', '-', '+', '=', '~', '|' //
));

public static String escape(String text) {
return escape(text, false);
}

public static String escapeQuery(String query) {
return escape(query, true);
}

public static String escape(String text, boolean querying) {
char[] chars = text.toCharArray();

StringBuilder sb = new StringBuilder();
for (char ch : chars) {
if (ESCAPE_CHARS.contains(ch)
|| (querying && ch == ' ')) {
sb.append("\\");
}
sb.append(ch);
}
return sb.toString();
}

public static String unescape(String text) {
return text.replace("\\", "");
}

private RediSearchUtil() {
throw new InstantiationError("Must not instantiate this class");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1157,4 +1157,26 @@ public void searchIterationCollect() {
"pupil:4444", "student:5555", "teacher:6666").stream().collect(Collectors.toSet()),
collect.stream().map(Document::getId).collect(Collectors.toSet()));
}

@Test
public void escapeUtil() {
assertOK(client.ftCreate(index, TextField.of("txt")));

client.hset("doc1", "txt", RediSearchUtil.escape("hello-world"));
assertNotEquals("hello-world", client.hget("doc1", "txt"));
assertEquals("hello-world", RediSearchUtil.unescape(client.hget("doc1", "txt")));

SearchResult resultNoEscape = client.ftSearch(index, "hello-world");
assertEquals(0, resultNoEscape.getTotalResults());

SearchResult resultEscaped = client.ftSearch(index, RediSearchUtil.escapeQuery("hello-world"));
assertEquals(1, resultEscaped.getTotalResults());
}

@Test
public void escapeMapUtil() {
client.hset("doc2", RediSearchUtil.toStringMap(Collections.singletonMap("txt", "hello-world"), true));
assertNotEquals("hello-world", client.hget("doc2", "txt"));
assertEquals("hello-world", RediSearchUtil.unescape(client.hget("doc2", "txt")));
}
}

0 comments on commit 443a377

Please sign in to comment.