Optimize tracked references (#16996)

* Optimed sql for checking if a list documents is tracked by a reference

* Remove unoptimzed method and update Media controller too

* Revert spacing, formatting and unneeded code change

This partially reverts commit d32b6acf4fa2f167e40b789e0cd02ce355a3a8ed.

* Cleanup temporary renaming

* Add default implementations

* Fix merge issue

---------

Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
Sven Geusens
2024-11-06 10:27:04 +01:00
committed by GitHub
parent 0bf2fbc24f
commit 24ec0ee4cb
9 changed files with 80 additions and 10 deletions

View File

@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Http;
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 class AreReferencedDocumentController : DocumentControllerBase
}
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// Used when bulk deleting content/media and bulk unpublishing content (delete and unpublish on List view).
@@ -37,11 +38,11 @@ public class AreReferencedDocumentController : DocumentControllerBase
int skip = 0,
int take = 20)
{
PagedModel<RelationItemModel> distinctByKeyItemsWithReferencedRelations = await _trackedReferencesSkipTakeService.GetPagedItemsWithRelationsAsync(ids, skip, take, true);
PagedModel<Guid> distinctByKeyItemsWithReferencedRelations = await _trackedReferencesSkipTakeService.GetPagedKeysWithDependentReferencesAsync(ids, Constants.ObjectTypes.Document, skip, take);
var pagedViewModel = new PagedViewModel<ReferenceByIdModel>
{
Total = distinctByKeyItemsWithReferencedRelations.Total,
Items = _umbracoMapper.MapEnumerable<RelationItemModel, ReferenceByIdModel>(distinctByKeyItemsWithReferencedRelations.Items),
Items = _umbracoMapper.MapEnumerable<Guid, ReferenceByIdModel>(distinctByKeyItemsWithReferencedRelations.Items),
};
return await Task.FromResult(pagedViewModel);

View File

@@ -22,7 +22,7 @@ public class ReferencedByDocumentController : DocumentControllerBase
}
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// Used by info tabs on content, media etc. and for the delete and unpublish of single items.

View File

@@ -22,7 +22,7 @@ public class ReferencedDescendantsDocumentController : DocumentControllerBase
}
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// Used when deleting and unpublishing a single item to check if this item has any descending items that are in any

View File

@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Http;
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 class AreReferencedMediaController : MediaControllerBase
int skip = 0,
int take = 20)
{
PagedModel<RelationItemModel> distinctByKeyItemsWithReferencedRelations = await _trackedReferencesSkipTakeService.GetPagedItemsWithRelationsAsync(ids, skip, take, true);
PagedModel<Guid> distinctByKeyItemsWithReferencedRelations = await _trackedReferencesSkipTakeService.GetPagedKeysWithDependentReferencesAsync(ids, Constants.ObjectTypes.Media, skip, take);
var pagedViewModel = new PagedViewModel<ReferenceByIdModel>
{
Total = distinctByKeyItemsWithReferencedRelations.Total,
Items = _umbracoMapper.MapEnumerable<RelationItemModel, ReferenceByIdModel>(distinctByKeyItemsWithReferencedRelations.Items),
Items = _umbracoMapper.MapEnumerable<Guid, ReferenceByIdModel>(distinctByKeyItemsWithReferencedRelations.Items),
};
return await Task.FromResult(pagedViewModel);

View File

@@ -13,6 +13,7 @@ public class TrackedReferenceViewModelsMapDefinition : IMapDefinition
mapper.Define<RelationItemModel, MediaReferenceResponseModel>((source, context) => new MediaReferenceResponseModel(), Map);
mapper.Define<RelationItemModel, DefaultReferenceResponseModel>((source, context) => new DefaultReferenceResponseModel(), Map);
mapper.Define<RelationItemModel, ReferenceByIdModel>((source, context) => new ReferenceByIdModel(), Map);
mapper.Define<Guid, ReferenceByIdModel>((source, context) => new ReferenceByIdModel(), Map);
}
// Umbraco.Code.MapAll
@@ -56,4 +57,10 @@ public class TrackedReferenceViewModelsMapDefinition : IMapDefinition
{
target.Id = source.NodeKey;
}
// Umbraco.Code.MapAll
private void Map(Guid source, ReferenceByIdModel target, MapperContext context)
{
target.Id = source;
}
}

