Query optimization in TrackedReferencesRepository (#12488)
* Rewrote query https://github.com/umbraco/Umbraco-CMS/issues/12308 * Reuse new query * Updated the other queries in TrackedReferencesRepository * Fix GetPagedRelationsForItem to not include self * Update src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
This commit is contained in:
@@ -24,38 +24,41 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
/// </summary>
|
||||
public IEnumerable<RelationItem> GetPagedItemsWithRelations(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords)
|
||||
{
|
||||
Sql<ISqlContext> innerUnionSql = GetInnerUnionSql();
|
||||
var sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().SelectDistinct(
|
||||
"[pn].[id] as nodeId",
|
||||
"[pn].[uniqueId] as nodeKey",
|
||||
"[pn].[text] as nodeName",
|
||||
"[pn].[nodeObjectType] as nodeObjectType",
|
||||
"[ct].[icon] as contentTypeIcon",
|
||||
"[ct].[alias] as contentTypeAlias",
|
||||
"[ctn].[text] as contentTypeName",
|
||||
"[umbracoRelationType].[alias] as relationTypeAlias",
|
||||
"[umbracoRelationType].[name] as relationTypeName",
|
||||
"[umbracoRelationType].[isDependency] as relationTypeIsDependency",
|
||||
"[umbracoRelationType].[dual] as relationTypeIsBidirectional")
|
||||
.From<RelationDto>("r")
|
||||
.InnerJoin<RelationTypeDto>("umbracoRelationType").On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight: "umbracoRelationType")
|
||||
.InnerJoin<NodeDto>("cn").On<RelationDto, NodeDto, RelationTypeDto>((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId)), aliasLeft: "r", aliasRight: "cn", aliasOther: "umbracoRelationType")
|
||||
.InnerJoin<NodeDto>("pn").On<RelationDto, NodeDto, NodeDto>((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight: "pn", aliasOther: "cn")
|
||||
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft: "pn", aliasRight: "c")
|
||||
.LeftJoin<ContentTypeDto>("ct").On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft: "c", aliasRight: "ct")
|
||||
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId, aliasLeft: "ct", aliasRight: "ctn");
|
||||
|
||||
"[x].[id] as nodeId",
|
||||
"[n].[uniqueId] as nodeKey",
|
||||
"[n].[text] as nodeName",
|
||||
"[n].[nodeObjectType] as nodeObjectType",
|
||||
"[ct].[icon] as contentTypeIcon",
|
||||
"[ct].[alias] as contentTypeAlias",
|
||||
"[ctn].[text] as contentTypeName",
|
||||
"[x].[alias] as relationTypeAlias",
|
||||
"[x].[name] as relationTypeName",
|
||||
"[x].[isDependency] as relationTypeIsDependency",
|
||||
"[x].[dual] as relationTypeIsBidirectional")
|
||||
.From<NodeDto>("n")
|
||||
.InnerJoinNested(innerUnionSql, "x")
|
||||
.On<NodeDto, UnionHelperDto>((n, x) => n.NodeId == x.Id, "n", "x")
|
||||
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft: "n",
|
||||
aliasRight: "c")
|
||||
.LeftJoin<ContentTypeDto>("ct")
|
||||
.On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft: "c",
|
||||
aliasRight: "ct")
|
||||
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId,
|
||||
aliasLeft: "ct", aliasRight: "ctn");
|
||||
if (ids.Any())
|
||||
{
|
||||
sql = sql.Where<NodeDto>(x => ids.Contains(x.NodeId), "pn");
|
||||
sql = sql.Where<NodeDto>(x => ids.Contains(x.NodeId), "n");
|
||||
}
|
||||
|
||||
if (filterMustBeIsDependency)
|
||||
{
|
||||
sql = sql.Where<RelationTypeDto>(rt => rt.IsDependency, "umbracoRelationType");
|
||||
sql = sql.Where<RelationTypeDto>(rt => rt.IsDependency, "x");
|
||||
}
|
||||
|
||||
// Ordering is required for paging
|
||||
sql = sql.OrderBy<RelationTypeDto>(x => x.Alias);
|
||||
sql = sql.OrderBy<RelationTypeDto>(x => x.Alias, "x");
|
||||
|
||||
var pagedResult = _scopeAccessor.AmbientScope.Database.Page<RelationItemDto>(pageIndex + 1, pageSize, sql);
|
||||
totalRecords = pagedResult.TotalItems;
|
||||
@@ -63,6 +66,31 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
return pagedResult.Items.Select(MapDtoToEntity);
|
||||
}
|
||||
|
||||
private Sql<ISqlContext> GetInnerUnionSql()
|
||||
{
|
||||
var innerUnionSqlChild = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select(
|
||||
"[cr].childId as id", "[cr].parentId as otherId", "[rt].[alias]", "[rt].[name]", "[rt].[isDependency]", "[rt].[dual]")
|
||||
.From<RelationDto>("cr").InnerJoin<RelationTypeDto>("rt")
|
||||
.On<RelationDto, RelationTypeDto>((cr, rt) => rt.Dual == false && rt.Id == cr.RelationType, "cr", "rt");
|
||||
|
||||
var innerUnionSqlDualParent = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select(
|
||||
"[dpr].parentId as id", "[dpr].childId as otherId", "[dprt].[alias]", "[dprt].[name]", "[dprt].[isDependency]", "[dprt].[dual]")
|
||||
.From<RelationDto>("dpr").InnerJoin<RelationTypeDto>("dprt")
|
||||
.On<RelationDto, RelationTypeDto>((dpr, dprt) => dprt.Dual == true && dprt.Id == dpr.RelationType, "dpr",
|
||||
"dprt");
|
||||
|
||||
var innerUnionSql3 = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select(
|
||||
"[dcr].childId as id", "[dcr].parentId as otherId", "[dcrt].[alias]", "[dcrt].[name]", "[dcrt].[isDependency]", "[dcrt].[dual]")
|
||||
.From<RelationDto>("dcr").InnerJoin<RelationTypeDto>("dcrt")
|
||||
.On<RelationDto, RelationTypeDto>((dcr, dcrt) => dcrt.Dual == true && dcrt.Id == dcr.RelationType, "dcr",
|
||||
"dcrt");
|
||||
|
||||
|
||||
var innerUnionSql = innerUnionSqlChild.Union(innerUnionSqlDualParent).Union(innerUnionSql3);
|
||||
|
||||
return innerUnionSql;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a page of the descending items that have any references, given a parent id.
|
||||
/// </summary>
|
||||
@@ -98,35 +126,38 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
.WhereLike<NodeDto>(x => x.Path, subsubQuery);
|
||||
}
|
||||
|
||||
// Get all relations where parent is in the sub query
|
||||
Sql<ISqlContext> innerUnionSql = GetInnerUnionSql();
|
||||
var sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().SelectDistinct(
|
||||
"[pn].[id] as nodeId",
|
||||
"[pn].[uniqueId] as nodeKey",
|
||||
"[pn].[text] as nodeName",
|
||||
"[pn].[nodeObjectType] as nodeObjectType",
|
||||
"[ct].[icon] as contentTypeIcon",
|
||||
"[ct].[alias] as contentTypeAlias",
|
||||
"[ctn].[text] as contentTypeName",
|
||||
"[umbracoRelationType].[alias] as relationTypeAlias",
|
||||
"[umbracoRelationType].[name] as relationTypeName",
|
||||
"[umbracoRelationType].[isDependency] as relationTypeIsDependency",
|
||||
"[umbracoRelationType].[dual] as relationTypeIsBidirectional")
|
||||
.From<RelationDto>("r")
|
||||
.InnerJoin<RelationTypeDto>("umbracoRelationType").On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight: "umbracoRelationType")
|
||||
.InnerJoin<NodeDto>("cn").On<RelationDto, NodeDto, RelationTypeDto>((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId)), aliasLeft: "r", aliasRight: "cn", aliasOther: "umbracoRelationType")
|
||||
.InnerJoin<NodeDto>("pn").On<RelationDto, NodeDto, NodeDto>((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight: "pn", aliasOther: "cn")
|
||||
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft: "pn", aliasRight: "c")
|
||||
.LeftJoin<ContentTypeDto>("ct").On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft: "c", aliasRight: "ct")
|
||||
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId, aliasLeft: "ct", aliasRight: "ctn")
|
||||
.WhereIn((System.Linq.Expressions.Expression<Func<NodeDto, object>>)(x => x.NodeId), subQuery, "pn");
|
||||
"[x].[id] as nodeId",
|
||||
"[n].[uniqueId] as nodeKey",
|
||||
"[n].[text] as nodeName",
|
||||
"[n].[nodeObjectType] as nodeObjectType",
|
||||
"[ct].[icon] as contentTypeIcon",
|
||||
"[ct].[alias] as contentTypeAlias",
|
||||
"[ctn].[text] as contentTypeName",
|
||||
"[x].[alias] as relationTypeAlias",
|
||||
"[x].[name] as relationTypeName",
|
||||
"[x].[isDependency] as relationTypeIsDependency",
|
||||
"[x].[dual] as relationTypeIsBidirectional")
|
||||
.From<NodeDto>("n")
|
||||
.InnerJoinNested(innerUnionSql, "x")
|
||||
.On<NodeDto, UnionHelperDto>((n, x) => n.NodeId == x.Id, "n", "x")
|
||||
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft: "n",
|
||||
aliasRight: "c")
|
||||
.LeftJoin<ContentTypeDto>("ct")
|
||||
.On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft: "c",
|
||||
aliasRight: "ct")
|
||||
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId,
|
||||
aliasLeft: "ct", aliasRight: "ctn");
|
||||
sql = sql.WhereIn((System.Linq.Expressions.Expression<Func<NodeDto, object>>)(x => x.NodeId), subQuery, "n");
|
||||
|
||||
if (filterMustBeIsDependency)
|
||||
{
|
||||
sql = sql.Where<RelationTypeDto>(rt => rt.IsDependency, "umbracoRelationType");
|
||||
sql = sql.Where<RelationTypeDto>(rt => rt.IsDependency, "x");
|
||||
}
|
||||
|
||||
// Ordering is required for paging
|
||||
sql = sql.OrderBy<RelationTypeDto>(x => x.Alias);
|
||||
sql = sql.OrderBy<RelationTypeDto>(x => x.Alias, "x");
|
||||
|
||||
var pagedResult = _scopeAccessor.AmbientScope.Database.Page<RelationItemDto>(pageIndex + 1, pageSize, sql);
|
||||
totalRecords = pagedResult.TotalItems;
|
||||
@@ -134,41 +165,45 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
return pagedResult.Items.Select(MapDtoToEntity);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets a page of items which are in relation with the current item.
|
||||
/// Basically, shows the items which depend on the current item.
|
||||
/// </summary>
|
||||
public IEnumerable<RelationItem> GetPagedRelationsForItem(int id, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords)
|
||||
{
|
||||
Sql<ISqlContext> innerUnionSql = GetInnerUnionSql();
|
||||
var sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().SelectDistinct(
|
||||
"[cn].[id] as nodeId",
|
||||
"[cn].[uniqueId] as nodeKey",
|
||||
"[cn].[text] as nodeName",
|
||||
"[cn].[nodeObjectType] as nodeObjectType",
|
||||
"[ct].[icon] as contentTypeIcon",
|
||||
"[ct].[alias] as contentTypeAlias",
|
||||
"[ctn].[text] as contentTypeName",
|
||||
"[umbracoRelationType].[alias] as relationTypeAlias",
|
||||
"[umbracoRelationType].[name] as relationTypeName",
|
||||
"[umbracoRelationType].[isDependency] as relationTypeIsDependency",
|
||||
"[umbracoRelationType].[dual] as relationTypeIsBidirectional")
|
||||
.From<RelationDto>("r")
|
||||
.InnerJoin<RelationTypeDto>("umbracoRelationType").On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight: "umbracoRelationType")
|
||||
.InnerJoin<NodeDto>("cn").On<RelationDto, NodeDto, RelationTypeDto>((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId)), aliasLeft: "r", aliasRight: "cn", aliasOther: "umbracoRelationType")
|
||||
.InnerJoin<NodeDto>("pn").On<RelationDto, NodeDto, NodeDto>((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight: "pn", aliasOther: "cn")
|
||||
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft: "cn", aliasRight: "c")
|
||||
.LeftJoin<ContentTypeDto>("ct").On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft: "c", aliasRight: "ct")
|
||||
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId, aliasLeft: "ct", aliasRight: "ctn")
|
||||
.Where<NodeDto>(x => x.NodeId == id, "pn")
|
||||
.Where<RelationDto>(x => x.ChildId == id || x.ParentId == id, "r"); // This last Where is purely to help SqlServer make a smarter query plan. More info https://github.com/umbraco/Umbraco-CMS/issues/12190
|
||||
"[x].[otherId] as nodeId",
|
||||
"[n].[uniqueId] as nodeKey",
|
||||
"[n].[text] as nodeName",
|
||||
"[n].[nodeObjectType] as nodeObjectType",
|
||||
"[ct].[icon] as contentTypeIcon",
|
||||
"[ct].[alias] as contentTypeAlias",
|
||||
"[ctn].[text] as contentTypeName",
|
||||
"[x].[alias] as relationTypeAlias",
|
||||
"[x].[name] as relationTypeName",
|
||||
"[x].[isDependency] as relationTypeIsDependency",
|
||||
"[x].[dual] as relationTypeIsBidirectional")
|
||||
.From<NodeDto>("n")
|
||||
.InnerJoinNested(innerUnionSql, "x")
|
||||
.On<NodeDto, UnionHelperDto>((n, x) => n.NodeId == x.OtherId, "n", "x")
|
||||
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft: "n",
|
||||
aliasRight: "c")
|
||||
.LeftJoin<ContentTypeDto>("ct")
|
||||
.On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft: "c",
|
||||
aliasRight: "ct")
|
||||
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId,
|
||||
aliasLeft: "ct", aliasRight: "ctn")
|
||||
.Where<UnionHelperDto>(x => x.OtherId != id, "x");
|
||||
|
||||
if (filterMustBeIsDependency)
|
||||
{
|
||||
sql = sql.Where<RelationTypeDto>(rt => rt.IsDependency, "umbracoRelationType");
|
||||
sql = sql.Where<RelationTypeDto>(rt => rt.IsDependency, "x");
|
||||
}
|
||||
|
||||
// Ordering is required for paging
|
||||
sql = sql.OrderBy<RelationTypeDto>(x => x.Alias);
|
||||
sql = sql.OrderBy<RelationTypeDto>(x => x.Alias, "x");
|
||||
|
||||
var pagedResult = _scopeAccessor.AmbientScope.Database.Page<RelationItemDto>(pageIndex + 1, pageSize, sql);
|
||||
totalRecords = pagedResult.TotalItems;
|
||||
@@ -176,6 +211,27 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
return pagedResult.Items.Select(MapDtoToEntity);
|
||||
}
|
||||
|
||||
private class UnionHelperDto
|
||||
{
|
||||
[Column("id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Column("otherId")]
|
||||
public int OtherId { get; set; }
|
||||
|
||||
[Column("alias")]
|
||||
public string Alias { get; set; }
|
||||
|
||||
[Column("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Column("isDependency")]
|
||||
public bool IsDependency { get; set; }
|
||||
|
||||
[Column("dual")]
|
||||
public bool Dual { get; set; }
|
||||
}
|
||||
|
||||
private RelationItem MapDtoToEntity(RelationItemDto dto)
|
||||
{
|
||||
return new RelationItem()
|
||||
|
||||
Reference in New Issue
Block a user