From 1c5b2f338412a33266b8922bdaacd526d4345c89 Mon Sep 17 00:00:00 2001 From: M Sazzadul Hoque <7600764+sazzad16@users.noreply.github.com> Date: Tue, 19 Sep 2023 20:22:16 +0600 Subject: [PATCH 1/5] Escape utils for RediSearch queries --- .../clients/jedis/search/RediSearchUtil.java | 36 +++++++++++++++++++ .../modules/search/SearchWithParamsTest.java | 15 ++++++++ 2 files changed, 51 insertions(+) diff --git a/src/main/java/redis/clients/jedis/search/RediSearchUtil.java b/src/main/java/redis/clients/jedis/search/RediSearchUtil.java index a6a82486b7..e08e0d2dd8 100644 --- a/src/main/java/redis/clients/jedis/search/RediSearchUtil.java +++ b/src/main/java/redis/clients/jedis/search/RediSearchUtil.java @@ -2,9 +2,12 @@ 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; @@ -54,6 +57,39 @@ public static byte[] ToByteArray(float[] input) { return bytes; } + public static final Set TAG_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 (TAG_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"); } diff --git a/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java b/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java index 98bc5968e8..046fccfb54 100644 --- a/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java +++ b/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java @@ -1210,4 +1210,19 @@ 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()); + } } From 6d0a72e8ead82340971127ff272a7378154bb9e5 Mon Sep 17 00:00:00 2001 From: M Sazzadul Hoque <7600764+sazzad16@users.noreply.github.com> Date: Tue, 19 Sep 2023 20:23:51 +0600 Subject: [PATCH 2/5] format import --- src/main/java/redis/clients/jedis/search/RediSearchUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/redis/clients/jedis/search/RediSearchUtil.java b/src/main/java/redis/clients/jedis/search/RediSearchUtil.java index e08e0d2dd8..48f89adfbe 100644 --- a/src/main/java/redis/clients/jedis/search/RediSearchUtil.java +++ b/src/main/java/redis/clients/jedis/search/RediSearchUtil.java @@ -2,8 +2,8 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.Arrays; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; From 9bd3afe894e870a242dd3b8c0c419ad8233ffae9 Mon Sep 17 00:00:00 2001 From: M Sazzadul Hoque <7600764+sazzad16@users.noreply.github.com> Date: Tue, 19 Sep 2023 20:49:59 +0600 Subject: [PATCH 3/5] private variable --- src/main/java/redis/clients/jedis/search/RediSearchUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/redis/clients/jedis/search/RediSearchUtil.java b/src/main/java/redis/clients/jedis/search/RediSearchUtil.java index 48f89adfbe..b32a7d1f7e 100644 --- a/src/main/java/redis/clients/jedis/search/RediSearchUtil.java +++ b/src/main/java/redis/clients/jedis/search/RediSearchUtil.java @@ -57,7 +57,7 @@ public static byte[] ToByteArray(float[] input) { return bytes; } - public static final Set TAG_ESCAPE_CHARS = new HashSet<>(Arrays.asList(// + private static final Set ESCAPE_CHARS = new HashSet<>(Arrays.asList(// ',', '.', '<', '>', '{', '}', '[', // ']', '"', '\'', ':', ';', '!', '@', // '#', '$', '%', '^', '&', '*', '(', // @@ -77,7 +77,7 @@ public static String escape(String text, boolean querying) { StringBuilder sb = new StringBuilder(); for (char ch : chars) { - if (TAG_ESCAPE_CHARS.contains(ch) + if (ESCAPE_CHARS.contains(ch) || (querying && ch == ' ')) { sb.append("\\"); } From 2220f3407d8d95ae049dde4c578d1093d43b9b5a Mon Sep 17 00:00:00 2001 From: M Sazzadul Hoque <7600764+sazzad16@users.noreply.github.com> Date: Tue, 19 Sep 2023 21:17:05 +0600 Subject: [PATCH 4/5] Escape strings in Map --- .../clients/jedis/search/RediSearchUtil.java | 16 ++++++++++++++-- .../modules/search/SearchWithParamsTest.java | 7 +++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/java/redis/clients/jedis/search/RediSearchUtil.java b/src/main/java/redis/clients/jedis/search/RediSearchUtil.java index b32a7d1f7e..43a16d4e20 100644 --- a/src/main/java/redis/clients/jedis/search/RediSearchUtil.java +++ b/src/main/java/redis/clients/jedis/search/RediSearchUtil.java @@ -21,6 +21,18 @@ public class RediSearchUtil { * @return map with string value */ public static Map toStringMap(Map 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 toStringMap(Map input, boolean stringEscape) { Map output = new HashMap<>(input.size()); for (Map.Entry entry : input.entrySet()) { String key = entry.getKey(); @@ -35,9 +47,9 @@ public static Map toStringMap(Map 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); } diff --git a/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java b/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java index 046fccfb54..4784eaa3d3 100644 --- a/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java +++ b/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java @@ -1225,4 +1225,11 @@ public void escapeUtil() { 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"))); + } } From b4c733f5d3bfffa06e1bc88d27b456a3c414f5b7 Mon Sep 17 00:00:00 2001 From: M Sazzadul Hoque <7600764+sazzad16@users.noreply.github.com> Date: Tue, 19 Sep 2023 21:20:24 +0600 Subject: [PATCH 5/5] fix capitalized method name --- .../redis/clients/jedis/search/RediSearchUtil.java | 10 +++++++++- .../redis/clients/jedis/modules/search/UtilTest.java | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/redis/clients/jedis/search/RediSearchUtil.java b/src/main/java/redis/clients/jedis/search/RediSearchUtil.java index 43a16d4e20..14cb963bde 100644 --- a/src/main/java/redis/clients/jedis/search/RediSearchUtil.java +++ b/src/main/java/redis/clients/jedis/search/RediSearchUtil.java @@ -63,12 +63,20 @@ public static Map toStringMap(Map input, boolean * @param input float array * @return byte array */ - public static byte[] ToByteArray(float[] input) { + public static byte[] toByteArray(float[] input) { byte[] bytes = new byte[Float.BYTES * input.length]; ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer().put(input); return bytes; } + /** + * @deprecated Use {@link RediSearchUtil#toByteArray(float[])}. + */ + @Deprecated + public static byte[] ToByteArray(float[] input) { + return toByteArray(input); + } + private static final Set ESCAPE_CHARS = new HashSet<>(Arrays.asList(// ',', '.', '<', '>', '{', '}', '[', // ']', '"', '\'', ':', ';', '!', '@', // diff --git a/src/test/java/redis/clients/jedis/modules/search/UtilTest.java b/src/test/java/redis/clients/jedis/modules/search/UtilTest.java index d63c934a37..efd9ff4726 100644 --- a/src/test/java/redis/clients/jedis/modules/search/UtilTest.java +++ b/src/test/java/redis/clients/jedis/modules/search/UtilTest.java @@ -9,7 +9,7 @@ public class UtilTest { @Test public void floatArrayToByteArray() { float[] floats = new float[]{0.2f}; - byte[] bytes = RediSearchUtil.ToByteArray(floats); + byte[] bytes = RediSearchUtil.toByteArray(floats); byte[] expected = new byte[]{-51, -52, 76, 62}; Assert.assertArrayEquals(expected, bytes); }