diff --git a/PackDB.Core.Tests/DataManagerTester.cs b/PackDB.Core.Tests/DataManagerTester.cs index 9100541..dc7c1bf 100644 --- a/PackDB.Core.Tests/DataManagerTester.cs +++ b/PackDB.Core.Tests/DataManagerTester.cs @@ -27,6 +27,7 @@ public void Setup() ExpectedIndexedEntity = new IndexedEntity {Id = Randomizer.Next(), IndexedValue = Randomizer.GetString()}; ExpectedAuditedEntity = new AuditedEntity {Id = Randomizer.Next()}; ExpectedAuditedEntityAuditLog = new AuditLog(); + ExpectedIndexKey = new IndexKey() { Value = Randomizer.GetString() }; MockDataStream = new Mock(); MockDataStream @@ -93,6 +94,10 @@ public void Setup() .Setup( x => x.GetIdsFromIndex("IndexedValue", ExpectedIndexedEntity.IndexedValue)) .Returns(ExpectedIndexEntityList); + MockIndexer + .Setup( + x => x.GetKeysFromIndex("IndexedValue")) + .Returns(ExpectedIndexKeysEntityList); MockIndexer .Setup(x => x.Index(ExpectedBasicEntity1)) .ReturnsAsync(true); @@ -154,6 +159,22 @@ private async IAsyncEnumerable RandomIndexEntityList() yield return await Task.FromResult(Randomizer.Next()); } + private async IAsyncEnumerable> EmptyIndexKeysEntityList() + { + await Task.CompletedTask; + yield break; + } + + private async IAsyncEnumerable> RandomIndexKeysEntityList() + { + yield return await Task.FromResult(new IndexKey(){ Value = Randomizer.GetString() }); + } + + private async IAsyncEnumerable> ExpectedIndexKeysEntityList() + { + yield return await Task.FromResult(ExpectedIndexKey); + } + private DataManager DataManager { get; set; } private static BasicEntity ExpectedBasicEntity1 { get; set; } private static BasicEntity ExpectedBasicEntity2 { get; set; } @@ -161,6 +182,7 @@ private async IAsyncEnumerable RandomIndexEntityList() private static AuditedEntity ExpectedAuditedEntity { get; set; } private static AuditLog ExpectedAuditedEntityAuditLog { get; set; } private static int ExpectedNextBasicEntityId { get; set; } + private static IndexKey ExpectedIndexKey { get; set; } private Randomizer Randomizer { get; set; } private Mock MockDataStream { get; set; } private Mock MockIndexer { get; set; } @@ -266,7 +288,50 @@ public async Task ReadWhenIndexHasAValueAndThereIsData() Assert.AreEqual(result.Count(), 1); Assert.AreSame(ExpectedIndexedEntity, result.ElementAt(0)); } + + [Test(Author = "PackDB Creator")] + public async Task ReadKeysWhenIndexPropertyIsNotIndexed() + { + var data = DataManager.ReadIndexKeys(x => x.Value1); + var result = new List>(); + await foreach (var d in data) result.Add(d); + Assert.IsEmpty(result); + MockIndexer.Verify(x => x.IndexExist(It.IsAny()), Times.Never); + } + [Test(Author = "PackDB Creator")] + public async Task ReadKeysWhenIndexDoesNotExist() + { + MockIndexer.Setup(x => x.IndexExist("IndexedValue")).ReturnsAsync(false); + var data = DataManager.ReadIndexKeys(x => x.IndexedValue); + var result = new List>(); + await foreach (var d in data) result.Add(d); + Assert.IsEmpty(result); + } + + [Test(Author = "PackDB Creator")] + public async Task ReadKeysWhenIndexHasNoValue() + { + MockIndexer + .Setup( + x => x.GetKeysFromIndex("IndexedValue")) + .Returns(EmptyIndexKeysEntityList); + var data = DataManager.ReadIndexKeys(x => x.IndexedValue); + var results = new List>(); + await foreach (var d in data) results.Add(d); + Assert.IsFalse(results.Any()); + } + + [Test(Author = "PackDB Creator")] + public async Task ReadKeysWhenIndexHasAValueAndThereIsData() + { + var data = DataManager.ReadIndexKeys(x => x.IndexedValue); + var result = new List>(); + await foreach (var d in data) result.Add(d); + Assert.AreEqual(result.Count(), 1); + Assert.AreSame(ExpectedIndexKey, result.ElementAt(0)); + } + [Test(Author = "PackDB Creator", ExpectedResult = false)] public async Task WriteDataWithNoAuditFails() { diff --git a/PackDB.Core/DataManager.cs b/PackDB.Core/DataManager.cs index b48b6bd..e3a346e 100644 --- a/PackDB.Core/DataManager.cs +++ b/PackDB.Core/DataManager.cs @@ -84,6 +84,30 @@ public async IAsyncEnumerable ReadIndex(TKeyType } } + public async IAsyncEnumerable> ReadIndexKeys(Expression> indexProperty) where TDataType : DataEntity + { + var indexMember = ((MemberExpression) indexProperty.Body).Member; + using (Logger.BeginScope("{Operation} is {Action} {IndexName} for {DataType}", + nameof(DataManager), "getting keys from index", indexMember.Name, typeof(TDataType).Name)) + { + if (indexMember.IsDefined(typeof(IndexAttribute), true)) + { + Logger.LogTrace("Property is indexed"); + var indexName = indexMember.Name; + if (await IndexWorker.IndexExist(indexName)) + { + var keys = IndexWorker.GetKeysFromIndex(indexName); + Logger.LogInformation("Found keys in index", keys); + await foreach (var key in keys) yield return key; + } + + yield break; + } + + Logger.LogWarning("The property used is not marked as indexed"); + } + } + public async Task Write(TDataType data) where TDataType : DataEntity { using (Logger.BeginScope("{Operation} is {Action} {DataType} with Id ({Id})", nameof(DataManager), diff --git a/PackDB.Core/IDataManager.cs b/PackDB.Core/IDataManager.cs index 9e457af..f42e5fe 100644 --- a/PackDB.Core/IDataManager.cs +++ b/PackDB.Core/IDataManager.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using PackDB.Core.Auditing; using PackDB.Core.Data; +using PackDB.Core.Indexing; namespace PackDB.Core { @@ -45,6 +46,15 @@ public interface IDataManager /// An enumerable of the data indexed under the key IAsyncEnumerable ReadIndex(TKeyType key, Expression> indexProperty) where TDataType : DataEntity; + + /// + /// Get keys that have been indexed for the property provided + /// + /// The property the key is for + /// The type of data that is being got + /// The type of the key + /// + IAsyncEnumerable> ReadIndexKeys(Expression> indexProperty) where TDataType : DataEntity; /// /// Writes the data to the storage system diff --git a/PackDB.Core/Indexing/IIndexWorker.cs b/PackDB.Core/Indexing/IIndexWorker.cs index d67faa2..ed45e1f 100644 --- a/PackDB.Core/Indexing/IIndexWorker.cs +++ b/PackDB.Core/Indexing/IIndexWorker.cs @@ -44,5 +44,7 @@ IAsyncEnumerable GetIdsFromIndex(string indexName, TKe /// The data type that should have indexable properties /// True if all properties are unindexed Task Unindex(TDataType data) where TDataType : DataEntity; + + IAsyncEnumerable> GetKeysFromIndex(string indexName) where TDataType : DataEntity; } } \ No newline at end of file