How to handle search result URLs for document types with no detail view

0
Comments
2
Votes
Login to vote

Have you ever clicked on a search result's URL only to see the framework of the site and a blank content area?

01

 

 

I recently encountered this with a site I was working on. The issue was caused by a particular document type being returned in the search results which did not have a selected item transformation.  Documents of this type are listed on a parent page, however, there is no data to display on the document's page itself.  In this case, I could not exclude the document type from the search index since the results are valid.  Thus, the issue became "what do we want to happen when the search result's link is clicked?".  It was decided that the appropriate action would be to display the listing page.  We also decided to use page anchors to direct the user to the particular listing item related to the search result, but more on that later.

For this example we will be working within a few guidelines.  I will refer to this document type as a "resource" document from here on out.  This resource document type will contain data fields for title, Resource URL, and a brief summary.  The Resource URL will be used to direct the user to an external resource such as an article, login page, image or what-have-you.  The individual resource documents will have a listing view that is displayed on a parent page, however, they will not have a detail view if you navigate directly to the Resource document.

There's the issue.  The search result's link URL points to the resource document itself since that is where the data is stored.  Unfortunately, data for resource documents is only displayed on a parent page.

The pertinent document tree structure is as follows:
02

 

 

Repeater setup:

03

 

04

 

 

In this case the listing page is the immediate parent of the resource documents, however, this is not always true.  In my real-world situation there was a resource category document type between the resource documents and the listing page.  This was done to help keep down clutter in the document tree as well as providing me a way to group out resource documents on the listing page using sub-headers.

05

 

 

Here is what the listing page look like

06

 

 

I decided to use Kentico's IfCompare method in the search results transformation as my starting point. (link)

Kentico's deffinition of IfCompare
IfCompare(object value, object comparableValue, object falseResult, object trueResult)

<%# IfCompare(1, 2, "The values are different", "The values are equal") %>

Compares values of the first and the second parameter. If the parameters are different or are both NULL, the third parameter is returned. If they are equal, the fourth parameter is returned.

 

 

In the vein of flexibility, I wanted to write a method (GetResourceParent) that would take parameters of the resource document's node ID and the class name of the desired listing page's document type.

Here is my fully-formed use of IfCompare:

<%# IfCompare(GetSearchValue("ClassName"), "CUSTOM.Resource", SearchResultUrl(true), _Functions.GetResourceParent(GetSearchValue("NodeID"), "CMS.MenuItem") + "#" + GetSearchValue("NodeID") + "\"&gt;") %&gt;

&nbsp;

&nbsp;

Here is the default search results transformation:
Document types>CMS.Root>Transformations>SmartSearchResults

<div style="margin-bottom: 30px;">
    <%-- Search result title --%>
    <div>
        <a style="font-weight: bold" href='<%# SearchResultUrl(true) %&gt;'&gt;
            &lt;%# SearchHighlight(HTMLHelper.HTMLEncode(DataHelper.GetNotEmpty(Eval("Title"), "/")),"&lt;span style=\"font-weight:bold;\"&gt;","&lt;/span&gt;") %&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    &lt;%-- Search result content --%&gt;
    &lt;div style="margin-top: 5px; width: 590px;"&gt;
        &lt;%# SearchHighlight(HTMLHelper.HTMLEncode(TextHelper.LimitLength(HttpUtility.HtmlDecode(HTMLHelper.StripTags(GetSearchedContent(DataHelper.GetNotEmpty(Eval("Content"), "")), false, " ")), 280, "...")),"&lt;span style=\"background-color: #FEFF8F\"&gt;","&lt;/span&gt;") %&gt;&lt;br /&gt;
    &lt;/div&gt;
    &lt;%-- Relevance, URL, Creattion --%&gt;
    &lt;div style="margin-top: 5px;"&gt;
        &lt;%-- Relevance --%&gt;
        &lt;div title="&lt;%# "Relevance: " + Convert.ToInt32(ValidationHelper.GetDouble(Eval("Score"),0.0)*100)  + "%" %&gt;"
            style="width: 50px; border: solid 1px #aaaaaa; margin-top: 7px; margin-right: 6px; float: left; color: #0000ff; font-size: 2pt; line-height: 4px; height: 4px;"&gt;
            &lt;div style='&lt;%# "background-color:#a7d3a7;width:"+ Convert.ToString(Convert.ToInt32((ValidationHelper.GetDouble(Eval("Score"),0.0)/2)*100))  + "px;height:4px;line-height: 4px;"%&gt;'&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;%-- URL --%&gt;
        &lt;span style="color: #008000"&gt;
            &lt;%# SearchHighlight(SearchResultUrl(true),"&lt;strong&gt;","&lt;/strong&gt;")%&gt;
        &lt;/span&gt;
        &lt;%-- Creation --%&gt;
        &lt;span style="color: #888888; font-size: 9pt"&gt;
            &lt;%# GetDateTimeString(ValidationHelper.GetDateTime(Eval("Created"), DateTimeHelper.ZERO_TIME), true) %&gt;
        &lt;/span&gt;
    &lt;/div&gt;
&lt;/div&gt;

We will be modifying the href for the anchor on line 4.

&nbsp;

&nbsp;

Using the IfCompare method I am able to compare the class name of the current document (the document returned in the search result) to the class name of my resource document type.  From there I can do one of two things.  If they do not match; I will leave the anchor's href as default "SearchResultURL(true)".&nbsp; If they do match (the search result is a resource document); I will call GetResourceParent, a custom method I wrote that resides in my _Functions class.

Here is the revisted transformation:

