From d32b6acf4fa2f167e40b789e0cd02ce355a3a8ed Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Wed, 28 Aug 2024 15:52:14 +0200 Subject: [PATCH 1/6] Optimed sql for checking if a list documents is tracked by a reference --- .../AreReferencedDocumentController.cs | 29 +++++++- .../ReferencedByDocumentController.cs | 2 +- ...ReferencedDescendantsDocumentController.cs | 2 +- ...TrackedReferenceViewModelsMapDefinition.cs | 7 ++ .../ITrackedReferencesRepository.cs | 6 ++ .../Services/ITrackedReferencesService.cs | 2 + .../Services/TrackedReferencesService.cs | 6 ++ .../Persistence/NPocoSqlExtensions.cs | 18 +++++ .../Implement/TrackedReferencesRepository.cs | 73 +++++++++++++++---- .../Persistence/SqlContextExtensions.cs | 19 +++++ 10 files changed, 148 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/References/AreReferencedDocumentController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/References/AreReferencedDocumentController.cs index b73aa9ac94a2..108371f1ff84 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/References/AreReferencedDocumentController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/References/AreReferencedDocumentController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.ViewModels.Pagination; using Umbraco.Cms.Api.Management.ViewModels; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; @@ -22,7 +23,7 @@ public AreReferencedDocumentController(ITrackedReferencesService trackedReferenc } /// - /// Gets a page list of the items used in any kind of relation from selected keys. + /// Gets a paged list of the items used in any kind of relation from selected keys. /// /// /// Used when bulk deleting content/media and bulk unpublishing content (delete and unpublish on List view). @@ -46,4 +47,30 @@ public async Task>> GetPagedRefe return await Task.FromResult(pagedViewModel); } + + /// + /// Gets a paged list of the items used in any kind of relation from selected keys. + /// + /// + /// Used when bulk deleting content/media and bulk unpublishing content (delete and unpublish on List view). + /// This is basically finding children of relations. + /// + [HttpGet("are-referenced-fast")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + public async Task>> GetPagedReferencedItemsFast( + CancellationToken cancellationToken, + [FromQuery(Name="id")]HashSet ids, + int skip = 0, + int take = 20) + { + PagedModel distinctByKeyItemsWithReferencedRelations = await _trackedReferencesSkipTakeService.GetPagedKeysWithDependentReferencesAsync(ids, Constants.ObjectTypes.Document, skip, take); + var pagedViewModel = new PagedViewModel + { + Total = distinctByKeyItemsWithReferencedRelations.Total, + Items = _umbracoMapper.MapEnumerable(distinctByKeyItemsWithReferencedRelations.Items), + }; + + return await Task.FromResult(pagedViewModel); + } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedByDocumentController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedByDocumentController.cs index 0e7e05a29f56..a818e70af89f 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedByDocumentController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedByDocumentController.cs @@ -22,7 +22,7 @@ public ReferencedByDocumentController(ITrackedReferencesService trackedReference } /// - /// Gets a page list of tracked references for the current item, so you can see where an item is being used. + /// Gets a paged list of tracked references for the current item, so you can see where an item is being used. /// /// /// Used by info tabs on content, media etc. and for the delete and unpublish of single items. diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedDescendantsDocumentController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedDescendantsDocumentController.cs index 7ee7bae77f4e..4b931632e1dc 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedDescendantsDocumentController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedDescendantsDocumentController.cs @@ -22,7 +22,7 @@ public ReferencedDescendantsDocumentController(ITrackedReferencesService tracked } /// - /// Gets a page list of the child nodes of the current item used in any kind of relation. + /// Gets a paged list of the descendant nodes of the current item used in any kind of relation. /// /// /// Used when deleting and unpublishing a single item to check if this item has any descending items that are in any diff --git a/src/Umbraco.Cms.Api.Management/Mapping/TrackedReferences/TrackedReferenceViewModelsMapDefinition.cs b/src/Umbraco.Cms.Api.Management/Mapping/TrackedReferences/TrackedReferenceViewModelsMapDefinition.cs index 5d53d120a224..6a4fe40c6203 100644 --- a/src/Umbraco.Cms.Api.Management/Mapping/TrackedReferences/TrackedReferenceViewModelsMapDefinition.cs +++ b/src/Umbraco.Cms.Api.Management/Mapping/TrackedReferences/TrackedReferenceViewModelsMapDefinition.cs @@ -13,6 +13,7 @@ public void DefineMaps(IUmbracoMapper mapper) mapper.Define((source, context) => new MediaReferenceResponseModel(), Map); mapper.Define((source, context) => new DefaultReferenceResponseModel(), Map); mapper.Define((source, context) => new ReferenceByIdModel(), Map); + mapper.Define((source, context) => new ReferenceByIdModel(), Map); } // Umbraco.Code.MapAll @@ -56,4 +57,10 @@ private void Map(RelationItemModel source, ReferenceByIdModel target, MapperCont { target.Id = source.NodeKey; } + + // Umbraco.Code.MapAll + private void Map(Guid source, ReferenceByIdModel target, MapperContext context) + { + target.Id = source; + } } diff --git a/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs index fda745c2f68b..481b37116d1d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs @@ -131,4 +131,10 @@ IEnumerable GetPagedDescendantsInReferences( long take, bool filterMustBeIsDependency, out long totalRecords); + + Task> GetPagedNodeKeysWithDependantReferencesAsync( + ISet keys, + Guid nodeObjectTypeId, + long skip, + long take); } diff --git a/src/Umbraco.Core/Services/ITrackedReferencesService.cs b/src/Umbraco.Core/Services/ITrackedReferencesService.cs index 2b5dcfb83323..c051953981e2 100644 --- a/src/Umbraco.Core/Services/ITrackedReferencesService.cs +++ b/src/Umbraco.Core/Services/ITrackedReferencesService.cs @@ -97,4 +97,6 @@ PagedModel GetPagedItemsWithRelations(int[] ids, long skip, l /// A paged result of objects. Task> GetPagedItemsWithRelationsAsync(ISet keys, long skip, long take, bool filterMustBeIsDependency); + + Task> GetPagedKeysWithDependentReferencesAsync(ISet keys, Guid nodeObjectTypeId, long skip, long take); } diff --git a/src/Umbraco.Core/Services/TrackedReferencesService.cs b/src/Umbraco.Core/Services/TrackedReferencesService.cs index 06b8fc551063..5d5d76f7f041 100644 --- a/src/Umbraco.Core/Services/TrackedReferencesService.cs +++ b/src/Umbraco.Core/Services/TrackedReferencesService.cs @@ -141,4 +141,10 @@ public async Task> GetPagedItemsWithRelationsAsync return await Task.FromResult(pagedModel); } + + public async Task> GetPagedKeysWithDependentReferencesAsync(ISet keys, Guid objectTypeId, long skip, long take) + { + using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true); + return await _trackedReferencesRepository.GetPagedNodeKeysWithDependantReferencesAsync(keys, objectTypeId, skip, take); + } } diff --git a/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs b/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs index 645557cfe060..19064ae3d909 100644 --- a/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs +++ b/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs @@ -45,6 +45,24 @@ public static Sql Where(this Sql sql, Ex return sql.Where(s, a); } + /// + /// Appends a WHERE clause to the Sql statement. + /// + /// The type of Dto 1. + /// The type of Dto 2. + /// The type of Dto 3. + /// The Sql statement. + /// A predicate to transform and append to the Sql statement. + /// An optional alias for Dto 1 table. + /// An optional alias for Dto 2 table. + /// An optional alias for Dto 3 table. + /// The Sql statement. + public static Sql Where(this Sql sql, Expression> predicate, string? alias1 = null, string? alias2 = null, string? alias3 = null) + { + var (s, a) = sql.SqlContext.VisitDto(predicate, alias1, alias2, alias3); + return sql.Where(s, a); + } + /// /// Appends a WHERE IN clause to the Sql statement. /// diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs index 62d3f3acd68c..cea635cee24b 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs @@ -92,7 +92,8 @@ private Sql GetInnerUnionSql() } var innerUnionSqlChild = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select( - "[cn].uniqueId as [key]", "[pn].uniqueId as otherKey, [cr].childId as id", "[cr].parentId as otherId", "[rt].[alias]", "[rt].[name]", + "[cn].uniqueId as [key]", "[pn].uniqueId as otherKey, [cr].childId as id", + "[cr].parentId as otherId", "[rt].[alias]", "[rt].[name]", "[rt].[isDependency]", "[rt].[dual]") .From("cr") .InnerJoin("rt") @@ -103,7 +104,8 @@ private Sql GetInnerUnionSql() .On((cr, pn) => cr.ParentId == pn.NodeId, "cr", "pn"); var innerUnionSqlDualParent = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select( - "[pn].uniqueId as [key]", "[cn].uniqueId as otherKey, [dpr].parentId as id", "[dpr].childId as otherId", "[dprt].[alias]", "[dprt].[name]", + "[pn].uniqueId as [key]", "[cn].uniqueId as otherKey, [dpr].parentId as id", + "[dpr].childId as otherId", "[dprt].[alias]", "[dprt].[name]", "[dprt].[isDependency]", "[dprt].[dual]") .From("dpr") .InnerJoin("dprt") @@ -115,7 +117,8 @@ private Sql GetInnerUnionSql() .On((dpr, pn) => dpr.ParentId == pn.NodeId, "dpr", "pn"); var innerUnionSql3 = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select( - "[cn].uniqueId as [key]", "[pn].uniqueId as otherKey, [dcr].childId as id", "[dcr].parentId as otherId", "[dcrt].[alias]", "[dcrt].[name]", + "[cn].uniqueId as [key]", "[pn].uniqueId as otherKey, [dcr].childId as id", + "[dcr].parentId as otherId", "[dcrt].[alias]", "[dcrt].[name]", "[dcrt].[isDependency]", "[dcrt].[dual]") .From("dcr") .InnerJoin("dcrt") @@ -190,7 +193,8 @@ public IEnumerable GetPagedDescendantsInReferences(int parentId, l aliasLeft: "n", aliasRight: "d"); - sql = sql?.WhereIn((System.Linq.Expressions.Expression>)(x => x.NodeId), subQuery, "n"); + sql = sql?.WhereIn((System.Linq.Expressions.Expression>)(x => x.NodeId), subQuery, + "n"); if (filterMustBeIsDependency) { @@ -274,7 +278,7 @@ public IEnumerable GetPagedRelationsForItem( bool filterMustBeIsDependency, out long totalRecords) { - Sql innerUnionSql = GetInnerUnionSql(); + Sql innerUnionSql = GetInnerUnionSql(); var sql = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql().SelectDistinct( "[x].[otherId] as nodeId", "[n].[uniqueId] as nodeKey", @@ -404,7 +408,7 @@ public IEnumerable GetPagedRelationsForItem( return _umbracoMapper.MapEnumerable(pagedResult); } - public IEnumerable GetPagedItemsWithRelations( + public IEnumerable GetPagedItemsWithRelations( ISet keys, long skip, long take, @@ -454,7 +458,7 @@ public IEnumerable GetPagedItemsWithRelations( if (totalRecords > 0) { // Ordering is required for paging - sql = sql?.OrderBy(x => x.Alias, "x"); + sql = sql?.OrderBy(x => x.UniqueId, "n"); pagedResult = _scopeAccessor.AmbientScope?.Database.SkipTake(skip, take, sql).ToArray() ?? @@ -468,10 +472,53 @@ public IEnumerable GetPagedItemsWithRelations( return _umbracoMapper.MapEnumerable(pagedResult); } - public IEnumerable GetPagedDescendantsInReferences(Guid parentKey, long skip, long take, bool filterMustBeIsDependency, - out long totalRecords) - { - var syntax = _scopeAccessor.AmbientScope?.Database.SqlContext.SqlSyntax; + public async Task> GetPagedNodeKeysWithDependantReferencesAsync( + ISet keys, + Guid nodeObjectTypeId, + long skip, + long take) + { + if (_scopeAccessor.AmbientScope is null) + { + throw new InvalidOperationException("Can not execute without a valid AmbientScope"); + } + + Sql? sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql() + .SelectDistinct(node => node.UniqueId) + .From() + .InnerJoin() + .On((node, relation) => + node.NodeId == relation.ParentId || node.NodeId == relation.ParentId || node.NodeId == relation.ChildId) + .InnerJoin() + .On((relation, relationType) => relation.RelationType == relationType.Id && relationType.IsDependency) + .Where( + (node, relation, relationType) + => node.NodeObjectType == nodeObjectTypeId + && keys.Contains(node.UniqueId) + && (node.NodeId == relation.ChildId + || (relationType.Dual && relation.ParentId == node.NodeId))); + + var totalRecords = _scopeAccessor.AmbientScope.Database.Count(sql); + + // no need to process further if no records are found + if (totalRecords < 1) + { + return new PagedModel(totalRecords, Enumerable.Empty()); + } + + // Ordering is required for paging + sql = sql.OrderBy(node => node.UniqueId); + + IEnumerable result = await _scopeAccessor.AmbientScope.Database.SkipTakeAsync(skip, take, sql); + + return new PagedModel(totalRecords, result); + } + + public IEnumerable GetPagedDescendantsInReferences(Guid parentKey, long skip, long take, + bool filterMustBeIsDependency, + out long totalRecords) + { + var syntax = _scopeAccessor.AmbientScope?.Database.SqlContext.SqlSyntax; // Gets the path of the parent with ",%" added var subsubQuery = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql() @@ -544,9 +591,9 @@ public IEnumerable GetPagedDescendantsInReferences(Guid paren } return _umbracoMapper.MapEnumerable(pagedResult); - } + } - [Obsolete("Use overload that takes keys instead of ids. This will be removed in Umbraco 15.")] + [Obsolete("Use overload that takes keys instead of ids. This will be removed in Umbraco 15.")] public IEnumerable GetPagedItemsWithRelations( int[] ids, long skip, diff --git a/src/Umbraco.Infrastructure/Persistence/SqlContextExtensions.cs b/src/Umbraco.Infrastructure/Persistence/SqlContextExtensions.cs index e46963d73802..eca36b4cffdc 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlContextExtensions.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlContextExtensions.cs @@ -57,6 +57,25 @@ public static (string Sql, object[] Args) VisitDto(this ISqlContex return (visited, visitor.GetSqlParameters()); } + /// + /// Visit an expression. + /// + /// The type of the first DTO. + /// The type of the second DTO. + /// The type of the third DTO. + /// An . + /// An expression to visit. + /// An optional table alias for the first DTO. + /// An optional table alias for the second DTO. + /// An optional table alias for the third DTO. + /// A SQL statement, and arguments, corresponding to the expression. + public static (string Sql, object[] Args) VisitDto(this ISqlContext sqlContext, Expression> expression, string? alias1 = null, string? alias2 = null, string? alias3 = null) + { + var visitor = new PocoToSqlExpressionVisitor(sqlContext, alias1, alias2, alias3); + var visited = visitor.Visit(expression); + return (visited, visitor.GetSqlParameters()); + } + /// /// Visit an expression. /// From 1b5d34198501e92e301317d15e440e2e677c3bc8 Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Mon, 2 Sep 2024 14:42:07 +0200 Subject: [PATCH 2/6] Remove unoptimzed method and update Media controller too --- .../AreReferencedDocumentController.cs | 26 ------------------- .../AreReferencedMediaController.cs | 5 ++-- 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/References/AreReferencedDocumentController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/References/AreReferencedDocumentController.cs index 108371f1ff84..3d597688795c 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/References/AreReferencedDocumentController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/References/AreReferencedDocumentController.cs @@ -32,32 +32,6 @@ public AreReferencedDocumentController(ITrackedReferencesService trackedReferenc [HttpGet("are-referenced")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task>> GetPagedReferencedItems( - CancellationToken cancellationToken, - [FromQuery(Name="id")]HashSet ids, - int skip = 0, - int take = 20) - { - PagedModel distinctByKeyItemsWithReferencedRelations = await _trackedReferencesSkipTakeService.GetPagedItemsWithRelationsAsync(ids, skip, take, true); - var pagedViewModel = new PagedViewModel - { - Total = distinctByKeyItemsWithReferencedRelations.Total, - Items = _umbracoMapper.MapEnumerable(distinctByKeyItemsWithReferencedRelations.Items), - }; - - return await Task.FromResult(pagedViewModel); - } - - /// - /// Gets a paged list of the items used in any kind of relation from selected keys. - /// - /// - /// Used when bulk deleting content/media and bulk unpublishing content (delete and unpublish on List view). - /// This is basically finding children of relations. - /// - [HttpGet("are-referenced-fast")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] public async Task>> GetPagedReferencedItemsFast( CancellationToken cancellationToken, [FromQuery(Name="id")]HashSet ids, diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Media/References/AreReferencedMediaController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Media/References/AreReferencedMediaController.cs index 4ec013a0e7d9..89e0c2d1ba50 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Media/References/AreReferencedMediaController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Media/References/AreReferencedMediaController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.ViewModels.Pagination; using Umbraco.Cms.Api.Management.ViewModels; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; @@ -38,11 +39,11 @@ public async Task>> GetPagedRefe int skip = 0, int take = 20) { - PagedModel distinctByKeyItemsWithReferencedRelations = await _trackedReferencesSkipTakeService.GetPagedItemsWithRelationsAsync(ids, skip, take, true); + PagedModel distinctByKeyItemsWithReferencedRelations = await _trackedReferencesSkipTakeService.GetPagedKeysWithDependentReferencesAsync(ids, Constants.ObjectTypes.Media, skip, take); var pagedViewModel = new PagedViewModel { Total = distinctByKeyItemsWithReferencedRelations.Total, - Items = _umbracoMapper.MapEnumerable(distinctByKeyItemsWithReferencedRelations.Items), + Items = _umbracoMapper.MapEnumerable(distinctByKeyItemsWithReferencedRelations.Items), }; return await Task.FromResult(pagedViewModel); From 776c573d066fa0c82e7a8d049a49d381228a2a7f Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Mon, 2 Sep 2024 14:45:10 +0200 Subject: [PATCH 3/6] Revert spacing, formatting and unneeded code change This partially reverts commit d32b6acf4fa2f167e40b789e0cd02ce355a3a8ed. --- .../Implement/TrackedReferencesRepository.cs | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs index cea635cee24b..7db2aa710902 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs @@ -92,8 +92,7 @@ private Sql GetInnerUnionSql() } var innerUnionSqlChild = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select( - "[cn].uniqueId as [key]", "[pn].uniqueId as otherKey, [cr].childId as id", - "[cr].parentId as otherId", "[rt].[alias]", "[rt].[name]", + "[cn].uniqueId as [key]", "[pn].uniqueId as otherKey, [cr].childId as id", "[cr].parentId as otherId", "[rt].[alias]", "[rt].[name]", "[rt].[isDependency]", "[rt].[dual]") .From("cr") .InnerJoin("rt") @@ -104,8 +103,7 @@ private Sql GetInnerUnionSql() .On((cr, pn) => cr.ParentId == pn.NodeId, "cr", "pn"); var innerUnionSqlDualParent = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select( - "[pn].uniqueId as [key]", "[cn].uniqueId as otherKey, [dpr].parentId as id", - "[dpr].childId as otherId", "[dprt].[alias]", "[dprt].[name]", + "[pn].uniqueId as [key]", "[cn].uniqueId as otherKey, [dpr].parentId as id", "[dpr].childId as otherId", "[dprt].[alias]", "[dprt].[name]", "[dprt].[isDependency]", "[dprt].[dual]") .From("dpr") .InnerJoin("dprt") @@ -117,8 +115,7 @@ private Sql GetInnerUnionSql() .On((dpr, pn) => dpr.ParentId == pn.NodeId, "dpr", "pn"); var innerUnionSql3 = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select( - "[cn].uniqueId as [key]", "[pn].uniqueId as otherKey, [dcr].childId as id", - "[dcr].parentId as otherId", "[dcrt].[alias]", "[dcrt].[name]", + "[cn].uniqueId as [key]", "[pn].uniqueId as otherKey, [dcr].childId as id", "[dcr].parentId as otherId", "[dcrt].[alias]", "[dcrt].[name]", "[dcrt].[isDependency]", "[dcrt].[dual]") .From("dcr") .InnerJoin("dcrt") @@ -193,8 +190,7 @@ public IEnumerable GetPagedDescendantsInReferences(int parentId, l aliasLeft: "n", aliasRight: "d"); - sql = sql?.WhereIn((System.Linq.Expressions.Expression>)(x => x.NodeId), subQuery, - "n"); + sql = sql?.WhereIn((System.Linq.Expressions.Expression>)(x => x.NodeId), subQuery, "n"); if (filterMustBeIsDependency) { @@ -278,7 +274,7 @@ public IEnumerable GetPagedRelationsForItem( bool filterMustBeIsDependency, out long totalRecords) { - Sql innerUnionSql = GetInnerUnionSql(); + Sql innerUnionSql = GetInnerUnionSql(); var sql = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql().SelectDistinct( "[x].[otherId] as nodeId", "[n].[uniqueId] as nodeKey", @@ -408,7 +404,7 @@ public IEnumerable GetPagedRelationsForItem( return _umbracoMapper.MapEnumerable(pagedResult); } - public IEnumerable GetPagedItemsWithRelations( + public IEnumerable GetPagedItemsWithRelations( ISet keys, long skip, long take, @@ -458,7 +454,7 @@ public IEnumerable GetPagedItemsWithRelations( if (totalRecords > 0) { // Ordering is required for paging - sql = sql?.OrderBy(x => x.UniqueId, "n"); + sql = sql?.OrderBy(x => x.Alias, "x"); pagedResult = _scopeAccessor.AmbientScope?.Database.SkipTake(skip, take, sql).ToArray() ?? @@ -591,9 +587,9 @@ public IEnumerable GetPagedDescendantsInReferences(Guid paren } return _umbracoMapper.MapEnumerable(pagedResult); - } + } - [Obsolete("Use overload that takes keys instead of ids. This will be removed in Umbraco 15.")] + [Obsolete("Use overload that takes keys instead of ids. This will be removed in Umbraco 15.")] public IEnumerable GetPagedItemsWithRelations( int[] ids, long skip, From c84f09d51c5bb9f34440bbe66c8d8e41bac5077d Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Tue, 29 Oct 2024 10:20:14 +0100 Subject: [PATCH 4/6] Cleanup temporary renaming --- .../Document/References/AreReferencedDocumentController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/References/AreReferencedDocumentController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/References/AreReferencedDocumentController.cs index 3d597688795c..ed593e9c9ec2 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/References/AreReferencedDocumentController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/References/AreReferencedDocumentController.cs @@ -32,7 +32,7 @@ public AreReferencedDocumentController(ITrackedReferencesService trackedReferenc [HttpGet("are-referenced")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task>> GetPagedReferencedItemsFast( + public async Task>> GetPagedReferencedItems( CancellationToken cancellationToken, [FromQuery(Name="id")]HashSet ids, int skip = 0, From 06e6f18eca1bce22ddb2e7a79f2c924aa5d63108 Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Tue, 29 Oct 2024 14:28:22 +0100 Subject: [PATCH 5/6] Add default implementations --- .../Repositories/ITrackedReferencesRepository.cs | 6 +++++- src/Umbraco.Core/Services/ITrackedReferencesService.cs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs index 481b37116d1d..38b1388be915 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs @@ -136,5 +136,9 @@ Task> GetPagedNodeKeysWithDependantReferencesAsync( ISet keys, Guid nodeObjectTypeId, long skip, - long take); + long take) + { + IEnumerable pagedItems = GetPagedItemsWithRelations(keys, skip, take, true, out var total); + return Task.FromResult(new PagedModel(total, pagedItems.Select(i => i.NodeKey))); + } } diff --git a/src/Umbraco.Core/Services/ITrackedReferencesService.cs b/src/Umbraco.Core/Services/ITrackedReferencesService.cs index c051953981e2..a75f17d8be1e 100644 --- a/src/Umbraco.Core/Services/ITrackedReferencesService.cs +++ b/src/Umbraco.Core/Services/ITrackedReferencesService.cs @@ -98,5 +98,9 @@ PagedModel GetPagedItemsWithRelations(int[] ids, long skip, l Task> GetPagedItemsWithRelationsAsync(ISet keys, long skip, long take, bool filterMustBeIsDependency); - Task> GetPagedKeysWithDependentReferencesAsync(ISet keys, Guid nodeObjectTypeId, long skip, long take); + Task> GetPagedKeysWithDependentReferencesAsync(ISet keys, Guid nodeObjectTypeId, long skip, long take) + { + PagedModel pagedItems = GetPagedItemsWithRelationsAsync(keys, skip, take, true).GetAwaiter().GetResult(); + return Task.FromResult(new PagedModel(pagedItems.Total, pagedItems.Items.Select(i => i.NodeKey))); + } } From e1ba128dd13b1c945ed1e9a409c75481d4996771 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 6 Nov 2024 09:16:18 +0100 Subject: [PATCH 6/6] Fix merge issue --- .../Persistence/NPocoSqlExtensions.cs | 18 ------------------ .../Persistence/SqlContextExtensions.cs | 19 ------------------- 2 files changed, 37 deletions(-) diff --git a/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs b/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs index 55f98f58e5dd..820657006169 100644 --- a/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs +++ b/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs @@ -63,24 +63,6 @@ public static Sql Where(this Sql return sql.Where(s, a); } - /// - /// Appends a WHERE clause to the Sql statement. - /// - /// The type of Dto 1. - /// The type of Dto 2. - /// The type of Dto 3. - /// The Sql statement. - /// A predicate to transform and append to the Sql statement. - /// An optional alias for Dto 1 table. - /// An optional alias for Dto 2 table. - /// An optional alias for Dto 3 table. - /// The Sql statement. - public static Sql Where(this Sql sql, Expression> predicate, string? alias1 = null, string? alias2 = null, string? alias3 = null) - { - var (s, a) = sql.SqlContext.VisitDto(predicate, alias1, alias2, alias3); - return sql.Where(s, a); - } - /// /// Appends a WHERE IN clause to the Sql statement. /// diff --git a/src/Umbraco.Infrastructure/Persistence/SqlContextExtensions.cs b/src/Umbraco.Infrastructure/Persistence/SqlContextExtensions.cs index 1df2821d321d..894c50109332 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlContextExtensions.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlContextExtensions.cs @@ -57,25 +57,6 @@ public static (string Sql, object[] Args) VisitDto(this ISqlContex return (visited, visitor.GetSqlParameters()); } - /// - /// Visit an expression. - /// - /// The type of the first DTO. - /// The type of the second DTO. - /// The type of the third DTO. - /// An . - /// An expression to visit. - /// An optional table alias for the first DTO. - /// An optional table alias for the second DTO. - /// An optional table alias for the third DTO. - /// A SQL statement, and arguments, corresponding to the expression. - public static (string Sql, object[] Args) VisitDto(this ISqlContext sqlContext, Expression> expression, string? alias1 = null, string? alias2 = null, string? alias3 = null) - { - var visitor = new PocoToSqlExpressionVisitor(sqlContext, alias1, alias2, alias3); - var visited = visitor.Visit(expression); - return (visited, visitor.GetSqlParameters()); - } - /// /// Visit an expression. ///