Show language: C# VB.NET Both

How To Work With Custom Data

Custom data, containing specific information about a document, can be assigned to a document at the indexing stage and then recalled during a search. The data's origin, type and relevance are determined by the developer and can be presented to the end user (as extra fields or an image for example) or processed to affect the search results in some way (such as result order).

Source Of Custom Data

The origin of the data is open to the developer, if for example the document represents an item from a database, then the data could be specially selected database columns (such as dates, ownership or other meta data). The data could also consist of the result of an analysis of the document, or data that was originally used in the creation of the document (such as product names, related page URLs or thumbnail images).

Types of Custom Data

The custom data is held as a string, however it can consist of simple or complex types (through the use of delimiters for example) and can be used to store any kind of data (from simple strings through to UUEncoded binary).


Custom date data used for sorting and additional user info.

Adding Custom Data To A Document

This can be achieved either through meta tags in HTML based documents, or through the Central Event System. The indexer will read the data and store it with the document record.

Meta-tags

By adding the following meta tag to a document

<meta name="Keyoti_Search_Custom_Data" content="..." />

The content attribute holds the custom data that should be stored with the document. Technically there is no limit to the length of the data, however large amounts of data (kilobytes) will make reading the document table slower, and thus affect most operations to some degree. If the document is dynamically generated then the custom data can be inserted at run-time, otherwise the data will be static.

Central Event System

By subscribing to the Action event and processing ActionName.ReadText. The associated ActionData.Data is of type DocumentText which has a property called MetaCustomData, which can be set to the data that should be stored. See the Central Event System section for more information on handling actions.

C#
private void CentralEventDispatcher_Action(object sender, Keyoti.SearchEngine.Events.ActionEventArgs e)

{

    if(e.ActionData.Name== ActionName.ReadText)

    {

        string documentUri = (sender as Document).Uri.AbsoluteUri;

        (e.ActionData.Data as DocumentText).MetaCustomData = "my custom data"; 


    } 

}
VB.NET
Private Sub CentralEventDispatcher_Action(ByVal sender As Object, ByVal e As Keyoti.SearchEngine.Events.ActionEventArgs)
        
		If (e.ActionData.Name = ActionName.ReadText) Then
        
			Dim documentUri As String = CType(sender,Document).Uri.AbsoluteUri
            
			CType(e.ActionData.Data,DocumentText).MetaCustomData = "my custom data"

		End If

	End Sub

In this example every document will have it's custom data set to the string "my custom data". The documentUri variable could be used to determine what custom data should be set for the document, for example the document's URI could be used to lookup data in a database, or find a related image.

Processing Custom Data In A Search

The custom data can be presented to the user easily through;

-Web Controls: Keyoti.SearchEngine.Web.ResultItem.CustomData or <%# Container.CustomData %> in the ResultItem template

-SearchAgent: Keyoti.SearchEngine.Search.ResultItem.DocumentRecord.CustomData for programmatic searches

For example, in the ResultItem template, to show the custom data as-is.

<ResultItemTemplate>

...other elements such as the title...

<%# Container.CustomData %>

...

</ResultItemTemplate>

Furthermore, to perform processing on the data before presenting it to the user, in a Web context with the SearchResult control, the ItemCreated event can be handled

For example, if CustomData holds a date string, it can be processed like this;

C#
protected void Sr1_ItemCreated(object sender, Keyoti.SearchEngine.Web.SearchResultItemEventArgs e)

{

    if (e.Item is Keyoti.SearchEngine.Web.ResultItem){

        Label dateLabel = e.Item.FindControl("dateLabel") as Label;

        if (dateLabel != null){

            try {

                DateTime docDate = DateTime.Parse((e.Data as Keyoti.SearchEngine.Search.ResultItem).DocumentRecord.CustomData);

                dateLabel.Text = docDate.ToLongDateString();

            }

            catch (FormatException){

                //wasn't a date

            }

        }

    }

}
VB.NET
Protected Sub Sr1_ItemCreated(ByVal sender As Object, ByVal e As Keyoti.SearchEngine.Web.SearchResultItemEventArgs) Handles SearchResult1.ItemCreated


        If (TypeOf e.Item Is Keyoti.SearchEngine.Web.ResultItem) Then
            Dim dateLabel As Label = CType(e.Item.FindControl("dateLabel"), Label)
            If (Not (dateLabel) Is Nothing) Then
                Try
                    Dim docDate As DateTime = DateTime.Parse(CType(e.Data, Keyoti.SearchEngine.Search.ResultItem).DocumentRecord.CustomData)
                    dateLabel.Text = docDate.ToLongDateString
                Catch ex As FormatException
                    'wasn't a date
                End Try
            End If
        End If
    End Sub

