Skip to content

Commit c3bc5d3

Browse files
committed
feature: implements summarization/highlight for EntityStreams with Hashes (resolved gh-324)
1 parent 2c7ea83 commit c3bc5d3

File tree

9 files changed

+3743
-0
lines changed

9 files changed

+3743
-0
lines changed

redis-om-spring/src/main/java/com/redis/om/spring/search/stream/ReturnFieldsSearchStreamImpl.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.redis.om.spring.metamodel.indexed.NumericField;
77
import com.redis.om.spring.ops.search.SearchOperations;
88
import com.redis.om.spring.search.stream.predicates.SearchFieldPredicate;
9+
import com.redis.om.spring.tuple.Pair;
910
import com.redis.om.spring.tuple.Tuple;
1011
import com.redis.om.spring.tuple.Tuples;
1112
import com.redis.om.spring.util.ObjectUtils;
@@ -452,4 +453,24 @@ public String backingQuery() {
452453
return entitySearchStream.backingQuery();
453454
}
454455

456+
@Override
457+
public <R> SearchStream<T> summarize(Function<? super T, ? extends R> field) {
458+
throw new UnsupportedOperationException("summarize is not supported on a ReturnFieldSearchStream");
459+
}
460+
461+
@Override
462+
public <R> SearchStream<T> summarize(Function<? super T, ? extends R> field, SummarizeParams params) {
463+
throw new UnsupportedOperationException("summarize is not supported on a ReturnFieldSearchStream");
464+
}
465+
466+
@Override
467+
public <R> SearchStream<T> highlight(Function<? super T, ? extends R> field) {
468+
throw new UnsupportedOperationException("highlight is not supported on a ReturnFieldSearchStream");
469+
}
470+
471+
@Override
472+
public <R> SearchStream<T> highlight(Function<? super T, ? extends R> field, Pair<String,String> tags) {
473+
throw new UnsupportedOperationException("highlight is not supported on a ReturnFieldSearchStream");
474+
}
475+
455476
}

redis-om-spring/src/main/java/com/redis/om/spring/search/stream/SearchStream.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.redis.om.spring.metamodel.indexed.NumericField;
55
import com.redis.om.spring.ops.search.SearchOperations;
66
import com.redis.om.spring.search.stream.predicates.SearchFieldPredicate;
7+
import com.redis.om.spring.tuple.Pair;
78
import org.springframework.data.domain.Example;
89
import org.springframework.data.domain.Pageable;
910
import org.springframework.data.domain.Slice;
@@ -14,6 +15,7 @@
1415
import java.util.Comparator;
1516
import java.util.Map;
1617
import java.util.Optional;
18+
import java.util.Set;
1719
import java.util.function.*;
1820
import java.util.stream.*;
1921

@@ -119,4 +121,11 @@ public interface SearchStream<E> extends BaseStream<E, SearchStream<E>> {
119121
<R> SearchStream<E> project(MetamodelField<? super E, ? extends R> ...field);
120122

121123
String backingQuery();
124+
125+
<R> SearchStream<E> summarize(Function<? super E, ? extends R> field);
126+
127+
<R> SearchStream<E> summarize(Function<? super E, ? extends R> field, SummarizeParams params);
128+
129+
<R> SearchStream<E> highlight(Function<? super E, ? extends R> field);
130+
<R> SearchStream<E> highlight(Function<? super E, ? extends R> field, Pair<String,String> tags);
122131
}

