From d325e325789b65c97e7f1cccd722fa984d174f2b Mon Sep 17 00:00:00 2001 From: atakavci Date: Thu, 16 Jan 2025 12:20:47 +0300 Subject: [PATCH 1/3] handle ft.profile response processing with "ProfilingInformation" --- src/NRedisStack/ResponseParser.cs | 12 +- src/NRedisStack/Search/ISearchCommands.cs | 4 +- .../Search/ISearchCommandsAsync.cs | 4 +- .../Search/ProfilingInformation.cs | 15 ++ src/NRedisStack/Search/SearchCommands.cs | 4 +- src/NRedisStack/Search/SearchCommandsAsync.cs | 4 +- tests/NRedisStack.Tests/CustomAssertions.cs | 20 ++ tests/NRedisStack.Tests/Search/SearchTests.cs | 179 ++++++++++++++---- 8 files changed, 192 insertions(+), 50 deletions(-) create mode 100644 src/NRedisStack/Search/ProfilingInformation.cs create mode 100644 tests/NRedisStack.Tests/CustomAssertions.cs diff --git a/src/NRedisStack/ResponseParser.cs b/src/NRedisStack/ResponseParser.cs index b6ff2632..40b0084d 100644 --- a/src/NRedisStack/ResponseParser.cs +++ b/src/NRedisStack/ResponseParser.cs @@ -685,13 +685,13 @@ public static Dictionary ToStringRedisResultDictionary(this return dict; } - public static Tuple> ToProfileSearchResult(this RedisResult result, Query q) + public static Tuple ToProfileSearchResult(this RedisResult result, Query q) { var results = (RedisResult[])result!; var searchResult = results[0].ToSearchResult(q); - var profile = results[1].ToStringRedisResultDictionary(); - return new Tuple>(searchResult, profile); + var profile = new ProfilingInformation(results[1]); + return new Tuple(searchResult, profile); } public static SearchResult ToSearchResult(this RedisResult result, Query q) @@ -699,12 +699,12 @@ public static SearchResult ToSearchResult(this RedisResult result, Query q) return new SearchResult((RedisResult[])result!, !q.NoContent, q.WithScores, q.WithPayloads/*, q.ExplainScore*/); } - public static Tuple> ToProfileAggregateResult(this RedisResult result, AggregationRequest q) + public static Tuple ToProfileAggregateResult(this RedisResult result, AggregationRequest q) { var results = (RedisResult[])result!; var aggregateResult = results[0].ToAggregationResult(q); - var profile = results[1].ToStringRedisResultDictionary(); - return new Tuple>(aggregateResult, profile); + var profile = new ProfilingInformation(results[1]); + return new Tuple(aggregateResult, profile); } public static AggregationResult ToAggregationResult(this RedisResult result, AggregationRequest query) diff --git a/src/NRedisStack/Search/ISearchCommands.cs b/src/NRedisStack/Search/ISearchCommands.cs index 45843dbe..a11ffcc0 100644 --- a/src/NRedisStack/Search/ISearchCommands.cs +++ b/src/NRedisStack/Search/ISearchCommands.cs @@ -177,7 +177,7 @@ public interface ISearchCommands /// The query string. /// Removes details of reader iterator. /// - Tuple> ProfileSearch(string indexName, Query q, bool limited = false); + Tuple ProfileSearch(string indexName, Query q, bool limited = false); /// /// Apply FT.AGGREGATE command to collect performance details. @@ -186,7 +186,7 @@ public interface ISearchCommands /// The query string. /// Removes details of reader iterator. /// - Tuple> ProfileAggregate(string indexName, AggregationRequest query, bool limited = false); + Tuple ProfileAggregate(string indexName, AggregationRequest query, bool limited = false); /// /// Search the index diff --git a/src/NRedisStack/Search/ISearchCommandsAsync.cs b/src/NRedisStack/Search/ISearchCommandsAsync.cs index b42013f8..3242953b 100644 --- a/src/NRedisStack/Search/ISearchCommandsAsync.cs +++ b/src/NRedisStack/Search/ISearchCommandsAsync.cs @@ -177,7 +177,7 @@ public interface ISearchCommandsAsync /// The query string. /// Removes details of reader iterator. /// - Task>> ProfileSearchAsync(string indexName, Query q, bool limited = false); + Task> ProfileSearchAsync(string indexName, Query q, bool limited = false); /// @@ -187,7 +187,7 @@ public interface ISearchCommandsAsync /// The query string. /// Removes details of reader iterator. /// - Task>> ProfileAggregateAsync(string indexName, AggregationRequest query, bool limited = false); + Task> ProfileAggregateAsync(string indexName, AggregationRequest query, bool limited = false); /// /// Search the index diff --git a/src/NRedisStack/Search/ProfilingInformation.cs b/src/NRedisStack/Search/ProfilingInformation.cs new file mode 100644 index 00000000..4ccc1590 --- /dev/null +++ b/src/NRedisStack/Search/ProfilingInformation.cs @@ -0,0 +1,15 @@ +using StackExchange.Redis; + +namespace NRedisStack.Search +{ + + public class ProfilingInformation + { + public RedisResult Info { get; private set; } + public ProfilingInformation(RedisResult info) + { + this.Info = info; + } + + } +} \ No newline at end of file diff --git a/src/NRedisStack/Search/SearchCommands.cs b/src/NRedisStack/Search/SearchCommands.cs index 6d9a64e6..b1614d38 100644 --- a/src/NRedisStack/Search/SearchCommands.cs +++ b/src/NRedisStack/Search/SearchCommands.cs @@ -131,13 +131,13 @@ public InfoResult Info(RedisValue index) => new InfoResult(_db.Execute(SearchCommandBuilder.Info(index))); /// - public Tuple> ProfileSearch(string indexName, Query q, bool limited = false) + public Tuple ProfileSearch(string indexName, Query q, bool limited = false) { return _db.Execute(SearchCommandBuilder.ProfileSearch(indexName, q, limited)) .ToProfileSearchResult(q); } /// - public Tuple> ProfileAggregate(string indexName, AggregationRequest query, bool limited = false) + public Tuple ProfileAggregate(string indexName, AggregationRequest query, bool limited = false) { setDefaultDialectIfUnset(query); return _db.Execute(SearchCommandBuilder.ProfileAggregate(indexName, query, limited)) diff --git a/src/NRedisStack/Search/SearchCommandsAsync.cs b/src/NRedisStack/Search/SearchCommandsAsync.cs index 0da1c1f0..ab3ea0fb 100644 --- a/src/NRedisStack/Search/SearchCommandsAsync.cs +++ b/src/NRedisStack/Search/SearchCommandsAsync.cs @@ -162,13 +162,13 @@ public async Task InfoAsync(RedisValue index) => new InfoResult(await _db.ExecuteAsync(SearchCommandBuilder.Info(index))); /// - public async Task>> ProfileSearchAsync(string indexName, Query q, bool limited = false) + public async Task> ProfileSearchAsync(string indexName, Query q, bool limited = false) { return (await _db.ExecuteAsync(SearchCommandBuilder.ProfileSearch(indexName, q, limited))) .ToProfileSearchResult(q); } /// - public async Task>> ProfileAggregateAsync(string indexName, AggregationRequest query, bool limited = false) + public async Task> ProfileAggregateAsync(string indexName, AggregationRequest query, bool limited = false) { return (await _db.ExecuteAsync(SearchCommandBuilder.ProfileAggregate(indexName, query, limited))) .ToProfileAggregateResult(query); diff --git a/tests/NRedisStack.Tests/CustomAssertions.cs b/tests/NRedisStack.Tests/CustomAssertions.cs new file mode 100644 index 00000000..62db4087 --- /dev/null +++ b/tests/NRedisStack.Tests/CustomAssertions.cs @@ -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 actual, T expected) where T : IComparable + { + 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 actual, T expected) where T : IComparable + { + Assert.True(actual.CompareTo(expected) < 0, + $"Failure: Expected value to be less than {expected}, but found {actual}."); + } +} diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index c19c7457..ffb774b9 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -2796,7 +2796,7 @@ public async Task getSuggestionLengthAndDeleteSuggestionAsync() Assert.Equal(2L, await ft.SugLenAsync(key)); } - [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.3.240")] + [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.3")] public void TestProfileSearch() { IDatabase db = redisFixture.Redis.GetDatabase(); @@ -2812,15 +2812,14 @@ public void TestProfileSearch() var profile = ft.ProfileSearch(index, new Query("foo")); // Iterators profile={Type=TEXT, Time=0.0, Term=foo, Counter=1, Size=1} - profile.Item2["Iterators profile"].ToDictionary(); - var iteratorsProfile = profile.Item2["Iterators profile"].ToDictionary(); - Assert.Equal("TEXT", iteratorsProfile["Type"].ToString()); - Assert.Equal("foo", iteratorsProfile["Term"].ToString()); - Assert.Equal("1", iteratorsProfile["Counter"].ToString()); - Assert.Equal("1", iteratorsProfile["Size"].ToString()); + var info = (RedisResult[])profile.Item2.Info; + int shardsIndex = Array.FindIndex(info, item => item.ToString() == "Shards"); + int coordinatorIndex = Array.FindIndex(info, item => item.ToString() == "Coordinator"); + CustomAssertions.GreaterThan(shardsIndex, -1); + CustomAssertions.GreaterThan(coordinatorIndex, -1); } - [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.3.240")] + [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.3")] public async Task TestProfileSearchAsync() { IDatabase db = redisFixture.Redis.GetDatabase(); @@ -2835,17 +2834,52 @@ public async Task TestProfileSearchAsync() new HashEntry("t2", "bar")}); var profile = await ft.ProfileSearchAsync(index, new Query("foo")); - // Iterators profile={Type=TEXT, Time=0.0, Term=foo, Counter=1, Size=1} - profile.Item2["Iterators profile"].ToDictionary(); - var iteratorsProfile = profile.Item2["Iterators profile"].ToDictionary(); - Assert.Equal("TEXT", iteratorsProfile["Type"].ToString()); - Assert.Equal("foo", iteratorsProfile["Term"].ToString()); - Assert.Equal("1", iteratorsProfile["Counter"].ToString()); - Assert.Equal("1", iteratorsProfile["Size"].ToString()); + var info = (RedisResult[])profile.Item2.Info; + int shardsIndex = Array.FindIndex(info, item => item.ToString() == "Shards"); + int coordinatorIndex = Array.FindIndex(info, item => item.ToString() == "Coordinator"); + CustomAssertions.GreaterThan(shardsIndex, -1); + CustomAssertions.GreaterThan(coordinatorIndex, -1); } + [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.3")] + public void TestProfileSearch_WithoutCoordinator() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); - [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.3.240")] + Schema sc = new Schema().AddTextField("t1", 1.0).AddTextField("t2", 1.0); + Assert.True(ft.Create(index, new FTCreateParams(), sc)); + + db.HashSet("doc1", new HashEntry[] { + new HashEntry("t1", "foo"), + new HashEntry("t2", "bar")}); + + var profile = ft.ProfileSearch(index, new Query("foo")); + var info = (RedisResult[])profile.Item2.Info; + CustomAssertions.GreaterThan(info.Length, 4); + } + + [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.3")] + public async Task TestProfileSearchAsync_WithoutCoordinator() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); + + Schema sc = new Schema().AddTextField("t1", 1.0).AddTextField("t2", 1.0); + Assert.True(ft.Create(index, new FTCreateParams(), sc)); + + db.HashSet("doc1", new HashEntry[] { + new HashEntry("t1", "foo"), + new HashEntry("t2", "bar")}); + + var profile = await ft.ProfileSearchAsync(index, new Query("foo")); + var info = (RedisResult[])profile.Item2.Info; + CustomAssertions.GreaterThan(info.Length, 4); + } + + [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.3")] public void TestProfile() { IDatabase db = redisFixture.Redis.GetDatabase(); @@ -2860,22 +2894,28 @@ public void TestProfile() var q = new Query("hello|world").SetNoContent(); var profileSearch = ft.ProfileSearch(index, q); var searchRes = profileSearch.Item1; - var searchDet = profileSearch.Item2; + var searchDet = (RedisResult[])profileSearch.Item2.Info; - Assert.Equal(5, searchDet.Count); Assert.Equal(2, searchRes.Documents.Count); - + int shardsIndex = Array.FindIndex(searchDet, item => item.ToString() == "Shards"); + int coordinatorIndex = Array.FindIndex(searchDet, item => item.ToString() == "Coordinator"); + CustomAssertions.GreaterThan(shardsIndex, -1); + CustomAssertions.GreaterThan(coordinatorIndex, -1); // check using AggregationRequest var aggReq = new AggregationRequest("*").Load(FieldName.Of("t")).Apply("startswith(@t, 'hel')", "prefix"); var profileAggregate = ft.ProfileAggregate(index, aggReq); var aggregateRes = profileAggregate.Item1; - var aggregateDet = profileAggregate.Item2; - Assert.Equal(5, aggregateDet.Count); + var aggregateDet = (RedisResult[])profileAggregate.Item2.Info; + Assert.Equal(2, aggregateRes.TotalResults); + shardsIndex = Array.FindIndex(aggregateDet, item => item.ToString() == "Shards"); + coordinatorIndex = Array.FindIndex(aggregateDet, item => item.ToString() == "Coordinator"); + CustomAssertions.GreaterThan(shardsIndex, -1); + CustomAssertions.GreaterThan(coordinatorIndex, -1); } - [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.3.240")] + [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.3")] public async Task TestProfileAsync() { IDatabase db = redisFixture.Redis.GetDatabase(); @@ -2890,21 +2930,88 @@ public async Task TestProfileAsync() var q = new Query("hello|world").SetNoContent(); var profileSearch = await ft.ProfileSearchAsync(index, q); var searchRes = profileSearch.Item1; - var searchDet = profileSearch.Item2; + var searchDet = (RedisResult[])profileSearch.Item2.Info; - Assert.Equal(5, searchDet.Count); Assert.Equal(2, searchRes.Documents.Count); + int shardsIndex = Array.FindIndex(searchDet, item => item.ToString() == "Shards"); + int coordinatorIndex = Array.FindIndex(searchDet, item => item.ToString() == "Coordinator"); + CustomAssertions.GreaterThan(shardsIndex, -1); + CustomAssertions.GreaterThan(coordinatorIndex, -1); // check using AggregationRequest var aggReq = new AggregationRequest("*").Load(FieldName.Of("t")).Apply("startswith(@t, 'hel')", "prefix"); var profileAggregate = await ft.ProfileAggregateAsync(index, aggReq); var aggregateRes = profileAggregate.Item1; - var aggregateDet = profileAggregate.Item2; - Assert.Equal(5, aggregateDet.Count); + var aggregateDet = (RedisResult[])profileAggregate.Item2.Info; + + Assert.Equal(2, aggregateRes.TotalResults); + shardsIndex = Array.FindIndex(aggregateDet, item => item.ToString() == "Shards"); + coordinatorIndex = Array.FindIndex(aggregateDet, item => item.ToString() == "Coordinator"); + CustomAssertions.GreaterThan(shardsIndex, -1); + CustomAssertions.GreaterThan(coordinatorIndex, -1); + } + + [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.3")] + public void TestProfile_WithoutCoordinator() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); + + ft.Create(index, new Schema().AddTextField("t")); // Calling FT.CREATR without FTCreateParams + db.HashSet("1", "t", "hello"); + db.HashSet("2", "t", "world"); + + // check using Query + var q = new Query("hello|world").SetNoContent(); + var profileSearch = ft.ProfileSearch(index, q); + var searchRes = profileSearch.Item1; + var searchDet = (RedisResult[])profileSearch.Item2.Info; + + Assert.Equal(2, searchRes.Documents.Count); + CustomAssertions.GreaterThan(searchDet.Length, 4); + + // check using AggregationRequest + var aggReq = new AggregationRequest("*").Load(FieldName.Of("t")).Apply("startswith(@t, 'hel')", "prefix"); + var profileAggregate = ft.ProfileAggregate(index, aggReq); + var aggregateRes = profileAggregate.Item1; + var aggregateDet = (RedisResult[])profileAggregate.Item2.Info; + + Assert.Equal(2, aggregateRes.TotalResults); + CustomAssertions.GreaterThan(searchDet.Length, 4); + } + + [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.3")] + public async Task TestProfileAsync_WithoutCoordinator() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); + + await ft.CreateAsync(index, new Schema().AddTextField("t")); // Calling FT.CREATR without FTCreateParams + db.HashSet("1", "t", "hello"); + db.HashSet("2", "t", "world"); + + // check using Query + var q = new Query("hello|world").SetNoContent(); + var profileSearch = await ft.ProfileSearchAsync(index, q); + var searchRes = profileSearch.Item1; + var searchDet = (RedisResult[])profileSearch.Item2.Info; + + Assert.Equal(2, searchRes.Documents.Count); + CustomAssertions.GreaterThan(searchDet.Length, 4); + + // check using AggregationRequest + var aggReq = new AggregationRequest("*").Load(FieldName.Of("t")).Apply("startswith(@t, 'hel')", "prefix"); + var profileAggregate = await ft.ProfileAggregateAsync(index, aggReq); + var aggregateRes = profileAggregate.Item1; + var aggregateDet = (RedisResult[])profileAggregate.Item2.Info; + Assert.Equal(2, aggregateRes.TotalResults); + CustomAssertions.GreaterThan(searchDet.Length, 4); } - [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.3.242")] + [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.1.240")] public void TestProfileIssue306() { IDatabase db = redisFixture.Redis.GetDatabase(); @@ -2919,9 +3026,9 @@ public void TestProfileIssue306() var q = new Query("hello|world").SetNoContent(); var profileSearch = ft.ProfileSearch(index, q); var searchRes = profileSearch.Item1; - var searchDet = profileSearch.Item2; + var searchDet = (RedisResult[])profileSearch.Item2.Info; - Assert.Equal(6, searchDet.Count); + CustomAssertions.GreaterThan(searchDet.Length, 3); Assert.Equal(2, searchRes.Documents.Count); @@ -2929,12 +3036,12 @@ public void TestProfileIssue306() var aggReq = new AggregationRequest("*").Load(FieldName.Of("t")).Apply("startswith(@t, 'hel')", "prefix"); var profileAggregate = ft.ProfileAggregate(index, aggReq); var aggregateRes = profileAggregate.Item1; - var aggregateDet = profileAggregate.Item2; - Assert.True(aggregateDet.Count >= 6); + var aggregateDet = (RedisResult[])profileAggregate.Item2.Info; + CustomAssertions.GreaterThan(aggregateDet.Length, 3); Assert.Equal(2, aggregateRes.TotalResults); } - [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.3.242")] + [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.1.240")] public async Task TestProfileAsyncIssue306() { IDatabase db = redisFixture.Redis.GetDatabase(); @@ -2949,17 +3056,17 @@ public async Task TestProfileAsyncIssue306() var q = new Query("hello|world").SetNoContent(); var profileSearch = await ft.ProfileSearchAsync(index, q); var searchRes = profileSearch.Item1; - var searchDet = profileSearch.Item2; + var searchDet = (RedisResult[])profileSearch.Item2.Info; - Assert.Equal(6, searchDet.Count); + CustomAssertions.GreaterThan(searchDet.Length, 3); Assert.Equal(2, searchRes.Documents.Count); // check using AggregationRequest var aggReq = new AggregationRequest("*").Load(FieldName.Of("t")).Apply("startswith(@t, 'hel')", "prefix"); var profileAggregate = await ft.ProfileAggregateAsync(index, aggReq); var aggregateRes = profileAggregate.Item1; - var aggregateDet = profileAggregate.Item2; - Assert.True(aggregateDet.Count >= 6); + var aggregateDet = (RedisResult[])profileAggregate.Item2.Info; + CustomAssertions.GreaterThan(aggregateDet.Length, 3); Assert.Equal(2, aggregateRes.TotalResults); } From d62de2d050240ebc87f9e6b9d0680afa46b5a815 Mon Sep 17 00:00:00 2001 From: atakavci Date: Thu, 16 Jan 2025 14:02:48 +0300 Subject: [PATCH 2/3] fix formatting --- tests/NRedisStack.Tests/CustomAssertions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/NRedisStack.Tests/CustomAssertions.cs b/tests/NRedisStack.Tests/CustomAssertions.cs index 62db4087..c1a6f596 100644 --- a/tests/NRedisStack.Tests/CustomAssertions.cs +++ b/tests/NRedisStack.Tests/CustomAssertions.cs @@ -7,14 +7,14 @@ public static class CustomAssertions // Generic method to assert that 'actual' is greater than 'expected' public static void GreaterThan(T actual, T expected) where T : IComparable { - Assert.True(actual.CompareTo(expected) > 0, + 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 actual, T expected) where T : IComparable { - Assert.True(actual.CompareTo(expected) < 0, + Assert.True(actual.CompareTo(expected) < 0, $"Failure: Expected value to be less than {expected}, but found {actual}."); } } From b39a87da9c7a8b6bd93a54204469215146c4bae1 Mon Sep 17 00:00:00 2001 From: atakavci Date: Thu, 23 Jan 2025 11:46:47 +0300 Subject: [PATCH 3/3] make old methods obsolete --- src/NRedisStack/ResponseParser.cs | 21 ++++- src/NRedisStack/Search/ISearchCommands.cs | 24 +++++- .../Search/ISearchCommandsAsync.cs | 22 ++++- src/NRedisStack/Search/SearchCommands.cs | 25 +++++- src/NRedisStack/Search/SearchCommandsAsync.cs | 19 ++++- tests/NRedisStack.Tests/Search/SearchTests.cs | 82 +++++++++---------- 6 files changed, 140 insertions(+), 53 deletions(-) diff --git a/src/NRedisStack/ResponseParser.cs b/src/NRedisStack/ResponseParser.cs index 40b0084d..46421344 100644 --- a/src/NRedisStack/ResponseParser.cs +++ b/src/NRedisStack/ResponseParser.cs @@ -685,7 +685,16 @@ public static Dictionary ToStringRedisResultDictionary(this return dict; } - public static Tuple ToProfileSearchResult(this RedisResult result, Query q) + public static Tuple> ToProfileSearchResult(this RedisResult result, Query q) + { + var results = (RedisResult[])result!; + + var searchResult = results[0].ToSearchResult(q); + var profile = results[1].ToStringRedisResultDictionary(); + return new Tuple>(searchResult, profile); + } + + public static Tuple ParseProfileSearchResult(this RedisResult result, Query q) { var results = (RedisResult[])result!; @@ -699,7 +708,15 @@ public static SearchResult ToSearchResult(this RedisResult result, Query q) return new SearchResult((RedisResult[])result!, !q.NoContent, q.WithScores, q.WithPayloads/*, q.ExplainScore*/); } - public static Tuple ToProfileAggregateResult(this RedisResult result, AggregationRequest q) + public static Tuple> ToProfileAggregateResult(this RedisResult result, AggregationRequest q) + { + var results = (RedisResult[])result!; + var aggregateResult = results[0].ToAggregationResult(q); + var profile = results[1].ToStringRedisResultDictionary(); + return new Tuple>(aggregateResult, profile); + } + + public static Tuple ParseProfileAggregateResult(this RedisResult result, AggregationRequest q) { var results = (RedisResult[])result!; var aggregateResult = results[0].ToAggregationResult(q); diff --git a/src/NRedisStack/Search/ISearchCommands.cs b/src/NRedisStack/Search/ISearchCommands.cs index a11ffcc0..2d9e090b 100644 --- a/src/NRedisStack/Search/ISearchCommands.cs +++ b/src/NRedisStack/Search/ISearchCommands.cs @@ -177,7 +177,27 @@ public interface ISearchCommands /// The query string. /// Removes details of reader iterator. /// - Tuple ProfileSearch(string indexName, Query q, bool limited = false); + [Obsolete("Consider using ProfileOnSearch with Redis CE 8.0 and later")] + Tuple> ProfileSearch(string indexName, Query q, bool limited = false); + + /// + /// Apply FT.SEARCH command to collect performance details. + /// + /// The index name, created using FT.CREATE. + /// The query string. + /// Removes details of reader iterator. + /// + Tuple ProfileOnSearch(string indexName, Query q, bool limited = false); + + /// + /// Apply FT.AGGREGATE command to collect performance details. + /// + /// The index name, created using FT.CREATE. + /// The query string. + /// Removes details of reader iterator. + /// + [Obsolete("Consider using ProfileOnAggregate with Redis CE 8.0 and later")] + Tuple> ProfileAggregate(string indexName, AggregationRequest query, bool limited = false); /// /// Apply FT.AGGREGATE command to collect performance details. @@ -186,7 +206,7 @@ public interface ISearchCommands /// The query string. /// Removes details of reader iterator. /// - Tuple ProfileAggregate(string indexName, AggregationRequest query, bool limited = false); + Tuple ProfileOnAggregate(string indexName, AggregationRequest query, bool limited = false); /// /// Search the index diff --git a/src/NRedisStack/Search/ISearchCommandsAsync.cs b/src/NRedisStack/Search/ISearchCommandsAsync.cs index 3242953b..0e1e7333 100644 --- a/src/NRedisStack/Search/ISearchCommandsAsync.cs +++ b/src/NRedisStack/Search/ISearchCommandsAsync.cs @@ -169,6 +169,15 @@ public interface ISearchCommandsAsync /// Task InfoAsync(RedisValue index); + /// + /// Apply FT.SEARCH command to collect performance details. + /// + /// The index name, created using FT.CREATE. + /// The query string. + /// Removes details of reader iterator. + /// + [Obsolete("Consider using ProfileOnSearchAsync with Redis CE 8.0 and later")] + Task>> ProfileSearchAsync(string indexName, Query q, bool limited = false); /// /// Apply FT.SEARCH command to collect performance details. @@ -177,8 +186,17 @@ public interface ISearchCommandsAsync /// The query string. /// Removes details of reader iterator. /// - Task> ProfileSearchAsync(string indexName, Query q, bool limited = false); + Task> ProfileOnSearchAsync(string indexName, Query q, bool limited = false); + /// + /// Apply FT.AGGREGATE command to collect performance details. + /// + /// The index name, created using FT.CREATE. + /// The query string. + /// Removes details of reader iterator. + /// + [Obsolete("Consider using ProfileOnAggregateAsync with Redis CE 8.0 and later")] + Task>> ProfileAggregateAsync(string indexName, AggregationRequest query, bool limited = false); /// /// Apply FT.AGGREGATE command to collect performance details. @@ -187,7 +205,7 @@ public interface ISearchCommandsAsync /// The query string. /// Removes details of reader iterator. /// - Task> ProfileAggregateAsync(string indexName, AggregationRequest query, bool limited = false); + Task> ProfileOnAggregateAsync(string indexName, AggregationRequest query, bool limited = false); /// /// Search the index diff --git a/src/NRedisStack/Search/SearchCommands.cs b/src/NRedisStack/Search/SearchCommands.cs index b1614d38..7a362ede 100644 --- a/src/NRedisStack/Search/SearchCommands.cs +++ b/src/NRedisStack/Search/SearchCommands.cs @@ -131,18 +131,37 @@ public InfoResult Info(RedisValue index) => new InfoResult(_db.Execute(SearchCommandBuilder.Info(index))); /// - public Tuple ProfileSearch(string indexName, Query q, bool limited = false) + [Obsolete("Consider using ProfileOnSearch with Redis CE 8.0 and later")] + public Tuple> ProfileSearch(string indexName, Query q, bool limited = false) { return _db.Execute(SearchCommandBuilder.ProfileSearch(indexName, q, limited)) - .ToProfileSearchResult(q); + .ToProfileSearchResult(q); } + /// - public Tuple ProfileAggregate(string indexName, AggregationRequest query, bool limited = false) + public Tuple ProfileOnSearch(string indexName, Query q, bool limited = false) + { + return _db.Execute(SearchCommandBuilder.ProfileSearch(indexName, q, limited)) + .ParseProfileSearchResult(q); + } + + /// + [Obsolete("Consider using ProfileOnAggregate with Redis CE 8.0 and later")] + public Tuple> ProfileAggregate(string indexName, AggregationRequest query, bool limited = false) { setDefaultDialectIfUnset(query); return _db.Execute(SearchCommandBuilder.ProfileAggregate(indexName, query, limited)) .ToProfileAggregateResult(query); } + + /// + public Tuple ProfileOnAggregate(string indexName, AggregationRequest query, bool limited = false) + { + setDefaultDialectIfUnset(query); + return _db.Execute(SearchCommandBuilder.ProfileAggregate(indexName, query, limited)) + .ParseProfileAggregateResult(query); + } + /// public SearchResult Search(string indexName, Query q) { diff --git a/src/NRedisStack/Search/SearchCommandsAsync.cs b/src/NRedisStack/Search/SearchCommandsAsync.cs index ab3ea0fb..23a24027 100644 --- a/src/NRedisStack/Search/SearchCommandsAsync.cs +++ b/src/NRedisStack/Search/SearchCommandsAsync.cs @@ -162,17 +162,32 @@ public async Task InfoAsync(RedisValue index) => new InfoResult(await _db.ExecuteAsync(SearchCommandBuilder.Info(index))); /// - public async Task> ProfileSearchAsync(string indexName, Query q, bool limited = false) + [Obsolete("Consider using ProfileOnSearchAsync with Redis CE 8.0 and later")] + public async Task>> ProfileSearchAsync(string indexName, Query q, bool limited = false) { return (await _db.ExecuteAsync(SearchCommandBuilder.ProfileSearch(indexName, q, limited))) .ToProfileSearchResult(q); } + + /// + public async Task> ProfileOnSearchAsync(string indexName, Query q, bool limited = false) + { + return (await _db.ExecuteAsync(SearchCommandBuilder.ProfileSearch(indexName, q, limited))) + .ParseProfileSearchResult(q); + } /// - public async Task> ProfileAggregateAsync(string indexName, AggregationRequest query, bool limited = false) + [Obsolete("Consider using ProfileOnSearchAsync with Redis CE 8.0 and later")] + public async Task>> ProfileAggregateAsync(string indexName, AggregationRequest query, bool limited = false) { return (await _db.ExecuteAsync(SearchCommandBuilder.ProfileAggregate(indexName, query, limited))) .ToProfileAggregateResult(query); } + /// + public async Task> ProfileOnAggregateAsync(string indexName, AggregationRequest query, bool limited = false) + { + return (await _db.ExecuteAsync(SearchCommandBuilder.ProfileAggregate(indexName, query, limited))) + .ParseProfileAggregateResult(query); + } /// public async Task SearchAsync(string indexName, Query q) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index 0ffad174..2bbf43b8 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -2776,7 +2776,7 @@ public async Task getSuggestionLengthAndDeleteSuggestionAsync() Assert.Equal(2L, await ft.SugLenAsync(key)); } - [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.3")] + [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.9")] [MemberData(nameof(EndpointsFixture.Env.StandaloneOnly), MemberType = typeof(EndpointsFixture.Env))] public void TestProfileSearch(string endpointId) { @@ -2790,7 +2790,7 @@ public void TestProfileSearch(string endpointId) new HashEntry("t1", "foo"), new HashEntry("t2", "bar")}); - var profile = ft.ProfileSearch(index, new Query("foo")); + var profile = ft.ProfileOnSearch(index, new Query("foo")); // Iterators profile={Type=TEXT, Time=0.0, Term=foo, Counter=1, Size=1} var info = (RedisResult[])profile.Item2.Info; int shardsIndex = Array.FindIndex(info, item => item.ToString() == "Shards"); @@ -2799,7 +2799,7 @@ public void TestProfileSearch(string endpointId) CustomAssertions.GreaterThan(coordinatorIndex, -1); } - [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.3")] + [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.9")] [MemberData(nameof(EndpointsFixture.Env.StandaloneOnly), MemberType = typeof(EndpointsFixture.Env))] public async Task TestProfileSearchAsync(string endpointId) { @@ -2813,7 +2813,7 @@ public async Task TestProfileSearchAsync(string endpointId) new HashEntry("t1", "foo"), new HashEntry("t2", "bar")}); - var profile = await ft.ProfileSearchAsync(index, new Query("foo")); + var profile = await ft.ProfileOnSearchAsync(index, new Query("foo")); var info = (RedisResult[])profile.Item2.Info; int shardsIndex = Array.FindIndex(info, item => item.ToString() == "Shards"); int coordinatorIndex = Array.FindIndex(info, item => item.ToString() == "Coordinator"); @@ -2821,12 +2821,11 @@ public async Task TestProfileSearchAsync(string endpointId) CustomAssertions.GreaterThan(coordinatorIndex, -1); } - [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.3")] + [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.9")] [MemberData(nameof(EndpointsFixture.Env.StandaloneOnly), MemberType = typeof(EndpointsFixture.Env))] - public void TestProfileSearch_WithoutCoordinator() + public void TestProfileSearch_WithoutCoordinator(string endpointId) { - IDatabase db = redisFixture.Redis.GetDatabase(); - db.Execute("FLUSHALL"); + IDatabase db = GetCleanDatabase(endpointId); var ft = db.FT(); Schema sc = new Schema().AddTextField("t1", 1.0).AddTextField("t2", 1.0); @@ -2837,16 +2836,15 @@ public void TestProfileSearch_WithoutCoordinator() new HashEntry("t2", "bar")}); var profile = ft.ProfileSearch(index, new Query("foo")); - var info = (RedisResult[])profile.Item2.Info; - CustomAssertions.GreaterThan(info.Length, 4); + var info = profile.Item2; + CustomAssertions.GreaterThan(info.Count, 4); } - [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.3")] + [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.9")] [MemberData(nameof(EndpointsFixture.Env.StandaloneOnly), MemberType = typeof(EndpointsFixture.Env))] - public async Task TestProfileSearchAsync_WithoutCoordinator() + public async Task TestProfileSearchAsync_WithoutCoordinator(string endpointId) { - IDatabase db = redisFixture.Redis.GetDatabase(); - db.Execute("FLUSHALL"); + IDatabase db = GetCleanDatabase(endpointId); var ft = db.FT(); Schema sc = new Schema().AddTextField("t1", 1.0).AddTextField("t2", 1.0); @@ -2857,11 +2855,11 @@ public async Task TestProfileSearchAsync_WithoutCoordinator() new HashEntry("t2", "bar")}); var profile = await ft.ProfileSearchAsync(index, new Query("foo")); - var info = (RedisResult[])profile.Item2.Info; - CustomAssertions.GreaterThan(info.Length, 4); + var info = profile.Item2; + CustomAssertions.GreaterThan(info.Count, 4); } - [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.3")] + [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.9")] [MemberData(nameof(EndpointsFixture.Env.StandaloneOnly), MemberType = typeof(EndpointsFixture.Env))] public void TestProfile(string endpointId) { @@ -2874,7 +2872,7 @@ public void TestProfile(string endpointId) // check using Query var q = new Query("hello|world").SetNoContent(); - var profileSearch = ft.ProfileSearch(index, q); + var profileSearch = ft.ProfileOnSearch(index, q); var searchRes = profileSearch.Item1; var searchDet = (RedisResult[])profileSearch.Item2.Info; @@ -2886,7 +2884,7 @@ public void TestProfile(string endpointId) // check using AggregationRequest var aggReq = new AggregationRequest("*").Load(FieldName.Of("t")).Apply("startswith(@t, 'hel')", "prefix"); - var profileAggregate = ft.ProfileAggregate(index, aggReq); + var profileAggregate = ft.ProfileOnAggregate(index, aggReq); var aggregateRes = profileAggregate.Item1; var aggregateDet = (RedisResult[])profileAggregate.Item2.Info; @@ -2897,7 +2895,7 @@ public void TestProfile(string endpointId) CustomAssertions.GreaterThan(coordinatorIndex, -1); } - [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.3")] + [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.9")] [MemberData(nameof(EndpointsFixture.Env.StandaloneOnly), MemberType = typeof(EndpointsFixture.Env))] public async Task TestProfileAsync(string endpointId) { @@ -2910,7 +2908,7 @@ public async Task TestProfileAsync(string endpointId) // check using Query var q = new Query("hello|world").SetNoContent(); - var profileSearch = await ft.ProfileSearchAsync(index, q); + var profileSearch = await ft.ProfileOnSearchAsync(index, q); var searchRes = profileSearch.Item1; var searchDet = (RedisResult[])profileSearch.Item2.Info; @@ -2922,7 +2920,7 @@ public async Task TestProfileAsync(string endpointId) // check using AggregationRequest var aggReq = new AggregationRequest("*").Load(FieldName.Of("t")).Apply("startswith(@t, 'hel')", "prefix"); - var profileAggregate = await ft.ProfileAggregateAsync(index, aggReq); + var profileAggregate = await ft.ProfileOnAggregateAsync(index, aggReq); var aggregateRes = profileAggregate.Item1; var aggregateDet = (RedisResult[])profileAggregate.Item2.Info; @@ -2933,11 +2931,11 @@ public async Task TestProfileAsync(string endpointId) CustomAssertions.GreaterThan(coordinatorIndex, -1); } - [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.3")] - public void TestProfile_WithoutCoordinator() + [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.9")] + [MemberData(nameof(EndpointsFixture.Env.StandaloneOnly), MemberType = typeof(EndpointsFixture.Env))] + public void TestProfile_WithoutCoordinator(string endpointId) { - IDatabase db = redisFixture.Redis.GetDatabase(); - db.Execute("FLUSHALL"); + IDatabase db = GetCleanDatabase(endpointId); var ft = db.FT(); ft.Create(index, new Schema().AddTextField("t")); // Calling FT.CREATR without FTCreateParams @@ -2948,26 +2946,26 @@ public void TestProfile_WithoutCoordinator() var q = new Query("hello|world").SetNoContent(); var profileSearch = ft.ProfileSearch(index, q); var searchRes = profileSearch.Item1; - var searchDet = (RedisResult[])profileSearch.Item2.Info; + var searchDet = profileSearch.Item2; Assert.Equal(2, searchRes.Documents.Count); - CustomAssertions.GreaterThan(searchDet.Length, 4); + CustomAssertions.GreaterThan(searchDet.Count, 4); // check using AggregationRequest var aggReq = new AggregationRequest("*").Load(FieldName.Of("t")).Apply("startswith(@t, 'hel')", "prefix"); var profileAggregate = ft.ProfileAggregate(index, aggReq); var aggregateRes = profileAggregate.Item1; - var aggregateDet = (RedisResult[])profileAggregate.Item2.Info; + var aggregateDet = profileAggregate.Item2; Assert.Equal(2, aggregateRes.TotalResults); - CustomAssertions.GreaterThan(searchDet.Length, 4); + CustomAssertions.GreaterThan(aggregateDet.Count, 4); } - [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.3")] - public async Task TestProfileAsync_WithoutCoordinator() + [SkipIfRedis(Is.Enterprise, Comparison.GreaterThanOrEqual, "7.9")] + [MemberData(nameof(EndpointsFixture.Env.StandaloneOnly), MemberType = typeof(EndpointsFixture.Env))] + public async Task TestProfileAsync_WithoutCoordinator(string endpointId) { - IDatabase db = redisFixture.Redis.GetDatabase(); - db.Execute("FLUSHALL"); + IDatabase db = GetCleanDatabase(endpointId); var ft = db.FT(); await ft.CreateAsync(index, new Schema().AddTextField("t")); // Calling FT.CREATR without FTCreateParams @@ -2978,19 +2976,19 @@ public async Task TestProfileAsync_WithoutCoordinator() var q = new Query("hello|world").SetNoContent(); var profileSearch = await ft.ProfileSearchAsync(index, q); var searchRes = profileSearch.Item1; - var searchDet = (RedisResult[])profileSearch.Item2.Info; + var searchDet = profileSearch.Item2; Assert.Equal(2, searchRes.Documents.Count); - CustomAssertions.GreaterThan(searchDet.Length, 4); + CustomAssertions.GreaterThan(searchDet.Count, 4); // check using AggregationRequest var aggReq = new AggregationRequest("*").Load(FieldName.Of("t")).Apply("startswith(@t, 'hel')", "prefix"); var profileAggregate = await ft.ProfileAggregateAsync(index, aggReq); var aggregateRes = profileAggregate.Item1; - var aggregateDet = (RedisResult[])profileAggregate.Item2.Info; + var aggregateDet = profileAggregate.Item2; Assert.Equal(2, aggregateRes.TotalResults); - CustomAssertions.GreaterThan(searchDet.Length, 4); + CustomAssertions.GreaterThan(searchDet.Count, 4); } [SkipIfRedis(Is.Enterprise, Comparison.LessThan, "7.3.240")] @@ -3006,7 +3004,7 @@ public void TestProfileIssue306(string endpointId) // check using Query var q = new Query("hello|world").SetNoContent(); - var profileSearch = ft.ProfileSearch(index, q); + var profileSearch = ft.ProfileOnSearch(index, q); var searchRes = profileSearch.Item1; var searchDet = (RedisResult[])profileSearch.Item2.Info; @@ -3016,7 +3014,7 @@ public void TestProfileIssue306(string endpointId) // check using AggregationRequest var aggReq = new AggregationRequest("*").Load(FieldName.Of("t")).Apply("startswith(@t, 'hel')", "prefix"); - var profileAggregate = ft.ProfileAggregate(index, aggReq); + var profileAggregate = ft.ProfileOnAggregate(index, aggReq); var aggregateRes = profileAggregate.Item1; var aggregateDet = (RedisResult[])profileAggregate.Item2.Info; CustomAssertions.GreaterThan(aggregateDet.Length, 3); @@ -3036,7 +3034,7 @@ public async Task TestProfileAsyncIssue306(string endpointId) // check using Query var q = new Query("hello|world").SetNoContent(); - var profileSearch = await ft.ProfileSearchAsync(index, q); + var profileSearch = await ft.ProfileOnSearchAsync(index, q); var searchRes = profileSearch.Item1; var searchDet = (RedisResult[])profileSearch.Item2.Info; @@ -3045,7 +3043,7 @@ public async Task TestProfileAsyncIssue306(string endpointId) // check using AggregationRequest var aggReq = new AggregationRequest("*").Load(FieldName.Of("t")).Apply("startswith(@t, 'hel')", "prefix"); - var profileAggregate = await ft.ProfileAggregateAsync(index, aggReq); + var profileAggregate = await ft.ProfileOnAggregateAsync(index, aggReq); var aggregateRes = profileAggregate.Item1; var aggregateDet = (RedisResult[])profileAggregate.Item2.Info; CustomAssertions.GreaterThan(aggregateDet.Length, 3);