Microsoft SharePoint Search Analytics in PowerShell – GetAnalyticsItemData

In SharePoint 2013 WebAnalytics was deprecated and Search Analytics has replaced it. With the new API you can get the number of total hits for a website or even the hits per day or month.

Niki Borell has written a great blog post oh how to query Search Analytics. Based on that Chris LaQuerre has created a CodePlex project SharePoint 2013 Web Analytics Data Export to CSV using PowerShell.

My project target was to get the total hits of each web page inside the all site collections so I can use this information to get a report about the most viewed pages of the last two months within the intranet. I checked out the script of Chris and it almost did, what I wanted. It collects the data of the total hits for a SharePoint SiteCollection or their Subwebs. For getting this information the method GetRollupAnalyticsItemData is used.

In short here’s the PowerShell code to get the total hits for a site collection:

$SiteUrl = "http://someserver"
$Site = Get-SPSite $SiteUrl
$SearchApp = Get-SPEnterpriseSearchServiceApplication
$UsageData = $SearchApp.GetRollupAnalyticsItemData(1,[System.Guid]::Empty,$Site.ID,[System.Guid]::Empty)
$TotalHits = $UsageData.TotalHits

Get Page Data with GetAnalyticsItemData

Now I want to get all pages within the pages library and their total hits of the last month. So there is a method to do that called GetAnalyticsItemData. So I checked out what MSDN tells me about the signature of the method:

public AnalyticsItemData GetAnalyticsItemData(
    int eventType,
    Guid tenantId,
    Guid siteId,
    string itemId
)

Okay, so what are the parameters, you might ask. They are no common, old school, SharePoint parameters, eh? Not quite self explaining, I guess. Let’s have a try.

eventType: The enumeration to find the necessary Ids is StandardEventtypeId. For my purpose I’ll stick with the Id 1 which is „View“.
tenantId: This is the tenant Id for Office 365, in my case I am on still sing on premises so I will stick with [System.Guid]::Empty.
siteId: Pretty straight forward, for a change.
itemId: Say what? itemId as a string? I only know the list item id as an int or maybe the Guid? I have tried to use item.id.ToString() or the item.id itself, but it did not do the trick. Unfortunately the URL worked. When looking into the assembly you can find itemId internally is handled and converted as a absolute URL to the item. Sounds strange but is true.

Decompiled Code of the SearchServiceApplication

Here is the decompiled code of the .Net assembly for the relevant functions – thanks to Marcel Roma ;-):

public AnalyticsItemData GetAnalyticsItemData(int eventType, Guid tenantId, Guid siteId, string itemId)
{
    return this.DoWebServiceBackedOperation("GetAnalyticsItemData", serviceApplication =>; serviceApplication.GetAnalyticsItemData(eventType, tenantId, siteId, AnalyticsUtils.RemapUrl(siteId, itemId)));
}

internal static string RemapUrl(Guid siteId, string strItemId)
{
    return SPAnalyticsUsageEntry.RemapEventToDefaultZone(siteId, strItemId);
}

public static string RemapEventToDefaultZone(Guid siteId, string itemId)
{
    SPSiteLookupInfo byId = SPSiteCache.LookupById(siteId, SPFarm.Local);
    if (byId != null)
    {
        if (byId.HostHeaderIsSiteName)
        {
            Uri uri = null;
            Uri.TryCreate(itemId, UriKind.Absolute, out uri);
            if (uri != null)
            {
                Uri uri2 = null;
                Uri.TryCreate(SPHostHeaderSiteUri.GetSchemeString(((ISPSiteLookupInfo) byId).HostHeaderSiteUriScheme) + Uri.SchemeDelimiter + byId.Path, UriKind.Absolute, out uri2);
                if (uri2 != null)
                {
                    return SPUrlUtility.CombineUrl(SPHostHeaderSiteUri.GetSchemeString(((ISPSiteLookupInfo) byId).HostHeaderSiteUriScheme) + Uri.SchemeDelimiter + uri2.Authority, uri.PathAndQuery);
                }
            }
            return itemId;
        }
        Uri result = null;
        Uri.TryCreate(itemId, UriKind.Absolute, out result);
        if (result != null)
        {
            SPAlternateUrl url = SPAlternateUrl.Lookup(result);
            if (url != null)
            {
                SPAlternateUrl responseUrl = url.Collection.GetResponseUrl(SPUrlZone.Default);
                if (responseUrl != null)
                {
                    return SPUrlUtility.CombineUrl(responseUrl.Uri.AbsoluteUri, result.PathAndQuery);
                }
            }
        }
    }
    return itemId;
}

Now it’s time to get the hits of a page

Okay, that was a short excursion of how to handle the parameters. Now let’s get back to the topic of fetching the total pages hits of a page.

The following snippet fetches all pages libraries within the site collections, gets their items and writes them to the console with the last two months hits.

$SiteUrl = "http://someserver";
$pagesListName = "Pages";

$Site = Get-SPSite $SiteUrl;
$SearchApp = Get-SPEnterpriseSearchServiceApplication;

ForEach($Web in $Site.AllWebs) 
{
    $list = $web.Lists[$pagesListName];
    $items = $list.Items;
    
    ForEach ($item in $items)
    {
        $UsageData = $SearchApp.GetAnalyticsItemData(1, [System.Guid]::Empty,$Site.ID,"$($item.Web.Url)/$($item.Url)");
        
        $FilterDateThisMonth = Get-Date;
        $FilterDateLastMonth = $FilterDateThisMonth.AddMonths(-1);
        $ThisMonthHits = 0;
        $LastMonthHits = 0;
        
        if ($UsageData)
        {
            $ThisMonthHits = $UsageData.GetHitCountForMonth($FilterDateThisMonth);
            $LastMonthHits = $UsageData.GetHitCountForMonth($FilterDateLastMonth);
        }
        
        $TotalMonthHits = $ThisMonthHits + $LastMonthHits;
        Write-Host "$($item.Web.Url)/$($item.Url) with $TotalMonthHits Hits";
    }
}

One comment on the fetching data of a month: You cannot get the data of a timespan. The methods for that are not available – you can only select a concrete month or a day. So if you are fetching the current month you only get the data up untill the current day. Meaning, if you fetch the data on the first of a month there will be no data available. You just need to keep that in mind, if you want to do some calculation on that.

So, have fun and happy coding!

Schreibe einen Kommentar