diff --git a/CoreIntegrationTests/Infrastructure/MongoDbDocumentTestBase.cs b/CoreIntegrationTests/Infrastructure/MongoDbDocumentTestBase.cs index a1f3944..105d7a8 100644 --- a/CoreIntegrationTests/Infrastructure/MongoDbDocumentTestBase.cs +++ b/CoreIntegrationTests/Infrastructure/MongoDbDocumentTestBase.cs @@ -71,7 +71,7 @@ namespace CoreIntegrationTests.Infrastructure public void AddOne() { // Arrange - var document = new T(); + var document = CreateTestDocument(); // Act SUT.AddOne(document); // Assert @@ -84,7 +84,7 @@ namespace CoreIntegrationTests.Infrastructure public async Task AddOneAsync() { // Arrange - var document = new T(); + var document = CreateTestDocument(); // Act await SUT.AddOneAsync(document); // Assert @@ -97,7 +97,7 @@ namespace CoreIntegrationTests.Infrastructure public void AddMany() { // Arrange - var documents = new List { new T(), new T() }; + var documents = CreateTestDocuments(2); // Act SUT.AddMany(documents); // Assert @@ -115,7 +115,7 @@ namespace CoreIntegrationTests.Infrastructure if (!string.IsNullOrEmpty(PartitionKey)) { // Arrange - var documents = new List { new T(), new T(), new T(), new T() }; + var documents = CreateTestDocuments(4); if (documents.Any(e => e is IPartitionedDocument)) { var secondPartitionKey = $"{PartitionKey}-2"; @@ -138,7 +138,7 @@ namespace CoreIntegrationTests.Infrastructure public async Task AddManyAsync() { // Arrange - var documents = new List { new T(), new T() }; + var documents = CreateTestDocuments(2); // Act await SUT.AddManyAsync(documents); // Assert @@ -156,7 +156,7 @@ namespace CoreIntegrationTests.Infrastructure if (!string.IsNullOrEmpty(PartitionKey)) { // Arrange - var documents = new List { new T(), new T(), new T(), new T() }; + var documents = CreateTestDocuments(4); if (documents.Any(e => e is IPartitionedDocument)) { var secondPartitionKey = $"{PartitionKey}-2"; diff --git a/CoreIntegrationTests/Infrastructure/MongoDbTKeyDocumentTestBase.cs b/CoreIntegrationTests/Infrastructure/MongoDbTKeyDocumentTestBase.cs index feb2d10..5414e48 100644 --- a/CoreIntegrationTests/Infrastructure/MongoDbTKeyDocumentTestBase.cs +++ b/CoreIntegrationTests/Infrastructure/MongoDbTKeyDocumentTestBase.cs @@ -1,4 +1,5 @@ -using MongoDbGenericRepository.Models; +using MongoDB.Driver; +using MongoDbGenericRepository.Models; using System; using System.Collections.Generic; using System.Diagnostics; @@ -72,7 +73,7 @@ namespace CoreIntegrationTests.Infrastructure public void AddOne() { // Arrange - var document = new T(); + var document = CreateTestDocument(); // Act SUT.AddOne(document); // Assert @@ -85,7 +86,7 @@ namespace CoreIntegrationTests.Infrastructure public async Task AddOneAsync() { // Arrange - var document = new T(); + var document = CreateTestDocument(); // Act await SUT.AddOneAsync(document); // Assert @@ -98,7 +99,7 @@ namespace CoreIntegrationTests.Infrastructure public void AddMany() { // Arrange - var documents = new List { new T(), new T() }; + var documents = CreateTestDocuments(2); // Act SUT.AddMany(documents); // Assert @@ -116,7 +117,7 @@ namespace CoreIntegrationTests.Infrastructure if (!string.IsNullOrEmpty(PartitionKey)) { // Arrange - var documents = new List { new T(), new T(), new T(), new T() }; + var documents = CreateTestDocuments(4); if (documents.Any(e => e is IPartitionedDocument)) { var secondPartitionKey = $"{PartitionKey}-2"; @@ -139,7 +140,7 @@ namespace CoreIntegrationTests.Infrastructure public async Task AddManyAsync() { // Arrange - var documents = new List { new T(), new T() }; + var documents = CreateTestDocuments(2); // Act await SUT.AddManyAsync(documents); // Assert @@ -157,7 +158,7 @@ namespace CoreIntegrationTests.Infrastructure if (!string.IsNullOrEmpty(PartitionKey)) { // Arrange - var documents = new List { new T(), new T(), new T(), new T() }; + var documents = CreateTestDocuments(4); if (documents.Any(e => e is IPartitionedDocument)) { var secondPartitionKey = $"{PartitionKey}-2"; @@ -1149,7 +1150,8 @@ namespace CoreIntegrationTests.Infrastructure // Act var result = SUT.GroupBy( - e => e.GroupingKey, g => new ProjectedGroup + e => e.GroupingKey, + g => new ProjectedGroup { Key = g.Key, Content = g.Select(doc => doc.SomeContent).ToList() @@ -1195,11 +1197,13 @@ namespace CoreIntegrationTests.Infrastructure // Act var result = SUT.GroupBy( e => e.Children.Any(c => c.Type == guid1), - e => e.GroupingKey, g => new ProjectedGroup + e => e.GroupingKey, + g => new ProjectedGroup { Key = g.Key, Content = g.Select(doc => doc.SomeContent).ToList() - }, PartitionKey); + }, + PartitionKey); // Assert var key1Group = result.First(e => e.Key == 4); @@ -1212,6 +1216,86 @@ namespace CoreIntegrationTests.Infrastructure #endregion Group By + #region Pagination + + public static Random Random = new Random(); + + [Fact] + public async Task GetSortedPaginatedAsync() + { + // Arrange + var content = $"{Guid.NewGuid()}"; + var documents = CreateTestDocuments(10); + for (var i = 0; i < 5; i++) + { + documents[i].GroupingKey = 8; + documents[i].Nested.SomeAmount = Random.Next(1, 500000); + documents[i].SomeContent = content; + } + for (var i = 5; i < documents.Count; i++) + { + documents[i].GroupingKey = 9; + documents[i].SomeContent = content; + } + SUT.AddMany(documents); + + documents = documents.OrderByDescending(e => e.Nested.SomeAmount).ToList(); + var notExpected = documents.First(); + var expectedFirstResult = documents[1]; + + // Act + var result = await SUT.GetSortedPaginatedAsync( + e => e.GroupingKey == 8 && e.SomeContent == content, + e => e.Nested.SomeAmount, + false, + 1,5, + PartitionKey); + + // Assert + Assert.Equal(4, result.Count); + Assert.True(!result.Contains(notExpected)); + Assert.Equal(expectedFirstResult.Id, result[0].Id); + } + + [Fact] + public async Task GetSortedPaginatedAsyncWithSortOptions() + { + // Arrange + var content = $"{Guid.NewGuid()}"; + var documents = CreateTestDocuments(10); + for (var i = 0; i < 5; i++) + { + documents[i].GroupingKey = 8; + documents[i].Nested.SomeAmount = Random.Next(1, 500000); + documents[i].SomeContent = content; + } + for (var i = 5; i < documents.Count; i++) + { + documents[i].GroupingKey = 9; + documents[i].SomeContent = content; + } + SUT.AddMany(documents); + + documents = documents.OrderByDescending(e => e.Nested.SomeAmount).ToList(); + var notExpected = documents.First(); + var expectedFirstResult = documents[1]; + var sorting = Builders.Sort.Descending(e => e.Nested.SomeAmount); + + // Act + var result = await SUT.GetSortedPaginatedAsync( + e => e.GroupingKey == 8 && e.SomeContent == content, + sorting, + 1, 5, + PartitionKey); + + // Assert + Assert.Equal(4, result.Count); + Assert.True(!result.Contains(notExpected)); + Assert.Equal(expectedFirstResult.Id, result[0].Id); + } + + #endregion Pagination + #region Test Utils [MethodImpl(MethodImplOptions.NoInlining)] diff --git a/CoreIntegrationTests/Infrastructure/MongoDbTestFixture.cs b/CoreIntegrationTests/Infrastructure/MongoDbTestFixture.cs index 7675ba4..057f27f 100644 --- a/CoreIntegrationTests/Infrastructure/MongoDbTestFixture.cs +++ b/CoreIntegrationTests/Infrastructure/MongoDbTestFixture.cs @@ -16,15 +16,15 @@ namespace CoreIntegrationTests.Infrastructure public MongoDbTestFixture() { - DocsToDelete = new ConcurrentBag(); } public string PartitionKey { get; set; } - public ConcurrentBag DocsToDelete { get; set; } + public static ConcurrentBag DocsToDelete { get; set; } = new ConcurrentBag(); public virtual void Dispose() { + if (DocsToDelete.Any()) { TestRepository.Instance.DeleteMany(DocsToDelete.ToList()); diff --git a/MongoDbGenericRepository/Abstractions/IBaseReadOnlyRepository.cs b/MongoDbGenericRepository/Abstractions/IBaseReadOnlyRepository.cs index 32d5c92..155a54c 100644 --- a/MongoDbGenericRepository/Abstractions/IBaseReadOnlyRepository.cs +++ b/MongoDbGenericRepository/Abstractions/IBaseReadOnlyRepository.cs @@ -406,5 +406,49 @@ namespace MongoDbGenericRepository where TProjection : class, new(); #endregion Group By + + #region Pagination + + /// + /// Asynchronously returns a paginated list of the documents matching the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// The property selector. + /// Order of the sorting. + /// The number of documents you want to skip. Default value is 0. + /// The number of documents you want to take. Default value is 50. + /// An optional partition key. + Task> GetSortedPaginatedAsync( + Expression> filter, + Expression> sortSelector, + bool ascending = true, + int skipNumber = 0, + int takeNumber = 50, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable; + + /// + /// Asynchronously returns a paginated list of the documents matching the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// The sort definition. + /// The number of documents you want to skip. Default value is 0. + /// The number of documents you want to take. Default value is 50. + /// An optional partition key. + Task> GetSortedPaginatedAsync( + Expression> filter, + SortDefinition sortDefinition, + int skipNumber = 0, + int takeNumber = 50, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable; + + #endregion Pagination } } diff --git a/MongoDbGenericRepository/Abstractions/IReadOnlyMongoRepository.cs b/MongoDbGenericRepository/Abstractions/IReadOnlyMongoRepository.cs index e36e8bd..1dad5fb 100644 --- a/MongoDbGenericRepository/Abstractions/IReadOnlyMongoRepository.cs +++ b/MongoDbGenericRepository/Abstractions/IReadOnlyMongoRepository.cs @@ -1,8 +1,4 @@ -using MongoDbGenericRepository.Models; -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Threading.Tasks; +using System; namespace MongoDbGenericRepository { diff --git a/MongoDbGenericRepository/BaseMongoRepository.Main.cs b/MongoDbGenericRepository/BaseMongoRepository.Main.cs index 11223b2..09664e8 100644 --- a/MongoDbGenericRepository/BaseMongoRepository.Main.cs +++ b/MongoDbGenericRepository/BaseMongoRepository.Main.cs @@ -39,37 +39,6 @@ namespace MongoDbGenericRepository { } - /// - /// Asynchronously returns a paginated list of the documents matching the filter condition. - /// - /// The type representing a Document. - /// A LINQ expression filter. - /// The property selector. - /// Order of the sorting. - /// The number of documents you want to skip. Default value is 0. - /// The number of documents you want to take. Default value is 50. - /// An optional partition key. - public virtual async Task> GetSortedPaginatedAsync( - Expression> filter, - Expression> sortSelector, - bool ascending = true, - int skipNumber = 0, - int takeNumber = 50, - string partitionKey = null) - where TDocument : IDocument - { - var sorting = ascending - ? Builders.Sort.Ascending(sortSelector) - : Builders.Sort.Descending(sortSelector); - - return await HandlePartitioned(partitionKey) - .Find(filter) - .Sort(sorting) - .Skip(skipNumber) - .Limit(takeNumber) - .ToListAsync(); - } - /// /// Asynchronously returns a paginated list of the documents matching the filter condition. /// diff --git a/MongoDbGenericRepository/DataAccess/Read/MongoDbReader.Main.cs b/MongoDbGenericRepository/DataAccess/Read/MongoDbReader.Main.cs index 1282efd..b07dc0f 100644 --- a/MongoDbGenericRepository/DataAccess/Read/MongoDbReader.Main.cs +++ b/MongoDbGenericRepository/DataAccess/Read/MongoDbReader.Main.cs @@ -328,6 +328,7 @@ namespace MongoDbGenericRepository.DataAccess.Read /// Sums the values of a selected field for a given filtered collection of documents. /// /// The type representing a Document. + /// The type of the primary key for a Document. /// A LINQ expression filter. /// The field you want to sum. /// The partition key of your document, if any. @@ -344,6 +345,7 @@ namespace MongoDbGenericRepository.DataAccess.Read /// Sums the values of a selected field for a given filtered collection of documents. /// /// The type representing a Document. + /// The type of the primary key for a Document. /// A LINQ expression filter. /// The field you want to sum. /// The partition key of your document, if any. @@ -360,6 +362,7 @@ namespace MongoDbGenericRepository.DataAccess.Read /// Sums the values of a selected field for a given filtered collection of documents. /// /// The type representing a Document. + /// The type of the primary key for a Document. /// A LINQ expression filter. /// The field you want to sum. /// The partition key of your document, if any. @@ -376,6 +379,7 @@ namespace MongoDbGenericRepository.DataAccess.Read /// Sums the values of a selected field for a given filtered collection of documents. /// /// The type representing a Document. + /// The type of the primary key for a Document. /// A LINQ expression filter. /// The field you want to sum. /// The partition key of your document, if any. @@ -398,6 +402,7 @@ namespace MongoDbGenericRepository.DataAccess.Read /// and returns a dictionary of listed document groups with keys having the different values of the grouping criteria. /// /// The type representing a Document. + /// The type of the primary key for a Document. /// The type of the grouping criteria. /// The type of the projected group. /// The grouping criteria. @@ -423,6 +428,7 @@ namespace MongoDbGenericRepository.DataAccess.Read /// and returns a dictionary of listed document groups with keys having the different values of the grouping criteria. /// /// The type representing a Document. + /// The type of the primary key for a Document. /// The type of the grouping criteria. /// The type of the projected group. /// A LINQ expression filter. @@ -444,5 +450,65 @@ namespace MongoDbGenericRepository.DataAccess.Read .Group(selector, projection) .ToList(); } + + /// + /// Asynchronously returns a paginated list of the documents matching the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// The property selector. + /// Order of the sorting. + /// The number of documents you want to skip. Default value is 0. + /// The number of documents you want to take. Default value is 50. + /// An optional partition key. + public virtual async Task> GetSortedPaginatedAsync( + Expression> filter, + Expression> sortSelector, + bool ascending = true, + int skipNumber = 0, + int takeNumber = 50, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + var sorting = ascending + ? Builders.Sort.Ascending(sortSelector) + : Builders.Sort.Descending(sortSelector); + + return await HandlePartitioned(partitionKey) + .Find(filter) + .Sort(sorting) + .Skip(skipNumber) + .Limit(takeNumber) + .ToListAsync(); + } + + /// + /// Asynchronously returns a paginated list of the documents matching the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// The sort definition. + /// The number of documents you want to skip. Default value is 0. + /// The number of documents you want to take. Default value is 50. + /// An optional partition key. + public virtual async Task> GetSortedPaginatedAsync( + Expression> filter, + SortDefinition sortDefinition, + int skipNumber = 0, + int takeNumber = 50, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await HandlePartitioned(partitionKey) + .Find(filter) + .Sort(sortDefinition) + .Skip(skipNumber) + .Limit(takeNumber) + .ToListAsync(); + } } } diff --git a/MongoDbGenericRepository/KeyTypedRepository/BaseMongoRepository.TKey.ReadOnly.cs b/MongoDbGenericRepository/KeyTypedRepository/BaseMongoRepository.TKey.ReadOnly.cs index a103005..e4e373e 100644 --- a/MongoDbGenericRepository/KeyTypedRepository/BaseMongoRepository.TKey.ReadOnly.cs +++ b/MongoDbGenericRepository/KeyTypedRepository/BaseMongoRepository.TKey.ReadOnly.cs @@ -485,5 +485,47 @@ namespace MongoDbGenericRepository { return MongoDbReader.GroupBy(filter, groupingCriteria, groupProjection, partitionKey); } + + /// + /// Asynchronously returns a paginated list of the documents matching the filter condition. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// The property selector. + /// Order of the sorting. + /// The number of documents you want to skip. Default value is 0. + /// The number of documents you want to take. Default value is 50. + /// An optional partition key. + public virtual async Task> GetSortedPaginatedAsync( + Expression> filter, + Expression> sortSelector, + bool ascending = true, + int skipNumber = 0, + int takeNumber = 50, + string partitionKey = null) + where TDocument : IDocument + { + return await MongoDbReader.GetSortedPaginatedAsync(filter, sortSelector, ascending, skipNumber, takeNumber, partitionKey); + } + + /// + /// Asynchronously returns a paginated list of the documents matching the filter condition. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// The sort definition. + /// The number of documents you want to skip. Default value is 0. + /// The number of documents you want to take. Default value is 50. + /// An optional partition key. + public virtual async Task> GetSortedPaginatedAsync( + Expression> filter, + SortDefinition sortDefinition, + int skipNumber = 0, + int takeNumber = 50, + string partitionKey = null) + where TDocument : IDocument + { + return await MongoDbReader.GetSortedPaginatedAsync(filter, sortDefinition, skipNumber, takeNumber, partitionKey); + } } } \ No newline at end of file diff --git a/MongoDbGenericRepository/ReadOnlyMongoRepository.cs b/MongoDbGenericRepository/ReadOnlyMongoRepository.cs index c8bcb99..2a3f9ba 100644 --- a/MongoDbGenericRepository/ReadOnlyMongoRepository.cs +++ b/MongoDbGenericRepository/ReadOnlyMongoRepository.cs @@ -510,6 +510,73 @@ namespace MongoDbGenericRepository #endregion Group By TKey + + #region Pagination + + /// + /// Asynchronously returns a paginated list of the documents matching the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// The property selector. + /// Order of the sorting. + /// The number of documents you want to skip. Default value is 0. + /// The number of documents you want to take. Default value is 50. + /// An optional partition key. + public virtual async Task> GetSortedPaginatedAsync( + Expression> filter, + Expression> sortSelector, + bool ascending = true, + int skipNumber = 0, + int takeNumber = 50, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + var sorting = ascending + ? Builders.Sort.Ascending(sortSelector) + : Builders.Sort.Descending(sortSelector); + + return await HandlePartitioned(partitionKey) + .Find(filter) + .Sort(sorting) + .Skip(skipNumber) + .Limit(takeNumber) + .ToListAsync(); + } + + /// + /// Asynchronously returns a paginated list of the documents matching the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// The sort definition. + /// The number of documents you want to skip. Default value is 0. + /// The number of documents you want to take. Default value is 50. + /// An optional partition key. + public virtual async Task> GetSortedPaginatedAsync( + Expression> filter, + SortDefinition sortDefinition, + int skipNumber = 0, + int takeNumber = 50, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await HandlePartitioned(partitionKey) + .Find(filter) + .Sort(sortDefinition) + .Skip(skipNumber) + .Limit(takeNumber) + .ToListAsync(); + } + + #endregion Pagination + + + /// /// Gets a collections for a potentially partitioned document type. ///