NRedisStack

repository·master·Indexed 43 days ago

https://github.com/redis/nredisstack

.NET client providing native support for Redis Stack modules, including Search, JSON, TimeSeries, Bloom Filter, Cuckoo Filter, T-Digest, Count-min Sketch, and Top-K, built on top of the StackExchange.Redis library.

Tokens
12.3K
Snippets
39
Records
44
Agent score
56%

What's inside NRedisStack

  1. Combine filters using Logical AND and OR

    master

    JSONPath supports logical operators to combine multiple relational expressions within a filter:

    • Logical AND (&&): Both conditions must be true.
    • Logical OR (||): At least one condition must be true.

    Example of Logical AND:

    json.Get("warehouse:1", "$.inventory[?(@.gender==\"Men\"&&@.price>20)]");

    Example of Logical OR:

    json.Get("warehouse:1", "$.inventory[?(@.price<100||@.gender==\"Women\")].id");
  2. Important: Search Dialect behavior

    master

    Starting from version 1.0.0-beta1, NRedisStack automatically overrides the server-side dialect to version 2 by default. This means the client automatically appends DIALECT 2 to commands like FT.AGGREGATE and FT.SEARCH.

    This change may impact the results returned compared to older versions. If your application requires a different dialect, you must configure the client accordingly.

  3. Filter JSON arrays using relational operators

    master

    You can use JSONPath filter expressions [?(<expression>)] to query items within an array based on relational logic. Supported operators include:

    • Equality (==): Match a field against a specific value.
    • Less Than (<): Match numeric fields less than a value.
    • Greater Than or Equal (>=): Match numeric fields greater than or equal to a value.

    Example of equality:

    // Matches items where description is exactly 'Turtle Check Men Navy Blue Shirt'
    json.Get("warehouse:1", "$.inventory[?(@.description==\"Turtle Check Men Navy Blue Shirt\")]");
  4. How NRedisStack modules work

    master

    NRedisStack extends the standard StackExchange.Redis client by providing specialized command classes for different Redis Stack modules.

    To use a module, you first connect using the standard StackExchange.Redis ConnectionMultiplexer and obtain an IDatabase. You then access the module-specific commands by calling an extension method on the IDatabase instance (e.g., db.JSON() for JSON commands).

    Each module has its own dedicated command class that exposes the relevant Redis Stack commands.

    using NRedisStack;
    using NRedisStack.RedisStackCommands;
    using StackExchange.Redis;
    
    ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost");
    IDatabase db = redis.GetDatabase();
    
    // Access module commands via extension methods
    JsonCommands json = db.JSON();
    SearchCommands ft = db.FT();
  5. Filter JSON arrays using Regex

    master

    Use the =~ operator in a JSONPath filter to perform regular expression matching on string fields.

    • Exact Match/Contains: [?(@.field =~ "pattern")] matches if the pattern is found.
    • Case Insensitive: Use the (?i) flag within the regex string (e.g., "(?i)term").
    • Begins With: Use the ^ anchor (e.g., "^Term").

    Examples:

    json.Get("warehouse:1", "$.inventory[?(@.description =~ \"Blue\")]");
    
    // Case-insensitive 'watch'
    json.Get("warehouse:1", "$.inventory[?(@.description =~ \"(?i)watch\")]");
    
    // Begins with 'T'
    json.Get("warehouse:1", "$.inventory[?(@.description =~ \"^T\")]");
  6. Use async methods in NRedisStack

    master

    All NRedisStack methods provide both synchronous and asynchronous implementations. Asynchronous methods are identified by the Async suffix (e.g., SetAsync, GetAsync) and are fully await-able. To use them, ensure you are working within an async context and await the calls to prevent blocking.

    // Connect to a Redis server and get a JSON instance
    var redis = await ConnectionMultiplexer.ConnectAsync("localhost");
    var db = redis.GetDatabase();
    var json = db.JSON();
    
    // Store data asynchronously
    await json.SetAsync("key", "$", new { name = "John", age = 30, city = "New York" });
    
    // Retrieve data asynchronously
    var john = await json.GetAsync("key");
  7. Search and filter JSON documents

    master

    Combine RediSearch with JSONPath filtering to extract specific nested data from JSON documents.

    1. Indexing JSON

    Use FT.CREATE with IndexDataType.JSON. Define fields using FieldName with JSONPath syntax (e.g., $.city).

    2. Filtering with JSONPath

    Use .ReturnFields() in your Query to apply JSONPath filters. This allows you to return only specific elements of an array that match a condition (e.g., $.inventory[?(@.price>50)].id). Use Dialect(3) to ensure full JSONPath support.

    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);
    }
  8. Perform basic RediSearch query operations

    master

    Use ft.Search(indexName, query) to execute searches. You can convert results to JSON using .ToJson() for easy iteration.

    Query Patterns:

    • Retrieve All: Use new Query("*").
    • Single Term Text: @field:term (e.g., @description:Slippers).
    • Exact Phrase: @field:("phrase") (e.g., @description:("Blue Shirt")).
    • Numeric Range: @field:[min, max] (e.g., @price:[40,130]).
    • Tag Array: @field:{value} (e.g., @season:{Spring}).
    • Logical AND: Combine terms with spaces (e.g., @price:[40, 100] @description:Blue).
    • Logical OR: Use | (e.g., (@gender:{Women})|(@city:Boston)).
    • Negation: Use - (e.g., -(@description:Shirt)).
    • Prefix/Suffix: Use * (e.g., @description:Nav* or @description:*Watch).
    • Fuzzy Search: Use %word% for Levenshtein distance (e.g., @description:%wavy%).
    • Geo Search: Use @field:[long lat radius] (e.g., @coords:[-104.800644 38.846127 100 mi]).
    // Example: Retrieve all documents
    foreach (var doc in ft.Search("idx1", new Query("*")).ToJson())
    {
        Console.WriteLine(doc);
    }
    
    // Example: Numeric range search
    foreach (var doc in ft.Search("idx1", new Query("@price:[40,130]")).ToJson())
    {
        Console.WriteLine(doc);
    }
  9. Required modules for advanced querying

    master

    To perform advanced operations like Vector Similarity Search, JSON filtering, and Aggregations, you must include the following namespaces in your C# project:

    using StackExchange.Redis;
    using NRedisStack;
    using NRedisStack.RedisStackCommands;
    using NRedisStack.Search;
    using NRedisStack.Search.Literals.Enums;
    using NRedisStack.Search.Aggregation;
    using NRedisStack;
    using NRedisStack.RedisStackCommands;
    using NRedisStack.Search;
    using NRedisStack.Search.Literals.Enums;
    using NRedisStack.Search.Aggregation;
  10. Enable the REDUCE COLLECT unstable feature in Redis

    master

    The REDUCE COLLECT reducer (used with FT.AGGREGATE and GROUPBY) is currently gated behind the server's unstable features flag. If this flag is not enabled, the server will return an error when attempting to use this feature.

    To enable it, you must run the following command on your Redis server:

    Note for Clusters: In a Redis Cluster environment, the flag must be enabled on all nodes, not just the coordinator or the node receiving the connection, as the query is fanned out to every shard.

    CONFIG SET search-enable-unstable-features yes
  11. Use Aggregations to extract analytic insights

    master

    Use the FT.AGGREGATE command via ft.Aggregate to create processing pipelines on search results.

    Aggregation Workflow

    1. Create an Index: Index your JSON or Hash data with appropriate fields (Text, Numeric, etc.).
    2. Define an AggregationRequest: Specify a query string (e.g., "*" for all documents) and use .GroupBy() to define buckets.
    3. Apply Reducers: Use Reducers to perform calculations within groups, such as Reducers.Count() or Reducers.Sum("@field").
    4. Access Results: Iterate through result.TotalResults and use result.GetRow(i) to retrieve the computed values.
    var request = new AggregationRequest("*").GroupBy("@year", Reducers.Count().As("count"));
    var result = ft.Aggregate("book_idx", request);
    
    for (var i=0; i<result.TotalResults; i++)
    {
        var row = result.GetRow(i);
        Console.WriteLine($"{row["year"]}: {row["count"]}");
    }
    
    // Sum of price by year
    var sumRequest = new AggregationRequest("*").GroupBy("@year", Reducers.Sum("@price").As("sum"));
    var sumResult = ft.Aggregate("book_idx", sumRequest);