Skip to content

Instantly share code, notes, and snippets.

@kamsar
Last active September 10, 2018 07:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kamsar/d6bfcdd2f3ee7163f0f9 to your computer and use it in GitHub Desktop.
Save kamsar/d6bfcdd2f3ee7163f0f9 to your computer and use it in GitHub Desktop.
Subcontent Computed Field
using System.Collections.Generic;
using System.Linq;
using Blade.Utility;
using Sitecore;
using Sitecore.ContentSearch;
using Sitecore.ContentSearch.ComputedFields;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Layouts;
namespace Foo.ContentSearch.ComputedFields
{
/// <summary>
/// Computed field that contains all textual content of items that are rendering data sources on the current item's layout details
/// </summary>
public class SubcontentField : IComputedIndexField
{
public object ComputeFieldValue(IIndexable indexable)
{
var sitecoreIndexable = indexable as SitecoreIndexableItem;
if (sitecoreIndexable == null) return null;
// find renderings with datasources set
var customDataSources = ExtractRenderingDataSourceItems(sitecoreIndexable.Item);
// extract text from data sources
var contentToAdd = customDataSources.SelectMany(GetItemContent).ToList();
if (contentToAdd.Count == 0) return null;
return string.Join(" ", contentToAdd);
}
/// <summary>
/// Finds all renderings on an item's layout details with valid custom data sources set and returns the data source items.
/// </summary>
protected virtual IEnumerable<Item> ExtractRenderingDataSourceItems(Item baseItem)
{
string currentLayoutXml = LayoutField.GetFieldValue(baseItem.Fields[FieldIDs.LayoutField]);
if (string.IsNullOrEmpty(currentLayoutXml)) yield break;
LayoutDefinition layout = LayoutDefinition.Parse(currentLayoutXml);
// loop over devices in the rendering
for (int deviceIndex = layout.Devices.Count - 1; deviceIndex >= 0; deviceIndex--)
{
var device = layout.Devices[deviceIndex] as DeviceDefinition;
if (device == null) continue;
// loop over renderings within the device
for (int renderingIndex = device.Renderings.Count - 1; renderingIndex >= 0; renderingIndex--)
{
var rendering = device.Renderings[renderingIndex] as RenderingDefinition;
if (rendering == null) continue;
// if the rendering has a custom data source, we resolve the data source item and place its text fields into the content to add
if (!string.IsNullOrWhiteSpace(rendering.Datasource))
{
// DataSourceHelper is a component of Blade
var dataSource = DataSourceHelper.ResolveDataSource(rendering.Datasource, baseItem);
if (dataSource != baseItem)
{
yield return dataSource;
}
}
}
}
}
/// <summary>
/// Extracts textual content from an item's fields
/// </summary>
protected virtual IEnumerable<string> GetItemContent(Item dataSource)
{
foreach (Field field in dataSource.Fields)
{
// this check is what Sitecore uses to determine if a field belongs in _content (see LuceneDocumentBuilder.AddField())
if (!IndexOperationsHelper.IsTextField(new SitecoreItemDataField(field))) continue;
string fieldValue = (field.Value ?? string.Empty).StripHtml();
if (!string.IsNullOrWhiteSpace(fieldValue)) yield return fieldValue;
}
}
public string FieldName { get; set; }
public string ReturnType { get; set; }
}
}
@pc-pdx
Copy link

pc-pdx commented Jul 5, 2018

you rock. thanks.
not sure there's a better way to propose a gist update, but in interest of extending lessons learned back to the blog (also updated a fork if my lousy note-patch is unclear):

40- var currentLayoutXml = LayoutField.GetFieldValue(baseItem.Fields[FieldIDs.LayoutField]);
40+var currentLayoutXml = LayoutField.GetFieldValue(baseItem.Fields[FieldIDs.FinalLayoutField]);
-> Updates currentLayoutXml to use FinalLayoutField (where the datasources are!)

80+ dataSource.Fields.ReadAll();
-> ensures dataSource FieldCollection._fields is populated with all fields

@stevenhayles
Copy link

Doesn't iterating over all devices run the risk of duplicating some/most of the text? Would it be better to choose the one that gives the best text for indexing. Which one would be implementation dependent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment