diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs index e60fd6623e..4c7cb57f19 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs @@ -80,7 +80,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource /// /// /// - private Sql ContentSourcesCount(IScope scope) + private Sql ContentSourcesCount(IScope scope, Func, Sql> joins = null) { var sqlTemplate = scope.SqlContext.Templates.Get(Constants.SqlTemplates.NuCacheDatabaseDataSource.ContentSourcesCount, tsql => tsql.Select(x => Alias(x.NodeId, "Id")) @@ -90,6 +90,9 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource var sql = sqlTemplate.Sql(); + if (joins != null) + sql = joins(sql); + // TODO: We can't use a template with this one because of the 'right.Current' and 'right.Published' ends up being a parameter so not sure how we can do that sql = sql .InnerJoin().On((left, right) => left.NodeId == right.NodeId && right.Current) @@ -147,12 +150,20 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource .Where(x => x.NodeId == id, "x") .OrderBy(x => x.Level, x => x.ParentId, x => x.SortOrder); + // create a more efficient COUNT query without the join on the cmsContentNu table + var sqlCountQuery = ContentSourcesCount(scope, + s => s.InnerJoin("x").On((left, right) => left.NodeId == right.NodeId || SqlText(left.Path, right.Path, (lp, rp) => $"({lp} LIKE {syntax.GetConcat(rp, "',%'")})"), aliasRight: "x")) + .Where(x => x.NodeObjectType == Constants.ObjectTypes.Document && !x.Trashed) + .Where(x => x.NodeId == id, "x"); + var sqlCount = scope.SqlContext.Sql("SELECT COUNT(*) FROM (").Append(sqlCountQuery).Append(") npoco_tbl"); + + var serializer = _contentCacheDataSerializerFactory.Create(ContentCacheDataSerializerEntityType.Document); // We need to page here. We don't want to iterate over every single row in one connection cuz this can cause an SQL Timeout. // We also want to read with a db reader and not load everything into memory, QueryPaged lets us do that. - foreach (var row in scope.Database.QueryPaged(PageSize, sql)) + foreach (var row in scope.Database.QueryPaged(PageSize, sql, sqlCount)) { yield return CreateContentNodeKit(row, serializer); } @@ -167,12 +178,17 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource .WhereIn(x => x.ContentTypeId, ids) .OrderBy(x => x.Level, x => x.ParentId, x => x.SortOrder); + var sqlCountQuery = ContentSourcesCount(scope) + .Where(x => x.NodeObjectType == Constants.ObjectTypes.Document && !x.Trashed) + .WhereIn(x => x.ContentTypeId, ids); + var sqlCount = scope.SqlContext.Sql("SELECT COUNT(*) FROM (").Append(sqlCountQuery).Append(") npoco_tbl"); + var serializer = _contentCacheDataSerializerFactory.Create(ContentCacheDataSerializerEntityType.Document); // We need to page here. We don't want to iterate over every single row in one connection cuz this can cause an SQL Timeout. // We also want to read with a db reader and not load everything into memory, QueryPaged lets us do that. - foreach (var row in scope.Database.QueryPaged(PageSize, sql)) + foreach (var row in scope.Database.QueryPaged(PageSize, sql, sqlCount)) { yield return CreateContentNodeKit(row, serializer); } @@ -182,9 +198,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource { var sql = scope.SqlContext.Sql() - .Select(x => Alias(x.NodeId, "Id"), x => Alias(x.UniqueId, "Uid"), - x => Alias(x.Level, "Level"), x => Alias(x.Path, "Path"), x => Alias(x.SortOrder, "SortOrder"), x => Alias(x.ParentId, "ParentId"), - x => Alias(x.CreateDate, "CreateDate"), x => Alias(x.UserId, "CreatorId")) + .Select(x => Alias(x.NodeId, "Id")) .AndSelect(x => Alias(x.ContentTypeId, "ContentTypeId")) .AndSelect(x => Alias(x.Id, "VersionId"), x => Alias(x.Text, "EditName"), x => Alias(x.VersionDate, "EditVersionDate"), x => Alias(x.UserId, "EditWriterId")) .AndSelect("nuEdit", x => Alias(x.Data, "EditData")) @@ -201,6 +215,22 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource return sql; } + private Sql MediaSourcesCount(IScope scope, Func, Sql> joins = null) + { + var sql = scope.SqlContext.Sql() + + .Select(x => Alias(x.NodeId, "Id")) + .From(); + + if (joins != null) + sql = joins(sql); + + sql = sql + .InnerJoin().On((left, right) => left.NodeId == right.NodeId) + .InnerJoin().On((left, right) => left.NodeId == right.NodeId && right.Current); + + return sql; + } public ContentNodeKit GetMediaSource(IScope scope, int id) { @@ -222,12 +252,17 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource .Where(x => x.NodeObjectType == Constants.ObjectTypes.Media && !x.Trashed) .OrderBy(x => x.Level, x => x.ParentId, x => x.SortOrder); + var sqlCountQuery = MediaSourcesCount(scope) + .Where(x => x.NodeObjectType == Constants.ObjectTypes.Media && !x.Trashed); + + var sqlCount = scope.SqlContext.Sql("SELECT COUNT(*) FROM (").Append(sqlCountQuery).Append(") npoco_tbl"); + var serializer = _contentCacheDataSerializerFactory.Create(ContentCacheDataSerializerEntityType.Media); // We need to page here. We don't want to iterate over every single row in one connection cuz this can cause an SQL Timeout. // We also want to read with a db reader and not load everything into memory, QueryPaged lets us do that. - foreach (var row in scope.Database.QueryPaged(PageSize, sql)) + foreach (var row in scope.Database.QueryPaged(PageSize, sql, sqlCount)) { yield return CreateMediaNodeKit(row, serializer); } @@ -242,12 +277,19 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource .Where(x => x.NodeId == id, "x") .OrderBy(x => x.Level, x => x.ParentId, x => x.SortOrder); + var sqlCountQuery = MediaSourcesCount(scope, + s => s.InnerJoin("x").On((left, right) => left.NodeId == right.NodeId || SqlText(left.Path, right.Path, (lp, rp) => $"({lp} LIKE {syntax.GetConcat(rp, "',%'")})"), aliasRight: "x")) + .Where(x => x.NodeObjectType == Constants.ObjectTypes.Media && !x.Trashed) + .Where(x => x.NodeId == id, "x"); + + var sqlCount = scope.SqlContext.Sql("SELECT COUNT(*) FROM (").Append(sqlCountQuery).Append(") npoco_tbl"); + var serializer = _contentCacheDataSerializerFactory.Create(ContentCacheDataSerializerEntityType.Media); // We need to page here. We don't want to iterate over every single row in one connection cuz this can cause an SQL Timeout. // We also want to read with a db reader and not load everything into memory, QueryPaged lets us do that. - foreach (var row in scope.Database.QueryPaged(PageSize, sql)) + foreach (var row in scope.Database.QueryPaged(PageSize, sql, sqlCount)) { yield return CreateMediaNodeKit(row, serializer); } @@ -262,12 +304,18 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource .WhereIn(x => x.ContentTypeId, ids) .OrderBy(x => x.Level, x => x.ParentId, x => x.SortOrder); + var sqlCountQuery = MediaSourcesCount(scope) + .Where(x => x.NodeObjectType == Constants.ObjectTypes.Media && !x.Trashed) + .WhereIn(x => x.ContentTypeId, ids); + + var sqlCount = scope.SqlContext.Sql("SELECT COUNT(*) FROM (").Append(sqlCountQuery).Append(") npoco_tbl"); + var serializer = _contentCacheDataSerializerFactory.Create(ContentCacheDataSerializerEntityType.Media); // We need to page here. We don't want to iterate over every single row in one connection cuz this can cause an SQL Timeout. // We also want to read with a db reader and not load everything into memory, QueryPaged lets us do that. - foreach (var row in scope.Database.QueryPaged(PageSize, sql)) + foreach (var row in scope.Database.QueryPaged(PageSize, sql, sqlCount)) { yield return CreateMediaNodeKit(row, serializer); }