Skip to content

Commit db043ab

Browse files
committed
fix: prevent double escaping on searchable text fields when using .containing (resolves gh-373)
1 parent e477ee9 commit db043ab

File tree

6 files changed

+177
-7
lines changed

6 files changed

+177
-7
lines changed

redis-om-spring/src/main/java/com/redis/om/spring/search/stream/predicates/fulltext/ContainingPredicate.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.redis.om.spring.search.stream.predicates.fulltext;
22

33
import com.redis.om.spring.metamodel.SearchFieldAccessor;
4-
import com.redis.om.spring.repository.query.QueryUtils;
54
import com.redis.om.spring.search.stream.predicates.BaseAbstractPredicate;
65
import org.apache.commons.lang3.ObjectUtils;
76
import redis.clients.jedis.search.querybuilder.Node;
@@ -22,7 +21,7 @@ public T getValue() {
2221

2322
@Override
2423
public Node apply(Node root) {
25-
return ObjectUtils.isNotEmpty(getValue()) ? QueryBuilders.intersect(root).add(getSearchAlias(), "*" + QueryUtils.escape(getValue().toString(), true) + "*") : root;
24+
return ObjectUtils.isNotEmpty(getValue()) ? QueryBuilders.intersect(root).add(getSearchAlias(), "*" + getValue().toString() + "*") : root;
2625
}
2726

2827
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.redis.om.spring.annotations.hash.fixtures;
2+
3+
import com.redis.om.spring.annotations.Searchable;
4+
import lombok.*;
5+
import org.springframework.data.annotation.Id;
6+
import org.springframework.data.redis.core.RedisHash;
7+
8+
@Data
9+
@RequiredArgsConstructor(staticName = "of")
10+
@AllArgsConstructor(access = AccessLevel.PROTECTED)
11+
@NoArgsConstructor(force = true)
12+
@RedisHash
13+
public class ASimpleHash {
14+
@Id
15+
private String id;
16+
17+
@Searchable(sortable = true)
18+
@NonNull
19+
private String first;
20+
21+
@Searchable(sortable = true)
22+
private String second;
23+
24+
@Searchable(sortable = true)
25+
private String third;
26+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.redis.om.spring.annotations.hash.fixtures;
2+
3+
import com.redis.om.spring.repository.RedisEnhancedRepository;
4+
5+
public interface ASimpleHashRepository extends RedisEnhancedRepository<ASimpleHash, String> {
6+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.redis.om.spring.annotations.hash.fixtures;
22

3-
import com.redis.om.spring.repository.RedisDocumentRepository;
3+
import com.redis.om.spring.repository.RedisEnhancedRepository;
44

5-
public interface TextRepository extends RedisDocumentRepository<Text, String> {
5+
public interface TextRepository extends RedisEnhancedRepository<Text, String> {
66
}

redis-om-spring/src/test/java/com/redis/om/spring/search/stream/EntityStreamDocsTest.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2440,4 +2440,75 @@
24402440

24412441
assertThat(names).isEmpty();
24422442
}
2443+
2444+
@Test void testContainingPredicateOnFreeFormTextStartMatches() {
2445+
doc3Repository.deleteAll();
2446+
var doc = Doc3.of("someDoc");
2447+
doc.setSecond("some text about nothing");
2448+
doc.setThird("some other text");
2449+
2450+
doc3Repository.save(doc);
2451+
2452+
var docs = entityStream.of(Doc3.class)
2453+
.filter(Doc3$.SECOND.containing("some text"))
2454+
.collect(Collectors.toList());
2455+
2456+
assertEquals(1, docs.size());
2457+
assertThat(docs.get(0).getSecond()).isEqualTo("some text about nothing");
2458+
2459+
doc3Repository.delete(doc);
2460+
}
2461+
2462+
@Test void testContainingPredicateOnFreeFormTextMiddleMatches() {
2463+
doc3Repository.deleteAll();
2464+
var doc = Doc3.of("someDoc");
2465+
doc.setSecond("some text about nothing");
2466+
doc.setThird("some other text");
2467+
2468+
doc3Repository.save(doc);
2469+
2470+
var docs = entityStream.of(Doc3.class)
2471+
.filter(Doc3$.SECOND.containing("text about"))
2472+
.collect(Collectors.toList());
2473+
2474+
assertEquals(1, docs.size());
2475+
assertThat(docs.get(0).getSecond()).isEqualTo("some text about nothing");
2476+
2477+
doc3Repository.delete(doc);
2478+
}
2479+
2480+
@Test void testContainingPredicateOnFreeFormTextEndMatches() {
2481+
doc3Repository.deleteAll();
2482+
var doc = Doc3.of("someDoc");
2483+
doc.setSecond("some text about nothing");
2484+
doc.setThird("some other text");
2485+
2486+
doc3Repository.save(doc);
2487+
2488+
var docs = entityStream.of(Doc3.class)
2489+
.filter(Doc3$.SECOND.containing("about nothing"))
2490+
.collect(Collectors.toList());
2491+
2492+
assertEquals(1, docs.size());
2493+
assertThat(docs.get(0).getSecond()).isEqualTo("some text about nothing");
2494+
2495+
doc3Repository.delete(doc);
2496+
}
2497+
2498+
@Test void testContainingPredicateOnFreeFormTextMiddleIncompleteWords() {
2499+
doc3Repository.deleteAll();
2500+
var doc = Doc3.of("someDoc");
2501+
doc.setSecond("some text about nothing");
2502+
doc.setThird("some other text");
2503+
doc3Repository.save(doc);
2504+
2505+
var docs = entityStream.of(Doc3.class)
2506+
.filter(Doc3$.SECOND.containing("ext abou"))
2507+
.collect(Collectors.toList());
2508+
2509+
assertEquals(1, docs.size());
2510+
assertThat(docs.get(0).getSecond()).isEqualTo("some text about nothing");
2511+
2512+
doc3Repository.delete(doc);
2513+
}
24432514
}

redis-om-spring/src/test/java/com/redis/om/spring/search/stream/EntityStreamHashTest.java

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
import com.google.common.collect.Iterators;
44
import com.redis.om.spring.AbstractBaseEnhancedRedisTest;
5-
import com.redis.om.spring.annotations.hash.fixtures.Company;
6-
import com.redis.om.spring.annotations.hash.fixtures.Company$;
7-
import com.redis.om.spring.annotations.hash.fixtures.CompanyRepository;
5+
import com.redis.om.spring.annotations.hash.fixtures.*;
86
import com.redis.om.spring.tuple.Fields;
97
import com.redis.om.spring.tuple.Pair;
108
import com.redis.om.spring.tuple.Triple;
@@ -29,6 +27,8 @@
2927

3028
@SuppressWarnings("SpellCheckingInspection") class EntityStreamHashTest extends AbstractBaseEnhancedRedisTest {
3129
@Autowired CompanyRepository repository;
30+
@Autowired
31+
ASimpleHashRepository aSimpleHashRepository;
3232
@Autowired EntityStream entityStream;
3333

3434
String redisId;
@@ -1532,4 +1532,72 @@
15321532

15331533
assertThat(names).isEmpty();
15341534
}
1535+
1536+
@Test void testContainingPredicateOnFreeFormTextStartMatches() {
1537+
var hash = ASimpleHash.of("someDoc");
1538+
hash.setSecond("some text about nothing");
1539+
hash.setThird("some other text");
1540+
1541+
aSimpleHashRepository.save(hash);
1542+
1543+
var hashes = entityStream.of(ASimpleHash.class)
1544+
.filter(ASimpleHash$.SECOND.containing("some text"))
1545+
.collect(Collectors.toList());
1546+
1547+
assertEquals(1, hashes.size());
1548+
assertThat(hashes.get(0).getSecond()).isEqualTo("some text about nothing");
1549+
1550+
aSimpleHashRepository.delete(hash);
1551+
}
1552+
1553+
@Test void testContainingPredicateOnFreeFormTextMiddleMatches() {
1554+
var hash = ASimpleHash.of("someDoc");
1555+
hash.setSecond("some text about nothing");
1556+
hash.setThird("some other text");
1557+
1558+
aSimpleHashRepository.save(hash);
1559+
1560+
var hashes = entityStream.of(ASimpleHash.class)
1561+
.filter(ASimpleHash$.SECOND.containing("text about"))
1562+
.collect(Collectors.toList());
1563+
1564+
assertEquals(1, hashes.size());
1565+
assertThat(hashes.get(0).getSecond()).isEqualTo("some text about nothing");
1566+
1567+
aSimpleHashRepository.delete(hash);
1568+
}
1569+
1570+
@Test void testContainingPredicateOnFreeFormTextEndMatches() {
1571+
var hash = ASimpleHash.of("someDoc");
1572+
hash.setSecond("some text about nothing");
1573+
hash.setThird("some other text");
1574+
1575+
aSimpleHashRepository.save(hash);
1576+
1577+
var hashes = entityStream.of(ASimpleHash.class)
1578+
.filter(ASimpleHash$.SECOND.containing("about nothing"))
1579+
.collect(Collectors.toList());
1580+
1581+
assertEquals(1, hashes.size());
1582+
assertThat(hashes.get(0).getSecond()).isEqualTo("some text about nothing");
1583+
1584+
aSimpleHashRepository.delete(hash);
1585+
}
1586+
1587+
@Test void testContainingPredicateOnFreeFormTextMiddleIncompleteWords() {
1588+
var hash = ASimpleHash.of("someDoc");
1589+
hash.setSecond("some text about nothing");
1590+
hash.setThird("some other text");
1591+
1592+
aSimpleHashRepository.save(hash);
1593+
1594+
var hashes = entityStream.of(ASimpleHash.class)
1595+
.filter(ASimpleHash$.SECOND.containing("ext abou"))
1596+
.collect(Collectors.toList());
1597+
1598+
assertEquals(1, hashes.size());
1599+
assertThat(hashes.get(0).getSecond()).isEqualTo("some text about nothing");
1600+
1601+
aSimpleHashRepository.delete(hash);
1602+
}
15351603
}

0 commit comments

Comments
 (0)