THIS IS ARCHIVED DOCUMENTATION

LINQ QueryResults Example: Adding a Did You Mean Section

Coveo for Sitecore (July 2016) Coveo for Sitecore 4.1 (November 2018)

This section contains information on how to add query corrections in your search page.

It uses the basic search page presented in LINQ QueryResults Example - Create a Basic Search Page.

Handling Query Corrections

The QueryResults object already contains a property to retrieve the corrected queries.

LinqBasedPageExample_3.aspx.cs

results.QueryCorrections

To handle those corrections, add the following section to your page, preferably after the ResultsSection. The Repeater shows each corrected query as a label.

LinqBasedPageExample_3.aspx

<div ID="DidYouMeanSection" Visible="False" runat="server">
    <asp:Repeater ID="DidYouMeanRepeater" runat="server">
        <ItemTemplate>
            <asp:Label Text='<%# Eval("CorrectedQuery") %>' runat="server" />
        </ItemTemplate>
    </asp:Repeater>
</div>

Then, you need to bind the query corrections to this Repeater. You want to render the query corrections whether the query has returned results or not. Change this in the RenderCommon method.

LinqBasedPageExample_3.aspx.cs

private void RenderCommon(SearchModel model,
                          QueryResults<SearchResult> results){
    if(results.QueryCorrections.Count > 0) {
        DidYouMeanRepeater.DataSource = results.QueryCorrections;
        DidYouMeanRepeater.DataBind();
        DidYouMeanSection.Visible = true;
    }
}

Result

The result should look like this:

Improving the Solution

You can improve the rendering by adding some tags in our DidYouMeanRepeater.

LinqBasedPageExample_3.aspx

<div ID="DidYouMeanSection" Visible="False" runat="server">
    <asp:Repeater ID="DidYouMeanRepeater" runat="server">
        <HeaderTemplate>Did you mean: </HeaderTemplate>
        <SeparatorTemplate> or </SeparatorTemplate>
        <ItemTemplate>
            <asp:Label Text='<%# Eval("CorrectedQuery") %>' runat="server" />
        </ItemTemplate>
        <FooterTemplate>?</FooterTemplate>
    </asp:Repeater>
</div>

The HeaderTemplate and FooterTemplate are only shown when there’s at least one correction, and the SeparatorTemplate is only shown between corrections.

Result

The result should look like this:

Adding Interactivity

This section shows you how a client can modify a query by clicking a query suggestion.

Note that the implementation of this method can differ, as there are many ways of doing this.

First, add this simple Javascript code in your client-side page:

LinqBasedPageExample_3.aspx

<script type="text/javascript">
    function ModifyQuery(newValue){
        document.getElementById("Querybox").value = newValue;
        document.getElementById("pageForm").submit();
    }
</script>

You can then replace the label with a button that modifies the query when clicked.

LinqBasedPageExample_3.aspx

<ItemTemplate>
    <asp:Button OnClientClick='ModifyQuery(this.value); return false;' Text='<%# Eval("CorrectedQuery") %>' runat="server" />
</ItemTemplate>

The ModifyQuery(this.value) code is called when the button is clicked with its current value. The return false; code ensures that there’s no PostBack.

Clicking the button should now refresh the page with the corrected query.

However, the suggestion is still shown after the correction. Add the following code in the RenderWithResults method:

LinqBasedPageExample_3.aspx

DidYouMeanSection.Visible = false;

Result

The result should look like this:

Client-Side code

The client-side code should now look like this:

LinqBasedPageExample_3.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="LinqBasedPageExample.aspx.cs" Inherits="Tutorial.LinqBasedPageExample" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>LINQ-based Page Example</title>
</head>
<body>
    <form id="pageForm" runat="server">
        <div class="searchSection">
            <asp:Literal Text="Query: " runat="server" />
            <asp:TextBox ID="Querybox" runat="server" />
            <asp:Button type="submit" Text="Run Query" runat="server" />
        </div>
        <div ID="ResultsSection" class="resultsSection" runat="server">
            <asp:GridView ID="LSTResults" AutoGenerateColumns="true" runat="server" />
        </div>
        <div ID="DidYouMeanSection" Visible="False" runat="server">
            <asp:Repeater ID="DidYouMeanRepeater" runat="server">
                <HeaderTemplate>Did you mean: </HeaderTemplate>
                <SeparatorTemplate> or </SeparatorTemplate>
                <ItemTemplate>
                    <asp:Button OnClientClick='ModifyQuery(this.value); return false;' Text='<%# Eval("CorrectedQuery") %>' runat="server" />
                </ItemTemplate>
                <FooterTemplate>?</FooterTemplate>
            </asp:Repeater>
        </div>
    </form>
</body>
</html>
<script type="text/javascript">
    function ModifyQuery(newValue){
        document.getElementById("Querybox").value = newValue;
        document.getElementById("pageForm").submit();
    }
</script>

Server-Side code

The server-side code should now look like this:

LinqBasedPageExample_3.aspx.cs

namespace Tutorial {
    using System;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using Coveo.Framework.SearchService;
    using Coveo.SearchProvider.Linq;
    using Coveo.SearchProvider.LinqBase;
    using Sitecore.ContentSearch;
    using Sitecore.ContentSearch.Linq;
    using Sitecore.ContentSearch.SearchTypes;
    public partial class LinqBasedPageExample : Page {
        protected void Page_Load(object sender, EventArgs e) {
            SearchModel searchModel = new SearchModel();
            if (Page.IsPostBack) {
                MapControlsToModel(searchModel);
                Render(searchModel);
            }
        }
        private void Render(SearchModel model) {
            if(!String.IsNullOrEmpty(model.Query)) {
                ClearResults();
                QueryResults<SearchResult> results = ExecuteQuery(model);
                if(results.TotalCount != 0) {
                    RenderWithResults(model, results);
                }else{
                    RenderWithoutResults(model, results);
                }
                RenderCommon(model, results);
            }
        }
        private void ClearResults() {
            LSTResults.DataSource = "";
            LSTResults.DataBind();
        }
        private void RenderWithResults(SearchModel model,
                                       QueryResults<SearchResult> results) {
            LSTResults.DataSource = results.Results;
            LSTResults.DataBind();
        }
        private void RenderWithoutResults(SearchModel model,
                                          QueryResults<SearchResult> results) {
            // No results handling.
        }
        private void RenderCommon(SearchModel model,
                                  QueryResults<SearchResult> results){
            if(results.QueryCorrections.Count > 0) {
                DidYouMeanRepeater.DataSource = results.QueryCorrections;
                DidYouMeanRepeater.DataBind();
                DidYouMeanSection.Visible = true;
            }
        }
        private QueryResults<SearchResult> ExecuteQuery(SearchModel model) {
            QueryResults<SearchResult> results;
            ISearchIndex index = ContentSearchManager.GetIndex("Coveo_web_index");
            using (var context = index.CreateSearchContext()) {
                  IQueryable<SearchResult> query = context.GetQueryable<SearchResult>()
                                                          .CoveoOr(model.Query);
                  results = query.GetCoveoQueryResults();
            }
            return results;
        }
        private void MapControlsToModel(SearchModel model) {
            model.Query = Querybox.Text;
        }
    }
    public class SearchModel {
        public string Query { get; set; }
        public SearchModel() {
            Query = "";
        }
    }
    public class SearchResult {
        public DateTime Date { get; set; }
        public string Title { get; set; }
        public string PrintableURI { get; set; }
    }
}