Integrate a recommendation interface in a web page

This is for:

Developer

When you want to take advantage of a Coveo Machine Learning (Coveo ML) Content Recommendation (CR) model on a site, you should integrate a Coveo JavaScript Search Framework recommendation interface into each page in which you want to display recommendations (see Deploy Content Recommendations (CR)).

To do this, you need to:

The following sample markup shows how to integrate a recommendation interface.

<head>
  <!-- ... -->
  <!-- Import the required Coveo JavaScript Search Framework resources. -->
  <link rel="stylesheet"
        href="https://static.cloud.coveo.com/searchui/v2.10107/css/CoveoFullSearch.css"/>
  <script class="coveo-script"
          src="https://static.cloud.coveo.com/searchui/v2.10107/js/CoveoJsSearch.Lazy.min.js">
  </script>
  <script>
    document.addEventListener("DOMContentLoaded", () => {
    // Configure a search endpoint for the recommendation interface.
    Coveo.SearchEndpoint.configureCloudV2Endpoint("<ORGANIZATION_ID>", "<ACCESS_TOKEN>");
    // Initialize the recommendation interface.
    Coveo.initRecommendation(Coveo.$$(document).find("<RECOMMENDATION_INTERFACE_SELECTOR>"));})
  </script>
  <!-- ... -->
</head>
<body>
  <!-- ... -->
  <!-- Configure the recommendation interface in the page markup -->
  <div id="recommendation"
       class="CoveoRecommendation"
       data-results-per-page="5"
       data-pipeline="<RECOMMENDATION_QUERY_PIPELINE>">
    <div class="CoveoAnalytics" data-search-hub="<RECOMMENDATION_SEARCH_HUB>"></div>
    <div class="coveo-recommendation-header">
      <div class="coveo-recommendation-title">RECOMMENDATION_INTERFACE_TITLE</div>
    </div>
    <div class="coveo-recommendation-body">
      <div class="CoveoResultList"></div>
    </div>
  </div>
  <!-- ... -->
</body>

Where:

  • <ORGANIZATION_ID>

    The unique identifier of the target Coveo organization (for example, mycoveocloudvorganizationg8tp8wu3).

  • <ACCESS_TOKEN>

    A public API key that only grants the privileges to execute queries and push analytics data in the target organization, or a valid search token if the page requires user authentication (see Search token authentication).

  • <RECOMMENDATION_INTERFACE_SELECTOR>

    A CSS selector that matches the HTML element with the CoveoRecommendation class in the search page markup. For example, if the target element has the id="recommendation" attribute, #recommendation would be a good selector.

  • <RECOMMENDATION_QUERY_PIPELINE>

    The name of the query pipeline to enforce for each query originating from the recommendation interface (for example, techDocsRecommendations). This should be set to the name of the query pipeline which contains the CR model that you want to use.

    Note

    For the sake of simplicity, the above code sample suggests that you enforce the query pipeline routing directly in the markup configuration of your recommendation interface.

    A more flexible approach would be to omit the data-pipeline attribute in the Recommendation component markup configuration, and instead use the condition-based query pipeline routing mechanism.

  • <RECOMMENDATION_SEARCH_HUB>

    Tip
    Leading practice

    Setting the recommendation interface searchHub to a distinctive and meaningful value is useful for reporting.

    The name of the origin level 1 to use when sending events from the recommendation interface. This value also defines the search hub to use for all queries originating from the recommendation interface.

    Note

    CR models don’t take the searchHub query parameter into account. However, each model can be configured to only learn from search and click events that match certain filters (for example, if the events have CommunitySearch as their originLevel1).

    UA view events don’t have an originLevel1, but they can still be filtered according to their contentType.

  • RECOMMENDATION_INTERFACE_TITLE

    The title to display for the recommendation interface (for example, People Also Viewed).

