From eaa90d67f5d06313869adf9588e06c9c805a6d65 Mon Sep 17 00:00:00 2001 From: Alexandre SPIESER Date: Wed, 2 Jan 2019 23:43:37 +0000 Subject: [PATCH 1/2] sum for decimal fields, untested. --- .../BaseMongoDbRepository.Maths.cs | 37 +++++++++++++++++++ .../BaseMongoDbRepository.cs | 32 ++++++++-------- 2 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 MongoDbGenericRepository/BaseMongoDbRepository.Maths.cs diff --git a/MongoDbGenericRepository/BaseMongoDbRepository.Maths.cs b/MongoDbGenericRepository/BaseMongoDbRepository.Maths.cs new file mode 100644 index 0000000..bbc6552 --- /dev/null +++ b/MongoDbGenericRepository/BaseMongoDbRepository.Maths.cs @@ -0,0 +1,37 @@ +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using MongoDbGenericRepository.Models; +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace MongoDbGenericRepository +{ + /// + /// The base Repository, it is meant to be inherited from by your custom custom MongoRepository implementation. + /// Its constructor must be given a connection string and a database name. + /// + public abstract partial class BaseMongoRepository : ReadOnlyMongoRepository, IBaseMongoRepository + { + /// + /// Sums the values of a selected field for a given filtered collection of documents. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// The field you want to sum. + /// The partition key of your document, if any. + public virtual async Task SumByAsync(Expression> filter, + Expression> selector, + string partitionKey = null) + where TDocument : IDocument + { + var collection = string.IsNullOrEmpty(partitionKey) ? GetCollection() : GetCollection(partitionKey); + + return await collection.AsQueryable() + .Where(filter) + .SumAsync(selector); + } + + } +} \ No newline at end of file diff --git a/MongoDbGenericRepository/BaseMongoDbRepository.cs b/MongoDbGenericRepository/BaseMongoDbRepository.cs index 7a3a76e..2a3f07e 100644 --- a/MongoDbGenericRepository/BaseMongoDbRepository.cs +++ b/MongoDbGenericRepository/BaseMongoDbRepository.cs @@ -822,7 +822,7 @@ namespace MongoDbGenericRepository /// /// The type representing a Document. /// The type representing the model you want to project to. - /// + /// A LINQ expression filter. /// The projection expression. /// An optional partition key. public virtual async Task ProjectOneAsync(Expression> filter, Expression> projection, string partitionKey = null) @@ -840,7 +840,7 @@ namespace MongoDbGenericRepository /// The type representing a Document. /// The type of the primary key for a Document. /// The type representing the model you want to project to. - /// + /// A LINQ expression filter. /// The projection expression. /// An optional partition key. public virtual async Task ProjectOneAsync(Expression> filter, Expression> projection, string partitionKey = null) @@ -849,8 +849,8 @@ namespace MongoDbGenericRepository where TProjection : class { return await HandlePartitioned(partitionKey).Find(filter) - .Project(projection) - .FirstOrDefaultAsync(); + .Project(projection) + .FirstOrDefaultAsync(); } /// @@ -858,7 +858,7 @@ namespace MongoDbGenericRepository /// /// The type representing a Document. /// The type representing the model you want to project to. - /// + /// A LINQ expression filter. /// The projection expression. /// An optional partition key. public virtual TProjection ProjectOne(Expression> filter, Expression> projection, string partitionKey = null) @@ -876,7 +876,7 @@ namespace MongoDbGenericRepository /// The type representing a Document. /// The type of the primary key for a Document. /// The type representing the model you want to project to. - /// + /// A LINQ expression filter. /// The projection expression. /// An optional partition key. public virtual TProjection ProjectOne(Expression> filter, Expression> projection, string partitionKey = null) @@ -885,8 +885,8 @@ namespace MongoDbGenericRepository where TProjection : class { return HandlePartitioned(partitionKey).Find(filter) - .Project(projection) - .FirstOrDefault(); + .Project(projection) + .FirstOrDefault(); } /// @@ -894,7 +894,7 @@ namespace MongoDbGenericRepository /// /// The type representing a Document. /// The type representing the model you want to project to. - /// + /// A LINQ expression filter. /// The projection expression. /// An optional partition key. public virtual async Task> ProjectManyAsync(Expression> filter, Expression> projection, string partitionKey = null) @@ -912,7 +912,7 @@ namespace MongoDbGenericRepository /// The type representing a Document. /// The type of the primary key for a Document. /// The type representing the model you want to project to. - /// + /// A LINQ expression filter. /// The projection expression. /// An optional partition key. public virtual async Task> ProjectManyAsync(Expression> filter, Expression> projection, string partitionKey = null) @@ -930,7 +930,7 @@ namespace MongoDbGenericRepository /// /// The type representing a Document. /// The type representing the model you want to project to. - /// + /// A LINQ expression filter. /// The projection expression. /// An optional partition key. public virtual List ProjectMany(Expression> filter, Expression> projection, string partitionKey = null) @@ -996,7 +996,7 @@ namespace MongoDbGenericRepository /// The type representing a Document. /// The type of the grouping criteria. /// The type of the projected group. - /// + /// A LINQ expression filter. /// The grouping criteria. /// The projected group result. /// The partition key of your document, if any. @@ -1021,7 +1021,7 @@ namespace MongoDbGenericRepository /// Asynchronously returns a paginated list of the documents matching the filter condition. /// /// The type representing a Document. - /// + /// A LINQ expression filter. /// 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. @@ -1036,7 +1036,7 @@ namespace MongoDbGenericRepository /// /// The type representing a Document. /// The type of the primary key for a Document. - /// + /// A LINQ expression filter. /// 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. @@ -1053,7 +1053,7 @@ namespace MongoDbGenericRepository /// GetAndUpdateOne with filter /// /// The type representing a Document. - /// + /// A LINQ expression filter. /// /// /// @@ -1067,7 +1067,7 @@ namespace MongoDbGenericRepository /// /// The type representing a Document. /// The type of the primary key for a Document. - /// + /// A LINQ expression filter. /// /// /// From 4ac0ffe91c4dd1a16c50f44bb34128c0bb69747d Mon Sep 17 00:00:00 2001 From: Alexandre SPIESER Date: Mon, 4 Feb 2019 23:11:36 +0000 Subject: [PATCH 2/2] added tests for the sumbyasync methods --- .../CoreIntegrationTests.csproj | 5 ++- .../Infrastructure/MongoDbDocumentTestBase.cs | 27 +++++++++++++ .../MongoDbTKeyDocumentTestBase.cs | 27 +++++++++++++ .../Infrastructure/TestClasses.cs | 5 ++- .../Abstractions/IReadOnlyMongoRepository.cs | 28 ++++++++++++++ .../BaseMongoDbRepository.cs | 38 ++++++++++++++++++- ...hs.cs => ReadOnlyMongoRepository.Maths.cs} | 25 +++++++++--- .../ReadOnlyMongoRepository.cs | 22 +++++------ 8 files changed, 158 insertions(+), 19 deletions(-) rename MongoDbGenericRepository/{BaseMongoDbRepository.Maths.cs => ReadOnlyMongoRepository.Maths.cs} (50%) diff --git a/CoreIntegrationTests/CoreIntegrationTests.csproj b/CoreIntegrationTests/CoreIntegrationTests.csproj index a3ba78f..720179b 100644 --- a/CoreIntegrationTests/CoreIntegrationTests.csproj +++ b/CoreIntegrationTests/CoreIntegrationTests.csproj @@ -9,13 +9,16 @@ - + + + + ..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\System.Configuration.dll diff --git a/CoreIntegrationTests/Infrastructure/MongoDbDocumentTestBase.cs b/CoreIntegrationTests/Infrastructure/MongoDbDocumentTestBase.cs index 8c6fc2e..c70056c 100644 --- a/CoreIntegrationTests/Infrastructure/MongoDbDocumentTestBase.cs +++ b/CoreIntegrationTests/Infrastructure/MongoDbDocumentTestBase.cs @@ -1075,6 +1075,33 @@ namespace CoreIntegrationTests.Infrastructure #endregion Index Management + #region Math + + [Fact] + public async Task SumByAsync() + { + // Arrange + var criteria = $"{GetTestName()}.{DocumentTypeName}.{Guid.NewGuid()}"; + var documents = CreateTestDocuments(5); + var i = 1; + documents.ForEach(e => + { + e.Nested.SomeDate = e.Nested.SomeDate.AddDays(i++); + e.Nested.SomeAmount = 5m; + e.SomeContent = criteria; + }); + SUT.AddMany(documents); + var expectedSum = documents.Sum(e => e.Nested.SomeAmount); + + // Act + var result = await SUT.SumByAsync(e => e.SomeContent == criteria, e => e.Nested.SomeAmount, PartitionKey); + + // Assert + Assert.Equal(expectedSum, result); + } + + #endregion Math + #region Test Utils [MethodImpl(MethodImplOptions.NoInlining)] diff --git a/CoreIntegrationTests/Infrastructure/MongoDbTKeyDocumentTestBase.cs b/CoreIntegrationTests/Infrastructure/MongoDbTKeyDocumentTestBase.cs index 98376f5..9d99665 100644 --- a/CoreIntegrationTests/Infrastructure/MongoDbTKeyDocumentTestBase.cs +++ b/CoreIntegrationTests/Infrastructure/MongoDbTKeyDocumentTestBase.cs @@ -1068,6 +1068,33 @@ namespace CoreIntegrationTests.Infrastructure #endregion Index Management + #region Math + + [Fact] + public async Task SumByAsync() + { + // Arrange + var criteria = $"{GetTestName()}.{DocumentTypeName}.{Guid.NewGuid()}"; + var documents = CreateTestDocuments(5); + var i = 1; + documents.ForEach(e => + { + e.Nested.SomeDate = e.Nested.SomeDate.AddDays(i++); + e.Nested.SomeAmount = 5m; + e.SomeContent = criteria; + }); + SUT.AddMany(documents); + var expectedSum = documents.Sum(e => e.Nested.SomeAmount); + + // Act + var result = await SUT.SumByAsync(e => e.SomeContent == criteria, e => e.Nested.SomeAmount, PartitionKey); + + // Assert + Assert.Equal(expectedSum, result); + } + + #endregion Math + #region Test Utils [MethodImpl(MethodImplOptions.NoInlining)] private string GetCurrentMethod() diff --git a/CoreIntegrationTests/Infrastructure/TestClasses.cs b/CoreIntegrationTests/Infrastructure/TestClasses.cs index 20b7fc2..c9ad2b9 100644 --- a/CoreIntegrationTests/Infrastructure/TestClasses.cs +++ b/CoreIntegrationTests/Infrastructure/TestClasses.cs @@ -1,4 +1,5 @@ -using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; using MongoDbGenericRepository.Models; using MongoDbGenericRepository.Utils; using System; @@ -21,6 +22,8 @@ namespace CoreIntegrationTests.Infrastructure public class Nested { public DateTime SomeDate { get; set; } + [BsonRepresentation(BsonType.Decimal128)] + public decimal SomeAmount { get; set; } } public class Child diff --git a/MongoDbGenericRepository/Abstractions/IReadOnlyMongoRepository.cs b/MongoDbGenericRepository/Abstractions/IReadOnlyMongoRepository.cs index 2632d66..a79d3e9 100644 --- a/MongoDbGenericRepository/Abstractions/IReadOnlyMongoRepository.cs +++ b/MongoDbGenericRepository/Abstractions/IReadOnlyMongoRepository.cs @@ -425,6 +425,34 @@ namespace MongoDbGenericRepository where TKey : IEquatable; #endregion + + #region Sum + + /// + /// Sums the values of a selected field for a given filtered collection of documents. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// The field you want to sum. + /// The partition key of your document, if any. + Task SumByAsync(Expression> filter, + Expression> selector, + string partitionKey = null) + where TDocument : IDocument; + + /// + /// Sums the values of a selected field for a given filtered collection of documents. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// The field you want to sum. + /// The partition key of your document, if any. + Task SumByAsync(Expression> filter, + Expression> selector, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable; + #endregion Sum } } diff --git a/MongoDbGenericRepository/BaseMongoDbRepository.cs b/MongoDbGenericRepository/BaseMongoDbRepository.cs index 2a3f07e..f8c9c75 100644 --- a/MongoDbGenericRepository/BaseMongoDbRepository.cs +++ b/MongoDbGenericRepository/BaseMongoDbRepository.cs @@ -1022,10 +1022,46 @@ namespace MongoDbGenericRepository /// /// 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> GetPaginatedAsync(Expression> filter, int skipNumber = 0, int takeNumber = 50, string partitionKey = null) + 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. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// 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> GetPaginatedAsync( + Expression> filter, + int skipNumber = 0, + int takeNumber = 50, + string partitionKey = null) where TDocument : IDocument { return await HandlePartitioned(partitionKey).Find(filter).Skip(skipNumber).Limit(takeNumber).ToListAsync(); diff --git a/MongoDbGenericRepository/BaseMongoDbRepository.Maths.cs b/MongoDbGenericRepository/ReadOnlyMongoRepository.Maths.cs similarity index 50% rename from MongoDbGenericRepository/BaseMongoDbRepository.Maths.cs rename to MongoDbGenericRepository/ReadOnlyMongoRepository.Maths.cs index bbc6552..b75fe11 100644 --- a/MongoDbGenericRepository/BaseMongoDbRepository.Maths.cs +++ b/MongoDbGenericRepository/ReadOnlyMongoRepository.Maths.cs @@ -12,7 +12,7 @@ namespace MongoDbGenericRepository /// The base Repository, it is meant to be inherited from by your custom custom MongoRepository implementation. /// Its constructor must be given a connection string and a database name. /// - public abstract partial class BaseMongoRepository : ReadOnlyMongoRepository, IBaseMongoRepository + public abstract partial class ReadOnlyMongoRepository { /// /// Sums the values of a selected field for a given filtered collection of documents. @@ -26,11 +26,26 @@ namespace MongoDbGenericRepository string partitionKey = null) where TDocument : IDocument { - var collection = string.IsNullOrEmpty(partitionKey) ? GetCollection() : GetCollection(partitionKey); + return await SumByAsync(filter, selector, partitionKey); + } - return await collection.AsQueryable() - .Where(filter) - .SumAsync(selector); + /// + /// Sums the values of a selected field for a given filtered collection of documents. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// The field you want to sum. + /// The partition key of your document, if any. + public virtual async Task SumByAsync(Expression> filter, + Expression> selector, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await GetCollection(partitionKey) + .AsQueryable() + .Where(filter) + .SumAsync(selector); } } diff --git a/MongoDbGenericRepository/ReadOnlyMongoRepository.cs b/MongoDbGenericRepository/ReadOnlyMongoRepository.cs index 0a8adb9..7638cdb 100644 --- a/MongoDbGenericRepository/ReadOnlyMongoRepository.cs +++ b/MongoDbGenericRepository/ReadOnlyMongoRepository.cs @@ -11,7 +11,7 @@ namespace MongoDbGenericRepository /// /// The ReadOnlyMongoRepository implements the readonly functionality of the IReadOnlyMongoRepository. /// - public class ReadOnlyMongoRepository : IReadOnlyMongoRepository + public abstract partial class ReadOnlyMongoRepository : IReadOnlyMongoRepository { /// /// The connection string. @@ -578,7 +578,7 @@ namespace MongoDbGenericRepository /// A LINQ expression filter. /// A property selector to order by ascending. /// An optional partition key. - public async Task GetMinValueAsync(Expression> filter, Expression> minValueSelector, string partitionKey = null) + public virtual async Task GetMinValueAsync(Expression> filter, Expression> minValueSelector, string partitionKey = null) where TDocument : IDocument { return await GetMinValueAsync(filter, minValueSelector, partitionKey); @@ -593,7 +593,7 @@ namespace MongoDbGenericRepository /// A LINQ expression filter. /// A property selector to order by ascending. /// An optional partition key. - public async Task GetMinValueAsync(Expression> filter, Expression> minValueSelector, string partitionKey = null) + public virtual async Task GetMinValueAsync(Expression> filter, Expression> minValueSelector, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { @@ -608,7 +608,7 @@ namespace MongoDbGenericRepository /// A LINQ expression filter. /// A property selector to order by ascending. /// An optional partition key. - public TValue GetMinValue(Expression> filter, Expression> minValueSelector, string partitionKey = null) + public virtual TValue GetMinValue(Expression> filter, Expression> minValueSelector, string partitionKey = null) where TDocument : IDocument { return GetMinValue(filter, minValueSelector, partitionKey); @@ -623,7 +623,7 @@ namespace MongoDbGenericRepository /// A LINQ expression filter. /// A property selector to order by ascending. /// An optional partition key. - public TValue GetMinValue(Expression> filter, Expression> minValueSelector, string partitionKey = null) + public virtual TValue GetMinValue(Expression> filter, Expression> minValueSelector, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { @@ -640,7 +640,7 @@ namespace MongoDbGenericRepository /// The document type. /// An optional partition key. /// An - protected IMongoCollection GetCollection(string partitionKey = null) where TDocument : IDocument + protected virtual IMongoCollection GetCollection(string partitionKey = null) where TDocument : IDocument { return MongoDbContext.GetCollection(partitionKey); } @@ -651,7 +651,7 @@ namespace MongoDbGenericRepository /// The document type. /// The document. /// - protected IMongoCollection HandlePartitioned(TDocument document) where TDocument : IDocument + protected virtual IMongoCollection HandlePartitioned(TDocument document) where TDocument : IDocument { if (document is IPartitionedDocument) { @@ -667,7 +667,7 @@ namespace MongoDbGenericRepository /// The type of the primary key. /// The document. /// - protected IMongoCollection HandlePartitioned(TDocument document) + protected virtual IMongoCollection HandlePartitioned(TDocument document) where TDocument : IDocument where TKey : IEquatable { @@ -684,7 +684,7 @@ namespace MongoDbGenericRepository /// The document type. /// The collection partition key. /// - protected IMongoCollection HandlePartitioned(string partitionKey) where TDocument : IDocument + protected virtual IMongoCollection HandlePartitioned(string partitionKey) where TDocument : IDocument { if (!string.IsNullOrEmpty(partitionKey)) { @@ -700,7 +700,7 @@ namespace MongoDbGenericRepository /// The type of the primary key. /// The collection partition key. /// - protected IMongoCollection GetCollection(string partitionKey = null) + protected virtual IMongoCollection GetCollection(string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { @@ -714,7 +714,7 @@ namespace MongoDbGenericRepository /// The type of the primary key. /// The collection partition key. /// - protected IMongoCollection HandlePartitioned(string partitionKey) + protected virtual IMongoCollection HandlePartitioned(string partitionKey) where TDocument : IDocument where TKey : IEquatable {