Skip to content

Handle FT.PROFILE response (impacted with Coordinator) processing with "ProfilingInformation" #373

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/NRedisStack/ResponseParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,15 @@ public static Tuple<SearchResult, Dictionary<string, RedisResult>> ToProfileSear
return new Tuple<SearchResult, Dictionary<string, RedisResult>>(searchResult, profile);
}

public static Tuple<SearchResult, ProfilingInformation> ParseProfileSearchResult(this RedisResult result, Query q)
{
var results = (RedisResult[])result!;

var searchResult = results[0].ToSearchResult(q);
var profile = new ProfilingInformation(results[1]);
return new Tuple<SearchResult, ProfilingInformation>(searchResult, profile);
}

public static SearchResult ToSearchResult(this RedisResult result, Query q)
{
return new SearchResult((RedisResult[])result!, !q.NoContent, q.WithScores, q.WithPayloads/*, q.ExplainScore*/);
Expand All @@ -707,6 +716,14 @@ public static Tuple<AggregationResult, Dictionary<string, RedisResult>> ToProfil
return new Tuple<AggregationResult, Dictionary<string, RedisResult>>(aggregateResult, profile);
}

public static Tuple<AggregationResult, ProfilingInformation> ParseProfileAggregateResult(this RedisResult result, AggregationRequest q)
{
var results = (RedisResult[])result!;
var aggregateResult = results[0].ToAggregationResult(q);
var profile = new ProfilingInformation(results[1]);
return new Tuple<AggregationResult, ProfilingInformation>(aggregateResult, profile);
}

