diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs index 44d93d8d4..6444ba638 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs @@ -119,12 +119,9 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets { var find = Collection.Find(x => ids.Contains(x.Id)).SortByDescending(x => x.LastModified); - var assetItems = find.ToListAsync(); - var assetCount = find.CountDocumentsAsync(); + var assetItems = await find.ToListAsync(); - await Task.WhenAll(assetItems, assetCount); - - return ResultList.Create(assetCount.Result, assetItems.Result.OfType()); + return ResultList.Create(assetItems.Count, assetItems.OfType()); } } diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs index 1bb4c6128..9424333c0 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs @@ -137,17 +137,14 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents { var find = Collection.Find(FilterFactory.IdsBySchema(schema.Id, ids, status)); - var contentItems = find.WithoutDraft(includeDraft).ToListAsync(); - var contentCount = find.CountDocumentsAsync(); - - await Task.WhenAll(contentItems, contentCount); + var contentItems = await find.WithoutDraft(includeDraft).ToListAsync(); - foreach (var entity in contentItems.Result) + foreach (var entity in contentItems) { entity.ParseData(schema.SchemaDef, serializer); } - return ResultList.Create(contentCount.Result, contentItems.Result); + return ResultList.Create(contentItems.Count, contentItems); } public async Task FindContentAsync(ISchemaEntity schema, Guid id, Status[] status, bool includeDraft) diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs index aeb62077b..c5dbf6024 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using GraphQL; using GraphQL.DataLoader; using Squidex.Domain.Apps.Entities.Assets; +using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types; using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Log; @@ -52,6 +53,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL execution.UserContext = this; } + public override Task FindAssetAsync(Guid id) + { + var dataLoader = GetAssetsLoader(); + + return dataLoader.LoadAsync(id); + } + + public override Task FindContentAsync(Guid schemaId, Guid id) + { + var dataLoader = GetContentsLoader(schemaId); + + return dataLoader.LoadAsync(id); + } + public async Task> GetReferencedAssetsAsync(IJsonValue value) { var ids = ParseIds(value); @@ -61,18 +76,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL return EmptyAssets; } - var dataLoader = - dataLoaderContextAccessor.Context.GetOrAddBatchLoader("Assets", - async batch => - { - var result = await GetReferencedAssetsAsync(new List(batch)); - - return result.ToDictionary(x => x.Id); - }); + var dataLoader = GetAssetsLoader(); - var assets = await Task.WhenAll(ids.Select(x => dataLoader.LoadAsync(x))); - - return assets.Where(x => x != null).ToList(); + return await dataLoader.LoadManyAsync(ids); } public async Task> GetReferencedContentsAsync(Guid schemaId, IJsonValue value) @@ -84,18 +90,31 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL return EmptyContents; } - var dataLoader = - dataLoaderContextAccessor.Context.GetOrAddBatchLoader($"Schema_{schemaId}", - async batch => - { - var result = await GetReferencedContentsAsync(schemaId, new List(batch)); + var dataLoader = GetContentsLoader(schemaId); + + return await dataLoader.LoadManyAsync(ids); + } + + private IDataLoader GetAssetsLoader() + { + return dataLoaderContextAccessor.Context.GetOrAddBatchLoader("Assets", + async batch => + { + var result = await GetReferencedAssetsAsync(new List(batch)); - return result.ToDictionary(x => x.Id); - }); + return result.ToDictionary(x => x.Id); + }); + } - var contents = await Task.WhenAll(ids.Select(x => dataLoader.LoadAsync(x))); + private IDataLoader GetContentsLoader(Guid schemaId) + { + return dataLoaderContextAccessor.Context.GetOrAddBatchLoader($"Schema_{schemaId}", + async batch => + { + var result = await GetReferencedContentsAsync(schemaId, new List(batch)); - return contents.Where(x => x != null).ToList(); + return result.ToDictionary(x => x.Id); + }); } private static ICollection ParseIds(IJsonValue value) diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Extensions.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Extensions.cs index 9531680b0..5b78b2728 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Extensions.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Extensions.cs @@ -7,6 +7,8 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; +using GraphQL.DataLoader; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; @@ -37,5 +39,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types return value; } + + public static async Task> LoadManyAsync(this IDataLoader dataLoader, ICollection keys) where T : class + { + var contents = await Task.WhenAll(keys.Select(x => dataLoader.LoadAsync(x))); + + return contents.Where(x => x != null).ToList(); + } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/QueryExecutionContext.cs b/src/Squidex.Domain.Apps.Entities/Contents/QueryExecutionContext.cs index 666b7474d..b1615f3a1 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/QueryExecutionContext.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/QueryExecutionContext.cs @@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Contents this.context = context; } - public async Task FindAssetAsync(Guid id) + public virtual async Task FindAssetAsync(Guid id) { var asset = cachedAssets.GetOrDefault(id); @@ -51,7 +51,7 @@ namespace Squidex.Domain.Apps.Entities.Contents return asset; } - public async Task FindContentAsync(Guid schemaId, Guid id) + public virtual async Task FindContentAsync(Guid schemaId, Guid id) { var content = cachedContents.GetOrDefault(id); diff --git a/src/Squidex.Domain.Apps.Entities/Q.cs b/src/Squidex.Domain.Apps.Entities/Q.cs index 8bd9b0f39..966bdd279 100644 --- a/src/Squidex.Domain.Apps.Entities/Q.cs +++ b/src/Squidex.Domain.Apps.Entities/Q.cs @@ -25,6 +25,11 @@ namespace Squidex.Domain.Apps.Entities return Clone(c => c.ODataQuery = odataQuery); } + public Q WithIds(params Guid[] ids) + { + return Clone(c => c.Ids = ids.ToList()); + } + public Q WithIds(IEnumerable ids) { return Clone(c => c.Ids = ids.ToList()); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs index 09b5afd92..2e503ffdf 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs @@ -212,8 +212,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL } }".Replace("", assetId.ToString()); - A.CallTo(() => assetQuery.FindAssetAsync(MatchsAssetContext(), assetId)) - .Returns(asset); + A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), MatchId(assetId))) + .Returns(ResultList.Create(1, asset)); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); @@ -544,8 +544,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL } }".Replace("", contentId.ToString()); - A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), schemaId.ToString(), contentId, EtagVersion.Any)) - .Returns(content); + A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.ToString(), MatchId(contentId))) + .Returns(ResultList.Create(1, content)); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); @@ -635,8 +635,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL } }".Replace("", contentId.ToString()); - A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), schemaId.ToString(), contentId, EtagVersion.Any)) - .Returns(content); + A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.ToString(), MatchId(contentId))) + .Returns(ResultList.Create(1, content)); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); @@ -730,12 +730,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL } }".Replace("", contentId.ToString()); - A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), schemaId.ToString(), contentId, EtagVersion.Any)) - .Returns(content); - A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.ToString(), A.Ignored)) .Returns(ResultList.Create(0, contentRef)); + A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.ToString(), MatchId(contentId))) + .Returns(ResultList.Create(1, content)); + var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); var expected = new @@ -788,8 +788,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL } }".Replace("", contentId.ToString()); - A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), schemaId.ToString(), contentId, EtagVersion.Any)) - .Returns(content); + A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.ToString(), MatchId(contentId))) + .Returns(ResultList.Create(1, content)); A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), A.Ignored)) .Returns(ResultList.Create(0, assetRef)); @@ -844,10 +844,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL } }".Replace("", assetId2.ToString()); - A.CallTo(() => assetQuery.FindAssetAsync(MatchsAssetContext(), assetId1)) - .Returns(asset1); - A.CallTo(() => assetQuery.FindAssetAsync(MatchsAssetContext(), assetId2)) - .Returns(asset2); + A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), MatchId(assetId1))) + .Returns(ResultList.Create(0, asset1)); + + A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), MatchId(assetId2))) + .Returns(ResultList.Create(0, asset2)); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query1 }, new GraphQLQuery { Query = query2 }); @@ -902,8 +903,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL } }".Replace("", contentId.ToString()); - A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), schemaId.ToString(), contentId, EtagVersion.Any)) - .Returns(content); + A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.ToString(), MatchId(contentId))) + .Returns(ResultList.Create(1, content)); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); @@ -940,8 +941,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL } }".Replace("", contentId.ToString()); - A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), schemaId.ToString(), contentId, EtagVersion.Any)) - .Returns(content); + A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.ToString(), MatchId(contentId))) + .Returns(ResultList.Create(1, content)); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); @@ -986,8 +987,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL } }".Replace("", contentId.ToString()); - A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), schemaId.ToString(), contentId, EtagVersion.Any)) - .Returns(content); + A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.ToString(), MatchId(contentId))) + .Returns(ResultList.Create(1, content)); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); @@ -1005,6 +1006,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL AssertResult(expected, result); } + private static Q MatchId(Guid contentId) + { + return A.That.Matches(x => x.Ids.Count == 1 && x.Ids[0] == contentId); + } + private QueryContext MatchsAssetContext() { return A.That.Matches(x => x.App == app && x.User == user);