From 4b62cb59b746ce39f36c33fb13415ce0500dec95 Mon Sep 17 00:00:00 2001 From: Mykyta Zakharov Date: Thu, 18 May 2023 14:28:00 +0300 Subject: [PATCH] Issue-12704: added new necessary indexes. --- .../Migrations/MigrationBase_Extra.cs | 20 ++++++ .../Migrations/Upgrade/UmbracoPlan.cs | 3 + .../V_12_1_0/TablesIndexesImprovement.cs | 65 +++++++++++++++++++ .../DatabaseAnnotations/IndexAttribute.cs | 2 +- .../DefinitionFactory.cs | 10 ++- .../Persistence/Dtos/ContentNuDto.cs | 3 + .../Dtos/ContentVersionCultureVariationDto.cs | 2 +- .../Persistence/Dtos/ContentVersionDto.cs | 2 +- .../Persistence/Dtos/DocumentVersionDto.cs | 4 +- .../Persistence/Dtos/LogDto.cs | 4 +- .../Persistence/Dtos/NodeDto.cs | 3 +- .../Persistence/Dtos/RedirectUrlDto.cs | 3 + .../Persistence/Dtos/TagDto.cs | 1 + .../Persistence/Dtos/TagRelationshipDto.cs | 1 + 14 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_1_0/TablesIndexesImprovement.cs diff --git a/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs b/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs index b548112d8f..23de94e824 100644 --- a/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs +++ b/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; +using Umbraco.Cms.Infrastructure.Migrations.Expressions.Execute.Expressions; using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; using Umbraco.Extensions; @@ -110,6 +111,25 @@ namespace Umbraco.Cms.Infrastructure.Migrations return indexes.Any(x => x.Item2.InvariantEquals(indexName)); } + protected void CreateIndex(string toCreate) + { + TableDefinition tableDef = DefinitionFactory.GetTableDefinition(typeof(T), Context.SqlContext.SqlSyntax); + IndexDefinition index = tableDef.Indexes.First(x => x.Name == toCreate); + new ExecuteSqlStatementExpression(Context) { SqlStatement = Context.SqlContext.SqlSyntax.Format(index) } + .Execute(); + } + + protected void DeleteIndex(string toDelete) + { + if (!IndexExists(toDelete)) + { + return; + } + + TableDefinition tableDef = DefinitionFactory.GetTableDefinition(typeof(T), Context.SqlContext.SqlSyntax); + Delete.Index(toDelete).OnTable(tableDef.Name).Do(); + } + protected bool PrimaryKeyExists(string tableName, string primaryKeyName) { return SqlSyntax.DoesPrimaryKeyExist(Context.Database, tableName, primaryKeyName); diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs index 092da29913..ba9437a34d 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs @@ -77,5 +77,8 @@ public class UmbracoPlan : MigrationPlan // To 12.0.0 To("{888A0D5D-51E4-4C7E-AA0A-01306523C7FB}"); To("{539F2F83-FBA7-4C48-81A3-75081A56BB9D}"); + + // To 12.1.0 + To("{1187192D-EDB5-4619-955D-91D48D738871}"); } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_1_0/TablesIndexesImprovement.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_1_0/TablesIndexesImprovement.cs new file mode 100644 index 0000000000..5483f25593 --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_1_0/TablesIndexesImprovement.cs @@ -0,0 +1,65 @@ +using Umbraco.Cms.Infrastructure.Persistence.Dtos; + +namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_12_1_0; + +public class TablesIndexesImprovement : MigrationBase +{ + public TablesIndexesImprovement(IMigrationContext context) : base(context) + { + } + + protected override void Migrate() + { + var nodeDtoTrashedIndex = $"IX_{NodeDto.TableName}_ObjectType_trashed_sorted"; + DeleteIndex(nodeDtoTrashedIndex); + CreateIndex(nodeDtoTrashedIndex); + + var redirectUrlCreateDateUtcIndex = $"IX_{RedirectUrlDto.TableName}_culture_hash"; + DeleteIndex(redirectUrlCreateDateUtcIndex); + CreateIndex(redirectUrlCreateDateUtcIndex); + + var contentVersionCultureVariationVersionIdIndex = $"IX_{ContentVersionCultureVariationDto.TableName}_VersionId"; + DeleteIndex(contentVersionCultureVariationVersionIdIndex); + CreateIndex(contentVersionCultureVariationVersionIdIndex); + + var contentVersionDtoNodeIdV2Index = $"IX_{ContentVersionDto.TableName}_NodeId"; + DeleteIndex(contentVersionDtoNodeIdV2Index); + CreateIndex(contentVersionDtoNodeIdV2Index); + + var tagRelationshipDtoTagNodeIndex = $"IX_{TagRelationshipDto.TableName}_tagId_nodeId"; + DeleteIndex(tagRelationshipDtoTagNodeIndex); + CreateIndex(tagRelationshipDtoTagNodeIndex); + + var tagDtoLanguageGroupIndex = $"IX_{TagDto.TableName}_languageId_group"; + DeleteIndex(tagDtoLanguageGroupIndex); + CreateIndex(tagDtoLanguageGroupIndex); + + var documentVersionDtoIdPublishedIndex = $"IX_{DocumentVersionDto.TableName}_id_published"; + DeleteIndex(documentVersionDtoIdPublishedIndex); + CreateIndex(documentVersionDtoIdPublishedIndex); + + var documentVersionDtoPublishedIndex = $"IX_{DocumentVersionDto.TableName}_published"; + DeleteIndex(documentVersionDtoPublishedIndex); + CreateIndex(documentVersionDtoPublishedIndex); + + var logDtoDatestampIndex = $"IX_{LogDto.TableName}_datestamp"; + DeleteIndex(logDtoDatestampIndex); + CreateIndex(logDtoDatestampIndex); + + var logDtoDatestampHeaderIndex = $"IX_{LogDto.TableName}_datestamp_logheader"; + DeleteIndex(logDtoDatestampHeaderIndex); + CreateIndex(logDtoDatestampHeaderIndex); + + var propertyDataDtoVersionIdIndex = $"IX_{PropertyDataDto.TableName}_VersionId"; + DeleteIndex(propertyDataDtoVersionIdIndex); + CreateIndex(propertyDataDtoVersionIdIndex); + + var contentNuDtoPublishedIdIndex = $"IX_{ContentNuDto.TableName}_published"; + DeleteIndex(contentNuDtoPublishedIdIndex); + CreateIndex(contentNuDtoPublishedIdIndex); + + var nodeDtoParentIdNodeObjectTypeIndex = $"IX_{NodeDto.TableName}_parentId_nodeObjectType"; + DeleteIndex(nodeDtoParentIdNodeObjectTypeIndex); + CreateIndex(nodeDtoParentIdNodeObjectTypeIndex); + } +} diff --git a/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/IndexAttribute.cs b/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/IndexAttribute.cs index 826e56ad89..0628da82aa 100644 --- a/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/IndexAttribute.cs +++ b/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/IndexAttribute.cs @@ -3,7 +3,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; /// /// Attribute that represents an Index /// -[AttributeUsage(AttributeTargets.Property)] +[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] public class IndexAttribute : Attribute { public IndexAttribute(IndexTypes indexType) => IndexType = indexType; diff --git a/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs b/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs index 32bdd213e6..ad043c08c4 100644 --- a/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs +++ b/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs @@ -55,8 +55,14 @@ public static class DefinitionFactory } // Creates an index definition and adds it to the collection on the table definition - IndexAttribute? indexAttribute = propertyInfo.FirstAttribute(); - if (indexAttribute != null) + IEnumerable? indexAttributes = propertyInfo.MultipleAttribute(); + + if (indexAttributes == null) + { + continue; + } + + foreach (IndexAttribute indexAttribute in indexAttributes) { IndexDefinition indexDefinition = GetIndexDefinition(modelType, propertyInfo, indexAttribute, columnName, tableName); diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentNuDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentNuDto.cs index 6fa45d9cce..a3a978f2e4 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentNuDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentNuDto.cs @@ -10,12 +10,15 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; [ExplicitColumns] public class ContentNuDto { + public const string TableName = Constants.DatabaseSchema.Tables.NodeData; + [Column("nodeId")] [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsContentNu", OnColumns = "nodeId, published")] [ForeignKey(typeof(ContentDto), Column = "nodeId", OnDelete = Rule.Cascade)] public int NodeId { get; set; } [Column("published")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_published", ForColumns = "published,nodeId,rv", IncludeColumns = "dataRaw")] public bool Published { get; set; } /// diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCultureVariationDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCultureVariationDto.cs index 48c6ee97ef..b7d675f9a8 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCultureVariationDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCultureVariationDto.cs @@ -18,7 +18,7 @@ internal class ContentVersionCultureVariationDto [Column("versionId")] [ForeignKey(typeof(ContentVersionDto))] - [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_VersionId", ForColumns = "versionId,languageId")] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_VersionId", ForColumns = "versionId,languageId", IncludeColumns = "id,name,date,availableUserId")] public int VersionId { get; set; } [Column("languageId")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionDto.cs index 3a6aae2aff..07fe6d9a84 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionDto.cs @@ -19,7 +19,7 @@ public class ContentVersionDto [Column("nodeId")] [ForeignKey(typeof(ContentDto))] - [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_NodeId", ForColumns = "nodeId,current", IncludeColumns = "id,versionDate,text,userId")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_NodeId", ForColumns = "nodeId,current", IncludeColumns = "id,versionDate,text,userId,preventCleanup")] public int NodeId { get; set; } [Column("versionDate")] // TODO: db rename to 'updateDate' diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/DocumentVersionDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/DocumentVersionDto.cs index 75dea080a2..f99a6676be 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/DocumentVersionDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/DocumentVersionDto.cs @@ -9,11 +9,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; [ExplicitColumns] public class DocumentVersionDto { - private const string TableName = Constants.DatabaseSchema.Tables.DocumentVersion; + public const string TableName = Constants.DatabaseSchema.Tables.DocumentVersion; [Column("id")] [PrimaryKeyColumn(AutoIncrement = false)] [ForeignKey(typeof(ContentVersionDto))] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_id_published", ForColumns = "id,published", IncludeColumns = "templateId")] public int Id { get; set; } [Column("templateId")] @@ -22,6 +23,7 @@ public class DocumentVersionDto public int? TemplateId { get; set; } [Column("published")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_published", ForColumns = "published", IncludeColumns = "id,templateId")] public bool Published { get; set; } [ResultColumn] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/LogDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/LogDto.cs index b464d6628d..89bfeb8612 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/LogDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/LogDto.cs @@ -35,14 +35,14 @@ internal class LogDto [NullSetting(NullSetting = NullSettings.Null)] public string? EntityType { get; set; } - // TODO: Should we have an index on this since we allow searching on it? [Column("Datestamp")] [Constraint(Default = SystemMethods.CurrentDateTime)] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_datestamp", ForColumns = "Datestamp,userId,NodeId")] public DateTime Datestamp { get; set; } - // TODO: Should we have an index on this since we allow searching on it? [Column("logHeader")] [Length(50)] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_datestamp_logheader", ForColumns = "Datestamp,logHeader")] public string Header { get; set; } = null!; [Column("logComment")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/NodeDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/NodeDto.cs index d11ebc96ce..5bf3a26207 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/NodeDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/NodeDto.cs @@ -26,7 +26,7 @@ public class NodeDto [Column("parentId")] [ForeignKey(typeof(NodeDto))] - [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_ParentId")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_parentId_nodeObjectType", ForColumns = "parentID,nodeObjectType", IncludeColumns = "trashed,nodeUser,level,path,sortOrder,uniqueID,text,createDate")] public int ParentId { get; set; } // NOTE: This index is primarily for the nucache data lookup, see https://github.com/umbraco/Umbraco-CMS/pull/8365#issuecomment-673404177 @@ -40,6 +40,7 @@ public class NodeDto public string Path { get; set; } = null!; [Column("sortOrder")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_ObjectType_trashed_sorted", ForColumns = "nodeObjectType,trashed,sortOrder,id", IncludeColumns = "uniqueID,parentID,level,path,nodeUser,text,createDate")] public int SortOrder { get; set; } [Column("trashed")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/RedirectUrlDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/RedirectUrlDto.cs index b377b49177..453ab1e308 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/RedirectUrlDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/RedirectUrlDto.cs @@ -9,6 +9,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; [ExplicitColumns] internal class RedirectUrlDto { + public const string TableName = Constants.DatabaseSchema.Tables.RedirectUrl; + public RedirectUrlDto() => CreateDateUtc = DateTime.UtcNow; // notes @@ -31,6 +33,7 @@ internal class RedirectUrlDto [Column("createDateUtc")] [NullSetting(NullSetting = NullSettings.NotNull)] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_culture_hash", ForColumns = "createDateUtc", IncludeColumns = "culture,url,urlHash,contentKey")] public DateTime CreateDateUtc { get; set; } [Column("url")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/TagDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/TagDto.cs index cc8b80c777..d2a68cf971 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/TagDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/TagDto.cs @@ -17,6 +17,7 @@ internal class TagDto [Column("group")] [Length(100)] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_languageId_group", ForColumns = "languageId,group", IncludeColumns = "id,tag")] public string Group { get; set; } = null!; [Column("languageId")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/TagRelationshipDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/TagRelationshipDto.cs index 3799679e4d..f5f9fd0b38 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/TagRelationshipDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/TagRelationshipDto.cs @@ -14,6 +14,7 @@ internal class TagRelationshipDto [Column("nodeId")] [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsTagRelationship", OnColumns = "nodeId, propertyTypeId, tagId")] [ForeignKey(typeof(ContentDto), Name = "FK_cmsTagRelationship_cmsContent", Column = "nodeId")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_tagId_nodeId", ForColumns = "tagId,nodeId", IncludeColumns = "propertyTypeId")] public int NodeId { get; set; } [Column("tagId")]