clean up + full support for bulk insertion and deletion of documents of the same type but with different partitionkey values

This commit is contained in:
alexandre-spieser
2018-09-09 12:14:43 +01:00
parent 7f126a098d
commit 72ea6e7999
3 changed files with 234 additions and 33 deletions
@@ -106,6 +106,32 @@ namespace CoreIntegrationTests.Infrastructure
Assert.True (2 == count, GetTestName()); Assert.True (2 == count, GetTestName());
} }
[Fact]
public void AddManyWithDifferentPartitionKey()
{
// only run this test for tests on documents with partition key
if (!string.IsNullOrEmpty(PartitionKey))
{
// Arrange
var documents = new List<T> { new T(), new T(), new T(), new T() };
if(documents.Any(e => e is IPartitionedDocument))
{
var secondPartitionKey = $"{PartitionKey}-2";
((IPartitionedDocument)documents[2]).PartitionKey = secondPartitionKey;
((IPartitionedDocument)documents[3]).PartitionKey = secondPartitionKey;
// Act
SUT.AddMany<T>(documents);
// Assert
long count = SUT.Count<T>(e => e.Id.Equals(documents[0].Id) || e.Id.Equals(documents[1].Id), PartitionKey);
long secondPartitionCount = SUT.Count<T>(e => e.Id.Equals(documents[2].Id) || e.Id.Equals(documents[3].Id), secondPartitionKey);
// Cleanup second partition
SUT.DeleteMany<T>(e => e.Id.Equals(documents[2].Id) || e.Id.Equals(documents[3].Id), secondPartitionKey);
Assert.True(2 == count, GetTestName());
Assert.True(2 == secondPartitionCount, GetTestName());
}
}
}
[Fact] [Fact]
public async Task AddManyAsync() public async Task AddManyAsync()
{ {
@@ -121,6 +147,31 @@ namespace CoreIntegrationTests.Infrastructure
Assert.True (2 == count, GetTestName()); Assert.True (2 == count, GetTestName());
} }
[Fact]
public async Task AddManyAsyncWithDifferentPartitionKey()
{
// only run this test for tests on documents with partition key
if (!string.IsNullOrEmpty(PartitionKey))
{
// Arrange
var documents = new List<T> { new T(), new T(), new T(), new T() };
if (documents.Any(e => e is IPartitionedDocument))
{
var secondPartitionKey = $"{PartitionKey}-2";
((IPartitionedDocument)documents[2]).PartitionKey = secondPartitionKey;
((IPartitionedDocument)documents[3]).PartitionKey = secondPartitionKey;
// Act
await SUT.AddManyAsync<T>(documents);
// Assert
long count = SUT.Count<T>(e => e.Id.Equals(documents[0].Id) || e.Id.Equals(documents[1].Id), PartitionKey);
long secondPartitionCount = SUT.Count<T>(e => e.Id.Equals(documents[2].Id) || e.Id.Equals(documents[3].Id), secondPartitionKey);
// Cleanup second partition
SUT.DeleteMany<T>(e => e.Id.Equals(documents[2].Id) || e.Id.Equals(documents[3].Id), secondPartitionKey);
Assert.True(2 == count, GetTestName());
Assert.True(2 == secondPartitionCount, GetTestName());
}
}
}
#endregion Add #endregion Add
@@ -523,12 +574,25 @@ namespace CoreIntegrationTests.Infrastructure
var criteria = $"{GetTestName()}.{DocumentTypeName}"; var criteria = $"{GetTestName()}.{DocumentTypeName}";
var documents = CreateTestDocuments(5); var documents = CreateTestDocuments(5);
documents.ForEach(e => e.SomeContent = criteria); documents.ForEach(e => e.SomeContent = criteria);
var canPartition = !string.IsNullOrEmpty(PartitionKey) && documents.Any(e => e is IPartitionedDocument);
string secondKey = null;
if (canPartition)
{
secondKey = $"{PartitionKey}-2";
((IPartitionedDocument)documents[3]).PartitionKey = secondKey;
((IPartitionedDocument)documents[4]).PartitionKey = secondKey;
}
SUT.AddMany<T>(documents); SUT.AddMany<T>(documents);
// Act // Act
var result = await SUT.DeleteManyAsync<T>(documents); var result = await SUT.DeleteManyAsync<T>(documents);
// Assert // Assert
Assert.True(5 == result); Assert.True(5 == result);
Assert.False(SUT.Any<T>(e => e.SomeContent == criteria, PartitionKey), GetTestName()); Assert.False(SUT.Any<T>(e => e.SomeContent == criteria, PartitionKey), GetTestName());
if (canPartition)
{
Assert.False(SUT.Any<T>(e => e.SomeContent == criteria, secondKey), GetTestName());
}
} }
[Fact] [Fact]
@@ -553,12 +617,25 @@ namespace CoreIntegrationTests.Infrastructure
var criteria = $"{GetTestName()}.{DocumentTypeName}"; var criteria = $"{GetTestName()}.{DocumentTypeName}";
var documents = CreateTestDocuments(5); var documents = CreateTestDocuments(5);
documents.ForEach(e => e.SomeContent = criteria); documents.ForEach(e => e.SomeContent = criteria);
var canPartition = !string.IsNullOrEmpty(PartitionKey) && documents.Any(e => e is IPartitionedDocument);
string secondKey = null;
if (canPartition)
{
secondKey = $"{PartitionKey}-2";
((IPartitionedDocument)documents[3]).PartitionKey = secondKey;
((IPartitionedDocument)documents[4]).PartitionKey = secondKey;
}
SUT.AddMany<T>(documents); SUT.AddMany<T>(documents);
// Act // Act
var result = SUT.DeleteMany<T>(documents); var result = SUT.DeleteMany<T>(documents);
// Assert // Assert
Assert.True(5 == result); Assert.True(5 == result);
Assert.False(SUT.Any<T>(e => e.SomeContent == criteria, PartitionKey), GetTestName()); Assert.False(SUT.Any<T>(e => e.SomeContent == criteria, PartitionKey), GetTestName());
if (canPartition)
{
Assert.False(SUT.Any<T>(e => e.SomeContent == criteria, secondKey), GetTestName());
}
} }
#endregion Delete #endregion Delete
@@ -107,6 +107,32 @@ namespace CoreIntegrationTests.Infrastructure
Assert.True (2 == count, GetTestName()); Assert.True (2 == count, GetTestName());
} }
[Fact]
public void AddManyWithDifferentPartitionKey()
{
// only run this test for tests on documents with partition key
if (!string.IsNullOrEmpty(PartitionKey))
{
// Arrange
var documents = new List<T> { new T(), new T(), new T(), new T() };
if (documents.Any(e => e is IPartitionedDocument))
{
var secondPartitionKey = $"{PartitionKey}-2";
((IPartitionedDocument)documents[2]).PartitionKey = secondPartitionKey;
((IPartitionedDocument)documents[3]).PartitionKey = secondPartitionKey;
// Act
SUT.AddMany<T, TKey>(documents);
// Assert
long count = SUT.Count<T, TKey>(e => e.Id.Equals(documents[0].Id) || e.Id.Equals(documents[1].Id), PartitionKey);
long secondPartitionCount = SUT.Count<T, TKey>(e => e.Id.Equals(documents[2].Id) || e.Id.Equals(documents[3].Id), secondPartitionKey);
// Cleanup second partition
SUT.DeleteMany<T, TKey>(e => e.Id.Equals(documents[2].Id) || e.Id.Equals(documents[3].Id), secondPartitionKey);
Assert.True(2 == count, GetTestName());
Assert.True(2 == secondPartitionCount, GetTestName());
}
}
}
[Fact] [Fact]
public async Task AddManyAsync() public async Task AddManyAsync()
{ {
@@ -122,6 +148,31 @@ namespace CoreIntegrationTests.Infrastructure
Assert.True (2 == count, GetTestName()); Assert.True (2 == count, GetTestName());
} }
[Fact]
public async Task AddManyAsyncWithDifferentPartitionKey()
{
// only run this test for tests on documents with partition key
if (!string.IsNullOrEmpty(PartitionKey))
{
// Arrange
var documents = new List<T> { new T(), new T(), new T(), new T() };
if (documents.Any(e => e is IPartitionedDocument))
{
var secondPartitionKey = $"{PartitionKey}-2";
((IPartitionedDocument)documents[2]).PartitionKey = secondPartitionKey;
((IPartitionedDocument)documents[3]).PartitionKey = secondPartitionKey;
// Act
await SUT.AddManyAsync<T, TKey>(documents);
// Assert
long count = SUT.Count<T, TKey>(e => e.Id.Equals(documents[0].Id) || e.Id.Equals(documents[1].Id), PartitionKey);
long secondPartitionCount = SUT.Count<T, TKey>(e => e.Id.Equals(documents[2].Id) || e.Id.Equals(documents[3].Id), secondPartitionKey);
// Cleanup second partition
SUT.DeleteMany<T, TKey>(e => e.Id.Equals(documents[2].Id) || e.Id.Equals(documents[3].Id), secondPartitionKey);
Assert.True(2 == count, GetTestName());
Assert.True(2 == secondPartitionCount, GetTestName());
}
}
}
#endregion Add #endregion Add
@@ -524,12 +575,25 @@ namespace CoreIntegrationTests.Infrastructure
var criteria = $"{GetTestName()}.{DocumentTypeName}"; var criteria = $"{GetTestName()}.{DocumentTypeName}";
var documents = CreateTestDocuments(5); var documents = CreateTestDocuments(5);
documents.ForEach(e => e.SomeContent = criteria); documents.ForEach(e => e.SomeContent = criteria);
var canPartition = !string.IsNullOrEmpty(PartitionKey) && documents.Any(e => e is IPartitionedDocument);
string secondKey = null;
if (canPartition)
{
secondKey = $"{PartitionKey}-2";
((IPartitionedDocument)documents[3]).PartitionKey = secondKey;
((IPartitionedDocument)documents[4]).PartitionKey = secondKey;
}
SUT.AddMany<T, TKey>(documents); SUT.AddMany<T, TKey>(documents);
// Act // Act
var result = await SUT.DeleteManyAsync<T, TKey>(documents); var result = await SUT.DeleteManyAsync<T, TKey>(documents);
// Assert // Assert
Assert.True(5 == result); Assert.True(5 == result);
Assert.False(SUT.Any<T, TKey>(e => e.SomeContent == criteria, PartitionKey), GetTestName()); Assert.False(SUT.Any<T, TKey>(e => e.SomeContent == criteria, PartitionKey), GetTestName());
if (canPartition)
{
Assert.False(SUT.Any<T, TKey>(e => e.SomeContent == criteria, secondKey), GetTestName());
}
} }
[Fact] [Fact]
@@ -554,12 +618,25 @@ namespace CoreIntegrationTests.Infrastructure
var criteria = $"{GetTestName()}.{DocumentTypeName}"; var criteria = $"{GetTestName()}.{DocumentTypeName}";
var documents = CreateTestDocuments(5); var documents = CreateTestDocuments(5);
documents.ForEach(e => e.SomeContent = criteria); documents.ForEach(e => e.SomeContent = criteria);
var canPartition = !string.IsNullOrEmpty(PartitionKey) && documents.Any(e => e is IPartitionedDocument);
string secondKey = null;
if (canPartition)
{
secondKey = $"{PartitionKey}-2";
((IPartitionedDocument)documents[3]).PartitionKey = secondKey;
((IPartitionedDocument)documents[4]).PartitionKey = secondKey;
}
SUT.AddMany<T, TKey>(documents); SUT.AddMany<T, TKey>(documents);
// Act // Act
var result = SUT.DeleteMany<T, TKey>(documents); var result = SUT.DeleteMany<T, TKey>(documents);
// Assert // Assert
Assert.True(5 == result); Assert.True(5 == result);
Assert.False(SUT.Any<T, TKey>(e => e.SomeContent == criteria, PartitionKey), GetTestName()); Assert.False(SUT.Any<T, TKey>(e => e.SomeContent == criteria, PartitionKey), GetTestName());
if (canPartition)
{
Assert.False(SUT.Any<T, TKey>(e => e.SomeContent == criteria, secondKey), GetTestName());
}
} }
#endregion Delete #endregion Delete
@@ -82,11 +82,22 @@ namespace MongoDbGenericRepository
{ {
return; return;
} }
foreach (var doc in documents) foreach (var document in documents)
{ {
FormatDocument(doc); FormatDocument(document);
}
// cannot use typeof(IPartitionedDocument).IsAssignableFrom(typeof(TDocument)), not available in netstandard 1.5
if (documents.Any(e => e is IPartitionedDocument))
{
foreach (var group in documents.GroupBy(e => ((IPartitionedDocument)e).PartitionKey))
{
await HandlePartitioned(group.FirstOrDefault()).InsertManyAsync(group.ToList());
}
}
else
{
await GetCollection<TDocument>().InsertManyAsync(documents.ToList());
} }
await HandlePartitioned(documents.FirstOrDefault()).InsertManyAsync(documents);
} }
/// <summary> /// <summary>
@@ -105,7 +116,18 @@ namespace MongoDbGenericRepository
{ {
FormatDocument(document); FormatDocument(document);
} }
HandlePartitioned(documents.FirstOrDefault()).InsertMany(documents.ToList()); // cannot use typeof(IPartitionedDocument).IsAssignableFrom(typeof(TDocument)), not available in netstandard 1.5
if (documents.Any(e => e is IPartitionedDocument))
{
foreach (var group in documents.GroupBy(e => ((IPartitionedDocument)e).PartitionKey))
{
HandlePartitioned(group.FirstOrDefault()).InsertMany(group.ToList());
}
}
else
{
GetCollection<TDocument>().InsertMany(documents.ToList());
}
} }
#endregion Create #endregion Create
@@ -157,11 +179,22 @@ namespace MongoDbGenericRepository
{ {
return; return;
} }
foreach (var doc in documents) foreach (var document in documents)
{ {
FormatDocument<TDocument, TKey>(doc); FormatDocument<TDocument, TKey>(document);
}
// cannot use typeof(IPartitionedDocument).IsAssignableFrom(typeof(TDocument)), not available in netstandard 1.5
if (documents.Any(e => e is IPartitionedDocument))
{
foreach (var group in documents.GroupBy(e => ((IPartitionedDocument)e).PartitionKey))
{
await HandlePartitioned<TDocument, TKey>(group.FirstOrDefault()).InsertManyAsync(group.ToList());
}
}
else
{
await GetCollection<TDocument, TKey>().InsertManyAsync(documents.ToList());
} }
await HandlePartitioned<TDocument, TKey>(documents.FirstOrDefault()).InsertManyAsync(documents);
} }
/// <summary> /// <summary>
@@ -487,9 +520,7 @@ namespace MongoDbGenericRepository
where TDocument : IDocument<TKey> where TDocument : IDocument<TKey>
where TKey : IEquatable<TKey> where TKey : IEquatable<TKey>
{ {
var collection = string.IsNullOrEmpty(partitionKey) ? GetCollection<TDocument, TKey>() : GetCollection<TDocument, TKey>(partitionKey); return await UpdateOneAsync<TDocument, TKey, TField>(Builders<TDocument>.Filter.Where(filter), field, value, partitionKey);
var updateRes = await collection.UpdateOneAsync(Builders<TDocument>.Filter.Where(filter), Builders<TDocument>.Update.Set(field, value));
return updateRes.ModifiedCount == 1;
} }
/// <summary> /// <summary>
@@ -525,9 +556,7 @@ namespace MongoDbGenericRepository
where TDocument : IDocument<TKey> where TDocument : IDocument<TKey>
where TKey : IEquatable<TKey> where TKey : IEquatable<TKey>
{ {
var collection = string.IsNullOrEmpty(partitionKey) ? GetCollection<TDocument, TKey>() : GetCollection<TDocument, TKey>(partitionKey); return UpdateOne<TDocument, TKey, TField>(Builders<TDocument>.Filter.Where(filter), field, value, partitionKey);
var updateRes = collection.UpdateOne(Builders<TDocument>.Filter.Where(filter), Builders<TDocument>.Update.Set(field, value));
return updateRes.ModifiedCount == 1;
} }
#endregion Update #endregion Update
@@ -542,7 +571,7 @@ namespace MongoDbGenericRepository
/// <returns>The number of documents deleted.</returns> /// <returns>The number of documents deleted.</returns>
public virtual async Task<long> DeleteOneAsync<TDocument>(TDocument document) where TDocument : IDocument public virtual async Task<long> DeleteOneAsync<TDocument>(TDocument document) where TDocument : IDocument
{ {
return (await HandlePartitioned(document).DeleteOneAsync(x => x.Id == document.Id)).DeletedCount; return await DeleteOneAsync<TDocument, Guid>(document);
} }
/// <summary> /// <summary>
@@ -553,7 +582,7 @@ namespace MongoDbGenericRepository
/// <returns>The number of documents deleted.</returns> /// <returns>The number of documents deleted.</returns>
public virtual long DeleteOne<TDocument>(TDocument document) where TDocument : IDocument public virtual long DeleteOne<TDocument>(TDocument document) where TDocument : IDocument
{ {
return HandlePartitioned(document).DeleteOne(x => x.Id == document.Id).DeletedCount; return DeleteOne<TDocument, Guid>(document);
} }
/// <summary> /// <summary>
@@ -565,7 +594,7 @@ namespace MongoDbGenericRepository
/// <returns>The number of documents deleted.</returns> /// <returns>The number of documents deleted.</returns>
public virtual long DeleteOne<TDocument>(Expression<Func<TDocument, bool>> filter, string partitionKey = null) where TDocument : IDocument public virtual long DeleteOne<TDocument>(Expression<Func<TDocument, bool>> filter, string partitionKey = null) where TDocument : IDocument
{ {
return HandlePartitioned<TDocument>(partitionKey).DeleteOne(filter).DeletedCount; return DeleteOne<TDocument, Guid>(filter, partitionKey);
} }
/// <summary> /// <summary>
@@ -577,7 +606,7 @@ namespace MongoDbGenericRepository
/// <returns>The number of documents deleted.</returns> /// <returns>The number of documents deleted.</returns>
public virtual async Task<long> DeleteOneAsync<TDocument>(Expression<Func<TDocument, bool>> filter, string partitionKey = null) where TDocument : IDocument public virtual async Task<long> DeleteOneAsync<TDocument>(Expression<Func<TDocument, bool>> filter, string partitionKey = null) where TDocument : IDocument
{ {
return (await HandlePartitioned<TDocument>(partitionKey).DeleteOneAsync(filter)).DeletedCount; return await DeleteOneAsync<TDocument, Guid>(filter, partitionKey);
} }
/// <summary> /// <summary>
@@ -600,12 +629,7 @@ namespace MongoDbGenericRepository
/// <returns>The number of documents deleted.</returns> /// <returns>The number of documents deleted.</returns>
public virtual async Task<long> DeleteManyAsync<TDocument>(IEnumerable<TDocument> documents) where TDocument : IDocument public virtual async Task<long> DeleteManyAsync<TDocument>(IEnumerable<TDocument> documents) where TDocument : IDocument
{ {
if (!documents.Any()) return await DeleteManyAsync<TDocument, Guid>(documents);
{
return 0;
}
var idsTodelete = documents.Select(e => e.Id).ToArray();
return (await HandlePartitioned(documents.FirstOrDefault()).DeleteManyAsync(x => idsTodelete.Contains(x.Id))).DeletedCount;
} }
/// <summary> /// <summary>
@@ -616,12 +640,7 @@ namespace MongoDbGenericRepository
/// <returns>The number of documents deleted.</returns> /// <returns>The number of documents deleted.</returns>
public virtual long DeleteMany<TDocument>(IEnumerable<TDocument> documents) where TDocument : IDocument public virtual long DeleteMany<TDocument>(IEnumerable<TDocument> documents) where TDocument : IDocument
{ {
if (!documents.Any()) return DeleteMany<TDocument, Guid>(documents);
{
return 0;
}
var idsTodelete = documents.Select(e => e.Id).ToArray();
return HandlePartitioned(documents.FirstOrDefault()).DeleteMany(x => idsTodelete.Contains(x.Id)).DeletedCount;
} }
/// <summary> /// <summary>
@@ -730,8 +749,22 @@ namespace MongoDbGenericRepository
{ {
return 0; return 0;
} }
var idsTodelete = documents.Select(e => e.Id).ToArray(); // cannot use typeof(IPartitionedDocument).IsAssignableFrom(typeof(TDocument)), not available in netstandard 1.5
return (await HandlePartitioned<TDocument, TKey>(documents.FirstOrDefault()).DeleteManyAsync(x => idsTodelete.Contains(x.Id))).DeletedCount; if (documents.Any(e => e is IPartitionedDocument))
{
long deleteCount = 0;
foreach (var group in documents.GroupBy(e => ((IPartitionedDocument)e).PartitionKey))
{
var groupIdsTodelete = group.Select(e => e.Id).ToArray();
deleteCount += (await HandlePartitioned<TDocument, TKey>(group.FirstOrDefault()).DeleteManyAsync(x => groupIdsTodelete.Contains(x.Id))).DeletedCount;
}
return deleteCount;
}
else
{
var idsTodelete = documents.Select(e => e.Id).ToArray();
return (await HandlePartitioned<TDocument, TKey>(documents.FirstOrDefault()).DeleteManyAsync(x => idsTodelete.Contains(x.Id))).DeletedCount;
}
} }
/// <summary> /// <summary>
@@ -749,8 +782,22 @@ namespace MongoDbGenericRepository
{ {
return 0; return 0;
} }
var idsTodelete = documents.Select(e => e.Id).ToArray(); // cannot use typeof(IPartitionedDocument).IsAssignableFrom(typeof(TDocument)), not available in netstandard 1.5
return HandlePartitioned<TDocument, TKey>(documents.FirstOrDefault()).DeleteMany(x => idsTodelete.Contains(x.Id)).DeletedCount; if (documents.Any(e => e is IPartitionedDocument))
{
long deleteCount = 0;
foreach (var group in documents.GroupBy(e => ((IPartitionedDocument)e).PartitionKey))
{
var groupIdsTodelete = group.Select(e => e.Id).ToArray();
deleteCount += (HandlePartitioned<TDocument, TKey>(group.FirstOrDefault()).DeleteMany(x => groupIdsTodelete.Contains(x.Id))).DeletedCount;
}
return deleteCount;
}
else
{
var idsTodelete = documents.Select(e => e.Id).ToArray();
return (HandlePartitioned<TDocument, TKey>(documents.FirstOrDefault()).DeleteMany(x => idsTodelete.Contains(x.Id))).DeletedCount;
}
} }
/// <summary> /// <summary>