SaaS CMS has officially launched! Learn more now.

Episerver Find Search Statistics Click tracking search result issue


I am trying to implement click tracking using EPiServer Find for a CMS application. I am able to generate the statistics but the page name in the search results is showing up as page not found.

Below is a screen shot for reference:

Any info on this will be of great help to me.

Mar 21, 2018 22:26

If you try to do the search and make sure that you are using it as a non logged in user and click on the page in the result, do you see the page?

It might be so that the result is pages that can't be accessed by ordinary users

Mar 22, 2018 9:15

@Henrik Fransas: Thank you for your reply, I actually went over your articles to implement this. 

Another thing is that I am creating the blog article pages dynamically from an xml file. I have created a custom page for it, and also has a corresponding viewmodel for the page. Below is the sample of my code when I use search:

Code to track the query and populate HitId and HitType properties

var searchResults = client.Search<ArticlePage>().For(q).Select(x => new ArticleListTrackViewModel
ArticleCategory = x.ArticleCategory,
Created = x.Created,
PageTitle = x.PageTitle,
ArticleLink = x.ExternalURL,
Author = ArticleHelper.GetAuthorName(x.Author),
HitType = "ArticlePage"    (I am hard coding this)


var hits = searchResults.Hits;
foreach( var obj in hits)
 obj.Document.HitId = ArticleHelper.GetPageID(obj.Document.PageTitle); (The helper gets the PageGuid)

model.TrackId = new TrackContext().Id;
SearchClient.Instance.Statistics().TrackQuery(q, x =>
x.Id = model.TrackId;
x.Tags = ServiceLocator.Current.GetInstance<IStatisticTagsHelper>().GetTags(false);
x.Query.Hits = searchResults.TotalMatching;
model.NumberOfHits = searchResults.Hits.Count();
model.Results = searchResults;

return View(model);

And on clicking the tracker the below mentioned action method is triggered, which in turn calls a helper function:

Controller Code:

public class ClickTrackingController : Controller
public JsonResult Track(string query, string hitId, string trackId)
  Helper.TrackClick(query, hitId, trackId);

  return Json(new { msg = "Click tracked" }, JsonRequestBehavior.AllowGet);
catch (Exception e)
return Json(new { Success = false }, JsonRequestBehavior.AllowGet);

Helper funtion code:

public static void TrackClick(string query, string hitId, string trackId)
SearchClient.Instance.Statistics().TrackHit(query, hitId, command =>
command.Hit.Id = hitId;
command.Id = trackId;
command.Ip = "";
command.Tags = ServiceLocator.Current.GetInstance<IStatisticTagsHelper>().GetTags(false); // don't get all tags
command.Hit.Position = null;

If you see I am hard coding the HitType and using a helper method to get the PageGuid to assign the value to HitId. I was having the same problem as you have mentioned in your article: But since I use a view model I am not sure if I can use the approach in the article. 

Also, the query string parameters are as shown below:

  1. query: sample
  2. hitId: ProjectName.Models.PageTypes.ArticlePage/94cb8651-9224-4531-b60d-29dc66eeba93   (PageType / PageGuid)
  3. trackId: 1B2M2Y8AsgTpgAmY7PhCfg== (this matches the track id used in TrackQuery)

A sample URL looks like the one mentioned below:


Edited, Mar 22, 2018 15:21

I had this issue recently and opened a support ticket for it.  I was told that I needed a "SearchTitle" property in my index model, and that whatever I loaded into that property would be what showed in the statistics where I was seeing "Page not found".  It worked!  Maybe you need to do the same ?

Apr 16, 2018 15:59

Any luck with this? I have the same issue. I have added SearchTitle but it did nothing. :-/

Jun 14, 2018 12:50

 Siddharth Gupta, Your problem might be that you hard coded "ArticlePage" instead of doing this;

HitType = SearchClient.Instance.Conventions.TypeNameConvention.GetTypeName(x),

also you might want to try this for your HitId;

SearchClient.Instance.Conventions.IdConvention.GetId(** the actual content (The articlePage)**)

Finally. Make sure your class has the property SearchTitle implemented as Dave pointed out.

Jun 14, 2018 15:45

Jens Qvist : Yes I got this working actually, and the problem was actually the fact that I was hard coding the HitType and the HitId as well was not implemented right. Let me know if you need a code snippet that will help you.

Edited, Jun 14, 2018 16:32

Nah, was the hitType for me aswell. It needs to match the type of the indexed object that is clicked :-)

Jun 14, 2018 16:33


Any more tips available for this one? I'm using the following to create the hit ID

string hitId = SearchClient.Instance.Conventions.IdConvention.GetId(x);
string hitType = SearchClient.Instance.Conventions.TypeNameConvention.GetTypeName(x.GetType());
string hitTrackId = $"{hitType}/{hitId}";

e.g. Namespace_Pages_MyPage/_ba434590-fd1a-4d8b-a99b-d821b9e5bb8f_en-NZ

I have a "SearchTitle$$string" populated in the EPi find index and I also have a number of click-through rates registered and bumping up the % but everything is coming through as "Page not found"

Only thing I'm wondering is if I can expect this to work correctly in a local dev environment or do the pages need to publicly available?



Sep 20, 2018 10:20

A little bit late to the party, just wanted to let anyone else know since I got it working with the built in "unifiedsearch"-style redirecting, for tracked links, using GetResult.

Meaning clicking a link with the below querystring-params (notice _t_ which matches the params defined here) applied will get you to the page and all tracked query params will be stripped from the url:

        public string GetTrackedSearchUrl(string url)
            if (string.IsNullOrEmpty(TrackId) || string.IsNullOrEmpty(HitId)) return url;

            var initialSign = url?.Contains("?") == true ? "&" : "?";
            var queryParams = string.Join("&", new[] {
                GetQueryParam("q", Query),
                GetQueryParam("", HitId),
                GetQueryParam("hit.pos", HitPosition.ToString()),
                GetQueryParam("tags", string.Join(",", Tags ?? Array.Empty<string>()))

            return url + initialSign + queryParams;

        private string GetQueryParam(string name, string value) =>

TrackId is the id returned from the TrackQuery()-call shown below.

HitId is from the SearchHit<T> (returned from GetResult, $"{hit.Type}/{hit.Id}").

HitPosition is just a counter offset byt the current skip-count for the query.

Tags are the fetched tags from the call below.

The tracking is enabled in the following way:

        private (bool tracked, string trackId, IEnumerable<string> tags) CheckQueryTracking(ISearch search, Domain.Models.SearchContext context, int totalMatching)
            if (context?.Tracked == true)
                var tags = _statisticsTagHelper.GetTags(false);
                var result = search.Client.Statistics().TrackQuery(context.Query, x =>
                    x.Tags = tags;
                    x.Query.Hits = totalMatching;
                return (true, result.TrackId, tags);
            return (false, null, null);

_statisticsTagHelper is a injected instance of IStatisticTagsHelpertotalMatching is fetched from GetResult (.TotalMatching).

It can probably can be simplified in a lot of ways for your use but we have some layers of transforming/IMapping etc meaning we store all "search shared"-props above.

This is what we do for each search hit:

TrackId = trackId,
Query = context.Query,
HitId = $"{hit.Type}/{hit.Id}",
HitPosition = ++pos,
Tags = tags,

For the redirecting to work find:s javascript needs to be loaded as well (/ClientResources/Epi/x.x.x/find.js).

I hope this helps someone else out!


In one environement we had an issue with the TrackContext-constructor (reflecting seems to point to HttpContext.Current.Session being null).

In this case update to a later version of find since it's fixed bug or you could construct your own TrackId (or temporarly setting HttpContext to null on a fallback call to TrackContext()).

// Magnus

Edited, Oct 03, 2019 22:51
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.