using System; using System.Collections.Generic; using System.Linq; using Examine; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; namespace Umbraco.Examine { /// /// Performs the data lookups required to rebuild a content index /// public class ContentIndexPopulator : IndexPopulator { private readonly IContentService _contentService; private readonly IValueSetBuilder _contentValueSetBuilder; /// /// This is a static query, it's parameters don't change so store statically /// private static IQuery _publishedQuery; private readonly bool _publishedValuesOnly; private readonly int? _parentId; /// /// Default constructor to lookup all content data /// /// /// /// public ContentIndexPopulator(IContentService contentService, ISqlContext sqlContext, IContentValueSetBuilder contentValueSetBuilder) : this(false, null, contentService, sqlContext, contentValueSetBuilder) { } /// /// Optional constructor allowing specifying custom query parameters /// /// /// /// /// /// public ContentIndexPopulator(bool publishedValuesOnly, int? parentId, IContentService contentService, ISqlContext sqlContext, IValueSetBuilder contentValueSetBuilder) { if (sqlContext == null) throw new ArgumentNullException(nameof(sqlContext)); _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); _contentValueSetBuilder = contentValueSetBuilder ?? throw new ArgumentNullException(nameof(contentValueSetBuilder)); if (_publishedQuery == null) _publishedQuery = sqlContext.Query().Where(x => x.Published); _publishedValuesOnly = publishedValuesOnly; _parentId = parentId; } public override bool IsRegistered(IUmbracoContentIndex2 index) { // check if it should populate based on published values return _publishedValuesOnly == index.PublishedValuesOnly; } protected override void PopulateIndexes(IReadOnlyList indexes) { if (indexes.Count == 0) 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(); // ReSharper disable once PossibleMultipleEnumeration foreach (var index in indexes) index.IndexItems(valueSets); } pageIndex++; } while (content.Length == pageSize); } } }