Browse Source

Fix parent id after migration.

pull/596/head
Sebastian 5 years ago
parent
commit
3abca658b5
  1. 5
      backend/src/Migrations/MigrationPath.cs
  2. 23
      backend/src/Migrations/Migrations/MongoDb/ConvertDocumentIds.cs
  3. 41
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository.cs
  4. 13
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Visitors/FindExtensions.cs
  5. 35
      backend/src/Squidex.Domain.Users.MongoDb/MongoUser.cs
  6. 16
      backend/src/Squidex.Domain.Users.MongoDb/MongoUserStore.cs
  7. 62
      backend/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs
  8. 5
      backend/src/Squidex.Infrastructure/CollectionExtensions.cs
  9. 2
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/MongoDbQueryTests.cs

5
backend/src/Migrations/MigrationPath.cs

@ -18,7 +18,7 @@ namespace Migrations
{
public sealed class MigrationPath : IMigrationPath
{
private const int CurrentVersion = 22;
private const int CurrentVersion = 23;
private readonly IServiceProvider serviceProvider;
public MigrationPath(IServiceProvider serviceProvider)
@ -108,7 +108,8 @@ namespace Migrations
}
// Version 22: Introduce domain id.
if (version < 22)
// Version 23: Fix parent id.
if (version < 23)
{
yield return serviceProvider.GetRequiredService<ConvertDocumentIds>().ForAssets();
}

23
backend/src/Migrations/Migrations/MongoDb/ConvertDocumentIds.cs

@ -59,25 +59,25 @@ namespace Migrations.Migrations.MongoDb
switch (scope)
{
case Scope.Assets:
await RebuildAsync(database, "States_Assets");
await RebuildAsync(database, "States_AssetFolders", null, ConvertParentId);
await RebuildAsync(database, ConvertParentId, "States_Assets");
await RebuildAsync(database, ConvertParentId, "States_AssetFolders");
break;
case Scope.Contents:
await RebuildAsync(databaseContent, "State_Contents_All", "States_Contents_All2");
await RebuildAsync(databaseContent, "State_Contents_Published", "States_Contents_Published2");
await RebuildAsync(databaseContent, null, "State_Contents_All");
await RebuildAsync(databaseContent, null, "State_Contents_Published");
break;
}
}
private static async Task RebuildAsync(IMongoDatabase database, string collectionNameOld, string? collectionNameNew = null, Action<BsonDocument>? extraAction = null)
private static async Task RebuildAsync(IMongoDatabase database, Action<BsonDocument>? extraAction, string collectionNameOld)
{
const int SizeOfBatch = 1000;
const int SizeOfQueue = 10;
if (string.IsNullOrWhiteSpace(collectionNameNew))
{
collectionNameNew = $"{collectionNameOld}2";
}
string collectionNameNew;
collectionNameNew = $"{collectionNameOld}2";
collectionNameNew = collectionNameNew.Replace("State_", "States_");
var collectionOld = database.GetCollection<BsonDocument>(collectionNameOld);
var collectionNew = database.GetCollection<BsonDocument>(collectionNameNew);
@ -151,7 +151,10 @@ namespace Migrations.Migrations.MongoDb
private static void ConvertParentId(BsonDocument document)
{
document["pi"] = document["pi"].AsGuid.ToString();
if (document.Contains("pi"))
{
document["pi"] = document["pi"].AsGuid.ToString();
}
}
}
}

41
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository.cs

