From c3230127110dd42fc6e2a34b7a59d14662570097 Mon Sep 17 00:00:00 2001 From: shacharPash Date: Mon, 29 May 2023 11:44:09 +0300 Subject: [PATCH 1/2] Add Hybrid query example + Tests --- Examples/AdvancedQueryOperations.md | 52 +++++++++++-- .../Examples/ExamplesTests.cs | 74 +++++++++++++++---- 2 files changed, 105 insertions(+), 21 deletions(-) diff --git a/Examples/AdvancedQueryOperations.md b/Examples/AdvancedQueryOperations.md index 1ced1dac..7f2fd88c 100644 --- a/Examples/AdvancedQueryOperations.md +++ b/Examples/AdvancedQueryOperations.md @@ -7,6 +7,7 @@ Aggregation and other more complex RediSearch queries 1. [Data Load](#vss_dataload) 2. [Index Creation](#vss_index) 3. [Search](#vss_search) + 4. [Hybrid query Search](#vss_hybrid_query_search) 4. [Advanced Search Queries](#adv_search) 1. [Data Set](#advs_dataset) 2. [Data Load](#advs_dataload) @@ -41,10 +42,26 @@ using NRedisStack.Search.Aggregation; ### Data Load ```c# -db.HashSet("vec:1", "vector", (new float[] { 1f, 1f, 1f, 1f }).SelectMany(BitConverter.GetBytes).ToArray()); -db.HashSet("vec:2", "vector", (new float[] { 2f, 2f, 2f, 2f }).SelectMany(BitConverter.GetBytes).ToArray()); -db.HashSet("vec:3", "vector", (new float[] { 3f, 3f, 3f, 3f }).SelectMany(BitConverter.GetBytes).ToArray()); -db.HashSet("vec:5", "vector", (new float[] { 4f, 4f, 4f, 4f }).SelectMany(BitConverter.GetBytes).ToArray()); +db.HashSet("vec:1", new HashEntry[] +{ + new("vector", (new float[] { 1f, 1f, 1f, 1f }).SelectMany(BitConverter.GetBytes).ToArray()), + new("tag", "A") +}); +db.HashSet("vec:2", new HashEntry[] +{ + new("vector", (new float[] { 2f, 2f, 2f, 2f }).SelectMany(BitConverter.GetBytes).ToArray()), + new("tag", "A") +}); +db.HashSet("vec:3", new HashEntry[] +{ + new("vector", (new float[] { 3f, 3f, 3f, 3f }).SelectMany(BitConverter.GetBytes).ToArray()), + new("tag", "B") +}); +db.HashSet("vec:4", new HashEntry[] +{ + new("vector", (new float[] { 4f, 4f, 4f, 4f }).SelectMany(BitConverter.GetBytes).ToArray()), + new("tag", "A") +}); ``` ### Index Creation #### Command @@ -53,6 +70,7 @@ ISearchCommands ft = db.FT(); try {ft.DropIndex("vss_idx");} catch {}; Console.WriteLine(ft.Create("vss_idx", new FTCreateParams().On(IndexDataType.HASH).Prefix("vec:"), new Schema() + .AddTagField("tag") .AddVectorField("vector", VectorField.VectorAlgo.FLAT, new Dictionary() { @@ -70,7 +88,7 @@ True ### Search #### Command ```c# -float[] vec = new[] { 2f, 2f, 3f, 3f}; +float[] vec = new[] { 2f, 3f, 3f, 3f}; var res = ft.Search("vss_idx", new Query("*=>[KNN 2 @vector $query_vec]") .AddParam("query_vec", vec.SelectMany(BitConverter.GetBytes).ToArray()) @@ -90,6 +108,30 @@ id: vec:2, score: 2 id: vec:3, score: 2 ``` +### Hybrid query Search +#### Search only documents with tag A +```c# +float[] vec = new[] { 2f, 3f, 3f, 3f}; +var res = ft.Search("vss_idx", + new Query("@tag:{A}=>[KNN 2 @vector $query_vec]") + .AddParam("query_vec", vec.SelectMany(BitConverter.GetBytes).ToArray()) + .SetSortBy("__vector_score") + .Dialect(2)); +foreach (var doc in res.Documents) { + foreach (var item in doc.GetProperties()) { + if (item.Key == "__vector_score") { + Console.WriteLine($"id: {doc.Id}, score: {item.Value}"); + } + } +} +``` +#### Result +```bash +id: vec:2, score: 3 +id: vec:4, score: 7 +``` +vec:3 is not returned because it has tag B + ## Advanced Search Queries ### Data Set ```json diff --git a/tests/NRedisStack.Tests/Examples/ExamplesTests.cs b/tests/NRedisStack.Tests/Examples/ExamplesTests.cs index 5dc02eed..2d8c2642 100644 --- a/tests/NRedisStack.Tests/Examples/ExamplesTests.cs +++ b/tests/NRedisStack.Tests/Examples/ExamplesTests.cs @@ -18,12 +18,12 @@ namespace NRedisStack.Tests; -public class ExaplesTests : AbstractNRedisStackTest, IDisposable +public class ExamplesTests : AbstractNRedisStackTest, IDisposable { private readonly ITestOutputHelper testOutputHelper; Mock _mock = new Mock(); private readonly string key = "EXAMPLES_TESTS"; - public ExaplesTests(RedisFixture redisFixture, ITestOutputHelper testOutputHelper) : base(redisFixture) + public ExamplesTests(RedisFixture redisFixture, ITestOutputHelper testOutputHelper) : base(redisFixture) { this.testOutputHelper = testOutputHelper; } @@ -41,7 +41,7 @@ public void HSETandSearch() // Get a reference to the database and for search commands: // var db = redis.GetDatabase(); - var db = redisFixture.Redis.GetDatabase(); + var db = redisFixture.Redis.GetDatabase(); db.Execute("FLUSHALL"); var ft = db.FT(); @@ -186,7 +186,7 @@ public async Task PipelineWithAsync() // Get a reference to the database // var db = redis.GetDatabase(); - var db = redisFixture.Redis.GetDatabase(); + var db = redisFixture.Redis.GetDatabase(); db.Execute("FLUSHALL"); // Setup pipeline connection @@ -286,7 +286,7 @@ public void TestJsonConvert() // ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); // IDatabase db = redis.GetDatabase(); - var db = redisFixture.Redis.GetDatabase(); + var db = redisFixture.Redis.GetDatabase(); db.Execute("FLUSHALL"); ISearchCommands ft = db.FT(); IJsonCommands json = db.JSON(); @@ -578,7 +578,7 @@ public void BasicJsonExamplesTest() { // ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); // IDatabase db = redis.GetDatabase(); - var db = redisFixture.Redis.GetDatabase(); + var db = redisFixture.Redis.GetDatabase(); db.Execute("FLUSHALL"); IJsonCommands json = db.JSON(); @@ -833,7 +833,7 @@ public void AdvancedJsonExamplesTest() { // ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); // IDatabase db = redis.GetDatabase(); - var db = redisFixture.Redis.GetDatabase(); + var db = redisFixture.Redis.GetDatabase(); db.Execute("FLUSHALL"); IJsonCommands json = db.JSON(); @@ -967,7 +967,7 @@ public void BasicQueryOperationsTest() { // ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); // IDatabase db = redis.GetDatabase(); - var db = redisFixture.Redis.GetDatabase(); + var db = redisFixture.Redis.GetDatabase(); db.Execute("FLUSHALL"); IJsonCommands json = db.JSON(); ISearchCommands ft = db.FT(); @@ -1127,22 +1127,39 @@ public void AdvancedQueryOperationsTest() { // ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); // IDatabase db = redis.GetDatabase(); - var db = redisFixture.Redis.GetDatabase(); + var db = redisFixture.Redis.GetDatabase(); db.Execute("FLUSHALL"); IJsonCommands json = db.JSON(); ISearchCommands ft = db.FT(); // Vector Similarity Search (VSS) // Data load: - db.HashSet("vec:1", "vector", (new float[] { 1f, 1f, 1f, 1f }).SelectMany(BitConverter.GetBytes).ToArray()); - db.HashSet("vec:2", "vector", (new float[] { 2f, 2f, 2f, 2f }).SelectMany(BitConverter.GetBytes).ToArray()); - db.HashSet("vec:3", "vector", (new float[] { 3f, 3f, 3f, 3f }).SelectMany(BitConverter.GetBytes).ToArray()); - db.HashSet("vec:5", "vector", (new float[] { 4f, 4f, 4f, 4f }).SelectMany(BitConverter.GetBytes).ToArray()); + db.HashSet("vec:1", new HashEntry[] + { + new("vector", (new float[] { 1f, 1f, 1f, 1f }).SelectMany(BitConverter.GetBytes).ToArray()), + new("tag", "A") + }); + db.HashSet("vec:2", new HashEntry[] + { + new("vector", (new float[] { 2f, 2f, 2f, 2f }).SelectMany(BitConverter.GetBytes).ToArray()), + new("tag", "A") + }); + db.HashSet("vec:3", new HashEntry[] + { + new("vector", (new float[] { 3f, 3f, 3f, 3f }).SelectMany(BitConverter.GetBytes).ToArray()), + new("tag", "B") + }); + db.HashSet("vec:4", new HashEntry[] + { + new("vector", (new float[] { 4f, 4f, 4f, 4f }).SelectMany(BitConverter.GetBytes).ToArray()), + new("tag", "A") + }); // Index creation: try { ft.DropIndex("vss_idx"); } catch { }; Assert.True(ft.Create("vss_idx", new FTCreateParams().On(IndexDataType.HASH).Prefix("vec:"), new Schema() + .AddTagField("tag") .AddVectorField("vector", VectorField.VectorAlgo.FLAT, new Dictionary() { @@ -1156,7 +1173,7 @@ public void AdvancedQueryOperationsTest() Thread.Sleep(2000); // Search: - float[] vec = new[] { 2f, 2f, 3f, 3f }; + float[] vec = new[] { 2f, 3f, 3f, 3f }; var res = ft.Search("vss_idx", new Query("*=>[KNN 2 @vector $query_vec]") .AddParam("query_vec", vec.SelectMany(BitConverter.GetBytes).ToArray()) @@ -1176,12 +1193,37 @@ public void AdvancedQueryOperationsTest() HashSet expectedResSet = new HashSet() { - "id: vec:2, score: 2", - "id: vec:3, score: 2", + "id: vec:3, score: 1", + "id: vec:2, score: 3", }; Assert.Equal(expectedResSet, resSet); + // hybrid query - search only documents with tag A: + res = ft.Search("vss_idx", + new Query("@tag:{A}=>[KNN 2 @vector $query_vec]") + .AddParam("query_vec", vec.SelectMany(BitConverter.GetBytes).ToArray()) + .SetSortBy("__vector_score") + .Dialect(2)); + + resSet.Clear(); + foreach (var doc in res.Documents) + { + foreach (var item in doc.GetProperties()) + { + if (item.Key == "__vector_score") + { + resSet.Add($"id: {doc.Id}, score: {item.Value}"); + } + } + } + + expectedResSet = new HashSet() + { + "id: vec:2, score: 3", + "id: vec:4, score: 7", + }; + //Advanced Search Queries: // data load: json.Set("warehouse:1", "$", new From 45b7cbdf217cc60d264c94381a7f4b574f52cf97 Mon Sep 17 00:00:00 2001 From: shacharPash <93581407+shacharPash@users.noreply.github.com> Date: Thu, 1 Jun 2023 11:57:32 +0300 Subject: [PATCH 2/2] Update and rename ExamplesTests.cs to ExampleTests.cs --- .../Examples/{ExamplesTests.cs => ExampleTests.cs} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename tests/NRedisStack.Tests/Examples/{ExamplesTests.cs => ExampleTests.cs} (99%) diff --git a/tests/NRedisStack.Tests/Examples/ExamplesTests.cs b/tests/NRedisStack.Tests/Examples/ExampleTests.cs similarity index 99% rename from tests/NRedisStack.Tests/Examples/ExamplesTests.cs rename to tests/NRedisStack.Tests/Examples/ExampleTests.cs index 2d8c2642..b549f0ad 100644 --- a/tests/NRedisStack.Tests/Examples/ExamplesTests.cs +++ b/tests/NRedisStack.Tests/Examples/ExampleTests.cs @@ -18,12 +18,12 @@ namespace NRedisStack.Tests; -public class ExamplesTests : AbstractNRedisStackTest, IDisposable +public class ExampleTests : AbstractNRedisStackTest, IDisposable { private readonly ITestOutputHelper testOutputHelper; Mock _mock = new Mock(); private readonly string key = "EXAMPLES_TESTS"; - public ExamplesTests(RedisFixture redisFixture, ITestOutputHelper testOutputHelper) : base(redisFixture) + public ExampleTests(RedisFixture redisFixture, ITestOutputHelper testOutputHelper) : base(redisFixture) { this.testOutputHelper = testOutputHelper; } @@ -1390,4 +1390,4 @@ private static void SortAndCompare(List expectedList, List res) Assert.Equal(expectedList[i], res[i].ToString()); } } -} \ No newline at end of file +}