diff --git a/Examples/CombinationModulesPipeline.md b/Examples/CombinationModulesPipeline.md index d7f1bd66..c48eac38 100644 --- a/Examples/CombinationModulesPipeline.md +++ b/Examples/CombinationModulesPipeline.md @@ -1,19 +1,24 @@ # Combination modules Pipeline + ## An example of pipelines mixing a pipeline with a combination of module commands with JSON & Search Connect to the Redis server: + ```csharp var redis = ConnectionMultiplexer.Connect("localhost"); ``` Setup pipeline connection + ```csharp var db = redis.GetDatabase(); var pipeline = new Pipeline(db); ``` ## JSON + Add JsonSet to pipeline + ```csharp pipeline.Json.SetAsync("person:01", "$", new { name = "John", age = 30, city = "New York" }); pipeline.Json.SetAsync("person:02", "$", new { name = "Joy", age = 25, city = "Los Angeles" }); @@ -23,27 +28,33 @@ pipeline.Json.SetAsync("person:05", "$", new { name = "Michael", age = 55, city ``` ## Search + Create the schema to index name as text field, age as a numeric field and city as tag field. + ```csharp var schema = new Schema().AddTextField("name").AddNumericField("age", true).AddTagField("city"); ``` Filter the index to only include Jsons with prefix of person: + ```csharp var parameters = FTCreateParams.CreateParams().On(IndexDataType.JSON).Prefix("person:"); ``` Create the index via pipeline + ```csharp pipeline.Ft.CreateAsync("person-idx", parameters, schema); ``` -Search for all indexed person records +Execute the pipeline + ```csharp -var getAllPersons = db.FT().SearchAsync("person-idx", new Query()); +pipeline.Execute(); ``` -Execute the pipeline +Search for all indexed person records + ```csharp -pipeline.Execute(); -``` \ No newline at end of file +var getAllPersons = db.FT().SearchAsync("person-idx", new Query()); +``` diff --git a/Examples/TransactionsExample.md b/Examples/TransactionsExample.md new file mode 100644 index 00000000..3f8dd761 --- /dev/null +++ b/Examples/TransactionsExample.md @@ -0,0 +1,55 @@ +# Transaction + +## An example of transactions with Redis modules (JSON.SET, JSON.GET & JSON.NUMINCRBY) + +Connect to the Redis server + +```cs +var redis = await ConnectionMultiplexer.ConnectAsync("localhost"); +var db = redis.GetDatabase(); +``` + +Setup transaction + +```cs +var tran = new Transactions(db); +``` + +Add account details with Json.Set + +```cs +tran.Json.SetAsync("accdetails:Jeeva", "$", new { name = "Jeeva", totalAmount= 1000, bankName = "City" }); +tran.Json.SetAsync("accdetails:Shachar", "$", new { name = "Shachar", totalAmount = 1000, bankName = "City" }); +``` + +Get the Json response for both Jeeva & Shachar + +```cs +var getShachar = tran.Json.GetAsync("accdetails:Shachar"); +var getJeeva = tran.Json.GetAsync("accdetails:Jeeva"); +``` + +Debit 200 from Jeeva + +```cs +tran.Json.NumIncrbyAsync("accdetails:Jeeva", "$.totalAmount", -200); +``` + +Credit 200 from Shachar + +```cs +tran.Json.NumIncrbyAsync("accdetails:Shachar", "$.totalAmount", 200); +``` + +Get total amount for both Jeeva = 800 & Shachar = 1200 + +```cs +var totalAmtOfJeeva = tran.Json.GetAsync("accdetails:Jeeva", path:"$.totalAmount"); +var totalAmtOfShachar = tran.Json.GetAsync("accdetails:Shachar", path:"$.totalAmount"); +``` + +Execute the transaction + +```cs +var condition = tran.ExecuteAsync(); +``` diff --git a/src/NRedisStack/Pipeline.cs b/src/NRedisStack/Pipeline.cs index 53f7fc31..bf0fa594 100644 --- a/src/NRedisStack/Pipeline.cs +++ b/src/NRedisStack/Pipeline.cs @@ -11,10 +11,8 @@ public Pipeline(IDatabase db) private IBatch _batch; - public void Execute() - { - _batch.Execute(); - } + public void Execute() => _batch.Execute(); + public IBloomCommandsAsync Bf => new BloomCommandsAsync(_batch); public ICmsCommandsAsync Cms => new CmsCommandsAsync(_batch); diff --git a/src/NRedisStack/Transactions.cs b/src/NRedisStack/Transactions.cs new file mode 100644 index 00000000..eed58a84 --- /dev/null +++ b/src/NRedisStack/Transactions.cs @@ -0,0 +1,31 @@ +using StackExchange.Redis; + +namespace NRedisStack +{ + public class Transactions + { + private ITransaction _transaction; + public IDatabaseAsync Db => _transaction; + + public Transactions(IDatabase db) + { + _transaction = db.CreateTransaction(); + } + + public ConditionResult AddCondition(Condition condition) => _transaction.AddCondition(condition); + + public bool Execute(CommandFlags flags = CommandFlags.None) => _transaction.Execute(flags); + + public Task ExecuteAsync(CommandFlags flags = CommandFlags.None) => _transaction.ExecuteAsync(flags); + + public IBloomCommandsAsync Bf => new BloomCommandsAsync(_transaction); + public ICmsCommandsAsync Cms => new CmsCommandsAsync(_transaction); + public ICuckooCommandsAsync Cf => new CuckooCommandsAsync(_transaction); + public IGraphCommandsAsync Graph => new GraphCommandsAsync(_transaction); + public IJsonCommandsAsync Json => new JsonCommandsAsync(_transaction); + public ISearchCommandsAsync Ft => new SearchCommandsAsync(_transaction); + public ITdigestCommandsAsync Tdigest => new TdigestCommandsAsync(_transaction); + public ITimeSeriesCommandsAsync Ts => new TimeSeriesCommandsAsync(_transaction); + public ITopKCommandsAsync TopK => new TopKCommandsAsync(_transaction); + } +} diff --git a/tests/NRedisStack.Tests/Examples/ExamplesTests.cs b/tests/NRedisStack.Tests/Examples/ExamplesTests.cs index c907694f..e5f6c6b4 100644 --- a/tests/NRedisStack.Tests/Examples/ExamplesTests.cs +++ b/tests/NRedisStack.Tests/Examples/ExamplesTests.cs @@ -1,11 +1,11 @@ -using Xunit; -using StackExchange.Redis; -using NRedisStack.RedisStackCommands; using Moq; -using NRedisStack.Search.FT.CREATE; -using NRedisStack.Search; using NRedisStack.DataTypes; using NRedisStack.Literals.Enums; +using NRedisStack.RedisStackCommands; +using NRedisStack.Search; +using NRedisStack.Search.FT.CREATE; +using StackExchange.Redis; +using Xunit; namespace NRedisStack.Tests; @@ -81,8 +81,6 @@ public async Task AsyncExample() [Fact] public void PipelineExample() { - // Connect to the Redis server and Setup 2 Pipelines - // Pipeline can get IDatabase for pipeline IDatabase db = redisFixture.Redis.GetDatabase(); db.Execute("FLUSHALL"); @@ -103,7 +101,7 @@ public void PipelineExample() // Get the Json response var getResponse = pipeline.Json.GetAsync("person"); - // Execute the pipeline2 + // Execute the pipeline pipeline.Execute(); // Get the result back JSON @@ -168,8 +166,8 @@ public async Task PipelineWithAsync() var db = redis.GetDatabase(); db.Execute("FLUSHALL"); // Setup pipeline connection - var pipeline = new Pipeline(db); + var pipeline = new Pipeline(db); // Create metedata lables for time-series. TimeSeriesLabel label1 = new TimeSeriesLabel("temp", "TLV"); @@ -216,8 +214,44 @@ public async Task PipelineWithAsync() } [Fact] - public void TransactionExample() + public async Task TransactionExample() { - // implementation for transaction + // Connect to the Redis server + var redis = ConnectionMultiplexer.Connect("localhost"); + + // Get a reference to the database + var db = redis.GetDatabase(); + db.Execute("FLUSHALL"); + + // Setup transaction with IDatabase + var tran = new Transactions(db); + + // Add account details with Json.Set to transaction + tran.Json.SetAsync("accdetails:Jeeva", "$", new { name = "Jeeva", totalAmount= 1000, bankName = "City" }); + tran.Json.SetAsync("accdetails:Shachar", "$", new { name = "Shachar", totalAmount = 1000, bankName = "City" }); + + // Get the Json response + var getShachar = tran.Json.GetAsync("accdetails:Shachar"); + var getJeeva = tran.Json.GetAsync("accdetails:Jeeva"); + + // Debit 200 from Jeeva + tran.Json.NumIncrbyAsync("accdetails:Jeeva", "$.totalAmount", -200); + + // Credit 200 from Shachar + tran.Json.NumIncrbyAsync("accdetails:Shachar", "$.totalAmount", 200); + + // Get total amount for both Jeeva = 800 & Shachar = 1200 + var totalAmtOfJeeva = tran.Json.GetAsync("accdetails:Jeeva", path:"$.totalAmount"); + var totalAmtOfShachar = tran.Json.GetAsync("accdetails:Shachar", path:"$.totalAmount"); + + // Execute the transaction + var condition = tran.ExecuteAsync(); + + // Assert + Assert.True(condition.Result); + Assert.NotEmpty(getJeeva.Result.ToString()); + Assert.NotEmpty(getShachar.Result.ToString()); + Assert.Equal("[800]", totalAmtOfJeeva.Result.ToString()); + Assert.Equal("[1200]", totalAmtOfShachar.Result.ToString()); } } \ No newline at end of file diff --git a/tests/NRedisStack.Tests/TransactionsTests.cs b/tests/NRedisStack.Tests/TransactionsTests.cs new file mode 100644 index 00000000..2ec82049 --- /dev/null +++ b/tests/NRedisStack.Tests/TransactionsTests.cs @@ -0,0 +1,89 @@ +using Moq; +using NRedisStack.RedisStackCommands; +using NRedisStack.Search; +using NRedisStack.Search.FT.CREATE; +using StackExchange.Redis; +using System.Text.Json; +using Xunit; + +namespace NRedisStack.Tests +{ + public class TransactionsTests : AbstractNRedisStackTest, IDisposable + { + Mock _mock = new Mock(); + private readonly string key = "TRX_TESTS"; + public TransactionsTests(RedisFixture redisFixture) : base(redisFixture) { } + + public void Dispose() + { + redisFixture.Redis.GetDatabase().KeyDelete(key); + } + + [Fact] + public async Task TestJsonTransactions() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var transaction = new Transactions(db); + string jsonPerson = JsonSerializer.Serialize(new Person { Name = "Shachar", Age = 23 }); + var setResponse = transaction.Json.SetAsync(key, "$", jsonPerson); + var getResponse = transaction.Json.GetAsync(key); + + transaction.Execute(); + + Assert.Equal("True", setResponse.Result.ToString()); + Assert.Equal("{\"Name\":\"Shachar\",\"Age\":23}", getResponse.Result.ToString()); + } + + [Fact] + public async Task TestModulsTransaction() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var tran = new Transactions(db); + + tran.Bf.ReserveAsync("bf-key", 0.001, 100); + tran.Bf.AddAsync("bf-key", "1"); + tran.Cms.InitByDimAsync("cms-key", 100, 5); + tran.Cf.ReserveAsync("cf-key", 100); + tran.Graph.QueryAsync("graph-key", "CREATE ({name:'shachar',age:23})"); + tran.Json.SetAsync("json-key", "$", "{}"); + tran.Ft.CreateAsync("ft-key", new FTCreateParams(), new Schema().AddTextField("txt")); + tran.Tdigest.CreateAsync("tdigest-key", 100); + tran.Ts.CreateAsync("ts-key", 100); + tran.TopK.ReserveAsync("topk-key", 100, 100, 100); + + Assert.False(db.KeyExists("bf-key")); + Assert.False(db.KeyExists("cms-key")); + Assert.False(db.KeyExists("cf-key")); + Assert.False(db.KeyExists("graph-key")); + Assert.False(db.KeyExists("json-key")); + Assert.Equal(0, db.FT()._List().Length); + Assert.False(db.KeyExists("tdigest-key")); + Assert.False(db.KeyExists("ts-key")); + Assert.False(db.KeyExists("topk-key")); + + tran.Execute(); + + Assert.True(db.KeyExists("bf-key")); + Assert.True(db.KeyExists("cms-key")); + Assert.True(db.KeyExists("cf-key")); + Assert.True(db.KeyExists("graph-key")); + Assert.True(db.KeyExists("json-key")); + Assert.True(db.FT()._List().Length == 1); + Assert.True(db.KeyExists("tdigest-key")); + Assert.True(db.KeyExists("ts-key")); + Assert.True(db.KeyExists("topk-key")); + + Assert.True(db.BF().Exists("bf-key", "1")); + Assert.True(db.CMS().Info("cms-key").Width == 100); + Assert.True(db.CF().Info("cf-key").Size > 0); + Assert.True(db.GRAPH().List().Count > 0); + Assert.False(db.JSON().Get("json-key").IsNull); + Assert.NotNull(db.FT().Info("ft-key")); + Assert.NotNull(db.TDIGEST().Info("tdigest-key")); + Assert.NotNull(db.TS().Info("ts-key")); + Assert.NotNull(db.TOPK().Info("topk-key")); + } + } +}