<div style="margin-bottom: 30px;">
    <%-- Search result title --%>
    <div>
        <a style="font-weight: bold" href='<%# IfCompare(GetSearchValue("ClassName"), "CUSTOM.Resource", SearchResultUrl(true), _Functions.GetResourceParent(GetSearchValue("NodeID"), "CMS.MenuItem")) %&gt;'&gt;
            &lt;%# SearchHighlight(HTMLHelper.HTMLEncode(DataHelper.GetNotEmpty(Eval("Title"), "/")),"&lt;span style=\"font-weight:bold;\"&gt;","&lt;/span&gt;") %&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    &lt;%-- Search result content --%&gt;
    &lt;div style="margin-top: 5px; width: 590px;"&gt;
        &lt;%# SearchHighlight(HTMLHelper.HTMLEncode(TextHelper.LimitLength(HttpUtility.HtmlDecode(HTMLHelper.StripTags(GetSearchedContent(DataHelper.GetNotEmpty(Eval("Content"), "")), false, " ")), 280, "...")),"&lt;span style=\"background-color: #FEFF8F\"&gt;","&lt;/span&gt;") %&gt;&lt;br /&gt;
    &lt;/div&gt;
    &lt;%-- Relevance, URL, Creattion --%&gt;
    &lt;div style="margin-top: 5px;"&gt;
        &lt;%-- Relevance --%&gt;
        &lt;div title="&lt;%# "Relevance: " + Convert.ToInt32(ValidationHelper.GetDouble(Eval("Score"),0.0)*100)  + "%" %&gt;"
            style="width: 50px; border: solid 1px #aaaaaa; margin-top: 7px; margin-right: 6px; float: left; color: #0000ff; font-size: 2pt; line-height: 4px; height: 4px;"&gt;
            &lt;div style='&lt;%# "background-color:#a7d3a7;width:"+ Convert.ToString(Convert.ToInt32((ValidationHelper.GetDouble(Eval("Score"),0.0)/2)*100))  + "px;height:4px;line-height: 4px;"%&gt;'&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;%-- URL --%&gt;
        &lt;span style="color: #008000"&gt;
            &lt;%# SearchHighlight(SearchResultUrl(true),"&lt;strong&gt;","&lt;/strong&gt;")%&gt;
        &lt;/span&gt;
        &lt;%-- Creation --%&gt;
        &lt;span style="color: #888888; font-size: 9pt"&gt;
            &lt;%# GetDateTimeString(ValidationHelper.GetDateTime(Eval("Created"), DateTimeHelper.ZERO_TIME), true) %&gt;
        &lt;/span&gt;
    &lt;/div&gt;
&lt;/div&gt;

I created a new transformation rather than modify the default, but that is personal preference.&nbsp; With transformation versioning in v6, there is no need to create a new transformation.

&nbsp;

&nbsp;

That takes care of the transformation. Now let's go over the custom method in my _Functions class.

public static string GetResourceParent(object nodeID, string className) 
{ 
    TreeNode currentNode = TreeHelper.SelectSingleNode(Convert.ToInt32(nodeID));

    if (currentNode == null) 
        return null;

    if (currentNode.NodeClassName.ToLower() == className.ToLower()) 
        return currentNode.NodeAliasPath + ".aspx";

    return GetResourceParent(TreeHelper.SelectSingleNode(currentNode.NodeParentID).NodeID, className); 
}

 

 

Here I simply retrieve the current node calling Kentico's SelectSingleNode method using the supplied node ID.  If the class name of that node equals the supplied class name we return that node's URL.  If they do not match, we call the method again with the node ID of the current document's parent.  Basically, it recursing up the document tree until it finds the nearest parent document of the supplied class name; returning that document's URL.

 

 

Back to the page anchor business.  Let's say your listing page is long enough to require scrolling.  This will be an issue if you are linking to an item beyond the bottom of the page.  To solve this I modified the transformation for the listing of resource documents on the parent listing page as well as the search results.  I use the node ID as the page anchor's unique identifier.

The search result item linked to item #*node ID of an off screen item*.  As you can see, once following the link from the search results page the intended content is not clear.
07

 

 

Example of my original anchor in the listing transformation:

<a href="<%# Eval("Link") %>" target="_blank"><%# Eval("Title") %></a>

 

My modifications to add a page anchor:

<a id="<%# Eval("NodeID") %>" href="<%# Eval("Link") %>" target="_blank"><%# Eval("Title") %></a>

Note the id="<%# Eval("NodeID") %>".  I am using the document’s node ID as the page anchor.

 

 

Modified search results transformation adding page anchor to the URL:

<a style="font-weight: bold" href='<%# IfCompare(GetSearchValue("ClassName"), "CUSTOM.Resource", SearchResultUrl(true), _Functions.GetResourceParent(GetSearchValue("NodeID"), "CMS.MenuItem") + "#" + GetSearchValue("NodeID")) %&gt;'&gt; 
    &lt;%# SearchHighlight(HTMLHelper.HTMLEncode(DataHelper.GetNotEmpty(Eval("Title"), "/")),"&lt;span style=\"font-weight:bold;\"&gt;","&lt;/span&gt;") %&gt; 
&lt;/a&gt;

Note the + "#" + GetSearchValue("NodeID").

 

 

Here we can see the benefit of using page anchors. It is now clear what the intended content is.
08

 

 

Thanks for reading and don't hesitate to comment if anything is unclear.

 
Posted by Ryan Pureber on 2/13/2012 2:47:00 PM
  
Comments
Blog post currently doesn't have any comments.