@ -37,8 +37,8 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
new CreateIndexModel<MongoAssetFolderEntity>(
Index
.Ascending(x => x.IndexedAppId)
.Ascending(x => x.IsDeleted)
.Ascending(x => x.ParentId))
.Ascending(x => x.ParentId)
.Ascending(x => x.IsDeleted))
}, ct);
}
@ -46,10 +46,11 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
{
using (Profiler.TraceMethod<MongoAssetFolderRepository>("QueryAsyncByQuery"))
{
var filter = BuildFilter(appId, parentId);
var assetFolderEntities =
await Collection
.Find(x => x.IndexedAppId == appId && !x.IsDeleted && x.ParentId == parentId).SortBy(x => x.FolderName)
.ToListAsync();
await Collection.Find(filter).SortBy(x => x.FolderName)
.ToListAsync();
return ResultList.Create<IAssetFolderEntity>(assetFolderEntities.Count, assetFolderEntities);
}
@ -59,8 +60,10 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
{
using (Profiler.TraceMethod<MongoAssetRepository>())
{
var filter = BuildFilter(appId, parentId);
var assetFolderEntities =
await Collection.Find(x => x.IndexedAppId == appId && !x.IsDeleted && x.ParentId == parentId).Only(x => x.Id)
await Collection.Find(filter).Only(x => x.Id)
.ToListAsync();
return assetFolderEntities.Select(x => DomainId.Create(x[Fields.AssetFolderId].AsString)).ToList();
@ -80,5 +83,31 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
return assetFolderEntity;
}
}
private static FilterDefinition<MongoAssetFolderEntity> BuildFilter(DomainId appId, DomainId? parentId)
{
var filters = new List<FilterDefinition<MongoAssetFolderEntity>>
{
Filter.Eq(x => x.IndexedAppId, appId),
Filter.Eq(x => x.IsDeleted, false)
};
if (parentId.HasValue)
{
if (parentId == DomainId.Empty)
{
filters.Add(
Filter.Or(
Filter.Exists(x => x.ParentId, false),
Filter.Eq(x => x.ParentId, DomainId.Empty)));
}
else
{
filters.Add(Filter.Eq(x => x.ParentId, parentId.Value));
}
}
return Filter.And(filters);
}
}
}

13
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Visitors/FindExtensions.cs

@ -73,18 +73,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets.Visitors
}
}
if (filters.Count > 1)
{
return Filter.And(filters);
}
else if (filters.Count == 1)
{
return filters[0];
}
else
{
return new BsonDocument();
}
return Filter.And(filters);
}
}
}

35
backend/src/Squidex.Domain.Users.MongoDb/MongoUser.cs

@ -25,7 +25,7 @@ namespace Squidex.Domain.Users.MongoDb
[BsonRequired]
[BsonElement]
public List<UserLoginInfo> Logins { get; set; } = new List<UserLoginInfo>();
public List<UserLogin> Logins { get; set; } = new List<UserLogin>();
[BsonRequired]
[BsonElement]
@ -38,7 +38,7 @@ namespace Squidex.Domain.Users.MongoDb
internal void AddLogin(UserLoginInfo login)
{
Logins.Add(new UserLoginInfo(login.LoginProvider, login.ProviderKey, login.ProviderDisplayName));
Logins.Add(new UserLogin(login));
}
internal void AddRole(string role)
@ -53,7 +53,7 @@ namespace Squidex.Domain.Users.MongoDb
internal void AddClaims(IEnumerable<Claim> claims)
{
claims.Foreach((x, _) => AddClaim(x));
claims.Foreach(x => AddClaim(x));
}
internal void AddToken(string provider, string name, string value)
@ -66,24 +66,24 @@ namespace Squidex.Domain.Users.MongoDb
Logins.RemoveAll(x => x.LoginProvider == provider && x.ProviderKey == providerKey);
}
internal void RemoveRole(string role)
internal void RemoveClaim(Claim claim)
{
Roles.Remove(role);
Claims.RemoveAll(x => x.Type == claim.Type && x.Value == claim.Value);
}
internal void RemoveClaim(Claim claim)
internal void RemoveToken(string provider, string name)
{
Claims.RemoveAll(x => x.Type == claim.Type && x.Value == claim.Value);
Tokens.RemoveAll(x => x.LoginProvider == provider && x.Name == name);
}
internal void RemoveClaims(IEnumerable<Claim> claims)
internal void RemoveRole(string role)
{
claims.Foreach((x, _) => RemoveClaim(x));
Roles.Remove(role);
}
internal void RemoveToken(string provider, string name)
internal void RemoveClaims(IEnumerable<Claim> claims)
{
Tokens.RemoveAll(x => x.LoginProvider == provider && x.Name == name);
claims.Foreach(x => RemoveClaim(x));
}
internal void ReplaceClaim(Claim existingClaim, Claim newClaim)
@ -104,4 +104,17 @@ namespace Squidex.Domain.Users.MongoDb
public sealed class UserTokenInfo : IdentityUserToken<string>
{
}
public sealed class UserLogin : UserLoginInfo
{
public UserLogin(string provider, string providerKey, string displayName)
: base(provider, providerKey, displayName)
{
}
public UserLogin(UserLoginInfo source)
: base(source.LoginProvider, source.ProviderKey, source.ProviderDisplayName)
{
}
}
}