Notes
  • For a Coveo organization in the HIPAA environment, you would include a third argument when calling the configureCloudV2Endpoint to set your search endpoint URI to https://searchhipaa.cloud.coveo.com/rest/search:

    const endpointUri = "https://searchhipaa.cloud.coveo.com/rest/search";
    Coveo.SearchEndpoint.configureCloudV2Endpoint(namespace.organizationId,
                                                  namespace.accessToken,
                                                  endpointUri);

    You would also set the endpoint option of your Analytics component to https://analyticshipaa.cloud.coveo.com/rest/ua:

    <div class="CoveoAnalytics"
         data-endpoint="https://analyticshipaa.cloud.coveo.com/rest/ua"></div>
  • If you’re already initializing a search endpoint in a web page (for example, for a standalone search box), you don’t need to initialize a new endpoint for your recommendation interface.

Complete JavaScript Search Framework code sample

You’ve configured a CR model in the techDocRecommendations query pipeline of the mycoveocloudvorganizationg8tp8wu3 organization.

You include code similar to the following in the pages of your technical documentation website to track users' action history, send view events, and render a People Also Viewed recommendation interface.

<!DOCTYPE html>
<!-- Set `language` metadata for view events send from this page. -->
<html lang="en">
  <head>
    <!-- Set `title` metadata for view events. sent from this page. -->
    <title>Customize the Acme Widget</title>
    <!-- ... -->
    <!-- Import required Coveo JavaScript Search Framework resources. -->
    <link rel="stylesheet"
          href="https://static.cloud.coveo.com/searchui/v2.10107/css/CoveoFullSearch.css"/>
    <script class="coveo-script"
            src="https://static.cloud.coveo.com/searchui/v2.10107/js/CoveoJsSearch.Lazy.min.js">
    </script>

    <!-- Import script to send view events and record actions history. -->
    <script type="text/javascript"
            src="https://static.cloud.coveo.com/coveo.analytics.js/2/coveoua.js">
    </script>

    <script>
      // Declare variables.
      namespace = new function() {

        // The API key or search token to use to authenticate the HTTP requests.
        this.accessToken = function() {
          /* Could return a public API key granting only the Execute Queries and
            Usage Analytics - Edit privileges in the target Coveo organization
            organization, or call a server-side service to return a valid search
            token. */
        }();

        // The name of a field that can map the current page to an index item.
        this.contentIdKey = "@clickableuri";

        // The `contentIdKey` field value for the current page.
        this.contentIdValue = window.location.href;

        // Optional. The type of content being tracked.
        this.contentType = "documentation";

        // Optional. Custom user context information.
        this.userRole = function() {
          // Could return a value such as "employee" or "partner".
        }();
        this.userTechnicalSkillLevel = function() {
          // Could return a value such as "intermediate" or "advanced".
        }();
        // ... Additional custom user context ...

        // The unique identifier of the target Coveo organization.
        this.organizationId = "mycoveocloudvorganizationg8tp8wu3";

      };

      // Record actions history and send view events.
      coveoua("init", namespace.accessToken);
      coveoua("send", "pageview", {
        contentIdKey: namespace.contentIdKey,
        contentIdValue: namespace.contentIdValue,
        contentType: namespace.contentType,
        context_userRole: namespace.userRole,
        context_userTechnicalSkillLevel: namespace.userTechnicalSkillLevel
        // ... Additional custom user context ...
      });

      // Configure search endpoint.
      document.addEventListener("DOMContentLoaded", () => {

        Coveo.SearchEndpoint.configureCloudV2Endpoint(namespace.organizationId,
                                                      namespace.accessToken);

        let peopleAlsoViewed = Coveo.$$(document).find("#recommendation");

        // Add user context to recommendation interface queries and UA events.
        Coveo.$$(peopleAlsoViewed).on("buildingQuery", function(e, args) {
          args.queryBuilder.addContext({
            "userRole": namespace.userRole,
            "userTechnicalSkillLevel": namespace.userTechnicalSkillLevel
          });
        });

        // Initialize recommendation interface
        Coveo.initRecommendation(peopleAlsoViewed);
      });
    </script>
    <!-- ... -->
  </head>
  <body>
    <!-- ... -->
    <!-- Configure recommendation interface in page markup. -->
    <div id="recommendation"
         class="CoveoRecommendation"
         data-results-per-page="5"
         data-pipeline="techDocRecommendations">
      <div class="CoveoAnalytics"
           data-search-hub="techDocSitePeopleAlsoViewed"></div>
      <div class="coveo-recommendation-header">
        <div class="coveo-recommendation-title">People Also Viewed</div>
      </div>
      <div class="coveo-recommendation-body">
        <div class="CoveoResultList"></div>
      </div>
    </div>
    <!-- ... -->
  </body>
