io.quarkus.redis.runtime.datasource.ReactiveSearchCommandsImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of quarkus-redis-client Show documentation
Show all versions of quarkus-redis-client Show documentation
Connect to Redis in either imperative or reactive style
package io.quarkus.redis.runtime.datasource;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import io.quarkus.redis.datasource.ReactiveRedisCommands;
import io.quarkus.redis.datasource.ReactiveRedisDataSource;
import io.quarkus.redis.datasource.search.AggregateArgs;
import io.quarkus.redis.datasource.search.AggregateDocument;
import io.quarkus.redis.datasource.search.AggregationResponse;
import io.quarkus.redis.datasource.search.CreateArgs;
import io.quarkus.redis.datasource.search.Document;
import io.quarkus.redis.datasource.search.IndexedField;
import io.quarkus.redis.datasource.search.QueryArgs;
import io.quarkus.redis.datasource.search.ReactiveSearchCommands;
import io.quarkus.redis.datasource.search.SearchQueryResponse;
import io.quarkus.redis.datasource.search.SpellCheckArgs;
import io.quarkus.redis.datasource.search.SpellCheckResponse;
import io.quarkus.redis.datasource.search.SynDumpResponse;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.redis.client.Response;
import io.vertx.redis.client.ResponseType;
public class ReactiveSearchCommandsImpl extends AbstractSearchCommands
implements ReactiveSearchCommands, ReactiveRedisCommands {
private final ReactiveRedisDataSource reactive;
protected final Type keyType;
public ReactiveSearchCommandsImpl(ReactiveRedisDataSourceImpl redis, Type k) {
super(redis, k);
this.reactive = redis;
this.keyType = k;
}
@Override
public ReactiveRedisDataSource getDataSource() {
return reactive;
}
@Override
public Uni> ft_list() {
return super._ft_list()
.map(r -> marshaller.decodeAsList(r, keyType));
}
@Override
public Uni ftAggregate(String indexName, String query, AggregateArgs args) {
return super._ftAggregate(indexName, query, args)
.map(r -> decodeAggregateResponse(r, args.hasCursor()));
}
AggregationResponse decodeAggregateResponse(Response response, boolean cursor) {
if (response == null) {
return new AggregationResponse(Collections.emptyList());
}
var payload = response;
var cursorId = -1L;
if (cursor) {
payload = response.get(0);
cursorId = response.get(1).toLong();
}
if (isMap(payload)) {
// Redis 7.2+ behavior
Response results = payload.get("results");
List docs = new ArrayList<>();
if (results != null) {
for (Response result : results) {
Map list = new HashMap<>();
if (result.containsKey("extra_attributes")) {
Response attributes = result.get("extra_attributes");
for (String propertyName : attributes.getKeys()) {
list.put(propertyName, new Document.Property(propertyName, attributes.get(propertyName)));
}
}
AggregateDocument doc = new AggregateDocument(list);
docs.add(doc);
}
}
return new AggregationResponse(cursorId, docs);
}
// Pre-Redis 7.2 behavior:
List docs = new ArrayList<>();
for (int i = 1; i < payload.size(); i++) {
var nested = payload.get(i);
String propertyName = null;
Map list = new HashMap<>();
for (Response n : nested) {
if (propertyName == null) {
propertyName = n.toString();
} else {
list.put(propertyName, new Document.Property(propertyName, n));
propertyName = null;
}
}
AggregateDocument doc = new AggregateDocument(list);
docs.add(doc);
}
return new AggregationResponse(cursorId, docs);
}
@Override
public Uni ftAggregate(String indexName, String query) {
return super._ftAggregate(indexName, query)
.map(r -> decodeAggregateResponse(r, false));
}
@Override
public Uni ftAliasAdd(String alias, String index) {
return super._ftAliasAdd(alias, index)
.replaceWithVoid();
}
@Override
public Uni ftAliasDel(String alias) {
return super._ftAliasDel(alias)
.replaceWithVoid();
}
@Override
public Uni ftAliasUpdate(String alias, String index) {
return super._ftAliasUpdate(alias, index)
.replaceWithVoid();
}
@Override
public Uni ftAlter(String index, IndexedField field, boolean skipInitialScan) {
return super._ftAlter(index, field, skipInitialScan)
.replaceWithVoid();
}
@Override
public Uni ftCreate(String index, CreateArgs args) {
return super._ftCreate(index, args)
.replaceWithVoid();
}
@Override
public Uni ftCursorDel(String index, long cursor) {
return super._ftCursorDel(index, cursor)
.replaceWithVoid();
}
@Override
public Uni ftCursorRead(String index, long cursor) {
return super._ftCursorRead(index, cursor)
.map(r -> decodeAggregateResponse(r, true));
}
@Override
public Uni ftCursorRead(String index, long cursor, int count) {
return super._ftCursorRead(index, cursor, count)
.map(r -> decodeAggregateResponse(r, true));
}
@Override
public Uni ftDropIndex(String index) {
return super._ftDropIndex(index)
.replaceWithVoid();
}
@Override
public Uni ftDropIndex(String index, boolean dd) {
return super._ftDropIndex(index, dd)
.replaceWithVoid();
}
@Override
public Uni ftDictAdd(String dict, String... words) {
return super._ftDictAdd(dict, words)
.replaceWithVoid();
}
@Override
public Uni ftDictDel(String dict, String... words) {
return super._ftDictDel(dict, words)
.replaceWithVoid();
}
@Override
public Uni> ftDictDump(String dict) {
return super._ftDictDump(dict)
.map(r -> marshaller.decodeAsList(r, String.class));
}
@Override
public Uni ftSearch(String index, String query, QueryArgs args) {
return super._ftSearch(index, query, args)
.map(r -> decodeSearchQueryResult(r, args));
}
@Override
public Uni ftSearch(String index, String query) {
return super._ftSearch(index, query)
.map(r -> decodeSearchQueryResult(r, null));
}
SearchQueryResponse decodeSearchQueryResult(Response response, QueryArgs args) {
if (response == null) {
return new SearchQueryResponse(0, Collections.emptyList());
}
if (isMap(response)) {
// Read it as a map - Redis 7.2 behavior
var count = response.get("total_results");
if (count == null || count.toInteger() == 0) {
return new SearchQueryResponse(0, Collections.emptyList());
}
var total = count.toInteger();
List docs = new ArrayList<>();
Response results = response.get("results");
for (int i = 0; i < results.size(); i++) {
Response result = results.get(i);
Response score = result.get("score");
Response payload = result.get("payload");
Response doc = result.get("extra_attributes");
Map properties = new HashMap<>();
if (doc != null) {
for (String k : doc.getKeys()) {
properties.put(k, new Document.Property(k, doc.get(k)));
}
}
docs.add(
new Document(result.get("id").toString(), score == null ? 1.0 : score.toDouble(), payload, properties));
}
return new SearchQueryResponse(total, docs);
}
// 7.2- behavior
var count = response.get(0).toInteger();
if (count == 0) {
return new SearchQueryResponse(0, Collections.emptyList());
}
List docs = new ArrayList<>();
for (int i = 1; i < response.size();) {
var offset = i;
Response key = response.get(offset);
Response score = null;
if (args != null && args.containsScore()) {
offset++;
score = response.get(offset);
}
Response payload = null;
if (args != null && args.containsPayload()) {
offset++;
payload = response.get(offset);
}
if (args != null && args.containsSortKeys()) {
offset++;
}
Response doc = null;
if (args == null || !args.nocontent) {
offset++;
doc = response.get(offset);
}
String att = null;
Map properties = new HashMap<>();
if (doc != null) {
for (Response nested : doc) {
if (att == null) {
att = nested.toString();
} else {
Document.Property property = new Document.Property(att, nested);
properties.put(att, property);
att = null;
}
}
}
docs.add(new Document(key.toString(), score == null ? 1.0 : score.toDouble(), payload, properties));
// Increment
i = offset + 1;
}
return new SearchQueryResponse(count, docs);
}
@Override
public Uni ftSpellCheck(String index, String query) {
return super._ftSpellCheck(index, query)
.map(this::decodeSpellcheckResponse);
}
SpellCheckResponse decodeSpellcheckResponse(Response response) {
if (response == null || response.size() == 0) {
return new SpellCheckResponse(Collections.emptyMap());
}
Map> resp = new LinkedHashMap<>();
if (isMap(response)) {
// Redis 7.2 behavior.
Response results = response.get("results");
Set terms = results.getKeys();
for (String word : terms) {
List list = new ArrayList<>();
Response suggestions = results.get(word);
if (suggestions.size() == 0) {
resp.put(word, Collections.emptyList());
} else {
for (Response suggestion : suggestions) {
for (String proposal : suggestion.getKeys()) {
double distance = suggestion.get(proposal).toDouble();
if (!proposal.equals(word)) {
list.add(new SpellCheckResponse.SpellCheckSuggestion(proposal, distance));
}
}
if (!list.isEmpty()) {
resp.put(word, list);
}
}
}
}
return new SpellCheckResponse(resp);
}
for (Response term : response) {
if (!term.get(0).toString().equals("TERM")) {
continue; // Unknown format
} else {
String word = term.get(1).toString();
Response suggestions = term.get(2);
if (suggestions.size() == 0) {
resp.put(word, Collections.emptyList());
} else {
List list = new ArrayList<>();
for (Response suggestion : suggestions) {
double distance = suggestion.get(0).toDouble();
String proposal = suggestion.get(1).toString();
if (!proposal.equals(word)) {
list.add(new SpellCheckResponse.SpellCheckSuggestion(proposal, distance));
}
}
if (!list.isEmpty()) {
resp.put(word, list);
}
}
}
}
return new SpellCheckResponse(resp);
}
@Override
public Uni ftSpellCheck(String index, String query, SpellCheckArgs args) {
return super._ftSpellCheck(index, query, args)
.map(this::decodeSpellcheckResponse);
}
@Override
public Uni ftSynDump(String index) {
return super._ftSynDump(index)
.map(this::decodeSynDumpResponse);
}
SynDumpResponse decodeSynDumpResponse(Response r) {
if (r == null || r.size() == 0) {
return new SynDumpResponse(Collections.emptyMap());
}
if (isMap(r)) {
Set keys = r.getKeys();
// Redis 7.2 behavior
Map> synonyms = new HashMap<>();
for (String key : keys) {
var groups = r.get(key);
for (Response group : groups) {
synonyms.computeIfAbsent(group.toString(), x -> new ArrayList<>()).add(key);
}
}
return new SynDumpResponse(synonyms);
}
Map> synonyms = new HashMap<>();
String term = null;
for (Response response : r) {
if (response.type() == ResponseType.BULK) {
term = response.toString();
} else if (response.type() == ResponseType.MULTI) {
for (Response group : response) {
synonyms.computeIfAbsent(group.toString(), x -> new ArrayList<>()).add(term);
}
}
}
return new SynDumpResponse(synonyms);
}
@Override
public Uni ftSynUpdate(String index, String groupId, String... words) {
return super._ftSynUpdate(index, groupId, words)
.replaceWithVoid();
}
@Override
public Uni ftSynUpdate(String index, String groupId, boolean skipInitialScan, String... words) {
return super._ftSynUpdate(index, groupId, skipInitialScan, words)
.replaceWithVoid();
}
@Override
public Uni> ftTagVals(String index, String field) {
return super._ftTagVals(index, field)
.map(r -> marshaller.decodeAsSet(r, String.class));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy