# Advanced Querying Aggregation and other more complex RediSearch queries ## Contents 1. [Business Value Statement](#value) 2. [Modules Needed](#modules) 3. [Vector Similarity Search](#vss) 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) 3. [Index Creation](#advs_index) 4. [Search w/JSON Filtering - Example 1](#advs_ex1) 5. [Search w/JSON Filtering - Example 2](#advs_ex2) 5. [Aggregation](#aggr) 1. [Data Set](#aggr_dataset) 2. [Data Load](#aggr_dataload) 3. [Index Creation](#aggr_index) 4. [Aggregation - Count](#aggr_count) 5. [Aggregation - Sum](#aggr_sum) ## Business Value Statement Redis provides the following additional advanced search capabilities to derive further value of Redis-held data: * Vector Similarity Search - Store and search by ML-generated encodings of text and images * Search + JSON Filtering - Combine the power of search with JSONPath filtering of search results * Aggregation - Create processing pipelines of search results to extract analytic insights. ## Modules Needed ```c# using StackExchange.Redis; using NRedisStack; using NRedisStack.RedisStackCommands; using NRedisStack.Search; using NRedisStack.Search.Literals.Enums; using NRedisStack.Search.Aggregation; ``` ## Vector Similarity Search (VSS) ### Syntax [VSS](https://redis.io/docs/stack/search/reference/vectors/) ### Data Load ```c# 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 ```c# SearchCommands 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() { ["TYPE"] = "FLOAT32", ["DIM"] = "4", ["DISTANCE_METRIC"] = "L2" } ))); ``` #### Result ```bash True ``` ### Search #### Command ```c# 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()) .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: 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 { "city": "Boston", "location": "42.361145, -71.057083", "inventory": [ { "id": 15970, "gender": "Men", "season":["Fall", "Winter"], "description": "Turtle Check Men Navy Blue Shirt", "price": 34.95 }, { "id": 59263, "gender": "Women", "season": ["Fall", "Winter", "Spring", "Summer"], "description": "Titan Women Silver Watch", "price": 129.99 }, { "id": 46885, "gender": "Boys", "season": ["Fall"], "description": "Ben 10 Boys Navy Blue Slippers", "price": 45.99 } ] }, { "city": "Dallas", "location": "32.779167, -96.808891", "inventory": [ { "id": 51919, "gender": "Women", "season":["Summer"], "description": "Nyk Black Horado Handbag", "price": 52.49 }, { "id": 4602, "gender": "Unisex", "season": ["Fall", "Winter"], "description": "Wildcraft Red Trailblazer Backpack", "price": 50.99 }, { "id": 37561, "gender": "Girls", "season": ["Spring", "Summer"], "description": "Madagascar3 Infant Pink Snapsuit Romper", "price": 23.95 } ] } ``` ### Data Load ```c# JsonCommands json = db.JSON(); json.Set("warehouse:1", "$", new { city = "Boston", location = "-71.057083, 42.361145", inventory = new[] { new { id = 15970, gender = "Men", season = new[] {"Fall", "Winter"}, description = "Turtle Check Men Navy Blue Shirt", price = 34.95 }, new { id = 59263, gender = "Women", season = new[] {"Fall", "Winter", "Spring", "Summer"}, description = "Titan Women Silver Watch", price = 129.99 }, new { id = 46885, gender = "Boys", season = new[] {"Fall"}, description = "Ben 10 Boys Navy Blue Slippers", price = 45.99 } } }); json.Set("warehouse:2", "$", new { city = "Dallas", location = "-96.808891, 32.779167", inventory = new[] { new { id = 51919, gender = "Women", season = new[] {"Summer"}, description = "Nyk Black Horado Handbag", price = 52.49 }, new { id = 4602, gender = "Unisex", season = new[] {"Fall", "Winter"}, description = "Wildcraft Red Trailblazer Backpack", price = 50.99 }, new { id = 37561, gender = "Girls", season = new[] {"Spring", "Summer"}, description = "Madagascar3 Infant Pink Snapsuit Romper", price = 23.95 } } }); ``` ### Index Creation #### Command ```c# SearchCommands ft = db.FT(); try {ft.DropIndex("wh_idx");} catch {}; Console.WriteLine(ft.Create("wh_idx", new FTCreateParams() .On(IndexDataType.JSON) .Prefix("warehouse:"), new Schema().AddTextField(new FieldName("$.city", "city")))); ``` #### Result ```bash True ``` ### Search w/JSON Filtering - Example 1 Find all inventory ids from all the Boston warehouse that have a price > $50. #### Command ```c# foreach (var doc in ft.Search("wh_idx", new Query("@city:Boston") .ReturnFields(new FieldName("$.inventory[?(@.price>50)].id", "result")) .Dialect(3)) .Documents.Select(x => x["result"])) { Console.WriteLine(doc); } ``` #### Result ```json [59263] ``` ### Search w/JSON Filtering - Example 2 Find all inventory items in Dallas that are for Women or Girls #### Command ```c# foreach (var doc in ft.Search("wh_idx", new Query("@city:(Dallas)") .ReturnFields(new FieldName("$.inventory[?(@.gender==\"Women\" || @.gender==\"Girls\")]", "result")) .Dialect(3)) .Documents.Select(x => x["result"])) { Console.WriteLine(doc); } ``` #### Result ```json [{"id":51919,"gender":"Women","season":["Summer"],"description":"Nyk Black Horado Handbag","price":52.49},{"id":37561,"gender":"Girls","season":["Spring","Summer"],"description":"Madagascar3 Infant Pink Snapsuit Romper","price":23.95}] ``` ## Aggregation ### Syntax [FT.AGGREGATE](https://redis.io/commands/ft.aggregate/) ### Data Set ```JSON { "title": "System Design Interview", "year": 2020, "price": 35.99 }, { "title": "The Age of AI: And Our Human Future", "year": 2021, "price": 13.99 }, { "title": "The Art of Doing Science and Engineering: Learning to Learn", "year": 2020, "price": 20.99 }, { "title": "Superintelligence: Path, Dangers, Stategies", "year": 2016, "price": 14.36 } ``` ### Data Load ```c# json.Set("book:1", "$", new { title = "System Design Interview", year = 2020, price = 35.99 }); json.Set("book:2", "$", new { title = "The Age of AI: And Our Human Future", year = 2021, price = 13.99 }); json.Set("book:3", "$", new { title = "The Art of Doing Science and Engineering: Learning to Learn", year = 2020, price = 20.99 }); json.Set("book:4", "$", new { title = "Superintelligence: Path, Dangers, Stategies", year = 2016, price = 14.36 }); ``` ### Index Creation #### Command ```c# Console.WriteLine(ft.Create("book_idx", new FTCreateParams() .On(IndexDataType.JSON) .Prefix("book:"), new Schema().AddTextField(new FieldName("$.title", "title")) .AddNumericField(new FieldName("$.year", "year")) .AddNumericField(new FieldName("$.price", "price")))); ``` #### Result ```bash True ``` ### Aggregation - Count Find the total number of books per year #### Command ```c# var request = new AggregationRequest("*").GroupBy("@year", Reducers.Count().As("count")); var result = ft.Aggregate("book_idx", request); for (var i=0; i Sum of inventory dollar value by year #### Command ```c# request = new AggregationRequest("*").GroupBy("@year", Reducers.Sum("@price").As("sum")); result = ft.Aggregate("book_idx", request); for (var i=0; i