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. 19
      backend/src/Migrations/Migrations/MongoDb/ConvertDocumentIds.cs
  3. 39
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository.cs
  4. 11
      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 public sealed class MigrationPath : IMigrationPath
{ {
private const int CurrentVersion = 22; private const int CurrentVersion = 23;
private readonly IServiceProvider serviceProvider; private readonly IServiceProvider serviceProvider;
public MigrationPath(IServiceProvider serviceProvider) public MigrationPath(IServiceProvider serviceProvider)
@ -108,7 +108,8 @@ namespace Migrations
} }
// Version 22: Introduce domain id. // Version 22: Introduce domain id.
if (version < 22) // Version 23: Fix parent id.
if (version < 23)
{ {
yield return serviceProvider.GetRequiredService<ConvertDocumentIds>().ForAssets(); yield return serviceProvider.GetRequiredService<ConvertDocumentIds>().ForAssets();
} }

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

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

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

@ -37,8 +37,8 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
new CreateIndexModel<MongoAssetFolderEntity>( new CreateIndexModel<MongoAssetFolderEntity>(
Index Index
.Ascending(x => x.IndexedAppId) .Ascending(x => x.IndexedAppId)
.Ascending(x => x.IsDeleted) .Ascending(x => x.ParentId)
.Ascending(x => x.ParentId)) .Ascending(x => x.IsDeleted))
}, ct); }, ct);
} }
@ -46,9 +46,10 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
{ {
using (Profiler.TraceMethod<MongoAssetFolderRepository>("QueryAsyncByQuery")) using (Profiler.TraceMethod<MongoAssetFolderRepository>("QueryAsyncByQuery"))
{ {
var filter = BuildFilter(appId, parentId);
var assetFolderEntities = var assetFolderEntities =
await Collection await Collection.Find(filter).SortBy(x => x.FolderName)
.Find(x => x.IndexedAppId == appId && !x.IsDeleted && x.ParentId == parentId).SortBy(x => x.FolderName)
.ToListAsync(); .ToListAsync();
return ResultList.Create<IAssetFolderEntity>(assetFolderEntities.Count, assetFolderEntities); return ResultList.Create<IAssetFolderEntity>(assetFolderEntities.Count, assetFolderEntities);
@ -59,8 +60,10 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
{ {
using (Profiler.TraceMethod<MongoAssetRepository>()) using (Profiler.TraceMethod<MongoAssetRepository>())
{ {
var filter = BuildFilter(appId, parentId);
var assetFolderEntities = 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(); .ToListAsync();
return assetFolderEntities.Select(x => DomainId.Create(x[Fields.AssetFolderId].AsString)).ToList(); return assetFolderEntities.Select(x => DomainId.Create(x[Fields.AssetFolderId].AsString)).ToList();
@ -80,5 +83,31 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
return assetFolderEntity; 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);
}
} }
} }

11
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); return Filter.And(filters);
} }
else if (filters.Count == 1)
{
return filters[0];
}
else
{
return new BsonDocument();
}
}
} }
} }

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

