Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ public DataTypeTreeControllerBase(IEntityService entityService, IDataTypeService

protected override UmbracoObjectTypes FolderObjectType => UmbracoObjectTypes.DataTypeContainer;

protected override Ordering ItemOrdering
{
get
{
var ordering = Ordering.By(nameof(Infrastructure.Persistence.Dtos.NodeDto.NodeObjectType), Direction.Descending); // We need to override to change direction
ordering.Next = Ordering.By(nameof(Infrastructure.Persistence.Dtos.NodeDto.Text));

return ordering;
}
}

protected override DataTypeTreeItemResponseModel[] MapTreeItemViewModels(Guid? parentId, IEntitySlim[] entities)
{
var dataTypes = _dataTypeService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ public abstract class FolderTreeControllerBase<TItem> : NamedEntityTreeControlle
private readonly Guid _folderObjectTypeId;
private bool _foldersOnly;



protected override Ordering ItemOrdering
{
get
{
// Override to order by type (folder vs item) before the text
var ordering = Ordering.By(nameof(Infrastructure.Persistence.Dtos.NodeDto.NodeObjectType));
ordering.Next = Ordering.By(nameof(Infrastructure.Persistence.Dtos.NodeDto.Text));

return ordering;
}
}

protected FolderTreeControllerBase(IEntityService entityService)
: base(entityService) =>
// ReSharper disable once VirtualMemberCallInConstructor
Expand Down Expand Up @@ -76,38 +90,19 @@ private IEntitySlim[] GetEntities(Guid? parentKey, int skip, int take, out long
{
totalItems = 0;

if (take == 0)
{
totalItems = _foldersOnly
? EntityService.CountChildren(parentKey, FolderObjectType)
: EntityService.CountChildren(parentKey, FolderObjectType)
+ EntityService.CountChildren(parentKey, ItemObjectType);
return Array.Empty<IEntitySlim>();
}
UmbracoObjectTypes[] childObjectTypes = _foldersOnly ? [FolderObjectType] : [FolderObjectType, ItemObjectType];

// EntityService is not able to paginate children of multiple item types, so we will only paginate the
// item type entities and always return all folders as part of the the first result "page" i.e. when skip is 0
IEntitySlim[] folderEntities = skip == 0
? EntityService.GetChildren(parentKey, FolderObjectType).OrderBy(c => c.Name).ToArray()
: Array.Empty<IEntitySlim>();
IEntitySlim[] itemEntities = _foldersOnly
? Array.Empty<IEntitySlim>()
: EntityService.GetPagedChildren(
IEntitySlim[] itemEntities = EntityService.GetPagedChildren(
parentKey,
new [] { FolderObjectType, ItemObjectType },
ItemObjectType,
childObjectTypes,
skip,
take,
false,
out totalItems,
ordering: ItemOrdering)
.ToArray();

// the GetChildren for folders does not return an amount and does not get executed when beyond the first page
// but the items still count towards the total, so add these to either 0 when only folders, or the out param from paged
totalItems += skip == 0
? folderEntities.Length
: EntityService.CountChildren(parentKey, FolderObjectType);

return folderEntities.Union(itemEntities).ToArray();
return itemEntities;
}
}
14 changes: 13 additions & 1 deletion src/Umbraco.Core/Persistence/Repositories/IEntityRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,19 @@ IEnumerable<IEntitySlim> GetPagedResultsByQuery(
int pageSize,
out long totalRecords,
IQuery<IUmbracoEntity>? filter,
Ordering? ordering) =>
GetPagedResultsByQuery(query, new HashSet<Guid>(){objectType}, pageIndex, pageSize, out totalRecords, filter, ordering);

IEnumerable<IEntitySlim> GetPagedResultsByQuery(
IQuery<IUmbracoEntity> query,
ISet<Guid> objectTypes,
long pageIndex,
int pageSize,
out long totalRecords,
IQuery<IUmbracoEntity>? filter,
Ordering? ordering);

int CountByQuery(IQuery<IUmbracoEntity> query, Guid objectType, IQuery<IUmbracoEntity>? filter);
int CountByQuery(IQuery<IUmbracoEntity> query, Guid objectType, IQuery<IUmbracoEntity>? filter) =>
CountByQuery(query, new HashSet<Guid>() { objectType }, filter);
int CountByQuery(IQuery<IUmbracoEntity> query, IEnumerable<Guid> objectTypes, IQuery<IUmbracoEntity>? filter);
}
73 changes: 55 additions & 18 deletions src/Umbraco.Core/Services/EntityService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,12 @@ public IEnumerable<IEntitySlim> GetPagedChildren(
return Enumerable.Empty<IEntitySlim>();
}

if (take == 0)
{
totalRecords = CountChildren(parentId, childObjectType, filter);
return Enumerable.Empty<IEntitySlim>();
}

PaginationHelper.ConvertSkipTakeToPaging(skip, take, out var pageNumber, out var pageSize);

IEnumerable<IEntitySlim> children = GetPagedChildren(
Expand Down Expand Up @@ -645,34 +651,23 @@ public int ReserveId(Guid key)
}
}

public int CountChildren(
private int CountChildren(int id, UmbracoObjectTypes objectType, IQuery<IUmbracoEntity>? filter = null) =>
CountChildren(id, new HashSet<UmbracoObjectTypes>() { objectType }, filter);

private int CountChildren(
int id,
UmbracoObjectTypes objectType,
IEnumerable<UmbracoObjectTypes> objectTypes,
IQuery<IUmbracoEntity>? filter = null)
{
using (ScopeProvider.CreateCoreScope(autoComplete: true))
{
IQuery<IUmbracoEntity> query = Query<IUmbracoEntity>().Where(x => x.ParentId == id && x.Trashed == false);

return _entityRepository.CountByQuery(query, objectType.GetGuid(), filter);
var objectTypeGuids = objectTypes.Select(x => x.GetGuid()).ToHashSet();
return _entityRepository.CountByQuery(query, objectTypeGuids, filter);
}
}

public int CountChildren(Guid? key, UmbracoObjectTypes objectType, IQuery<IUmbracoEntity>? filter = null)
{
using ICoreScope scope = ScopeProvider.CreateCoreScope();

if (ResolveKey(key, objectType, out var parentId) is false)
{
return 0;
}

var count = CountChildren(parentId, objectType, filter);

scope.Complete();
return count;
}

private bool ResolveKey(Guid? key, UmbracoObjectTypes objectType, out int id)
{
// We have to explicitly check for "root key" since this value is null, and GetId does not accept null.
Expand Down Expand Up @@ -721,5 +716,47 @@ private IEnumerable<IEntitySlim> GetPagedChildren(
return _entityRepository.GetPagedResultsByQuery(query, objectType.GetGuid(), pageIndex, pageSize, out totalRecords, filter, ordering);
}
}

public IEnumerable<IEntitySlim> GetPagedChildren(
Guid? parentKey,
IEnumerable<UmbracoObjectTypes> parentObjectTypes,
IEnumerable<UmbracoObjectTypes> childObjectTypes,
int skip,
int take,
bool trashed,
out long totalRecords,
IQuery<IUmbracoEntity>? filter = null,
Ordering? ordering = null)
{
using (ScopeProvider.CreateCoreScope(autoComplete: true))
{
var parentId = 0;
var parentIdResolved = parentObjectTypes.Any(parentObjectType => ResolveKey(parentKey, parentObjectType, out parentId));
if (parentIdResolved is false)
{
totalRecords = 0;
return Enumerable.Empty<IEntitySlim>();
}

if (take == 0)
{
totalRecords = CountChildren(parentId, childObjectTypes, filter);
return Array.Empty<IEntitySlim>();
}

IQuery<IUmbracoEntity> query = Query<IUmbracoEntity>().Where(x => x.ParentId == parentId && x.Trashed == trashed);

PaginationHelper.ConvertSkipTakeToPaging(skip, take, out var pageNumber, out var pageSize);

var objectTypeGuids = childObjectTypes.Select(x => x.GetGuid()).ToHashSet();
if (pageSize == 0)
{
totalRecords = _entityRepository.CountByQuery(query, objectTypeGuids, filter);
return Enumerable.Empty<IEntitySlim>();
}

return _entityRepository.GetPagedResultsByQuery(query, objectTypeGuids, pageNumber, pageSize, out totalRecords, filter, ordering);
}
}
}

22 changes: 15 additions & 7 deletions src/Umbraco.Core/Services/IEntityService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,21 @@ IEnumerable<IEntitySlim> GetPagedChildren(
return Array.Empty<IEntitySlim>();
}

IEnumerable<IEntitySlim> GetPagedChildren(
Guid? parentKey,
IEnumerable<UmbracoObjectTypes> parentObjectTypes,
IEnumerable<UmbracoObjectTypes> childObjectTypes,
int skip,
int take,
bool trashed,
out long totalRecords,
IQuery<IUmbracoEntity>? filter = null,
Ordering? ordering = null)
{
totalRecords = 0;
return Array.Empty<IEntitySlim>();
}

/// <summary>
/// Gets children of an entity.
/// </summary>
Expand Down Expand Up @@ -367,11 +382,4 @@ IEnumerable<IEntitySlim> GetPagedDescendants(
/// <returns>The identifier.</returns>
/// <remarks>When a new content or a media is saved with the key, it will have the reserved identifier.</remarks>
int ReserveId(Guid key);

/// <summary>
/// Counts the children of an entity
/// </summary>
int CountChildren(int id, UmbracoObjectTypes objectType, IQuery<IUmbracoEntity>? filter = null);

public int CountChildren(Guid? key, UmbracoObjectTypes objectType, IQuery<IUmbracoEntity>? filter = null) => 0;
}
1 change: 1 addition & 0 deletions src/Umbraco.Core/Services/Ordering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public Ordering(string? orderBy, Direction direction = Direction.Ascending, stri
IsCustomField = isCustomField;
}

public Ordering? Next { get; set; } = null;
/// <summary>
/// Gets the name of the ordering field.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ public EntityRepository(IScopeAccessor scopeAccessor, AppCaches appCaches)

#region Repository

public int CountByQuery(IQuery<IUmbracoEntity> query, Guid objectType, IQuery<IUmbracoEntity>? filter)
public int CountByQuery(IQuery<IUmbracoEntity> query, IEnumerable<Guid> objectTypes, IQuery<IUmbracoEntity>? filter)
{
Sql<ISqlContext> sql = Sql();
sql.SelectCount();
sql
.From<NodeDto>();
sql.WhereIn<NodeDto>(x => x.NodeObjectType, new[] { objectType } );
sql.WhereIn<NodeDto>(x => x.NodeObjectType, objectTypes );

foreach (Tuple<string, object[]> queryClause in query.GetWhereClauses())
{
Expand All @@ -54,10 +54,11 @@ public int CountByQuery(IQuery<IUmbracoEntity> query, Guid objectType, IQuery<IU
return Database.ExecuteScalar<int>(sql);
}

public IEnumerable<IEntitySlim> GetPagedResultsByQuery(IQuery<IUmbracoEntity> query, Guid objectType,
public IEnumerable<IEntitySlim> GetPagedResultsByQuery(IQuery<IUmbracoEntity> query, ISet<Guid> objectTypes,
long pageIndex, int pageSize, out long totalRecords,
IQuery<IUmbracoEntity>? filter, Ordering? ordering) =>
GetPagedResultsByQuery(query, new[] {objectType}, pageIndex, pageSize, out totalRecords, filter, ordering);
GetPagedResultsByQuery(query, objectTypes.ToArray(), pageIndex, pageSize, out totalRecords, filter, ordering);


// get a page of entities
public IEnumerable<IEntitySlim> GetPagedResultsByQuery(IQuery<IUmbracoEntity> query, Guid[] objectTypes,
Expand Down Expand Up @@ -635,25 +636,37 @@ private void ApplyOrdering(ref Sql<ISqlContext> sql, Ordering ordering)
// TODO: although the default ordering string works for name, it wont work for others without a table or an alias of some sort
// As more things are attempted to be sorted we'll prob have to add more expressions here
string orderBy;
switch (ordering.OrderBy?.ToUpperInvariant())
{
case "PATH":
orderBy = SqlSyntax.GetQuotedColumn(NodeDto.TableName, "path");
break;

default:
orderBy = ordering.OrderBy ?? string.Empty;
break;
}
Ordering? runner = ordering;

if (ordering.Direction == Direction.Ascending)
do
{
sql.OrderBy(orderBy);
}
else
{
sql.OrderByDescending(orderBy);

switch (runner.OrderBy?.ToUpperInvariant())
{
case "PATH":
orderBy = SqlSyntax.GetQuotedColumn(NodeDto.TableName, "path");
break;

default:
orderBy = runner.OrderBy ?? string.Empty;
break;
}

if (runner.Direction == Direction.Ascending)
{
sql.OrderBy(orderBy);
}
else
{
sql.OrderByDescending(orderBy);
}

runner = runner.Next;
}
while (runner is not null);


}

#endregion
Expand Down