Salesforce

Display Deep Attributes of Salesforce Items in Coveo Search

Let’s start with a look at deep attributes.

What is a deep attribute?

For every search solution, we rely heavily on the search engine to return relevant results for a search term. Once search results are returned, the users often want to check related information to the search results, such as the rating of an article, the number of followers of a hot topic in a community. Oftentimes, this type of information is not directly defined to the targeted object but closely associated with it.

During content-ingestion stage, usually middle layer components (aka connectors) are used to traverse the content source and retrieve the record information (i.e. metadata, permission configuration, and its binary content, if available). If the metadata in question is constructed as part of the original record, the search engine would already have it indexed, and it would be very easy to surface the attribute. Deep attributes are those not directly associated with a record in search engine. But we want to show them as part of the UI display. Even deep attributes can be converted to “shallow” attributes by the connectors if the requirements are well-defined during connector implementation; any new requirements may easily bring in deep attribute into the picture.

I recently encountered a user case where the requested information was not indexed by the Salesforce connector for Coveo. Here is a general approach to deal with these type of requirements, one that would also be applicable to more general cases.

In summary, here are the major components for the approach:

  • JavaScript function to collect the ids of search results, along with other attributes that are pertinent to the result records
  • Apex controller to retrieve deep attributes in question
  • Apex JavaScript Remoting call to Apex controller and display retrieved deep attributes onto UI

Collect record info in question

We are only interested in the records being displayed to the users. In order to collect all the record ids returned in Coveo search result page, we need define a data structure to hold the records. A map in this case would work best. In order to match the results from JavaScript Remoting call, we’d better have an unique identifier for the UI element. Coveo happened to have a metadata field, urihash, which is the best candidate to link a record’s attributes to its UI display.

As for when to trigger the call, Coveo event handler, Coveo.QueryEvents.preprocessResults and/or Coveo.QueryEvents.preprocessMoreResults would be a good choice:

    searchInterface.on(Coveo.QueryEvents.preprocessResults, function (e, data) {
       Coveo._.each(data.results.results, function (r, idx) {
         if(r.raw.sfkbid){
           recordInfo[normalizeUriHash(r.raw.urihash)] = JSON.stringify({ ‘kbid’: r.raw.sfkbid,
               ‘objecttypename’: r.raw.objecttypename});
… …

Please note the map associates the urihash of a record with its detailed information. The normalizeUriHash() will be explained later.

Apex JavaScript Remoting call

Nothing special here. Actually, you can find tons of examples of JavaScript Remoting. The only interesting piece of information would be the usage of urihash. We expect the Apex controller returns back a list of records, and each record contains an attribute of urihash.

  <script type=”text/javascript”>
       function getRecordInfo(mapInfo) {
           Visualforce.remoting.Manager.invokeAction(
               ‘{!$RemoteAction.MyRemoteController.getInfo}’,
               mapInfo,
               function(result, event){
                   if (event.status) {
                      console.log(result);
                      Coveo._.each(result, function(data) {
                           console.log(data);
                           // update UI element with the available deep attributes
                           var rateElem = Coveo.$$(document).find(‘#’ + data[‘urihash’]’);
… …
                      });
                   } else if (event.type === ‘exception’) {
                       console.log(event.message);
                   } else {
                       console.log(event.message);
                   }
               },
               {escape: true}
           );
       }
   </script>

 

Apex controller

A controller should be defined to retrieve deep attributes for the given set of records. The controller is doing the heavy lifting where we could define complicated logic to retrieve related information.  

global with sharing class MyRemoteController {
   global class MyInfo {
       public String urihash {get; set;}
public String rating {get; set;}

       public String followerCount {get; set;}

       public MyInfo(String urihash, String followerCount, String rating){
           this.urihash = urihash;
           this.rating = rating;
           this.followerCount = followerCount;
       }
   }

   @RemoteAction
   global static List<MyInfo> getInfo(Map<String, String> mapInput) {
       List<MyInfo> listRet = new List<MyInfo>();
… …
       return listRet;
   }
}

 

When to issue the Javascript remoting call?

From Coveo event mode, the last displaying event is deferredQuerySuccess. It’s a good point to trigger the JavaScript Remoting call, and get the results back. Then the UI elements can be updated with the results from the JavaScript remoting call.

searchInterface.on(Coveo.QueryEvents.deferredQuerySuccess, function (e, data) {
         // Trigger JavaScript Remoting to get deep attributes
         console.log(“deferredQuerySuccess … “);
         getRecordInfo(recordInfo);
         console.log(” / deferredQuerySuccess … “);
         recordInfo = {};
     });

How to map deep attributes to their correct UI elements?

In the template definition, the id attribute for the whole element display can be padded with an underscore expression.

  <div class=”coveo-result-frame” id=”{{=normalizeUriHash(raw.urihash)}}”>

However, during testing I noticed the some  values of urihash were not supported by DOM as id, specifically characters, such as +, /, and number at the beginning. The following Javascript function was used to normalize it:

    function normalizeUriHash(urihash) {

          return ‘A’ + urihash.replace(/\+/g, ‘_’).replace(/\//g, ‘_’);

      }

The approach utilizes Salesforce JavaScript Remoting feature to grab deep attributes for the records in the search result page. Since there is only one Apex controller call for one search result page, it will not incur performance hit. Actually the approach can also be extended to other content sources as long as there is a service (similar to Apex controller) available to provide deep attributes for the records.

References:

What Is JavaScript Remoting?

Coveo Events for JSUI

Apex Code Best Practices

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Categories
Follow Us
TwitterLinkedinFacebookYoutubeInstagram