View File

@@ -131,4 +131,14 @@ public interface ITrackedReferencesRepository
long take,
bool filterMustBeIsDependency,
out long totalRecords);
Task<PagedModel<Guid>> GetPagedNodeKeysWithDependantReferencesAsync(
ISet<Guid> keys,
Guid nodeObjectTypeId,
long skip,
long take)
{
IEnumerable<RelationItemModel> pagedItems = GetPagedItemsWithRelations(keys, skip, take, true, out var total);
return Task.FromResult(new PagedModel<Guid>(total, pagedItems.Select(i => i.NodeKey)));
}
}

View File

@@ -97,4 +97,10 @@ public interface ITrackedReferencesService
/// <returns>A paged result of <see cref="RelationItemModel" /> objects.</returns>
Task<PagedModel<RelationItemModel>> GetPagedItemsWithRelationsAsync(ISet<Guid> keys, long skip, long take,
bool filterMustBeIsDependency);
Task<PagedModel<Guid>> GetPagedKeysWithDependentReferencesAsync(ISet<Guid> keys, Guid nodeObjectTypeId, long skip, long take)
{
PagedModel<RelationItemModel> pagedItems = GetPagedItemsWithRelationsAsync(keys, skip, take, true).GetAwaiter().GetResult();
return Task.FromResult(new PagedModel<Guid>(pagedItems.Total, pagedItems.Items.Select(i => i.NodeKey)));
}
}

View File

@@ -141,4 +141,10 @@ public class TrackedReferencesService : ITrackedReferencesService
return await Task.FromResult(pagedModel);
}
public async Task<PagedModel<Guid>> GetPagedKeysWithDependentReferencesAsync(ISet<Guid> keys, Guid objectTypeId, long skip, long take)
{
using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true);
return await _trackedReferencesRepository.GetPagedNodeKeysWithDependantReferencesAsync(keys, objectTypeId, skip, take);
}
}

View File

@@ -477,10 +477,49 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
return _umbracoMapper.MapEnumerable<RelationItemDto, RelationItemModel>(pagedResult);
}
public IEnumerable<RelationItemModel> GetPagedDescendantsInReferences(
Guid parentKey,
public async Task<PagedModel<Guid>> GetPagedNodeKeysWithDependantReferencesAsync(
ISet<Guid> keys,
Guid nodeObjectTypeId,
long skip,
long take,
long take)
{
if (_scopeAccessor.AmbientScope is null)
{
throw new InvalidOperationException("Can not execute without a valid AmbientScope");
}
Sql<ISqlContext>? sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql()
.SelectDistinct<NodeDto>(node => node.UniqueId)
.From<NodeDto>()
.InnerJoin<RelationDto>()
.On<NodeDto, RelationDto>((node, relation) =>
node.NodeId == relation.ParentId || node.NodeId == relation.ParentId || node.NodeId == relation.ChildId)
.InnerJoin<RelationTypeDto>()
.On<RelationDto, RelationTypeDto>((relation, relationType) => relation.RelationType == relationType.Id && relationType.IsDependency)
.Where<NodeDto, RelationDto, RelationTypeDto>(
(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<Guid>(totalRecords, Enumerable.Empty<Guid>());
}
// Ordering is required for paging
sql = sql.OrderBy<NodeDto>(node => node.UniqueId);
IEnumerable<Guid> result = await _scopeAccessor.AmbientScope.Database.SkipTakeAsync<Guid>(skip, take, sql);
return new PagedModel<Guid>(totalRecords, result);
}
public IEnumerable<RelationItemModel> GetPagedDescendantsInReferences(Guid parentKey, long skip, long take,
bool filterMustBeIsDependency,
out long totalRecords)
{