</html>

Request backup recommendations

In a secured search context, the current user may not be permitted to see all of the items which are requested by a CR model.

Example

You create a recommendation interface whose output may include items from a source that indexes permissions.

In the markup configuration of your Recommendation component, you set the data-results-per-page attribute to 5.

Whenever your recommendation interface sends a query, the CR model in the target query pipeline generates a disjunction query expression (dq) which requests the five items that it deems the most contextually relevant.

However, because the CR model isn’t aware of security identities or effective permissions, it may request five items which the current user isn’t permitted to see.

In this case, the index won’t return any results. Consequently, the recommendation interface either displays an empty result list, or remains entirely invisible.

To reduce the chances of a recommendation interface yielding less than the desired number of results, you may want to request more items from the CR model than the specified data-results-per-page value.

This can be done by using customQueryParameters when associating your CR model with a query pipeline. This lets you specify additional parameters to send to Coveo ML on all queries.

For example, you could request ten recommendations from the model, but only request five results from the index by using the num mlParameters.

{
  "position": 1,
  "modelId": "XXXXXX_eventrecommendation_XXXXXXXX_XXXX_XXXX_XXXX_XXXXXXXXXXXX",
  "condition": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "modelEngine": "eventrecommendation",
  "rankingModifier": 1000,
  "cacheMaximumAge": "PT10S",
  "exclusive": true,
  "customQueryParameters": {
    "num": <NUMBER_OF_RECOMMENDATIONS_TO_REQUEST>
  },
  "useAdvancedConfiguration": false
}

Where you replace <NUMBER_OF_RECOMMENDATIONS_TO_REQUEST> with the number of recommendations that you want to request.

Enable content recommendations padding

When configuring a search endpoint for a recommendation interface, you may want your CR model to pad its output with popular or trending items, up to the desired number of results.

Doing so can be useful if you want your recommendation interface to display the desired number of results even when the target ML model has not yet learned from enough UA data, or when the user’s action history is empty.

This can be done by using customQueryParameters when associating your CR model with a query pipeline. This lets you specify additional parameters to send to Coveo ML on all queries, such as the padding mlParameters.

{
  "position": 1,
  "modelId": "XXXXXX_eventrecommendation_XXXXXXXX_XXXX_XXXX_XXXX_XXXXXXXXXXXX",
  "condition": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "modelEngine": "eventrecommendation",
  "rankingModifier": 1000,
  "cacheMaximumAge": "PT10S",
  "exclusive": true,
  "customQueryParameters": {
    "padding": "<PADDING_TYPE>"
  },
  "useAdvancedConfiguration": false
}

Where you replace <PADDING_TYPE> with the padding value that you want to use (this can be either popular or trending).

Leverage user context data in a recommendation interface

If you send user context custom data in your view events, you should ensure that every query sent from your recommendation interface includes the current user context. Otherwise, recorded user context data will have no impact on the output of your CR model.

const recommendationElement = Coveo.$$(document).find("<RECOMMENDATION_INTERFACE_SELECTOR>");
// Add user context to recommendation interface queries and UA events.
Coveo.$$(recommendationElement).on("buildingQuery", function(e, args) {
  args.queryBuilder.addContext({
    "<USER_CONTEXT_KEY_1>": "<USER_CONTEXT_VALUE_1>",
    "<USER_CONTEXT_KEY_2>": "<USER_CONTEXT_VALUE_2>",
    // ...
    "<USER_CONTEXT_KEY_N>": "<USER_CONTEXT_VALUE_N>"
  });
});
Coveo.initRecommendation(recommendationElement);

Initializing an embedded recommendation interface

When you include a Recommendation component inside a SearchInterface component (that is, in a full search page), the initRecommendation function call requires a second argument which references the main search interface element.

const recommendationElement = Coveo.$$(document).find("<RECOMMENDATION_SELECTOR>");
const searchInterfaceElement = Coveo.$$(document).find("<SEARCH_INTERFACE_SELECTOR>");
Coveo.initRecommendation(recommendationElement, searchInterfaceElement);