Skip to content

Commit

Permalink
FT.PROFILE.
Browse files Browse the repository at this point in the history
Signed-off-by: Yury-Fridlyand <yury.fridlyand@improving.com>
  • Loading branch information
Yury-Fridlyand committed Oct 17, 2024
2 parents 8c7addd + 61636f5 commit 6641d4d
Show file tree
Hide file tree
Showing 6 changed files with 653 additions and 40 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* Java: Added `FT.CREATE` ([#2414](https://github.com/valkey-io/valkey-glide/pull/2414))
* Java: Added `FT.DROPINDEX` ([#2440](https://github.com/valkey-io/valkey-glide/pull/2440))
* Java: Added `FT.AGGREGATE` ([#2466](https://github.com/valkey-io/valkey-glide/pull/2466))
* Java: Added `FT.SEARCH` ([#2439](https://github.com/valkey-io/valkey-glide/pull/2439))
* Core: Update routing for commands from server modules ([#2461](https://github.com/valkey-io/valkey-glide/pull/2461))

#### Breaking Changes
Expand Down
99 changes: 96 additions & 3 deletions glide-core/src/client/value_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use redis::{
cluster_routing::Routable, from_owned_redis_value, Cmd, ErrorKind, RedisResult, Value,
};

#[derive(Clone, Copy)]
#[derive(Clone, Copy, Debug)]
pub(crate) enum ExpectedReturnType<'a> {
Map {
key_type: &'a Option<ExpectedReturnType<'a>>,
Expand All @@ -23,6 +23,8 @@ pub(crate) enum ExpectedReturnType<'a> {
ArrayOfBools,
ArrayOfDoubleOrNull,
FTAggregateReturnType,
FTSearchReturnType,
FTProfileReturnType(&'a Option<ExpectedReturnType<'a>>),
Lolwut,
ArrayOfStringAndArrays,
ArrayOfArraysOfDoubleOrNull,
Expand Down Expand Up @@ -952,11 +954,93 @@ pub(crate) fn convert_to_expected_type(
}
_ => Err((
ErrorKind::TypeError,
"Response couldn't be converted to FT.AGGREGATION",
"Response couldn't be converted for FT.AGGREGATE",
format!("(response was {:?})", get_value_type(&value)),
)
.into()),
},
ExpectedReturnType::FTSearchReturnType => match value {
/*
Example of the response
1) (integer) 2
2) "json:2"
3) 1) "__VEC_score"
2) "11.1100006104"
3) "$"
4) "{\"vec\":[1.1,1.2,1.3,1.4,1.5,1.6]}"
4) "json:0"
5) 1) "__VEC_score"
2) "91"
3) "$"
4) "{\"vec\":[1,2,3,4,5,6]}"
Converting response to
1) (integer) 2
2) 1# "json:2" =>
1# "__VEC_score" => "11.1100006104"
2# "$" => "{\"vec\":[1.1,1.2,1.3,1.4,1.5,1.6]}"
2# "json:0" =>
1# "__VEC_score" => "91"
2# "$" => "{\"vec\":[1,2,3,4,5,6]}"
Response may contain only 1 element, no conversion in that case.
*/
Value::Array(ref array) if array.len() == 1 => Ok(value),
Value::Array(mut array) => {
Ok(Value::Array(vec![
array.remove(0),
convert_to_expected_type(Value::Array(array), Some(ExpectedReturnType::Map {
key_type: &Some(ExpectedReturnType::BulkString),
value_type: &Some(ExpectedReturnType::Map {
key_type: &Some(ExpectedReturnType::BulkString),
value_type: &Some(ExpectedReturnType::BulkString),
}),
}))?
]))
},
_ => Err((
ErrorKind::TypeError,
"Response couldn't be converted for FT.SEARCH",
format!("(response was {:?})", get_value_type(&value)),
)
.into())
},
ExpectedReturnType::FTProfileReturnType(type_of_query) => match value {
/*
Example of the response
1) <query response>
2) 1) 1) "parse.time"
2) 119
2) 1) "all.count"
2) 4
3) 1) "sync.time"
2) 0
Converting response to
1) <converted query>
2) 1# "parse.time" => 119
2# "all.count" => 4
3# "sync.time" => 0
Converting first array element as it is needed for the inner query and second element to a map.
*/
Value::Array(mut array) if array.len() == 2 => {
let res = vec![
convert_to_expected_type(array.remove(0), *type_of_query)?,
convert_to_expected_type(array.remove(0), Some(ExpectedReturnType::Map {
key_type: &None,
value_type: &None,
}))?];

Ok(Value::Array(res))
},
_ => Err((
ErrorKind::TypeError,
"Response couldn't be converted for FT.PROFILE",
format!("(response was {:?})", get_value_type(&value)),
)
.into())
}
}
}

Expand Down Expand Up @@ -1077,7 +1161,7 @@ fn convert_inner_map_by_type(

/// Converts the given array into a map, and converts key-value elements using the specified types.
///
/// `array` Aa 2-dimensional array. Each entry of the array has two values: the first
/// `array` is a 2-dimensional array. Each entry of the array has two values: the first
/// element is the key for the map, and the second element is the value for the map.
/// `key_type` is used to convert each key when collecting into the resulting map.
/// If `None` is given, then the key is not converted.
Expand Down Expand Up @@ -1322,6 +1406,15 @@ pub(crate) fn expected_type_for_cmd(cmd: &Cmd) -> Option<ExpectedReturnType> {
value_type: &None,
}),
b"FT.AGGREGATE" => Some(ExpectedReturnType::FTAggregateReturnType),
b"FT.SEARCH" => Some(ExpectedReturnType::FTSearchReturnType),
// TODO replace with tuple
b"FT.PROFILE" => Some(ExpectedReturnType::FTProfileReturnType(
if cmd.arg_idx(2).is_some_and(|a| a == b"SEARCH") {
&Some(ExpectedReturnType::FTSearchReturnType)
} else {
&Some(ExpectedReturnType::FTAggregateReturnType)
},
)),
_ => None,
}
}
Expand Down
110 changes: 110 additions & 0 deletions java/client/src/main/java/glide/api/commands/servermodules/FT.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import glide.api.models.commands.FT.FTAggregateOptions;
import glide.api.models.commands.FT.FTCreateOptions;
import glide.api.models.commands.FT.FTCreateOptions.FieldInfo;
import glide.api.models.commands.FT.FTProfileOptions;
import glide.api.models.commands.FT.FTSearchOptions;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -144,6 +146,83 @@ public static CompletableFuture<String> create(
return executeCommand(client, args, false);
}

/**
* Uses the provided query expression to locate keys within an index. Once located, the count
* and/or content of indexed fields within those keys can be returned.
*
* @param client The client to execute the command.
* @param indexName The index name to search into.
* @param query The text query to search.
* @param options The search options - see {@link FTSearchOptions}.
* @return A two element array, where first element is count of documents in result set, and the
* second element, which has format <code>
* {@literal Map<GlideString, Map<GlideString, GlideString>>}</code> - a mapping between
* document names and map of their attributes.<br>
* If {@link FTSearchOptions.FTSearchOptionsBuilder#count()} or {@link
* FTSearchOptions.FTSearchOptionsBuilder#limit(int, int)} with values <code>0, 0</code> is
* set, the command returns array with only one element - the count of the documents.
* @example
* <pre>{@code
* byte[] vector = new byte[24];
* Arrays.fill(vector, (byte) 0);
* var result = client.ftsearch("json_idx1", "*=>[KNN 2 @VEC $query_vec]",
* FTSearchOptions.builder().params(Map.of(gs("query_vec"), gs(vector))).build())
* .get();
* assertArrayEquals(result, new Object[] { 2L, Map.of(
* gs("json:2"), Map.of(gs("__VEC_score"), gs("11.1100006104"), gs("$"), gs("{\"vec\":[1.1,1.2,1.3,1.4,1.5,1.6]}")),
* gs("json:0"), Map.of(gs("__VEC_score"), gs("91"), gs("$"), gs("{\"vec\":[1,2,3,4,5,6]}")))
* });
* }</pre>
*/
public static CompletableFuture<Object[]> search(
@NonNull BaseClient client,
@NonNull String indexName,
@NonNull String query,
@NonNull FTSearchOptions options) {
var args =
concatenateArrays(
new GlideString[] {gs("FT.SEARCH"), gs(indexName), gs(query)}, options.toArgs());
return executeCommand(client, args, false);
}

/**
* Uses the provided query expression to locate keys within an index. Once located, the count
* and/or content of indexed fields within those keys can be returned.
*
* @param client The client to execute the command.
* @param indexName The index name to search into.
* @param query The text query to search.
* @param options The search options - see {@link FTSearchOptions}.
* @return A two element array, where first element is count of documents in result set, and the
* second element, which has format <code>
* {@literal Map<GlideString, Map<GlideString, GlideString>>}</code> - a mapping between
* document names and map of their attributes.<br>
* If {@link FTSearchOptions.FTSearchOptionsBuilder#count()} or {@link
* FTSearchOptions.FTSearchOptionsBuilder#limit(int, int)} with values <code>0, 0</code> is
* set, the command returns array with only one element - the count of the documents.
* @example
* <pre>{@code
* byte[] vector = new byte[24];
* Arrays.fill(vector, (byte) 0);
* var result = client.ftsearch(gs("json_idx1"), gs("*=>[KNN 2 @VEC $query_vec]"),
* FTSearchOptions.builder().params(Map.of(gs("query_vec"), gs(vector))).build())
* .get();
* assertArrayEquals(result, new Object[] { 2L, Map.of(
* gs("json:2"), Map.of(gs("__VEC_score"), gs("11.1100006104"), gs("$"), gs("{\"vec\":[1.1,1.2,1.3,1.4,1.5,1.6]}")),
* gs("json:0"), Map.of(gs("__VEC_score"), gs("91"), gs("$"), gs("{\"vec\":[1,2,3,4,5,6]}")))
* });
* }</pre>
*/
public static CompletableFuture<Object[]> search(
@NonNull BaseClient client,
@NonNull GlideString indexName,
@NonNull GlideString query,
@NonNull FTSearchOptions options) {
var args =
concatenateArrays(new GlideString[] {gs("FT.SEARCH"), indexName, query}, options.toArgs());
return executeCommand(client, args, false);
}

/**
* Deletes an index and associated content. Indexed document keys are unaffected.
*
Expand Down Expand Up @@ -342,6 +421,37 @@ public static CompletableFuture<Map<GlideString, Object>[]> aggregate(
.thenApply(res -> castArray(res, Map.class));
}

/**
* Runs a search or aggregation query and collects performance profiling information.
*
* @param client The client to execute the command.
* @param indexName The index name.
* @param options Querying and profiling parameters - see {@link FTProfileOptions}.
* @return A two-element array. The first element contains results of query being profiled, the
* second element stores profiling information.
*/
public static CompletableFuture<Object[]> profile(
@NonNull BaseClient client, @NonNull String indexName, @NonNull FTProfileOptions options) {
return profile(client, gs(indexName), options);
}

/**
* Runs a search or aggregation query and collects performance profiling information.
*
* @param client The client to execute the command.
* @param indexName The index name.
* @param options Querying and profiling parameters - see {@link FTProfileOptions}.
* @return A two-element array. The first element contains results of query being profiled, the
* second element stores profiling information.
*/
public static CompletableFuture<Object[]> profile(
@NonNull BaseClient client,
@NonNull GlideString indexName,
@NonNull FTProfileOptions options) {
var args = concatenateArrays(new GlideString[] {gs("FT.PROFILE"), indexName}, options.toArgs());
return executeCommand(client, args, false);
}

/**
* A wrapper for custom command API.
*
Expand Down
Loading

0 comments on commit 6641d4d

Please sign in to comment.