Which will look for a Label control with the ID "dateLabel" (which should be added to the ResultItem template) and set it's Text to a formatted date.

Adding Result Sorting

Custom data (and use of dates for example) leads to the question of custom sorting, since it may be desirable to sort the results according to the additional data held. This is relatively simple through the Central Event System.

In this example it is assumed that the CustomData is either a date string (yyyy/mm/dd) or an empty string (which means that documents with dates will be handled as well as documents without). Two buttons will be added to allow the user to sort by date ascending or descending, instead of the standard keyword relevance sorting.

1. Two buttons are added to the HeaderTemplate

<asp:Button ID="sortDownBT" runat="server" OnClick="sortDownBT_Click" Text="Sort By Date (Newest)" />

<asp:Button ID="sortUpBT" runat="server" OnClick="sortUpBT_Click" Text="Sort By Date (Oldest)" />

2. Variables are added to the code-behind for the sorting

C#
bool sortByDate = false;

SortDirection sortDirection = SortDirection.Descending;
VB.NET
Dim sortByDate As Boolean = False

Dim sortDirection As SortDirection = SortDirection.Descending

3. The button Click handlers are added to the code-behind, which set the sort variables and also tell the SearchResult control ("Sr1") to regenerate it's search results after the post-back

C#
protected void sortDownBT_Click(object sender, EventArgs e)

{

    sortByDate = true;

    sortDirection = SortDirection.Descending;

    Sr1.InvalidateChildControlHierarchy();

}

protected void sortUpBT_Click(object sender, EventArgs e)

{

    sortByDate = true;

    sortDirection = SortDirection.Ascending ;

    Sr1.InvalidateChildControlHierarchy();

}
VB.NET
Protected Sub sortDownBT_Click(ByVal sender As Object, ByVal e As System.EventArgs)

        sortByDate = True

        sortDirection = sortDirection.Descending

        Sr1.InvalidateChildControlHierarchy()

End Sub

Protected Sub sortUpBT_Click(ByVal sender As Object, ByVal e As EventArgs)

        sortByDate = True

        sortDirection = sortDirection.Ascending

        Sr1.InvalidateChildControlHierarchy()

End Sub

4. In the Page_Load handler, the Central Event System's "Action" event is hooked up, and filter load level set to include custom data.

C#
protected void Page_Load(object sender, EventArgs e)
{
    Sr1.FilterLoadLevel = Keyoti.SearchEngine.Search.FilterLoadLevel.Everything;
    Sr1.Configuration.CentralEventDispatcher.Action += new Keyoti.SearchEngine.Events.ActionEventHandler(CentralEventDispatcher_Action);

}
VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    Sr1.FilterLoadLevel = Keyoti.SearchEngine.Search.FilterLoadLevel.Everything
    AddHandler (Sr1.Configuration.CentralEventDispatcher.Action), AddressOf CentralEventDispatcher_Action
        
End Sub

5. And CentralEventDispatcher_Action is added, which will call for the sorting to be done at the appropriate time

C#
void CentralEventDispatcher_Action(object sender, Keyoti.SearchEngine.Events.ActionEventArgs e)

{

    if (sortByDate && e.ActionData.Name == Keyoti.SearchEngine.Events.ActionName.ResultItemsFinalized)

    {

        Keyoti.SearchEngine.Utils.ResultItemList resultItems = e.ActionData.Data as Keyoti.SearchEngine.Utils.ResultItemList;

        resultItems.Sort(new DocumentDateComparer(sortDirection));

    }

}
VB.NET
Private Sub CentralEventDispatcher_Action(ByVal sender As Object, ByVal e As Keyoti.SearchEngine.Events.ActionEventArgs)

        If (sortByDate _

                    AndAlso (e.ActionData.Name = Keyoti.SearchEngine.Events.ActionName.ResultItemsFinalized)) Then

            Dim resultItems As Keyoti.SearchEngine.Utils.ResultItemList = CType(e.ActionData.Data, Keyoti.SearchEngine.Utils.ResultItemList)

            resultItems.Sort(New DocumentDateComparer(sortDirection))

        End If