@ -25,7 +25,7 @@ namespace Squidex.Domain.Users.MongoDb
[BsonRequired] [BsonRequired]
[BsonElement] [BsonElement]
public List<UserLoginInfo> Logins { get; set; } = new List<UserLoginInfo>(); public List<UserLogin> Logins { get; set; } = new List<UserLogin>();
[BsonRequired] [BsonRequired]
[BsonElement] [BsonElement]
@ -38,7 +38,7 @@ namespace Squidex.Domain.Users.MongoDb
internal void AddLogin(UserLoginInfo login) 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) internal void AddRole(string role)
@ -53,7 +53,7 @@ namespace Squidex.Domain.Users.MongoDb
internal void AddClaims(IEnumerable<Claim> claims) 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) 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); 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) 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 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); 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[] .SetArguments(new[]
{ {
nameof(UserLoginInfo.LoginProvider), nameof(UserLogin.LoginProvider),
nameof(UserLoginInfo.ProviderKey), nameof(UserLogin.ProviderKey),
nameof(UserLoginInfo.ProviderDisplayName) nameof(UserLogin.ProviderDisplayName)
}); });
cm.AutoMap(); 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, public static IFindFluent<T, BsonDocument> Only<T>(this IFindFluent<T, T> find,
Expression<Func<TDocument, object>> include) 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, public static IFindFluent<T, BsonDocument> Only<T>(this IFindFluent<T, T> find,
Expression<Func<TDocument, object>> include1, Expression<Func<T, object>> include1,
Expression<Func<TDocument, object>> include2) 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, public static IFindFluent<T, T> Not<T>(this IFindFluent<T, T> find,
Expression<Func<TDocument, object>> exclude) 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, public static IFindFluent<T, T> Not<T>(this IFindFluent<T, T> find,
Expression<Func<TDocument, object>> exclude1, Expression<Func<T, object>> exclude1,
Expression<Func<TDocument, object>> exclude2) 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) public static async Task UpsertVersionedAsync<T, TKey>(this IMongoCollection<T> collection, TKey key, long oldVersion, long newVersion, Func<UpdateDefinition<T>, UpdateDefinition<T>> updater)
where TEntity : IVersionedEntity<TKey> where T : IVersionedEntity<TKey>
where TKey : notnull where TKey : notnull
{ {
try 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) if (oldVersion > EtagVersion.Any)
{ {
@ -113,7 +113,7 @@ namespace Squidex.Infrastructure.MongoDb
if (existingVersion != null) if (existingVersion != null)
{ {
var versionField = GetVersionField<TEntity, TKey>(); var versionField = GetVersionField<T, TKey>();
throw new InconsistentStateException(existingVersion[versionField].AsInt64, oldVersion, ex); 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) public static async Task UpsertVersionedAsync<T, TKey>(this IMongoCollection<T> collection, TKey key, long oldVersion, long newVersion, T document)
where TEntity : IVersionedEntity<TKey> where T : IVersionedEntity<TKey>
where TKey : notnull where TKey : notnull
{ {
try try
{ {
doc.DocumentId = key; document.DocumentId = key;
doc.Version = newVersion; document.Version = newVersion;
if (oldVersion > EtagVersion.Any) 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 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) catch (MongoWriteException ex) when (ex.WriteError?.Category == ServerErrorCategory.DuplicateKey)
@ -150,7 +150,7 @@ namespace Squidex.Infrastructure.MongoDb
if (existingVersion != null) if (existingVersion != null)
{ {
var versionField = GetVersionField<TEntity, TKey>(); var versionField = GetVersionField<T, TKey>();
throw new InconsistentStateException(existingVersion[versionField].AsInt64, oldVersion, ex); throw new InconsistentStateException(existingVersion[versionField].AsInt64, oldVersion, ex);
} }
@ -161,14 +161,14 @@ namespace Squidex.Infrastructure.MongoDb
} }
} }
private static string GetVersionField<TEntity, TKey>() private static string GetVersionField<T, TKey>()
where TEntity : IVersionedEntity<TKey> where T : IVersionedEntity<TKey>
where TKey : notnull 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)) 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 selfToken = new CancellationTokenSource())
{ {
using (var combined = CancellationTokenSource.CreateLinkedTokenSource(selfToken.Token, cancellationToken)) using (var combined = CancellationTokenSource.CreateLinkedTokenSource(selfToken.Token, cancellationToken))
{ {
var actionBlock = var actionBlock =
new ActionBlock<TDocument>(async x => new ActionBlock<T>(async x =>
{ {
if (!combined.IsCancellationRequested) if (!combined.IsCancellationRequested)
{ {

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

@ -263,6 +263,11 @@ namespace Squidex.Infrastructure
return result; 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) public static void Foreach<T>(this IEnumerable<T> collection, Action<T, int> action)
{ {
var index = 0; 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) private string _Q(ClrQuery query)
{ {
var rendered = var rendered =
query.AdjustToModel(schemaDef).BuildFilter<MongoContentEntity>().Filter! query.AdjustToModel(schemaDef).BuildFilter<MongoContentEntity>()!
.Render(Serializer, Registry).ToString(); .Render(Serializer, Registry).ToString();
return rendered; return rendered;

Loading…
Cancel
Save