5

I'm trying to create a custom report on sitecore analytics to display Assets downloaded per contact.

Currently there is a report to show downloaded assets but it doesn't display who has downloaded those assets.

I saw the following post on sitecore community forum, where a person had achieved this same functionality. But, he hasn't given much information on how he achieved that. https://community.sitecore.net/developers/f/9/t/8384

Any help/guidance in the correct path would be really helpful.

Version: Sitecore 8.2

6

To be able to insert new fields into the analytics index, you should have the following:

  1. Custom Crawler

  2. Index Aggregator

  3. 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.

enter image description here

Here is the list of dlls I have referenced in my solution

  1. Sitecore.Analytics.dll
  2. Sitecore.Analytics.Aggregation.dll
  3. Sitecore.Analytics.Model.dll
  4. Sitecore.kernel.dll
  5. 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.