JavaScript Search Framework Tutorial 4 - Modifying the Query

In the previous tutorial, you saw some examples on how you could hook your code to the event system (see JavaScript Search Framework Tutorial 3 - Understanding the Event System). However, the main integration point that external code will want to interact with is the query.

This tutorial will focus on the different parts of a Coveo query, how you can modify a query, and which objects and classes you can use to do so.

If you significantly modified the ./bin/index.html page in previous tutorials, you can undo these changes by rebuilding the project. To do so, run the following command line from the root of the tutorial project folder:

npm run build

External Reference and Documentation

There are several different resources you can refer to in order to better understand the different properties and parameters that you can use to produce a valid query on the Coveo index.

The first one is the reference documentation for the Query Parameters described in the REST Search API Home (see Query Parameters).

Another useful resource is the IQuery interface, which is exposed in the Coveo JavaScript Search Framework reference documentation. Most of the interface properties map 1 to 1 with the Query Parameters available from the REST Search API.

Finally, to learn how to use these parameters correctly, you should consult the query syntax reference (see Coveo Query Syntax Reference).

Query Builder

The QueryBuilder class is essential to build queries in the Coveo JavaScript Search Framework.

You can take a look at the reference documentation to better understand what properties you can use to modify a query.

This step of the tutorial focuses on the two main properties: expression and advancedExpression.

Create a Query Builder

Once the Coveo namespace has been populated in the web browser, you can instantiate a new QueryBuilder like this:

var queryBuilder = new Coveo.QueryBuilder();

This can be used whenever you want to build a new query.

Reference the queryBuilder Argument in Query Event Handlers

Many events are triggered when a new query is launched (see JavaScript Search Framework Tutorial 3 - Understanding the Event System). These events provide access to a QueryBuilder instance, which handlers can use to modify the outgoing query as shown in the following example:

var root = document.body;
Coveo.$$(root).on('buildingQuery', function(e, args) {
  // This queryBuilder could be used to modify the query.
  var queryBuilder = args.queryBuilder;
})

The Basic Expression

Concretely, the basic expression is what the end user would type in a search box.

Since the basic expression value is assumed to come from end-user input, it is important to treat it as such.

Any value you put in the basic expression is processed by the Did you mean feature (see Query Correction Feature). It can also be processed differently in parts of the query pipeline (see What Is a Query Pipeline?). Moreover, it can be processed by the Coveo Machine Learning service (see Coveo Machine Learning).

It is not necessary to know and understand every part of the Coveo technology stack that might influence the basic expression. However, it is important to remember that the Coveo technology stack generally assumes that a value set as the basic expression needs to be treated as end-user input.

When using the REST Search API, the basic expression maps to the q parameter.

To set the basic expression value for a QueryBuilder instance, use the expression property as shown in the example below. This property is an instance of the ExpressionBuilder class.

var builder = new Coveo.QueryBuilder();
var myBasicExpression = 'foobar';
builder.expression.add(myBasicExpression);

Q: What values can I put in the basic expression?

A: Any string, really. However, to construct a coherent and valid query expression, you should refer to the query syntax reference (see Coveo Query Syntax Reference).

The Advanced Expression

The advanced expression part of the query is assumed to be generated by code based on various rules.

When you select values in a Facet component instance, a filter expression is generated to restrict the number of query results. This expression is added to the advanced expression part of the query.

Since the advanced expression value is assumed to contain code generated expressions, as opposed to values entered from end-user input, it is important to treat it as such. The different parts of the Coveo technology stack generally assume that this expression is “computer generated “for advanced filtering capability.

When using the REST Search API, the advanced expression maps to the aq parameter.

To set the advanced expression value for a QueryBuilder instance, use the advancedExpression property as shown in the example below. This property is an instance of the ExpressionBuilder class.

var builder = new Coveo.QueryBuilder();
// This generates a query expression meaning:
// "Any item whose `@fieldName` value is either `firstFieldValue` or `secondFieldValue`".
// Or, more succinctly, (@fieldName == (firstFieldValue, secondFieldValue)).
builder.advancedExpression.addFieldExpression('@fieldName', '==', ['firstFieldValue', 'secondFieldValue']);
 
// This generates a query expression meaning:
// "None of the items whose `@fieldName` value is either `firstFieldValue` or `secondFieldValue`".
// Or, more succinctly, (NOT @fieldName == (firstFieldValue, secondFieldValue)).
builder.advancedExpression.addFieldNotEqualExpression('@fieldName', ['firstFieldValue', 'secondFieldValue']);

Q: What is a field?

A: In the Coveo unified index, a field is a container of structured data, such as metadata extracted from items in a repository.

Thus, fields allow you to make queries like: “Show me all Google Drive items written by John Doe”, or “Show me all YouTube items with more than a million views”.

Q: Can any “advanced” query expression be used in the “basic” query expression, and vice versa?

A: Yes, there is no limitation to which query expression can be put in a specific part of the query. A valid query expression can be inserted in any part of the query (basic or advanced).

Exercises

  • All exercises in this section should be done by modifying the tutorial search page under ./bin/index.html (the exercises should not be done by modifying the original search page under ./pages/index.html ).
  • To actually see the results in your demo page, you must be running a webpack-dev-server (See JavaScript Search Framework Tutorial 0 - Environment Setup).
  1. Create an advanced expression returning only YouTube videos, excluding any video made in the current year.

    Solution:

     <script>
       document.addEventListener('DOMContentLoaded', function () {
         Coveo.SearchEndpoint.configureSampleEndpointV2();
         var root = document.body;
         Coveo.$$(root).on('buildingQuery', function(e, args) {
           var currentYear = new Date().getFullYear();
           args.queryBuilder.advancedExpression.addFieldNotEqualExpression('@year', [currentYear.toString()]);
           args.queryBuilder.advancedExpression.addFieldExpression('@filetype', '==', ['youtubevideo']);
         })
         Coveo.init(root);
       })
     </script>
    
  2. Create an advanced an expression returning all items whose author name begins with the letter “C”, ignoring case.

    Solution:

     <script>
       document.addEventListener('DOMContentLoaded', function () {
         Coveo.SearchEndpoint.configureSampleEndpoint();
         var root = document.body;
         Coveo.$$(root).on('buildingQuery', function(e, args) {
           args.queryBuilder.advancedExpression.addFieldExpression('@author', '*=', ['c*']);
         })
         Coveo.init(root);
       })
     </script>
    
  3. Create an advanced expression that will always take the complete current basic expression and set it as a filter for the @filetype field.

    Solution:

     <script>
       document.addEventListener('DOMContentLoaded', function () {
         Coveo.SearchEndpoint.configureSampleEndpoint();
         var root = document.body;
         // Binding to the doneBuildingQuery event allows the content of the current basic expression to be accessed.
         Coveo.$$(root).on('doneBuildingQuery', function(e, args) {
           var currentBasicExpression = args.queryBuilder.expression.build();
           if (currentBasicExpression) {
             args.queryBuilder.advancedExpression.addFieldExpression('@filetype', '==', [currentBasicExpression]);
           }
         })
         Coveo.init(root);
       })
     </script>
    

What’s Next?

You should now proceed to JavaScript Search Framework Tutorial 5 - Configuring Your Own Search Endpoint.