End Sub

The 'if' is satisfied when date sorting is enabled and the engine signals that the results are finalized. At this point the results are sorted by relevance, so the DocumentDateComparer is used to re-sort the results by date. The comparer class is fairly long because it handles documents with and without dates, as well as any formatting errors in the date strings.

C#
class DocumentDateComparer : IComparer<Keyoti.SearchEngine.Search.ResultItem>
//NOTE: .NET1 users, the above line should be "class DocumentDateComparer : IComparer"
{

    SortDirection sortDirection;

    public DocumentDateComparer(SortDirection sortDirection)

    {

        this.sortDirection = sortDirection;

    } 


    public int Compare(Keyoti.SearchEngine.Search.ResultItem x, Keyoti.SearchEngine.Search.ResultItem y)
//NOTE: .NET1 users, the above line should be "public int Compare(object x, object y)"
    {

        string xData = (x as Keyoti.SearchEngine.Search.ResultItem).DocumentRecord.CustomData;

        string yData = (y as Keyoti.SearchEngine.Search.ResultItem).DocumentRecord.CustomData;

        if(xData.Length==0 && yData.Length>0) return 1;

        if(yData.Length==0 && xData.Length>0) return -1;

        if(xData.Length==0 && yData.Length==0)return 0;

        DateTime xDate, yDate;

        try

        {

            xDate = DateTime.Parse(xData);

        }

        catch (FormatException)

        {

            //not a date, so make it historic so that it ends up lower in the sort order

            xDate = DateTime.MinValue;

        }

        try

        {

            yDate = DateTime.Parse(yData);

        }

        catch (FormatException)

        {

            //not a date, so make it historic so that it ends up lower in the sort order

            yDate = DateTime.MinValue;

        }

        if(sortDirection == SortDirection.Descending)

            return yDate.CompareTo(xDate);

        else

            return xDate.CompareTo(yDate);

        }

}
VB.NET
Class DocumentDateComparer
    Implements IComparer(Of Keyoti.SearchEngine.Search.ResultItem)
	 'NOTE: .NET1 users, above line should be "Implements IComparer"


    Private sortDirection As SortDirection


    Public Sub New(ByVal sortDirection As SortDirection)

        MyBase.New()

        Me.sortDirection = sortDirection

    End Sub


    Public Function Compare(x As Keyoti.SearchEngine.Search.ResultItem, y As Keyoti.SearchEngine.Search.ResultItem) _
		As Integer Implements IComparer(of Keyoti.SearchEngine.Search.ResultItem).Compare
	 'NOTE: .NET1 users, above line should be Public Function Compare(ByVal x As Object, ByVal y As Object) _
	  '		As Integer Implements IComparer.Compare"
        Dim xData As String = CType(x, Keyoti.SearchEngine.Search.ResultItem).DocumentRecord.CustomData

        Dim yData As String = CType(y, Keyoti.SearchEngine.Search.ResultItem).DocumentRecord.CustomData

        If ((xData.Length = 0) _

                    AndAlso (yData.Length > 0)) Then

            Return 1

        End If

        If ((yData.Length = 0) _

        
					AndAlso (xData.Length > 0)) Then

            Return -1

        End If

        If ((xData.Length = 0) _

                    AndAlso (yData.Length = 0)) Then

            Return 0

        End If

        Dim yDate As DateTime

        Dim xDate As DateTime

        Try

            xDate = DateTime.Parse(xData)

        Catch ex As FormatException

            'not a date, so make it historic so that it ends up lower in the sort order

            xDate = DateTime.MinValue

        End Try

        Try

            yDate = DateTime.Parse(yData)

        Catch ex As FormatException

            'not a date, so make it historic so that it ends up lower in the sort order

            yDate = DateTime.MinValue

        End Try

        If (sortDirection = sortDirection.Descending) Then

            Return yDate.CompareTo(xDate)

        Else

            Return xDate.CompareTo(yDate)

        End If

    End Function


    
End Class