redis-om-spring/src/main/java/com/redis/om/spring/search/stream/SearchStreamImpl.java

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.data.domain.Sort.Order;
2626
import org.springframework.data.redis.core.convert.ReferenceResolverImpl;
2727
import redis.clients.jedis.search.Query;
28+
import redis.clients.jedis.search.Query.HighlightTags;
2829
import redis.clients.jedis.search.SearchResult;
2930
import redis.clients.jedis.search.aggr.AggregationResult;
3031
import redis.clients.jedis.search.aggr.SortedField;
@@ -75,6 +76,12 @@ public class SearchStreamImpl<E> implements SearchStream<E> {
7576

7677
private final List<MetamodelField<E, ?>> projections = new ArrayList<>();
7778

79+
private final List<MetamodelField<E, ?>> summaryFields = new ArrayList<>();
80+
private SummarizeParams summarizeParams;
81+
private final List<MetamodelField<E, ?>> highlightFields = new ArrayList<>();
82+
83+
private Pair<String,String> highlightTags;
84+
7885
private final ExampleToNodeConverter<E> exampleToNodeConverter;
7986

8087
public SearchStreamImpl(Class<E> entityClass, RedisModulesOperations<String> modulesOperations, GsonBuilder gsonBuilder,
@@ -426,6 +433,36 @@ Query prepareQuery() {
426433
query.setSortBy(sortField.getField(), sortField.getOrder().equals("ASC"));
427434
}
428435

436+
if (!summaryFields.isEmpty()) {
437+
var fields = summaryFields.stream() //
438+
.map(foi -> ObjectUtils.isCollection(foi.getTargetClass()) ? "$." + foi.getSearchAlias() : foi.getSearchAlias())
439+
.collect(toCollection(ArrayList::new));
440+
441+
if (summarizeParams == null) {
442+
query.summarizeFields(fields.toArray(String[]::new));
443+
} else {
444+
query.summarizeFields( //
445+
summarizeParams.getFragSize(), //
446+
summarizeParams.getFragsNum(), //
447+
summarizeParams.getSeparator(), //
448+
fields.toArray(String[]::new) //
449+
);
450+
}
451+
}
452+
453+
if (!highlightFields.isEmpty()) {
454+
var fields = highlightFields.stream() //
455+
.map(foi -> ObjectUtils.isCollection(foi.getTargetClass()) ? "$." + foi.getSearchAlias() : foi.getSearchAlias())
456+
.collect(toCollection(ArrayList::new));
457+
458+
if (highlightTags == null) {
459+
query.highlightFields(fields.toArray(String[]::new));
460+
} else {
461+
HighlightTags tags = new HighlightTags(highlightTags.getFirst(),highlightTags.getSecond());
462+
query.highlightFields(tags, fields.toArray(String[]::new));
463+
}
464+
}
465+
429466
if (onlyIds) {
430467
query.returnFields(idField.getName());
431468
} else if (!projections.isEmpty()) {
@@ -652,6 +689,58 @@ public String backingQuery() {
652689
return rootNode.toString();
653690
}
654691

692+
@Override
693+
public <R> SearchStream<E> summarize(Function<? super E, ? extends R> field) {
694+
if (MetamodelField.class.isAssignableFrom(field.getClass())) {
695+
@SuppressWarnings("unchecked")
696+
MetamodelField<E, R> foi = (MetamodelField<E, R>) field;
697+
summaryFields.add(foi);
698+
699+
} else if (TupleMapper.class.isAssignableFrom(field.getClass())) {
700+
@SuppressWarnings("rawtypes")
701+
AbstractTupleMapper tm = (AbstractTupleMapper) field;
702+
703+
IntStream.range(0, tm.degree()).forEach(i -> {
704+
@SuppressWarnings("unchecked")
705+
MetamodelField<E, ?> foi = (MetamodelField<E, ?>) tm.get(i);
706+
summaryFields.add(foi);
707+
});
708+
}
709+
return this;
710+
}
711+
712+
@Override
713+
public <R> SearchStream<E> summarize(Function<? super E, ? extends R> field, SummarizeParams summarizeParams) {
714+
this.summarizeParams = summarizeParams;
715+
return summarize(field);
716+
}
717+
718+
@Override
719+
public <R> SearchStream<E> highlight(Function<? super E, ? extends R> field) {
720+
if (MetamodelField.class.isAssignableFrom(field.getClass())) {
721+
@SuppressWarnings("unchecked")
722+
MetamodelField<E, R> foi = (MetamodelField<E, R>) field;
723+
highlightFields.add(foi);
724+
725+
} else if (TupleMapper.class.isAssignableFrom(field.getClass())) {
726+
@SuppressWarnings("rawtypes")
727+
AbstractTupleMapper tm = (AbstractTupleMapper) field;
728+
729+
IntStream.range(0, tm.degree()).forEach(i -> {
730+
@SuppressWarnings("unchecked")
731+
MetamodelField<E, ?> foi = (MetamodelField<E, ?>) tm.get(i);
732+
highlightFields.add(foi);
733+
});
734+
}
735+
return this;
736+
}
737+
738+
@Override
739+
public <R> SearchStream<E> highlight(Function<? super E, ? extends R> field, Pair<String,String> tags) {
740+
highlightTags = tags;
741+
return highlight(field);
742+
}
743+
655744
public boolean isDocument() {
656745
return isDocument;
657746
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.redis.om.spring.search.stream;
2+
3+
import redis.clients.jedis.search.FTSearchParams;
4+
5+
public class SummarizeParams {
6+
public Integer getFragsNum() {
7+
return fragsNum;
8+
}
9+
10+
public Integer getFragSize() {
11+
return fragSize;
12+
}
13+
14+
public String getSeparator() {
15+
return separator;
16+
}
17+
18+
private Integer fragsNum = 3;
19+
private Integer fragSize = 20;
20+
private String separator = "...";
21+
22+
public SummarizeParams fragments(int num) {
23+
this.fragsNum = num;
24+
return this;
25+
}
26+
27+
public SummarizeParams size(int size) {
28+
this.fragSize = size;
29+
return this;
30+
}
31+
32+
public SummarizeParams separator(String separator) {
33+
this.separator = separator;
34+
return this;
35+
}
36+
37+
public static SummarizeParams instance() {
38+
return new SummarizeParams();
39+
}
40+
}

redis-om-spring/src/main/java/com/redis/om/spring/search/stream/WrapperSearchStream.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.redis.om.spring.metamodel.indexed.NumericField;
55
import com.redis.om.spring.ops.search.SearchOperations;
66
import com.redis.om.spring.search.stream.predicates.SearchFieldPredicate;
7+
import com.redis.om.spring.tuple.Pair;
78
import org.springframework.data.domain.Example;
89
import org.springframework.data.domain.Pageable;
910
import org.springframework.data.domain.Slice;
@@ -324,4 +325,24 @@ public String backingQuery() {
324325
throw new UnsupportedOperationException("backingQuery is not supported on a WrappedSearchStream");
325326
}
326327

328+
@Override
329+
public <R> SearchStream<E> summarize(Function<? super E, ? extends R> field) {
330+
throw new UnsupportedOperationException("summarize is not supported on a WrappedSearchStream");
331+
}
332+
333+
@Override
334+
public <R> SearchStream<E> summarize(Function<? super E, ? extends R> field, SummarizeParams params) {
335+
throw new UnsupportedOperationException("summarize is not supported on a WrappedSearchStream");
336+
}
337+
338+
@Override
339+
public <R> SearchStream<E> highlight(Function<? super E, ? extends R> field) {
340+
throw new UnsupportedOperationException("highlight is not supported on a WrappedSearchStream");
341+
}
342+
343+
@Override
344+
public <R> SearchStream<E> highlight(Function<? super E, ? extends R> field, Pair<String,String> tags) {
345+
throw new UnsupportedOperationException("highlight is not supported on a WrappedSearchStream");
346+
}
347+
327348
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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 Text {
14+
@Id
15+
private String id;
16+
17+
@NonNull
18+
@Searchable
19+
private String body;
20+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.redis.om.spring.annotations.hash.fixtures;
2+
3+
import com.redis.om.spring.annotations.hash.fixtures.Text;
4+
import com.redis.om.spring.repository.RedisDocumentRepository;
5+
6+
public interface TextRepository extends RedisDocumentRepository<Text, String> {
7+
}

0 commit comments

Comments
 (0)