Denormalize variants for perfs

This commit is contained in:
Stephan
2018-09-25 16:55:00 +02:00
parent 3d19acb97e
commit c791050058
8 changed files with 117 additions and 45 deletions

View File

@@ -118,6 +118,7 @@ namespace Umbraco.Core.Migrations.Upgrade
Chain<RenameTrueFalseField>("{517CE9EA-36D7-472A-BF4B-A0D6FB1B8F89}"); // from 7.12.0
Chain<SetDefaultTagsStorageType>("{BBD99901-1545-40E4-8A5A-D7A675C7D2F2}"); // from 7.12.0
Chain<UpdateDefaultMandatoryLanguage>("{2C87AA47-D1BC-4ECB-8A73-2D8D1046C27F}");
Chain<RefactorVariantsModel>("{B19BF0F2-E1C6-4AEB-A146-BC559D97A2C6}");
//FINAL

View File

@@ -0,0 +1,79 @@
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
{
public class RefactorVariantsModel : MigrationBase
{
public RefactorVariantsModel(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
Delete.Column("edited").FromTable(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation).Do();
// add available column
AddColumn<DocumentCultureVariationDto>("available", out var sqls);
// so far, only those cultures that were available had records in the table
Update.Table(DocumentCultureVariationDto.TableName).Set(new { available = true }).AllRows().Do();
foreach (var sql in sqls) Execute.Sql(sql).Do();
// add published column
AddColumn<DocumentCultureVariationDto>("published", out sqls);
// make it false by default
Update.Table(DocumentCultureVariationDto.TableName).Set(new { published = false }).AllRows().Do();
// now figure out whether these available cultures are published, too
var getPublished = Sql()
.Select<NodeDto>(x => x.NodeId)
.AndSelect<ContentVersionCultureVariationDto>(x => x.LanguageId)
.From<NodeDto>()
.InnerJoin<ContentVersionDto>().On<NodeDto, ContentVersionDto>((node, cv) => node.NodeId == cv.NodeId)
.InnerJoin<DocumentVersionDto>().On<ContentVersionDto, DocumentVersionDto>((cv, dv) => cv.Id == dv.Id && dv.Published)
.InnerJoin<ContentVersionCultureVariationDto>().On<ContentVersionDto, ContentVersionCultureVariationDto>((cv, ccv) => cv.Id == ccv.VersionId);
foreach (var dto in Database.Fetch<TempDto>(getPublished))
Database.Execute(Sql()
.Update<DocumentCultureVariationDto>(u => u.Set(x => x.Published, true))
.Where<DocumentCultureVariationDto>(x => x.NodeId == dto.NodeId && x.LanguageId == dto.LanguageId));
foreach (var sql in sqls) Execute.Sql(sql).Do();
// so far, it was kinda impossible to make a culture unavailable again,
// so we *should* not have anything published but not available - ignore
// add name column
AddColumn<DocumentCultureVariationDto>("name");
// so far, every record in the table mapped to an available culture
var getNames = Sql()
.Select<NodeDto>(x => x.NodeId)
.AndSelect<ContentVersionCultureVariationDto>(x => x.LanguageId, x => x.Name)
.From<NodeDto>()
.InnerJoin<ContentVersionDto>().On<NodeDto, ContentVersionDto>((node, cv) => node.NodeId == cv.NodeId && cv.Current)
.InnerJoin<ContentVersionCultureVariationDto>().On<ContentVersionDto, ContentVersionCultureVariationDto>((cv, ccv) => cv.Id == ccv.VersionId);
foreach (var dto in Database.Fetch<TempDto>(getNames))
Database.Execute(Sql()
.Update<DocumentCultureVariationDto>(u => u.Set(x => x.Name, dto.Name))
.Where<DocumentCultureVariationDto>(x => x.NodeId == dto.NodeId && x.LanguageId == dto.LanguageId));
}
// ReSharper disable once ClassNeverInstantiated.Local
// ReSharper disable UnusedAutoPropertyAccessor.Local
private class TempDto
{
public int NodeId { get; set; }
public int LanguageId { get; set; }
public string Name { get; set; }
}
// ReSharper restore UnusedAutoPropertyAccessor.Local
}
}

View File

@@ -269,7 +269,7 @@ namespace Umbraco.Core.Models
if (_publishInfos == null)
_publishInfos = new Dictionary<string, (string Name, DateTime Date)>(StringComparer.OrdinalIgnoreCase);
_publishInfos[culture] = (name, date);
_publishInfos[culture.ToLowerInvariant()] = (name, date);
}
private void ClearPublishInfos()
@@ -294,7 +294,7 @@ namespace Umbraco.Core.Models
throw new ArgumentNullOrEmptyException(nameof(culture));
if (_editedCultures == null)
_editedCultures = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
_editedCultures.Add(culture);
_editedCultures.Add(culture.ToLowerInvariant());
}
// sets all publish edited

View File

@@ -41,9 +41,5 @@ namespace Umbraco.Core.Persistence.Dtos
[ForeignKey(typeof(UserDto))]
[NullSetting(NullSetting = NullSettings.Null)]
public int? PublishedUserId { get => _publishedUserId == 0 ? null : _publishedUserId; set => _publishedUserId = value; } //return null if zero
// fixme: I've commented this out, it's never used, need to review
//[Column("edited")]
//public bool Edited { get; set; }
}
}

View File

@@ -28,7 +28,25 @@ namespace Umbraco.Core.Persistence.Dtos
[Ignore]
public string Culture { get; set; }
// authority on whether a culture has been edited
[Column("edited")]
public bool Edited { get; set; }
// de-normalized for perfs
// (means there is a current content version culture variation for the language)
[Column("available")]
public bool Available { get; set; }
// de-normalized for perfs
// (means there is a published content version culture variation for the language)
[Column("published")]
public bool Published { get; set; }
// de-normalized for perfs
// (when available, copies name from current content version culture variation for the language)
// (otherwise, it's the published one, 'cos we need to have one)
[Column("name")]
[NullSetting(NullSetting = NullSettings.Null)]
public string Name { get; set; }
}
}

