From c1461c122c3118a60ab0ae1ab97339eac37ca70f Mon Sep 17 00:00:00 2001 From: Jeevananthan <71455761+Jeevananthan-23@users.noreply.github.com> Date: Fri, 13 Jan 2023 19:06:09 +0530 Subject: [PATCH] Bulk Operations Delete & Update (#278) --- src/Redis.OM/RedisCommands.cs | 16 ++ src/Redis.OM/Searching/IRedisCollection.cs | 50 +++- src/Redis.OM/Searching/RedisCollection.cs | 109 ++++++-- .../RediSearchTests/BulkOperationsTests.cs | 261 ++++++++++++++++++ .../RediSearchTests/SearchFunctionalTests.cs | 57 ---- 5 files changed, 393 insertions(+), 100 deletions(-) create mode 100644 test/Redis.OM.Unit.Tests/RediSearchTests/BulkOperationsTests.cs diff --git a/src/Redis.OM/RedisCommands.cs b/src/Redis.OM/RedisCommands.cs index 557ef14a..0de473ce 100644 --- a/src/Redis.OM/RedisCommands.cs +++ b/src/Redis.OM/RedisCommands.cs @@ -754,6 +754,14 @@ public static async Task> HGetAllAsync(this IRedisCo /// the status. public static string Unlink(this IRedisConnection connection, string key) => connection.Execute("UNLINK", key); + /// + /// Unlinks array of keys. + /// + /// the connection. + /// the keys to unlink. + /// the status. + public static string Unlink(this IRedisConnection connection, string[] keys) => connection.Execute("UNLINK", keys); + /// /// Unlinks a key. /// @@ -762,6 +770,14 @@ public static async Task> HGetAllAsync(this IRedisCo /// the status. public static async Task UnlinkAsync(this IRedisConnection connection, string key) => await connection.ExecuteAsync("UNLINK", key); + /// + /// Unlinks array of keys. + /// + /// the connection. + /// the keys to unlink. + /// the status. + public static async Task UnlinkAsync(this IRedisConnection connection, string[] keys) => await connection.ExecuteAsync("UNLINK", keys); + /// /// Unlinks the key and then adds an updated value of it. /// diff --git a/src/Redis.OM/Searching/IRedisCollection.cs b/src/Redis.OM/Searching/IRedisCollection.cs index d89b28e2..4bd87fdc 100644 --- a/src/Redis.OM/Searching/IRedisCollection.cs +++ b/src/Redis.OM/Searching/IRedisCollection.cs @@ -87,6 +87,21 @@ public interface IRedisCollection : IOrderedQueryable, IAsyncEnumerable /// the Id of the newly inserted item, or null if not inserted. string? Insert(T item, WhenKey when, TimeSpan? timeSpan = null); + /// + /// Inserts list of items into redis. + /// + /// The items to insert. + /// The list of Keys. + Task> Insert(IEnumerable items); + + /// + /// Inserts list of items into redis. + /// + /// The items to insert. + /// The timespan of the document's (TTL). + /// /// The list of Keys. + Task> Insert(IEnumerable items, TimeSpan timeSpan); + /// /// finds an item by it's ID or keyname. /// @@ -127,12 +142,25 @@ public interface IRedisCollection : IOrderedQueryable, IAsyncEnumerable /// A representing the asynchronous operation. Task UpdateAsync(T item); + /// + /// Updates the provided items in Redis. Document must have a property marked with the . + /// + /// The items to update. + /// A representing the asynchronous operation. + ValueTask UpdateAsync(IEnumerable items); + /// /// Deletes the item from Redis. /// /// The item to be deleted. void Delete(T item); + /// + /// Deletes the List of items from Redis. + /// + /// The items to be deleted. + void Delete(IEnumerable items); + /// /// Deletes the item from Redis. /// @@ -140,6 +168,13 @@ public interface IRedisCollection : IOrderedQueryable, IAsyncEnumerable /// A representing the asynchronous operation. Task DeleteAsync(T item); + /// + /// Deletes the List of items from Redis. + /// + /// The items to be deleted. + /// A representing the asynchronous operation. + Task DeleteAsync(IEnumerable items); + /// /// Async method for enumerating the collection to a list. /// @@ -266,20 +301,5 @@ public interface IRedisCollection : IOrderedQueryable, IAsyncEnumerable /// The Ids to look up. /// A dictionary correlating the ids provided to the objects in Redis. Task> FindByIdsAsync(IEnumerable ids); - - /// - /// Inserts list of items into redis. - /// - /// The items to insert. - /// The list of Keys. - Task> Insert(IEnumerable items); - - /// - /// Inserts list of items into redis. - /// - /// The items to insert. - /// The timespan of the document's (TTL). - /// /// The list of Keys. - Task> Insert(IEnumerable items, TimeSpan timeSpan); } } diff --git a/src/Redis.OM/Searching/RedisCollection.cs b/src/Redis.OM/Searching/RedisCollection.cs index 518aed0c..2e0e5d2a 100644 --- a/src/Redis.OM/Searching/RedisCollection.cs +++ b/src/Redis.OM/Searching/RedisCollection.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Text; using System.Threading; using System.Threading.Tasks; using Redis.OM.Common; @@ -191,6 +192,14 @@ public async Task UpdateAsync(T item) SaveToStateManager(key, item); } + /// + public async ValueTask UpdateAsync(IEnumerable items) + { + var tasks = items.Select(UpdateAsync); + + await Task.WhenAll(tasks); + } + /// public void Delete(T item) { @@ -199,6 +208,23 @@ public void Delete(T item) StateManager.Remove(key); } + /// + public void Delete(IEnumerable items) + { + var keys = items.Select(x => x.GetKey()).ToArray(); + if (!keys.Any()) + { + return; + } + + foreach (var key in keys) + { + StateManager.Remove(key); + } + + _connection.Unlink(keys); + } + /// public async Task DeleteAsync(T item) { @@ -207,6 +233,23 @@ public async Task DeleteAsync(T item) StateManager.Remove(key); } + /// + public async Task DeleteAsync(IEnumerable items) + { + var keys = items.Select(x => x.GetKey()).ToArray(); + if (!keys.Any()) + { + return; + } + + foreach (var key in keys) + { + StateManager.Remove(key); + } + + await _connection.UnlinkAsync(keys); + } + /// public async Task> ToListAsync() { @@ -604,6 +647,44 @@ public async Task InsertAsync(T item, TimeSpan timeSpan) return ((RedisQueryProvider)Provider).Connection.Set(item, when, timeSpan); } + /// + public async Task> Insert(IEnumerable items) + { + var distinct = items.Distinct().ToArray(); + if (!distinct.Any()) + { + return new List(); + } + + var tasks = new List>(); + foreach (var item in distinct) + { + tasks.Add(((RedisQueryProvider)Provider).Connection.SetAsync(item)); + } + + var result = await Task.WhenAll(tasks); + return result.ToList(); + } + + /// + public async Task> Insert(IEnumerable items, TimeSpan timeSpan) + { + var distinct = items.Distinct().ToArray(); + if (!distinct.Any()) + { + return new List(); + } + + var tasks = new List>(); + foreach (var item in distinct) + { + tasks.Add(((RedisQueryProvider)Provider).Connection.SetAsync(item, timeSpan)); + } + + var result = await Task.WhenAll(tasks); + return result.ToList(); + } + /// public T? FindById(string id) { @@ -640,34 +721,6 @@ public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToke return new RedisCollectionEnumerator(Expression, provider.Connection, ChunkSize, StateManager, BooleanExpression, SaveState); } - /// - public async Task> Insert(IEnumerable items) - { - var tasks = new List>(); - foreach (var item in items.Distinct()) - { - tasks.Add(((RedisQueryProvider)Provider).Connection.SetAsync(item)); - } - - await Task.WhenAll(tasks); - var result = tasks.Select(x => x.Result).ToList(); - return result; - } - - /// - public async Task> Insert(IEnumerable items, TimeSpan timeSpan) - { - var tasks = new List>(); - foreach (var item in items.Distinct()) - { - tasks.Add(((RedisQueryProvider)Provider).Connection.SetAsync(item, timeSpan)); - } - - await Task.WhenAll(tasks); - var result = tasks.Select(x => x.Result).ToList(); - return result; - } - private static MethodInfo GetMethodInfo(Func f, T1 unused) { return f.Method; diff --git a/test/Redis.OM.Unit.Tests/RediSearchTests/BulkOperationsTests.cs b/test/Redis.OM.Unit.Tests/RediSearchTests/BulkOperationsTests.cs new file mode 100644 index 00000000..1cce5661 --- /dev/null +++ b/test/Redis.OM.Unit.Tests/RediSearchTests/BulkOperationsTests.cs @@ -0,0 +1,261 @@ +using Redis.OM.Contracts; +using Redis.OM.Searching; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Redis.OM.Unit.Tests.RediSearchTests +{ + [Collection("Redis")] + public class BulkOperationsTests + { + public BulkOperationsTests(RedisSetup setup) + { + _connection = setup.Connection; + } + private IRedisConnection _connection = null; + + [Fact] + public async Task Test_Bulk_InsertAsync() + { + var collection = new RedisCollection(_connection); + var persons = new List() { + new Person() { Name = "Alice", Age = 51, NickNames = new[] { "Ally", "Alie", "Al" } }, + new Person() { Name = "Robert", Age = 37, NickNames = new[] { "Bobby", "Rob", "Bob" } }, + new Person() { Name = "Jeeva", Age = 22, NickNames = new[] { "Jee", "Jeev", "J" } }, + new Person() { Name = "Martin", Age = 60, NickNames = new[] { "Mart", "Mat", "tin" } } + }; + var keys = await collection.Insert(persons); + + var people = collection.Where(x => x.NickNames.Contains("Bob") || x.NickNames.Contains("Alie")).ToList(); + Assert.Contains(people, x => x.Name == persons.First().Name); + } + + [Fact] + public async Task Test_Inserts_TwiceWith_SaveDataWith_ExactFields() + { + var collection = new RedisCollection(_connection); + var persons = new List() { + new Person() { Name = "Alice", Age = 14, NickNames = new[] { "Ally", "Alie", "Al" } }, + new Person() { Name = "Robert", Age = 30, NickNames = new[] { "Bobby", "Rob", "Bob" } }, + new Person() { Name = "Jeeva", Age = 22, NickNames = new[] { "Jee", "Jeev", "J" } }, + new Person() { Name = "Martin", Age = 61, NickNames = new[] { "Mart", "Mat", "tin" } } + }; + var keys = await collection.Insert(persons); //performs JSON.SET create keys and emit the list of keys. + + var persons2 = new List() { + new Person() { Name = "Alice", Age = 14, NickNames = new[] { "Ally", "Alie", "Al" }, IsEngineer = true }, + new Person() { Name = "Robert", Age = 30, NickNames = new[] { "Bobby", "Rob", "Bob" }, IsEngineer = false }, + new Person() { Name = "Jeeva", Age = 22, NickNames = new[] { "Jee", "Jeev", "J" }, DepartmentNumber = 201 }, + new Person() { Name = "Martin", Age = 61, NickNames = new[] { "Mart", "Mat", "tin" }, TagField = "Martin" } + }; + + var keys2 = await collection.Insert(persons2); //create keys and emit the list of keys. + + var people = collection.Where(x => x.Age >= 20 && x.Age <=30).ToList(); + Assert.NotEqual(keys, keys2); //not performs any re-indexing because keys are not same. + } + + [Fact] + public async Task Test_BulkInsert_WithSameIds() + { + var collection = new RedisCollection(_connection); + var persons = new List() { + new Person() {Id="01GFZ9Y6CTEDHHXKT055N1YP3A" , Name = "Alice", Age = 51, NickNames = new[] { "Ally", "Alie", "Al" } }, + new Person() {Id="01GFZ9Y6CTEDHHXKT055N1YP3A" , Name = "Jeevananthan", Age = 37, NickNames = new[] { "Jeeva", "Jee"} }, + new Person() { Name = "Jeeva", Age = 22, NickNames = new[] { "Jee", "Jeev", "J" }, }, + new Person() { Name = "Martin", Age = 60, NickNames = new[] { "Mart", "Mat", "tin" }, } + }; + await collection.Insert(persons); + var people = collection.Where(x => x.NickNames.Contains("Jeeva") || x.NickNames.Contains("Alie")).ToList(); + Assert.False(people.First().Name == persons.First().Name); // this fails because the Name field of people doesn't contains the Name value Alice + } + + [Fact] + public async Task Test_BulkInsert_HashesWith_Expiration() + { + var collection = new RedisCollection(_connection); + var PhineasFerb = new List() { + new HashPerson() { Name = "Ferb", Age = 14, IsEngineer = true, TagField = "SummerVacation" }, + new HashPerson() { Name = "Phineas", Age = 14, IsEngineer = true, TagField = "SummerVacation" } + }; + + await collection.Insert(PhineasFerb, TimeSpan.FromMilliseconds(8000)); + var ttl = (long)_connection.Execute("PTTL", PhineasFerb[0].GetKey()); + Assert.True(ttl <= 8000); + Assert.True(ttl >= 1000); + } + + [Fact] + public async Task Test_BulkInsert_WithExpiration() + { + var collection = new RedisCollection(_connection); + var PhineasFerb = new List() { + new Person() { Name = "Ferb", Age = 14, IsEngineer = true, TagField = "SummerVacation" , NickNames = new[] { "Feb", "Fee" } }, + new Person() { Name = "Phineas", Age = 14, IsEngineer = true, TagField = "SummerVacation", NickNames = new[] { "Phineas", "Triangle Head", "Phine" } } + }; + + await collection.Insert(PhineasFerb, TimeSpan.FromSeconds(8)); + var ttl = (long)_connection.Execute("PTTL", PhineasFerb[0].GetKey()); + Assert.True(ttl <= 8000); + Assert.True(ttl >= 1000); + } + + [Fact] + public async Task Test_Bulk_Insert_Del() + { + var collection = new RedisCollection(_connection); + var PhineasFerbShow = new List() { + new Person() { Name = "Ferb", Age = 14, IsEngineer = true, TagField = "SummerVacation" , Address = new Address { State = "Tri-State Area"} }, + new Person() { Name = "Phineas", Age = 14, IsEngineer = true, TagField = "SummerVacation", Address = new Address { State = "Tri-State Area"} }, + new Person() { Name = "Dr.Doofenshmirtz", Age = 38, IsEngineer = true, TagField = "Villain", Address = new Address { State = "Tri-State Area"} }, + new Person() { Name = "Perry", Age = 5, IsEngineer = false, TagField = "Agent", Address = new Address { State = "Tri-State Area "} } + }; + + await collection.Insert(PhineasFerbShow); + var searchByState = collection.Where(x => x.Address.State == "Tri-State Area").ToList(); + await collection.DeleteAsync(searchByState); + var searchByTag = collection.FindById(searchByState[0].GetKey()); + Assert.Null(searchByTag); + } + + [Fact] + public async Task Test_Bulk_InsertAsync_DelAsync_ForHashes() + { + var collection = new RedisCollection(_connection); + var PhineasFerbShow = new List() { + new HashPerson() { Name = "Ferb", Age = 14, IsEngineer = true, TagField = "SummerVacation" , Address = new Address { State = "Tri-State Area"} }, + new HashPerson() { Name = "Phineas", Age = 14, IsEngineer = true, TagField = "SummerVacation", Address = new Address { State = "Tri-State Area"} }, + new HashPerson() { Name = "Dr.Doofenshmirtz", Age = 38, IsEngineer = true, TagField = "Villain", Address = new Address { State = "Tri-State Area"} }, + new HashPerson() { Name = "Perry", Age = 5, IsEngineer = false, TagField = "Agent", Address = new Address { State = "Tri-State Area "} } + }; + + await collection.Insert(PhineasFerbShow); + var searchByName = await collection.Where(x => x.Name == "Dr.Doofenshmirtz" || x.Name == "Perry").ToListAsync(); + await collection.DeleteAsync(searchByName); + var searchByTag = await collection.FindByIdAsync(searchByName[0].GetKey()); + Assert.Null(searchByTag); + } + + [Fact] + public async Task Test_Bulk_UpdateAsync() + { + var collection = new RedisCollection(_connection); + var onepiece = new List() { + new Person() { Name = "Monkey D.Luffy", Age = 22, NickNames = new[] { "Luffy", "Straw Hat", "GumGum" }, TagField = "The Straw Hat Pirates" }, + new Person() { Name = "Roronano Zoro", Age = 26, NickNames = new[] { "Zoro", "Roronano", "Pirate Hunter" } , TagField = "The Straw Hat Pirates" }, + new Person() { Name = "Monkey D. Garp", Age = 70, NickNames = new[] { "Garp", "Garps", "Hero of the Navy" }, TagField = "Navy" }, + new Person() { Name = "Shanks", Age = 50, NickNames = new[] { "Shanks", "Red-Hair" }, TagField = "Red-Haired Pirates" } + }; + var keys = await collection.Insert(onepiece); + var people = collection.Where(x => x.NickNames.Contains("Luffy") || x.NickNames.Contains("Shanks")).ToList(); + Assert.Equal(onepiece[0].Age, people[0].Age); + people[0].Age = 25; + people[1].Age = 52; + await collection.UpdateAsync(people); + Assert.NotEqual(onepiece[0].Age, people[0].Age); + } + + [Fact] + public async Task Test_Bulk_UpdateSync_WithHashesNumeric() + { + var collection = new RedisCollection(_connection); + var onepiece = new List() { + new HashPerson() { Name = "Monkey D.Luffy", Age = 22, NickNames = new List { "Luffy", "Straw Hat", "GumGum" }, TagField = "The Straw Hat Pirates" }, + new HashPerson() { Name = "Roronano Zoro", Age = 26, NickNames = new List { "Zoro", "Roronano", "Pirate Hunter" } , TagField = "The Straw Hat Pirates" }, + new HashPerson() { Name = "Monkey D. Garp", Age = 70, NickNames = new List { "Garp", "Garps", "Hero of the Navy" }, TagField = "Navy" }, + new HashPerson() { Name = "Shanks", Age = 50, NickNames = new List { "Shanks", "Red-Hair" }, TagField = "Red-Haired Pirates" } + }; + var keys = collection.Insert(onepiece); + var people = collection.Where(x => x.Name.Contains("Luffy") || x.Name.Contains("Shanks")).ToList(); + Assert.Equal(onepiece[0].Age, people[0].Age); + people[0].Height = 20.2; + people[0].Age = 25; + people[1].Age = 52; + await collection.UpdateAsync(people); + Assert.NotEqual(onepiece[0].Age, people[0].Age); + } + + + [Fact] + public async Task Test_BulkUpdate_WithEmbbedObject() + { + var collection = new RedisCollection(_connection); + var onepiece = new List() { + new Person() { Name = "Monkey D.Luffy", Age = 22, NickNames = new[] { "Luffy", "Straw Hat", "GumGum" }, TagField = "The Straw Hat Pirates" }, + new Person() { Name = "Roronano Zoro", Age = 26, NickNames = new[] { "Zoro", "Roronano", "Pirate Hunter" } , TagField = "The Straw Hat Pirates" }, + new Person() { Name = "Monkey D. Garp", Age = 70, NickNames = new[] { "Garp", "Garps", "Hero of the Navy" }, TagField = "Navy" }, + new Person() { Name = "Shanks", Age = 50, NickNames = new[] { "Shanks", "Red-Hair" }, TagField = "Red-Haired Pirates" } + }; + var keys = collection.Insert(onepiece); + var people = collection.Where(x => x.NickNames.Contains("Luffy") || x.NickNames.Contains("Shanks")).ToList(); + people[0].Address = new Address { City = "Goa Kingdom" }; + people[1].Address = new Address { City = "Goa Kingdom" }; + await collection.UpdateAsync(people); + Assert.Contains(people, x => x.Name == onepiece.First().Name); + } + + [Fact] + public async Task Test_Bulk50_Records_Insert_Update_Del_Async() + { + var collection = new RedisCollection(_connection, false, 100); // consider using SaveState = false to avoid Concurrent issue + + var names = new[] { "Hassein", "Zoro", "Aegorn", "Jeeva", "Ajith", "Joe", "Mark", "Otto" }; + var rand = new Random(); + var people = new List(); + for (var i = 0; i < 50; i++) + { + people.Add(new Person + { + Name = names[rand.Next(0, names.Length)], + DepartmentNumber = rand.Next(1, 4), + Sales = rand.Next(50000, 1000000), + Age = rand.Next(17, 21), + Height = 58.0 + rand.NextDouble() * 15, + SalesAdjustment = rand.NextDouble() + } + ); + } + var keys = await collection.Insert(people); // 1000 records in an avg of 200ms. + var listofPeople = (await collection.FindByIdsAsync(keys)).Values.ToList(); + for (int i = 0; i < keys.Count; i++) + { + listofPeople[i].Name = names[rand.Next(0, names.Length)]; + listofPeople[i].DepartmentNumber = rand.Next(5, 9); + listofPeople[i].Sales = rand.Next(10000, 20000); + listofPeople[i].Age = rand.Next(30, 50); + listofPeople[i].Height = 50.0 + rand.NextDouble() * 15; + listofPeople[i].SalesAdjustment = rand.NextDouble(); + } + await collection.UpdateAsync(listofPeople); // 1000 records in an avg of 300ms. + var oldPeople = collection.Where(x => x.Age >= 17 && x.Age <= 21).ToList(); + var newPeople = collection.Where(x => x.Age >= 30 && x.Age <= 50).ToList(); + await collection.DeleteAsync(newPeople); // del + Assert.Empty(oldPeople); + Assert.DoesNotContain(people[0], newPeople); + } + + [Fact] + public async Task TestBulk_Insert_Update_Del_Async_WithHashes() + { + var collection = new RedisCollection(_connection); + var PhineasFerbShow = new List() { + new HashPerson() { Name = "Ferb", Age = 14, IsEngineer = true, TagField = "SummerVacation" , Address = new Address { State = "Tri-State Area"} }, + new HashPerson() { Name = "Phineas", Age = 14, IsEngineer = true, TagField = "SummerVacation", Address = new Address { State = "Tri-State Area"} }, + new HashPerson() { Name = "Dr.Doofenshmirtz", Age = 38, IsEngineer = true, TagField = "Villain", Address = new Address { State = "Tri-State Area"} }, + new HashPerson() { Name = "Perry", Age = 5, IsEngineer = false, TagField = "Agent", Address = new Address { State = "Tri-State Area "} } + }; + + await collection.Insert(PhineasFerbShow); + var searchByName = await collection.Where(x => x.Name == "Dr.Doofenshmirtz" || x.Name == "Perry").ToListAsync(); + searchByName[0].TagField = "Vacation"; + searchByName[1].DepartmentNumber = 2; + await collection.UpdateAsync(searchByName); + await collection.DeleteAsync(searchByName); + var searchByTag = await collection.FindByIdAsync(searchByName[0].GetKey()); + Assert.Null(searchByTag); + } + } +} diff --git a/test/Redis.OM.Unit.Tests/RediSearchTests/SearchFunctionalTests.cs b/test/Redis.OM.Unit.Tests/RediSearchTests/SearchFunctionalTests.cs index 9595eac5..6382c9b0 100644 --- a/test/Redis.OM.Unit.Tests/RediSearchTests/SearchFunctionalTests.cs +++ b/test/Redis.OM.Unit.Tests/RediSearchTests/SearchFunctionalTests.cs @@ -926,63 +926,6 @@ public async Task TestTimeStampRangesHash() Assert.Equal(obj.Id, first.Id); } - [Fact] - public async Task TestBulkInsert() - { - var collection = new RedisCollection(_connection); - var persons = new List() { - new Person() { Name = "Alice", Age = 51, NickNames = new[] { "Ally", "Alie", "Al" }, }, - new Person() { Name = "Robert", Age = 37, NickNames = new[] { "Bobby", "Rob", "Bob" }, }, - new Person() { Name = "Jeeva", Age = 22, NickNames = new[] { "Jee", "Jeev", "J" }, }, - new Person() { Name = "Martin", Age = 60, NickNames = new[] { "Mart", "Mat", "tin" }, } - }; - var keys = await collection.Insert(persons); - var people = collection.Where(x => x.NickNames.Contains("Bob") || x.NickNames.Contains("Alie")).ToList(); - Assert.Contains(people, x => x.Name == persons.First().Name); - } - - [Fact] - public async Task TestBulkInsertWithSameIds() - { - var collection = new RedisCollection(_connection); - var persons = new List() { - new Person() {Id="01GFZ9Y6CTEDHHXKT055N1YP3A" , Name = "Alice", Age = 51, NickNames = new[] { "Ally", "Alie", "Al" }, }, - new Person() {Id="01GFZ9Y6CTEDHHXKT055N1YP3A" , Name = "Robert", Age = 37, NickNames = new[] { "Bobby", "Rob", "Bob" }, }, - new Person() { Name = "Jeeva", Age = 22, NickNames = new[] { "Jee", "Jeev", "J" }, }, - new Person() { Name = "Martin", Age = 60, NickNames = new[] { "Mart", "Mat", "tin" }, } - }; - await collection.Insert(persons); - var people = await collection.Where(x => x.NickNames.Contains("Bob") || x.NickNames.Contains("Alie")).ToListAsync(); - Assert.Equal(people.Count, persons.Count - 3); - Assert.False(people.First().Name == persons.First().Name); // this fails because the Name field of people doesn't contains the Name value Alice - } - - [Fact] - public async Task BulkInsert50Records() - { - var collection = new RedisCollection(_connection); - - var names = new[] { "Stever", "Martin", "Aegorn", "Robert", "Mary", "Joe", "Mark", "Otto" }; - var rand = new Random(); - var people = new List(); - for (var i = 0; i < 50; i++) - { - people.Add(new Person - { - Name = names[rand.Next(0, names.Length)], - DepartmentNumber = rand.Next(1, 4), - Sales = rand.Next(50000, 1000000), - Age = rand.Next(17, 21), - Height = 58.0 + rand.NextDouble() * 15, - SalesAdjustment = rand.NextDouble() - } - ); - } - await collection.Insert(people); - var countPeople = collection.Where(x => x.Age >= 17 && x.Age <= 21).ToList().Count; - Assert.Equal(people.Count, countPeople); - } - [Fact] public async Task TestListContains() {