public static AggregationResult ToAggregationResult(this RedisResult result, AggregationRequest query)
{
if (query.IsWithCursor())
Expand Down
20 changes: 20 additions & 0 deletions src/NRedisStack/Search/ISearchCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,17 +177,37 @@ public interface ISearchCommands
/// <param name="q">The query string.</param>
/// <param name="limited">Removes details of reader iterator.</param>
/// <returns></returns>
[Obsolete("Consider using ProfileOnSearch with Redis CE 8.0 and later")]
Tuple<SearchResult, Dictionary<string, RedisResult>> ProfileSearch(string indexName, Query q, bool limited = false);

/// <summary>
/// Apply FT.SEARCH command to collect performance details.
/// </summary>
/// <param name="indexName">The index name, created using FT.CREATE.</param>
/// <param name="q">The query string.</param>
/// <param name="limited">Removes details of reader iterator.</param>
/// <returns></returns>
Tuple<SearchResult, ProfilingInformation> ProfileOnSearch(string indexName, Query q, bool limited = false);

/// <summary>
/// Apply FT.AGGREGATE command to collect performance details.
/// </summary>
/// <param name="indexName">The index name, created using FT.CREATE.</param>
/// <param name="query">The query string.</param>
/// <param name="limited">Removes details of reader iterator.</param>
/// <returns></returns>
[Obsolete("Consider using ProfileOnAggregate with Redis CE 8.0 and later")]
Tuple<AggregationResult, Dictionary<string, RedisResult>> ProfileAggregate(string indexName, AggregationRequest query, bool limited = false);

/// <summary>
/// Apply FT.AGGREGATE command to collect performance details.
/// </summary>
/// <param name="indexName">The index name, created using FT.CREATE.</param>
/// <param name="query">The query string.</param>
/// <param name="limited">Removes details of reader iterator.</param>
/// <returns></returns>
Tuple<AggregationResult, ProfilingInformation> ProfileOnAggregate(string indexName, AggregationRequest query, bool limited = false);

/// <summary>
/// Search the index
/// </summary>
Expand Down
20 changes: 19 additions & 1 deletion src/NRedisStack/Search/ISearchCommandsAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,16 +169,24 @@ public interface ISearchCommandsAsync
/// <remarks><seealso href="https://redis.io/commands/ft.info"/></remarks>
Task<InfoResult> InfoAsync(RedisValue index);


/// <summary>
/// Apply FT.SEARCH command to collect performance details.
/// </summary>
/// <param name="indexName">The index name, created using FT.CREATE.</param>
/// <param name="q">The query string.</param>
/// <param name="limited">Removes details of reader iterator.</param>
/// <returns></returns>
[Obsolete("Consider using ProfileOnSearchAsync with Redis CE 8.0 and later")]
Task<Tuple<SearchResult, Dictionary<string, RedisResult>>> ProfileSearchAsync(string indexName, Query q, bool limited = false);

/// <summary>
/// Apply FT.SEARCH command to collect performance details.
/// </summary>
/// <param name="indexName">The index name, created using FT.CREATE.</param>
/// <param name="q">The query string.</param>
/// <param name="limited">Removes details of reader iterator.</param>
/// <returns></returns>
Task<Tuple<SearchResult, ProfilingInformation>> ProfileOnSearchAsync(string indexName, Query q, bool limited = false);

/// <summary>
/// Apply FT.AGGREGATE command to collect performance details.
Expand All @@ -187,8 +195,18 @@ public interface ISearchCommandsAsync
/// <param name="query">The query string.</param>
/// <param name="limited">Removes details of reader iterator.</param>
/// <returns></returns>
[Obsolete("Consider using ProfileOnAggregateAsync with Redis CE 8.0 and later")]
Task<Tuple<AggregationResult, Dictionary<string, RedisResult>>> ProfileAggregateAsync(string indexName, AggregationRequest query, bool limited = false);

/// <summary>
/// Apply FT.AGGREGATE command to collect performance details.
/// </summary>
/// <param name="indexName">The index name, created using FT.CREATE.</param>
/// <param name="query">The query string.</param>
/// <param name="limited">Removes details of reader iterator.</param>
/// <returns></returns>
Task<Tuple<AggregationResult, ProfilingInformation>> ProfileOnAggregateAsync(string indexName, AggregationRequest query, bool limited = false);

/// <summary>
/// Search the index
/// </summary>
Expand Down
15 changes: 15 additions & 0 deletions src/NRedisStack/Search/ProfilingInformation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using StackExchange.Redis;

namespace NRedisStack.Search
{

public class ProfilingInformation
{
public RedisResult Info { get; private set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There doesn't seem to be a lot of value-add here (in fact I think the old structure is cleaner), also - this is a major breaking change, so this really ought to be fleshed out much more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi @slorello89, the intention is to avoid client API from throwing an exception due to response layout that Redis server provides across different versions. Considering, for a consumer app, server version would be updated first(most of the time), a fully mapped return type leaves consumer apps with no use of profile command when a breaking change on server side. Another compelling reason to not fully map is that command use cases are debugging purposes mostly.

public ProfilingInformation(RedisResult info)
{
this.Info = info;
}

}
}
21 changes: 20 additions & 1 deletion src/NRedisStack/Search/SearchCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,18 +131,37 @@ public InfoResult Info(RedisValue index) =>
new InfoResult(_db.Execute(SearchCommandBuilder.Info(index)));

/// <inheritdoc/>
[Obsolete("Consider using ProfileOnSearch with Redis CE 8.0 and later")]
public Tuple<SearchResult, Dictionary<string, RedisResult>> ProfileSearch(string indexName, Query q, bool limited = false)
{
return _db.Execute(SearchCommandBuilder.ProfileSearch(indexName, q, limited))
.ToProfileSearchResult(q);
.ToProfileSearchResult(q);
}

/// <inheritdoc/>
public Tuple<SearchResult, ProfilingInformation> ProfileOnSearch(string indexName, Query q, bool limited = false)
{
return _db.Execute(SearchCommandBuilder.ProfileSearch(indexName, q, limited))
.ParseProfileSearchResult(q);
}

/// <inheritdoc/>
[Obsolete("Consider using ProfileOnAggregate with Redis CE 8.0 and later")]
public Tuple<AggregationResult, Dictionary<string, RedisResult>> ProfileAggregate(string indexName, AggregationRequest query, bool limited = false)
{
setDefaultDialectIfUnset(query);
return _db.Execute(SearchCommandBuilder.ProfileAggregate(indexName, query, limited))
.ToProfileAggregateResult(query);
}

/// <inheritdoc/>
public Tuple<AggregationResult, ProfilingInformation> ProfileOnAggregate(string indexName, AggregationRequest query, bool limited = false)
{
setDefaultDialectIfUnset(query);
return _db.Execute(SearchCommandBuilder.ProfileAggregate(indexName, query, limited))
.ParseProfileAggregateResult(query);
}

/// <inheritdoc/>
public SearchResult Search(string indexName, Query q)
{
Expand Down
15 changes: 15 additions & 0 deletions src/NRedisStack/Search/SearchCommandsAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,17 +162,32 @@ public async Task<InfoResult> InfoAsync(RedisValue index) =>
new InfoResult(await _db.ExecuteAsync(SearchCommandBuilder.Info(index)));

/// <inheritdoc/>
[Obsolete("Consider using ProfileOnSearchAsync with Redis CE 8.0 and later")]
public async Task<Tuple<SearchResult, Dictionary<string, RedisResult>>> ProfileSearchAsync(string indexName, Query q, bool limited = false)
{
return (await _db.ExecuteAsync(SearchCommandBuilder.ProfileSearch(indexName, q, limited)))
.ToProfileSearchResult(q);
}

/// <inheritdoc/>
public async Task<Tuple<SearchResult, ProfilingInformation>> ProfileOnSearchAsync(string indexName, Query q, bool limited = false)
{
return (await _db.ExecuteAsync(SearchCommandBuilder.ProfileSearch(indexName, q, limited)))
.ParseProfileSearchResult(q);
}
/// <inheritdoc/>
[Obsolete("Consider using ProfileOnSearchAsync with Redis CE 8.0 and later")]
public async Task<Tuple<AggregationResult, Dictionary<string, RedisResult>>> ProfileAggregateAsync(string indexName, AggregationRequest query, bool limited = false)
{
return (await _db.ExecuteAsync(SearchCommandBuilder.ProfileAggregate(indexName, query, limited)))
.ToProfileAggregateResult(query);
}
/// <inheritdoc/>
public async Task<Tuple<AggregationResult, ProfilingInformation>> ProfileOnAggregateAsync(string indexName, AggregationRequest query, bool limited = false)
{
return (await _db.ExecuteAsync(SearchCommandBuilder.ProfileAggregate(indexName, query, limited)))
.ParseProfileAggregateResult(query);
}

/// <inheritdoc/>
public async Task<SearchResult> SearchAsync(string indexName, Query q)
Expand Down
20 changes: 20 additions & 0 deletions tests/NRedisStack.Tests/CustomAssertions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Xunit;

namespace NRedisStack.Tests;

public static class CustomAssertions
{
// Generic method to assert that 'actual' is greater than 'expected'
public static void GreaterThan<T>(T actual, T expected) where T : IComparable<T>
{
Assert.True(actual.CompareTo(expected) > 0,
$"Failure: Expected value to be greater than {expected}, but found {actual}.");
}

// Generic method to assert that 'actual' is less than 'expected'
public static void LessThan<T>(T actual, T expected) where T : IComparable<T>
{
Assert.True(actual.CompareTo(expected) < 0,
$"Failure: Expected value to be less than {expected}, but found {actual}.");
}
}
Loading
Loading