To be able to insert new fields into the analytics index, you should have the following:
Custom Crawler
Index Aggregator
Pipeline
The crawler inherits from the ObserverCrawler<T>
type. The generics type identifies the kind of IIndexable
object the crawler handles. For example, one of the crawler handles visit objects and another crawler handles contact objects.
using Sitecore.ContentSearch.Analytics.Crawlers;
using Sitecore.Extension.Analytics.Models;
public class AnalyticsDownloadCrawler : AnalyticsObserverCrawler<DownloadEventIndexable>
{
}
The implementation of the DownloadEventIndexable
using System;
using System.Globalization;
using Sitecore.Abstractions;
using Sitecore.Analytics.Model;
using Sitecore.ContentSearch;
using Sitecore.Extension.Analytics.Args;
using Sitecore.Extension.Analytics.Pipelines;
public class DownloadEventIndexable : AbstractIndexable
{
public DownloadEventIndexable(PageEventData eventData, string url, Guid interactionId, Guid contactId)
{
var str = interactionId + url + eventData.DataKey;
this.Id = (IndexableId<string>)str;
this.UniqueId = new IndexableUniqueId<Guid, string>(contactId, $"downloadEvent|{str}");
this.DataSource = "sitecore_aggregation";
this.AbsolutePath = string.Empty;
this.Culture = CultureInfo.GetCultureInfo(ContentSearchManager.SearchConfiguration.AnalyticsDefaultLanguage);
this.LoadFields(eventData, url, interactionId, contactId);
}
public override void LoadAllFields()
{
}
private void LoadFields(PageEventData eventData, string url, Guid interactionId, Guid contactId)
{
this.Fields = DownloadEventIndexableLoadFieldsPipeline.Run(ContentSearchManager.Locator.GetInstance<ICorePipeline>(), new DownloadEventIndexableLoadFieldsPipelineArgs(eventData, url, interactionId, contactId));
}
}
You need to implement a pipeline which will load the fields and add it to the index. Define the arguments for the pipeline first as shown below.
using System;
using Sitecore.Analytics.Model;
using Sitecore.ContentSearch.Analytics.Pipelines;
public class DownloadEventIndexableLoadFieldsPipelineArgs : IndexableLoadFieldsPipelineArgs
{
public DownloadEventIndexableLoadFieldsPipelineArgs(PageEventData eventData, string url, Guid interactionId, Guid contactId)
{
this.EventData = eventData;
this.Url = url;
this.InteractionId = interactionId;
this.ContactId = contactId;
}
public PageEventData EventData { get; private set; }
public string Url { get; private set; }
public Guid InteractionId { get; private set; }
public Guid ContactId { get; private set; }
}
Once you have implemented the arguments, create the pipeline which will trigger the loadfields
.
using System.Collections.Generic;
using Sitecore.Abstractions;
using Sitecore.ContentSearch;
using Sitecore.ContentSearch.Diagnostics;
using Sitecore.Extension.Analytics.Args;
public class DownloadEventIndexableLoadFieldsPipeline
{
public static List<IIndexableDataField> Run(ICorePipeline pipeline, DownloadEventIndexableLoadFieldsPipelineArgs args, string pipelineName = "downloadeventindexable.loadfields")
{
try
{
pipeline.Run(pipelineName, args);
}
catch
{
ObservationLog.Log.Debug(() => "Could not resolve pipeline - " + pipelineName);
}
return args.Fields;
}
}
This is the pipeline which will add the data to the index fields. If you want to change or add new fields, this is where it should be done.
using System;
using System.Collections.Generic;
using Sitecore.Analytics.Model;
using Sitecore.ContentSearch;
using Sitecore.Diagnostics;
using Sitecore.Extension.Analytics.Args;
public class DownloadLoadFields : DownloadIndexableLoadFieldsProcessor
{
protected override IEnumerable<IIndexableDataField> GetFields(DownloadEventIndexableLoadFieldsPipelineArgs args)
{
Assert.ArgumentNotNull(args, nameof(args));
PageEventData eventData = args.EventData;
Assert.IsNotNull(eventData, "eventData");
List<IIndexableDataField> indexableDataFieldList = new List<IIndexableDataField>()
{
new IndexableDataField<Guid>("visit.InteractionId", args.InteractionId),
new IndexableDataField<Guid>("contact.Id", args.ContactId),
new IndexableDataField<string>("contact.download", args.EventData.ItemId.ToString()),
new IndexableDataField<string>("type", "assetdownloadevent")
};
return indexableDataFieldList;
}
}
You need to implement the processor so that when the pipeline runs, it can resolve it.
using System;
using System.Collections.Generic;
using Sitecore.ContentSearch;
using Sitecore.Extension.Analytics.Args;
public abstract class DownloadIndexableLoadFieldsProcessor
{
public virtual void Process(DownloadEventIndexableLoadFieldsPipelineArgs args)
{
if (args == null)
throw new ArgumentNullException(nameof(args));
args.Fields.AddRange(this.GetFields(args));
}
protected abstract IEnumerable<IIndexableDataField> GetFields(DownloadEventIndexableLoadFieldsPipelineArgs args);
}
You need to have the aggregator which will create a new DownloadEventIndexable object.
using System;
using System.Collections.Generic;
using System.Linq;
using Sitecore.Analytics.Aggregation.Pipeline;
using Sitecore.Analytics.Model;
using Sitecore.ContentSearch.Analytics.Aggregators;
using Sitecore.ContentSearch.Analytics.Extensions;
using Sitecore.Extension.Analytics.Models;
public class AnalyticsDownloadEventAggregator : ObservableAggregator<DownloadEventIndexable>
{
public AnalyticsDownloadEventAggregator(string name) : base(name)
{
}
protected override IEnumerable<DownloadEventIndexable> ResolveIndexables(AggregationPipelineArgs args)
{
if (args.Context.Contact == null)
{
return Enumerable.Empty<DownloadEventIndexable>();
}
if (!args.Context.Contact.ShouldBeIndexed())
{
ContentSearch.Diagnostics.ObservationLog.Log.Debug(() => $"The visit page events will not be indexed because contact {args.Context.Contact.Id} does not have an identifier and the system is not configured to index anonymous contacts and their interactions.");
return Enumerable.Empty<DownloadEventIndexable>();
}
List<PageData> pages = args.Context.Visit.Pages;
Func<PageData, PageEventData, DownloadEventIndexable> resultSelector = (page, @event) => new DownloadEventIndexable(@event, page.Url.ToString(), args.Context.Visit.InteractionId, args.Context.Contact.Id.Guid);
return pages.SelectMany(page => page.PageEvents, resultSelector);
}
}
Once the code implementation has been done, you need to patch the Sitecore.ContentSearch.Lucene.Index.Analytics.config
and Sitecore.ContentSearch.Analytics.config
Sitecore.ContentSearch.Analytics.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<group groupName="analytics.aggregation">
<pipelines>
<interactions>
<!-- Custom Download Aggregator -->
<processor type="Sitecore.Extension.Analytics.Aggregators.AnalyticsDownloadEventAggregator, Sitecore.Extension.Analytics">
<param desc="name">DownloadAggregationObservable</param>
</processor>
<!-- END Custom Download Aggregator -->
</interactions>
</pipelines>
</group>
<downloadeventindexable.loadfields>
<processor type="Sitecore.Extension.Analytics.Pipelines.DownloadLoadFields, Sitecore.Extension.Analytics" />
</downloadeventindexable.loadfields>
</pipelines>
</sitecore>
</configuration>
Sitecore.ContentSearch.Lucene.Index.Analytics.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<contentSearch>
<configuration type="Sitecore.ContentSearch.ContentSearchConfiguration, Sitecore.ContentSearch">
<indexes hint="list:AddIndex">
<index id="sitecore_analytics_index" type="Sitecore.ContentSearch.LuceneProvider.LuceneIndex, Sitecore.ContentSearch.LuceneProvider">
<locations hint="list:AddCrawler">
<!-- Custom Download Crawler -->
<crawler type="Sitecore.Extension.Analytics.Indexes.AnalyticsDownloadCrawler, Sitecore.Extension.Analytics">
<CrawlerName>Lucene Download Crawler</CrawlerName>
<ObservableName>DownloadAggregationObservable</ObservableName>
<NumberOfSecondsToQueue>60</NumberOfSecondsToQueue>
<MinimumQueueSize>500</MinimumQueueSize>
<MaximumQueueSize>50000</MaximumQueueSize>
<ThrottlingEnabled>False</ThrottlingEnabled>
</crawler>
<!-- END Custom Download Crawler -->
</locations>
</index>
</indexes>
</configuration>
</contentSearch>
</sitecore>
</configuration>
Below is a screenshot of the values in the sitecore analytics index.
Here is the list of dlls I have referenced in my solution
- Sitecore.Analytics.dll
- Sitecore.Analytics.Aggregation.dll
- Sitecore.Analytics.Model.dll
- Sitecore.kernel.dll
- Sitecore.Abstractions.dll
Note
- The
Sitecore.Extension.Analytics
namespace is a custom from the solution where I implemented the code.
- I am using Lucene for the Sitecore Analytics index.
- I have not implemented the Analytics dashboard.
- You can find more information from my blogpost here