16
backend/src/Squidex.Domain.Users.MongoDb/MongoUserStore.cs

@ -66,14 +66,20 @@ namespace Squidex.Domain.Users.MongoDb
cm.MapMember(x => x.Value);
});
BsonClassMap.RegisterClassMap<UserLoginInfo>(cm =>
BsonClassMap.RegisterClassMap<UserLogin>(cm =>
{
cm.MapConstructor(typeof(UserLoginInfo).GetConstructors().First())
cm.MapConstructor(typeof(UserLogin).GetConstructors()
.First(x =>
{
var parameters = x.GetParameters();
return parameters.Length == 3;
}))
.SetArguments(new[]
{
nameof(UserLoginInfo.LoginProvider),
nameof(UserLoginInfo.ProviderKey),
nameof(UserLoginInfo.ProviderDisplayName)
nameof(UserLogin.LoginProvider),
nameof(UserLogin.ProviderKey),
nameof(UserLogin.ProviderDisplayName)
});
cm.AutoMap();

62
backend/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs

@ -62,39 +62,39 @@ namespace Squidex.Infrastructure.MongoDb
}
}
public static IFindFluent<TDocument, BsonDocument> Only<TDocument>(this IFindFluent<TDocument, TDocument> find,
Expression<Func<TDocument, object>> include)
public static IFindFluent<T, BsonDocument> Only<T>(this IFindFluent<T, T> find,
Expression<Func<T, object>> include)
{
return find.Project<BsonDocument>(Builders<TDocument>.Projection.Include(include));
return find.Project<BsonDocument>(Builders<T>.Projection.Include(include));
}
public static IFindFluent<TDocument, BsonDocument> Only<TDocument>(this IFindFluent<TDocument, TDocument> find,
Expression<Func<TDocument, object>> include1,
Expression<Func<TDocument, object>> include2)
public static IFindFluent<T, BsonDocument> Only<T>(this IFindFluent<T, T> find,
Expression<Func<T, object>> include1,
Expression<Func<T, object>> include2)
{
return find.Project<BsonDocument>(Builders<TDocument>.Projection.Include(include1).Include(include2));
return find.Project<BsonDocument>(Builders<T>.Projection.Include(include1).Include(include2));
}
public static IFindFluent<TDocument, TDocument> Not<TDocument>(this IFindFluent<TDocument, TDocument> find,
Expression<Func<TDocument, object>> exclude)
public static IFindFluent<T, T> Not<T>(this IFindFluent<T, T> find,
Expression<Func<T, object>> exclude)
{
return find.Project<TDocument>(Builders<TDocument>.Projection.Exclude(exclude));
return find.Project<T>(Builders<T>.Projection.Exclude(exclude));
}
public static IFindFluent<TDocument, TDocument> Not<TDocument>(this IFindFluent<TDocument, TDocument> find,
Expression<Func<TDocument, object>> exclude1,
Expression<Func<TDocument, object>> exclude2)
public static IFindFluent<T, T> Not<T>(this IFindFluent<T, T> find,
Expression<Func<T, object>> exclude1,
Expression<Func<T, object>> exclude2)
{
return find.Project<TDocument>(Builders<TDocument>.Projection.Exclude(exclude1).Exclude(exclude2));
return find.Project<T>(Builders<T>.Projection.Exclude(exclude1).Exclude(exclude2));
}
public static async Task UpsertVersionedAsync<TEntity, TKey>(this IMongoCollection<TEntity> collection, TKey key, long oldVersion, long newVersion, Func<UpdateDefinition<TEntity>, UpdateDefinition<TEntity>> updater)
where TEntity : IVersionedEntity<TKey>
public static async Task UpsertVersionedAsync<T, TKey>(this IMongoCollection<T> collection, TKey key, long oldVersion, long newVersion, Func<UpdateDefinition<T>, UpdateDefinition<T>> updater)
where T : IVersionedEntity<TKey>
where TKey : notnull
{
try
{
var update = updater(Builders<TEntity>.Update.Set(x => x.Version, newVersion));
var update = updater(Builders<T>.Update.Set(x => x.Version, newVersion));
if (oldVersion > EtagVersion.Any)
{
@ -113,7 +113,7 @@ namespace Squidex.Infrastructure.MongoDb
if (existingVersion != null)
{
var versionField = GetVersionField<TEntity, TKey>();
var versionField = GetVersionField<T, TKey>();
throw new InconsistentStateException(existingVersion[versionField].AsInt64, oldVersion, ex);
}
@ -124,22 +124,22 @@ namespace Squidex.Infrastructure.MongoDb
}
}
public static async Task UpsertVersionedAsync<TEntity, TKey>(this IMongoCollection<TEntity> collection, TKey key, long oldVersion, long newVersion, TEntity doc)
where TEntity : IVersionedEntity<TKey>
public static async Task UpsertVersionedAsync<T, TKey>(this IMongoCollection<T> collection, TKey key, long oldVersion, long newVersion, T document)
where T : IVersionedEntity<TKey>
where TKey : notnull
{
try
{
doc.DocumentId = key;
doc.Version = newVersion;
document.DocumentId = key;
document.Version = newVersion;
if (oldVersion > EtagVersion.Any)
{
await collection.ReplaceOneAsync(x => x.DocumentId.Equals(key) && x.Version == oldVersion, doc, UpsertReplace);
await collection.ReplaceOneAsync(x => x.DocumentId.Equals(key) && x.Version == oldVersion, document, UpsertReplace);
}
else
{
await collection.ReplaceOneAsync(x => x.DocumentId.Equals(key), doc, UpsertReplace);
await collection.ReplaceOneAsync(x => x.DocumentId.Equals(key), document, UpsertReplace);
}
}
catch (MongoWriteException ex) when (ex.WriteError?.Category == ServerErrorCategory.DuplicateKey)
@ -150,7 +150,7 @@ namespace Squidex.Infrastructure.MongoDb
if (existingVersion != null)
{
var versionField = GetVersionField<TEntity, TKey>();
var versionField = GetVersionField<T, TKey>();
throw new InconsistentStateException(existingVersion[versionField].AsInt64, oldVersion, ex);
}
@ -161,14 +161,14 @@ namespace Squidex.Infrastructure.MongoDb
}
}
private static string GetVersionField<TEntity, TKey>()
where TEntity : IVersionedEntity<TKey>
private static string GetVersionField<T, TKey>()
where T : IVersionedEntity<TKey>
where TKey : notnull
{
return BsonClassMap.LookupClassMap(typeof(TEntity)).GetMemberMap(nameof(IVersionedEntity<TKey>.Version)).ElementName;
return BsonClassMap.LookupClassMap(typeof(T)).GetMemberMap(nameof(IVersionedEntity<TKey>.Version)).ElementName;
}
public static async Task ForEachPipedAsync<TDocument>(this IAsyncCursorSource<TDocument> source, Func<TDocument, Task> processor, CancellationToken cancellationToken = default)
public static async Task ForEachPipedAsync<T>(this IAsyncCursorSource<T> source, Func<T, Task> processor, CancellationToken cancellationToken = default)
{
using (var cursor = await source.ToCursorAsync(cancellationToken))
{
@ -176,14 +176,14 @@ namespace Squidex.Infrastructure.MongoDb
}
}
public static async Task ForEachPipedAsync<TDocument>(this IAsyncCursor<TDocument> source, Func<TDocument, Task> processor, CancellationToken cancellationToken = default)
public static async Task ForEachPipedAsync<T>(this IAsyncCursor<T> source, Func<T, Task> processor, CancellationToken cancellationToken = default)
{
using (var selfToken = new CancellationTokenSource())
{
using (var combined = CancellationTokenSource.CreateLinkedTokenSource(selfToken.Token, cancellationToken))
{
var actionBlock =
new ActionBlock<TDocument>(async x =>
new ActionBlock<T>(async x =>
{
if (!combined.IsCancellationRequested)
{

5
backend/src/Squidex.Infrastructure/CollectionExtensions.cs

@ -263,6 +263,11 @@ namespace Squidex.Infrastructure
return result;
}
public static void Foreach<T>(this IEnumerable<T> collection, Action<T> action)
{
collection.Foreach((x, i) => action(x));
}
public static void Foreach<T>(this IEnumerable<T> collection, Action<T, int> action)
{
var index = 0;

2
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/MongoDbQueryTests.cs

@ -327,7 +327,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
private string _Q(ClrQuery query)
{
var rendered =
query.AdjustToModel(schemaDef).BuildFilter<MongoContentEntity>().Filter!
query.AdjustToModel(schemaDef).BuildFilter<MongoContentEntity>()!
.Render(Serializer, Registry).ToString();
return rendered;

Loading…
Cancel
Save