View File

@@ -8,14 +8,12 @@ using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics;
namespace Umbraco.Core.Persistence.Repositories.Implement
{
@@ -1099,16 +1097,22 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
private IEnumerable<DocumentCultureVariationDto> GetDocumentVariationDtos(IContent content, bool publishing, HashSet<string> editedCultures)
{
foreach (var (culture, name) in content.CultureNames)
var allCultures = content.AvailableCultures.Union(content.PublishedCultures); // union = distinct
foreach (var culture in allCultures)
yield return new DocumentCultureVariationDto
{
NodeId = content.Id,
LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."),
Culture = culture,
// if not published, always edited
// no need to check for availability: it *is* available since it is in content.CultureNames
Edited = !content.IsCulturePublished(culture) || (editedCultures != null && editedCultures.Contains(culture))
Name = content.GetCultureName(culture) ?? content.GetPublishName(culture),
// note: can't use IsCultureEdited at that point - hasn't been updated yet - see PersistUpdatedItem
Available = content.IsCultureAvailable(culture),
Published = content.IsCulturePublished(culture),
Edited = content.IsCultureAvailable(culture) &&
(!content.IsCulturePublished(culture) || (editedCultures != null && editedCultures.Contains(culture)))
};
}

View File

@@ -313,17 +313,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected Sql<ISqlContext> GetVariantInfos(IEnumerable<int> ids)
{
// this ... is an interesting query - could we make it simpler? probably, by having DocumentCultureVariationDto
// handle 'available' and 'published' in addition to 'edited' - would take (a bit) more time to save a document,
// but would make querying way faster
return Sql()
.Select<NodeDto>(x => x.NodeId)
.AndSelect<LanguageDto>(x => x.IsoCode)
.AndSelect<DocumentDto>("doc", x => Alias(x.Published, "DocumentPublished"), x => Alias(x.Edited, "DocumentEdited"))
.AndSelect<ContentVersionCultureVariationDto>("ccv", x => Alias(x.Id, "CultureAvailableData"), x => Alias(x.Name, "Name"))
.AndSelect<ContentVersionCultureVariationDto>("pcv", x => Alias(x.Id, "CulturePublishedData"))
.AndSelect<DocumentCultureVariationDto>("dcv", x => Alias(x.Edited, "CultureEditedData"))
.AndSelect<DocumentCultureVariationDto>("dcv",
x => Alias(x.Available, "CultureAvailable"), x => Alias(x.Published, "CulturePublished"), x => Alias(x.Edited, "CultureEdited"),
x => Alias(x.Name, "Name"))
// from node x language
.From<NodeDto>()
@@ -337,22 +333,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
.LeftJoin<DocumentCultureVariationDto>("dcv")
.On<NodeDto, DocumentCultureVariationDto, LanguageDto>((node, dcv, lang) => node.NodeId == dcv.NodeId && lang.Id == dcv.LanguageId, aliasRight: "dcv")
// join to current version - always exists
// left-join to current version variations - indicates which cultures are *available*
.InnerJoin<ContentVersionDto>("cv")
.On<NodeDto, ContentVersionDto>((node, ev) => node.NodeId == ev.NodeId && ev.Current, aliasRight: "cv")
.LeftJoin<ContentVersionCultureVariationDto>("ccv")
.On<ContentVersionDto, ContentVersionCultureVariationDto, LanguageDto>((cv, ccv, lang) => cv.Id == ccv.VersionId && lang.Id == ccv.LanguageId, "cv", "ccv")
// left-join to published version - exists when whole node is published
// left-join to published version variations - matches cultures that are *published*
.LeftJoin<ContentVersionDto>(nested => nested.InnerJoin<DocumentVersionDto>("dv")
.On<ContentVersionDto, DocumentVersionDto>((pv, dv) => pv.Id == dv.Id && dv.Published, "pv", "dv"),
"pv")
.On<NodeDto, ContentVersionDto>((node, pv) => node.NodeId == pv.NodeId, aliasRight: "pv")
.LeftJoin<ContentVersionCultureVariationDto>("pcv")
.On<ContentVersionDto, ContentVersionCultureVariationDto, LanguageDto>((pv, pcv, lang) => pv.Id == pcv.VersionId && lang.Id == pcv.LanguageId, "pv", "pcv")
// for selected nodes
.WhereIn<NodeDto>(x => x.NodeId, ids);
}
@@ -566,16 +546,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
public bool DocumentPublished { get; set; }
public bool DocumentEdited { get; set; }
public int? CultureAvailableData { get; set; }
public int? CulturePublishedData { get; set; }
public bool? CultureEditedData { get; set; }
[ResultColumn]
public bool CultureAvailable => CultureAvailableData.HasValue;
[ResultColumn]
public bool CulturePublished => CulturePublishedData.HasValue;
[ResultColumn]
public bool CultureEdited => CultureEditedData ?? false;
public bool CultureAvailable { get; set; }
public bool CulturePublished { get; set; }
public bool CultureEdited { get; set; }
}
// ReSharper disable once ClassNeverInstantiated.Local

View File

@@ -359,6 +359,7 @@
<Compile Include="Migrations\Upgrade\V_8_0_0\LanguageColumns.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\PropertyEditorsMigration.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\RefactorMacroColumns.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\RefactorVariantsModel.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\SuperZero.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\TagsMigration.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\UpdateDefaultMandatoryLanguage.cs" />