using System; using System.Collections.Generic; using System.Linq; using Examine; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Persistence; namespace Umbraco.Cms.Infrastructure.Examine { /// /// Performs the data lookups required to rebuild a content index /// public class ContentIndexPopulator : IndexPopulator { private readonly IContentService _contentService; private readonly IUmbracoDatabaseFactory _umbracoDatabaseFactory; private readonly IValueSetBuilder _contentValueSetBuilder; /// /// This is a static query, it's parameters don't change so store statically /// private IQuery _publishedQuery; private IQuery PublishedQuery => _publishedQuery ??= _umbracoDatabaseFactory.SqlContext.Query().Where(x => x.Published); private readonly bool _publishedValuesOnly; private readonly int? _parentId; private readonly ILogger _logger; /// /// Default constructor to lookup all content data /// /// /// /// public ContentIndexPopulator( ILogger logger, IContentService contentService, IUmbracoDatabaseFactory umbracoDatabaseFactory, IContentValueSetBuilder contentValueSetBuilder) : this(logger, false, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder) { } /// /// Optional constructor allowing specifying custom query parameters /// public ContentIndexPopulator( ILogger logger, bool publishedValuesOnly, int? parentId, IContentService contentService, IUmbracoDatabaseFactory umbracoDatabaseFactory, IValueSetBuilder contentValueSetBuilder) { _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); _umbracoDatabaseFactory = umbracoDatabaseFactory ?? throw new ArgumentNullException(nameof(umbracoDatabaseFactory)); _contentValueSetBuilder = contentValueSetBuilder ?? throw new ArgumentNullException(nameof(contentValueSetBuilder)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _publishedValuesOnly = publishedValuesOnly; _parentId = parentId; } public override bool IsRegistered(IUmbracoContentIndex index) { // check if it should populate based on published values return _publishedValuesOnly == index.PublishedValuesOnly; } protected override void PopulateIndexes(IReadOnlyList indexes) { if (indexes.Count == 0) { _logger.LogDebug($"{nameof(PopulateIndexes)} called with no indexes to populate. Typically means no index is registered with this populator."); return; } const int pageSize = 10000; var pageIndex = 0; var contentParentId = -1; if (_parentId.HasValue && _parentId.Value > 0) { contentParentId = _parentId.Value; } if (_publishedValuesOnly) { IndexPublishedContent(contentParentId, pageIndex, pageSize, indexes); } else { IndexAllContent(contentParentId, pageIndex, pageSize, indexes); } } protected void IndexAllContent(int contentParentId, int pageIndex, int pageSize, IReadOnlyList indexes) { IContent[] content; do { content = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out _).ToArray(); if (content.Length > 0) { var valueSets = _contentValueSetBuilder.GetValueSets(content).ToList(); // ReSharper disable once PossibleMultipleEnumeration foreach (var index in indexes) { index.IndexItems(valueSets); } } pageIndex++; } while (content.Length == pageSize); } protected void IndexPublishedContent(int contentParentId, int pageIndex, int pageSize, IReadOnlyList indexes) { IContent[] content; var publishedPages = new HashSet(); do { //add the published filter //note: We will filter for published variants in the validator content = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out _, PublishedQuery, Ordering.By("Path", Direction.Ascending)).ToArray(); if (content.Length > 0) { var indexableContent = new List(); foreach (var item in content) { if (item.Level == 1) { // first level pages are always published so no need to filter them indexableContent.Add(item); publishedPages.Add(item.Id); } else { if (publishedPages.Contains(item.ParentId)) { // only index when parent is published publishedPages.Add(item.Id); indexableContent.Add(item); } } } var valueSets = _contentValueSetBuilder.GetValueSets(indexableContent.ToArray()).ToList(); foreach (IIndex index in indexes) { index.IndexItems(valueSets); } } pageIndex++; } while (content.Length == pageSize); } } }