From 7a35e47341eabe0875a8b9ac4f3df314efedac50 Mon Sep 17 00:00:00 2001 From: Steve Lorello <42971704+slorello89@users.noreply.github.com> Date: Mon, 29 Jul 2024 11:26:45 -0400 Subject: [PATCH] Allowing SearchableAttribute to apply multiple times on one field (#473) --- src/Redis.OM/Modeling/SearchableAttribute.cs | 2 +- .../ObjectWithMultipleSearchableAttributes.cs | 13 ++++++++ .../RediSearchTests/SearchFunctionalTests.cs | 18 +++++++++++ .../RediSearchTests/SearchTests.cs | 32 +++++++++++++++++++ .../RedisSetupCollection.cs | 2 ++ 5 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 test/Redis.OM.Unit.Tests/RediSearchTests/ObjectWithMultipleSearchableAttributes.cs diff --git a/src/Redis.OM/Modeling/SearchableAttribute.cs b/src/Redis.OM/Modeling/SearchableAttribute.cs index 11554c7a..baf0629a 100644 --- a/src/Redis.OM/Modeling/SearchableAttribute.cs +++ b/src/Redis.OM/Modeling/SearchableAttribute.cs @@ -6,7 +6,7 @@ namespace Redis.OM.Modeling /// /// Marks a field as searchable within a Redis Document. /// - [AttributeUsage(AttributeTargets.Property)] + [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] public sealed class SearchableAttribute : SearchFieldAttribute { /// diff --git a/test/Redis.OM.Unit.Tests/RediSearchTests/ObjectWithMultipleSearchableAttributes.cs b/test/Redis.OM.Unit.Tests/RediSearchTests/ObjectWithMultipleSearchableAttributes.cs new file mode 100644 index 00000000..0d556d8f --- /dev/null +++ b/test/Redis.OM.Unit.Tests/RediSearchTests/ObjectWithMultipleSearchableAttributes.cs @@ -0,0 +1,13 @@ +using Redis.OM.Modeling; + +namespace Redis.OM.Unit.Tests.RediSearchTests; + +[Document(StorageType = StorageType.Json)] +public class ObjectWithMultipleSearchableAttributes +{ + [RedisIdField] public string Id { get; set; } + + [Searchable(JsonPath = "$.City")] + [Searchable(JsonPath = "$.State")] + public Address Address { get; set; } +} \ No newline at end of file diff --git a/test/Redis.OM.Unit.Tests/RediSearchTests/SearchFunctionalTests.cs b/test/Redis.OM.Unit.Tests/RediSearchTests/SearchFunctionalTests.cs index 9ca8f807..31fefcbf 100644 --- a/test/Redis.OM.Unit.Tests/RediSearchTests/SearchFunctionalTests.cs +++ b/test/Redis.OM.Unit.Tests/RediSearchTests/SearchFunctionalTests.cs @@ -1227,5 +1227,23 @@ public void SaveDateTimeOffset() Assert.Equal(intermediate.Offset,final.Offset); Assert.Equal(intermediate.DateTime,final.DateTime); } + + [Fact] + public void TestMultipleSearchAttributesOnEmbeddedDoc() + { + var obj = new ObjectWithMultipleSearchableAttributes() + { + Address = new Address + { + City = "Long Beach Island", + State = "New Jersey" + } + }; + + var collection = new RedisCollection(_connection); + collection.Insert(obj); + var res = collection.First(x => x.Address.City == "Long" && x.Address.State == "New"); + Assert.Equal(obj.Id, res.Id); + } } } \ No newline at end of file diff --git a/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs b/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs index 4449584f..813e08d9 100644 --- a/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs +++ b/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs @@ -3839,5 +3839,37 @@ public void TestBasicQueryCastToIQueryable() "0", "1234"); } + + [Fact] + public async Task TestIndexCreationWIthMultipleSearchFieldsForEmbeddedDoc() + { + _substitute.ExecuteAsync("FT.CREATE", Arg.Any()).Returns("OK"); + await _substitute.CreateIndexAsync(typeof(ObjectWithMultipleSearchableAttributes)); + await _substitute.Received().ExecuteAsync("FT.CREATE", + "objectwithmultiplesearchableattributes-idx", + "ON", + "Json", + "PREFIX", + "1", + "Redis.OM.Unit.Tests.RediSearchTests.ObjectWithMultipleSearchableAttributes:", + "SCHEMA", + "$.Address.City", "AS", "Address_City", "TEXT", + "$.Address.State", "AS", "Address_State", "TEXT"); + } + + [Fact] + public async Task TestMultipleTextSearchOnEmbeddedDoc() + { + _substitute.ClearSubstitute(); + _substitute.ExecuteAsync(Arg.Any(), Arg.Any()).Returns(_mockReply); + var collection = new RedisCollection(_substitute); + await collection.Where(x => x.Address.City == "Long" && x.Address.State == "New").ToListAsync(); + await _substitute.Received().ExecuteAsync("FT.SEARCH", + "objectwithmultiplesearchableattributes-idx", + "((@Address_City:\"Long\") (@Address_State:\"New\"))", + "LIMIT", + "0", + "100"); + } } } \ No newline at end of file diff --git a/test/Redis.OM.Unit.Tests/RedisSetupCollection.cs b/test/Redis.OM.Unit.Tests/RedisSetupCollection.cs index 374b1292..c0a3eb1c 100644 --- a/test/Redis.OM.Unit.Tests/RedisSetupCollection.cs +++ b/test/Redis.OM.Unit.Tests/RedisSetupCollection.cs @@ -30,6 +30,7 @@ public RedisSetup() Connection.CreateIndex(typeof(BasicJsonObjectTestSave)); Connection.CreateIndex(typeof(SelectTestObject)); Connection.CreateIndex(typeof(ObjectWithDateTimeOffsetJson)); + Connection.CreateIndex(typeof(ObjectWithMultipleSearchableAttributes)); } private IRedisConnectionProvider _provider; @@ -62,6 +63,7 @@ public void Dispose() Connection.DropIndexAndAssociatedRecords(typeof(BasicJsonObjectTestSave)); Connection.DropIndexAndAssociatedRecords(typeof(SelectTestObject)); Connection.DropIndexAndAssociatedRecords(typeof(ObjectWithDateTimeOffsetJson)); + Connection.DropIndexAndAssociatedRecords(typeof(ObjectWithMultipleSearchableAttributes)); } } }