Skip to content

Commit

Permalink
Support RediSearch DIALECT (#2960)
Browse files Browse the repository at this point in the history
* added dialect

* fixed test default dialect

* added javadoc

* format codes

Co-authored-by: M Sazzadul Hoque <[email protected]>
  • Loading branch information
DvirDukhan and sazzad16 committed Mar 31, 2022
1 parent 83af8c1 commit 5fa461e
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 9 deletions.
17 changes: 17 additions & 0 deletions src/main/java/redis/clients/jedis/search/Query.java
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ public HighlightTags(String open, String close) {
private boolean wantsSummarize = false;
private String _scorer = null;
private Map<String, Object> _params = null;
private int _dialect = 0;

public Query() {
this("*");
Expand Down Expand Up @@ -303,6 +304,11 @@ public void addParams(CommandArguments args) {
args.add(entry.getValue());
}
}

if (_dialect != 0) {
args.add(SearchKeyword.DIALECT.getRaw());
args.add(_dialect);
}
}

private static class DelayedRawable implements Rawable {
Expand Down Expand Up @@ -548,4 +554,15 @@ public Query addParam(String name, Object value) {
_params.put(name, value);
return this;
}

/**
* Set the dialect version to execute the query accordingly
*
* @param dialect integer
* @return the query object itself
*/
public Query dialect(int dialect) {
_dialect = dialect;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public enum SearchKeyword implements Rawable {
ASC, DESC, PAYLOAD, LIMIT, HIGHLIGHT, FIELDS, TAGS, SUMMARIZE, FRAGS, LEN, SEPARATOR, INKEYS,
RETURN, /*NOSAVE, PARTIAL, REPLACE,*/ FILTER, GEOFILTER, INCR, MAX, FUZZY, DD, /*DELETE,*/ DEL,
READ, COUNT, ADD, TEMPORARY, STOPWORDS, NOFREQS, NOFIELDS, NOOFFSETS, /*IF,*/ SET, GET, ON,
ASYNC, PREFIX, LANGUAGE_FIELD, SCORE_FIELD, SCORE, PAYLOAD_FIELD, SCORER, PARAMS;
ASYNC, PREFIX, LANGUAGE_FIELD, SCORE_FIELD, SCORE, PAYLOAD_FIELD, SCORER, PARAMS, DIALECT;

private final byte[] raw;

Expand Down
142 changes: 134 additions & 8 deletions src/test/java/redis/clients/jedis/modules/search/SearchTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package redis.clients.jedis.modules.search;

import static java.util.Collections.singletonMap;
import static org.junit.Assert.*;
import static redis.clients.jedis.search.RediSearchUtil.toStringMap;

Expand Down Expand Up @@ -451,10 +452,11 @@ public void testHNSWVVectorSimilarity() {
client.hset("b", "v", "aaaabaaa");
client.hset("c", "v", "aaaaabaa");

Query query = new Query("*=>[KNN 2 @v $vec]")
Query query = new Query("*=>[KNN 2 @v $vec]")
.addParam("vec", "aaaaaaaa")
.setSortBy("__v_score", true)
.returnFields("__v_score");
.returnFields("__v_score")
.dialect(2);
Document doc1 = client.ftSearch(index, query).getDocuments().get(0);
assertEquals("a", doc1.getId());
assertEquals("0", doc1.get("__v_score"));
Expand All @@ -474,15 +476,139 @@ public void testFlatVectorSimilarity() {
client.hset("b", "v", "aaaabaaa");
client.hset("c", "v", "aaaaabaa");

Query query = new Query("*=>[KNN 2 @v $vec]")
Query query = new Query("*=>[KNN 2 @v $vec]")
.addParam("vec", "aaaaaaaa")
.setSortBy("__v_score", true)
.returnFields("__v_score");
.returnFields("__v_score")
.dialect(2);
Document doc1 = client.ftSearch(index, query).getDocuments().get(0);
assertEquals("a", doc1.getId());
assertEquals("0", doc1.get("__v_score"));
}

@Test
public void testDialectConfig() {
// confirm default
assertEquals(singletonMap("DEFAULT_DIALECT", "1"), client.ftConfigGet("DEFAULT_DIALECT"));

assertEquals("OK", client.ftConfigSet("DEFAULT_DIALECT", "2"));
assertEquals(singletonMap("DEFAULT_DIALECT", "2"), client.ftConfigGet("DEFAULT_DIALECT"));

try {
client.ftConfigSet("DEFAULT_DIALECT", "0");
fail();
} catch (JedisDataException ex) {
}

try {
client.ftConfigSet("DEFAULT_DIALECT", "3");
fail();
} catch (JedisDataException ex) {
}

// Restore to default
assertEquals("OK", client.ftConfigSet("DEFAULT_DIALECT", "1"));
}

@Test
public void testDialectsWithFTExplain() throws Exception {
Map<String, Object> attr = new HashMap<>();
attr.put("TYPE", "FLOAT32");
attr.put("DIM", 2);
attr.put("DISTANCE_METRIC", "L2");

Schema sc = new Schema()
.addFlatVectorField("v", attr)
.addTagField("title")
.addTextField("t1", 1.0)
.addTextField("t2", 1.0)
.addNumericField("num");
assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc));

client.hset("1", "t1", "hello");

String q = "(*)";
Query query = new Query(q).dialect(1);
try {
client.ftExplain(index, query);
fail();
} catch (JedisDataException e) {
assertTrue("Should contain 'Syntax error'", e.getMessage().contains("Syntax error"));
}
query = new Query(q).dialect(2);
assertTrue("Should contain 'WILDCARD'", client.ftExplain(index, query).contains("WILDCARD"));

q = "$hello";
query = new Query(q).dialect(1);
try {
client.ftExplain(index, query);
fail();
} catch (JedisDataException e) {
assertTrue("Should contain 'Syntax error'", e.getMessage().contains("Syntax error"));
}
query = new Query(q).dialect(2).addParam("hello", "hello");
assertTrue("Should contain 'UNION {\n hello\n +hello(expanded)\n}\n'",
client.ftExplain(index, query).contains("UNION {\n hello\n +hello(expanded)\n}\n"));

q = "@title:(@num:[0 10])";
query = new Query(q).dialect(1);
assertTrue("Should contain 'NUMERIC {0.000000 <= @num <= 10.000000}'",
client.ftExplain(index, query).contains("NUMERIC {0.000000 <= @num <= 10.000000}"));
query = new Query(q).dialect(2);
try {
client.ftExplain(index, query);
fail();
} catch (JedisDataException e) {
assertTrue("Should contain 'Syntax error'", e.getMessage().contains("Syntax error"));
}

q = "@t1:@t2:@t3:hello";
query = new Query(q).dialect(1);
assertTrue("Should contain '@NULL:UNION {\n @NULL:hello\n @NULL:+hello(expanded)\n}\n'",
client.ftExplain(index, query).contains("@NULL:UNION {\n @NULL:hello\n @NULL:+hello(expanded)\n}\n"));
query = new Query(q).dialect(2);
try {
client.ftExplain(index, query);
fail();
} catch (JedisDataException e) {
assertTrue("Should contain 'Syntax error'", e.getMessage().contains("Syntax error"));
}

q = "@title:{foo}}}}}";
query = new Query(q).dialect(1);
assertTrue("Should contain 'TAG:@title {\n foo\n}\n'",
client.ftExplain(index, query).contains("TAG:@title {\n foo\n}\n"));
query = new Query(q).dialect(2);
try {
client.ftExplain(index, query);
fail();
} catch (JedisDataException e) {
assertTrue("Should contain 'Syntax error'", e.getMessage().contains("Syntax error"));
}

q = "*=>[KNN 10 @v $BLOB]";
query = new Query(q).addParam("BLOB", "aaaa").dialect(1);
try {
client.ftExplain(index, query);
fail();
} catch (JedisDataException e) {
assertTrue("Should contain 'Syntax error'", e.getMessage().contains("Syntax error"));
}
query = new Query(q).addParam("BLOB", "aaaa").dialect(2);
assertTrue("Should contain '{K=10 nearest vector'", client.ftExplain(index, query).contains("{K=10 nearest vector"));

q = "*=>[knn $K @vec_field $BLOB as score]";
query = new Query(q).addParam("BLOB", "aaaa").addParam("K", "10").dialect(1);
try {
client.ftExplain(index, query);
fail();
} catch (JedisDataException e) {
assertTrue("Should contain 'Syntax error'", e.getMessage().contains("Syntax error"));
}
query = new Query(q).addParam("BLOB", "aaaa").addParam("K", "10").dialect(2);
assertTrue("Should contain '{K=10 nearest vector'", client.ftExplain(index, query).contains("{K=10 nearest vector"));
}

@Test
public void testQueryParams() {
Schema sc = new Schema().addNumericField("numval");
Expand All @@ -492,7 +618,7 @@ public void testQueryParams() {
client.hset("2", "numval", "2");
client.hset("3", "numval", "3");

Query query = new Query("@numval:[$min $max]").addParam("min", 1).addParam("max", 2);
Query query = new Query("@numval:[$min $max]").addParam("min", 1).addParam("max", 2).dialect(2);
assertEquals(2, client.ftSearch(index, query).getTotalResults());
}

Expand Down Expand Up @@ -1218,8 +1344,8 @@ public void returnWithFieldNames() throws Exception {
addDocument("doc", map);

// Query
SearchResult res = client.ftSearch(index, new Query()
.returnFields(FieldName.of("a"), FieldName.of("b").as("d")));
SearchResult res = client.ftSearch(index,
new Query().returnFields(FieldName.of("a"), FieldName.of("b").as("d")));
assertEquals(1, res.getTotalResults());
Document doc = res.getDocuments().get(0);
assertEquals("value1", doc.get("a"));
Expand Down Expand Up @@ -1285,7 +1411,7 @@ public void config() throws Exception {
@Test
public void configOnTimeout() throws Exception {
assertEquals("OK", client.ftConfigSet("ON_TIMEOUT", "fail"));
assertEquals(Collections.singletonMap("ON_TIMEOUT", "fail"), client.ftConfigGet("ON_TIMEOUT"));
assertEquals(singletonMap("ON_TIMEOUT", "fail"), client.ftConfigGet("ON_TIMEOUT"));

try {
client.ftConfigSet("ON_TIMEOUT", "null");
Expand Down

0 comments on commit 5fa461e

Please sign in to comment.