Avoiding search queries in page Quick view
Avoiding search queries in page Quick view
With the HTMLContentInBodyWithRequest
or the FetchPageContent
processors active, Coveo for Sitecore performs additional web requests when indexing Sitecore items to get the item page content, which is essentially the item as seen by a visitor. When the layout contains search-driven component, it may send a lot of unnecessary search queries to the search index.
The purpose of this article is to explain how search queries can be disabled when the item is visited by Coveo to fill the item Quick view.
To do so, you need to add a condition in the search-driven component to detect whether the page request comes from a Coveo agent or not. Coveo for Sitecore always uses the Coveo Sitecore Search Provider
user agent to render the Quick view of your pages, making it easy to create a condition to ignore your search-driven components in your Quick view.
It’s best to avoid rendering the associated component completely when possible. If the component is rendered without data, messages like no data available
might be indexed with the item.
The best way to skip the search queries depends heavily on the implementation. The following sections present solutions for common scenarios.
You have a site that presents various products. For each product, there’s a small box for the related news. The Related news component is search-driven and uses the Search Provider API (via LINQ) to return its results.
Every time the search page is accessed, a search query is performed to get the news. In the on-premises Coveo Enterprise Search Console, you see following query:
Query @fz95xtemplatename6527=="News Article" performed by extranet\Anonymous [Sitecore Security Provider for tutorial]. 3 results
When using Coveo Cloud, you don’t have access to the console. However, the same behavior applies.
To solve the issue, you modify the search-driven component to detect if the page request comes from Coveo before filling the Quick view.
MVC view sending the search query
Coveo for Sitecore 4.1 (November 2018)
When using a search-driven component in an MVC view, you need to add the following statement to prevent the search from being triggered during the indexing process:
if (Request.UserAgent.Contains("Coveo Sitecore Search Provider")) {
return;
}
For the previous example, the full component would then look like this:
RelatedNews.cshtml
@using Sitecore.Mvc
@using Sitecore.ContentSearch
@using Sitecore.ContentSearch.Linq
@using Sitecore.ContentSearch.SearchTypes
@{
if (Request.UserAgent.Contains("Coveo Sitecore Search Provider")) {
return;
}
IEnumerable<SearchResultItem> articles = Enumerable.Empty<SearchResultItem>();
ISearchIndex index = ContentSearchManager.GetIndex("Coveo_web_index");
using (var context = index.CreateSearchContext()) {
articles = context.GetQueryable<SearchResultItem>().Where(item => item.TemplateName == "News Article").Take(3).ToList();
}
}
<style>
#relatednews {
min-height: 200px;
min-width: 200px;
border: solid 1px black;
margin: 20px;
margin-top: 0px;
padding: 20px;
padding-top: 0px;
}
</style>
<div id="relatednews">
<h2>Related news</h2>
@if (articles.Any()) {
<ol>
@foreach (var article in articles) {
<li>
<a href="@article.Url">@article["Title"]</a>
</li>
}
</ol>
} else {
<span>No articles found</span>
}
</div>
MVC model sending the search query
Support for LINQ queries has been discontinued as of November 2018. For November 2018 or later versions of Coveo for Sitecore 4.1, the solution below must be adjusted to take this into account (see Prevent Component Rendering).
Another, cleaner approach would be to use the model to fetch the results and then pass the news articles to the view. In this scenario, there are two things to do: skip the search query at the model level, and prevent the view from rendering.
The first thing to do is to detect if the request is made by Coveo so as to skip the query. It’s important to set the SkipRendering
property so the view can also stop rendering.
SkipRendering = HttpContext.Current.Request.UserAgent.Contains("Coveo Sitecore Search Provider");
For the previous example, the code of the model that populates the MVC view would therefore look like this:
RelatedNews.cs
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Sitecore.ContentSearch;
using Sitecore.ContentSearch.SearchTypes;
using Sitecore.Mvc.Presentation;
namespace Tutorial.Models
{
public class RelatedNews : IRenderingModel
{
public IEnumerable<NewsArticle> Articles { get; private set; }
public bool SkipRendering { get; private set; }
public RelatedNews()
{
Articles = Enumerable.Empty<NewsArticle>();
SkipRendering = false;
}
public void Initialize(Rendering p_Rendering)
{
SkipRendering = HttpContext.Current.Request.UserAgent.Contains("Coveo Sitecore Search Provider");
if (!SkipRendering) {
ISearchIndex index = ContentSearchManager.GetIndex("Coveo_web_index");
using (var context = index.CreateSearchContext()) {
Articles = context.GetQueryable<SearchResultItem>()
.Where(item => item.TemplateName == "News Article")
.Take(3)
.Select(result => new NewsArticle {
Title = result["Title"],
Url = result.Url
}).ToList();
}
}
}
}
public class NewsArticle
{
public string Title { get; set; }
public string Url { get; set; }
}
}
You then need to stop the rendering process by adding a SkipRendering
property.
@if (Model.SkipRendering) {
return;
}
For the same example, the code for the associated view would therefore look like this:
RelatedNews.cshtml
@using Sitecore.Mvc
@using Sitecore.ContentSearch
@using Sitecore.ContentSearch.Linq
@using Sitecore.ContentSearch.SearchTypes
@model Tutorial.Models.RelatedNews
@if (Model.SkipRendering) {
return;
}
<div id="relatednews">
<h2>Related news</h2>
@if (Model.Articles.Any()) {
<ol>
@foreach (var article in Model.Articles) {
<li>
<a href="@article.Url">@article.Title</a>
</li>
}
</ol>
} else {
<span>No articles found</span>
}
</div>
Sublayout sending the search query
Support for LINQ queries has been discontinued as of November 2018. For November 2018 or later versions of Coveo for Sitecore 4.1, the solution below must be adjusted to take this into account.
When using ASP.NET Web Forms, you would need to enter the following snippet.
if (Request.UserAgent.Contains("Coveo Sitecore Search Provider")) {
Visible = false;
return;
}
Setting Visible = false
tells ASP.NET not to render the component. The return
statement terminates the OnLoad
method, skipping the search query.
For the previous example, the sublayout would then look like this:
To keep things simpler, the logic was put directly in the layout instead of using a code-behind file.
RelatedNews.ascx
<%@ Control Language="c#" AutoEventWireup="true" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %>
<%@ Import namespace="Sitecore.ContentSearch" %>
<%@ Import namespace="Sitecore.ContentSearch.Linq" %>
<%@ Import namespace="Sitecore.ContentSearch.SearchTypes" %>
<div id="relatednews">
<h2>Related News</h2>
<asp:Repeater ID="ArticlesList" ItemType="NewsArticle" runat="server">
<HeaderTemplate>
<ol>
</HeaderTemplate>
<ItemTemplate>
<li><a href="<%#: Item.Url %>"><%#: Item.Title %></a></li>
</ItemTemplate>
<FooterTemplate>
</ol>
</FooterTemplate>
</asp:Repeater>
</div>
<script runat="server">
protected override void OnLoad(EventArgs p_Args)
{
if (Request.UserAgent.Contains("Coveo Sitecore Search Provider")) {
Visible = false;
return;
}
IEnumerable<NewsArticle> articles = Enumerable.Empty<NewsArticle>();
ISearchIndex index = ContentSearchManager.GetIndex("Coveo_web_index");
using (var context = index.CreateSearchContext()) {
articles = context.GetQueryable<SearchResultItem>()
.Where(item => item.TemplateName == "News Article")
.Take(3)
.Select(result => new NewsArticle {
Title = result["title"],
Url = result.Url
})
.ToList();
}
ArticlesList.DataSource = articles;
ArticlesList.DataBind();
}
public class NewsArticle
{
public string Title { get; set; }
public string Url { get; set; }
}
</script>