Query rules play an important role in search applications to tune search relevancy where we want to configure search results as per our business needs. For example, promote iphone at top when user searched for mobile phones or boost the score for documents containing iphone by boost factor, so they can be ranked in top results.

Deploy this pipeline with one-click

Demo

Pre Setup

Let's define the basics of the pipeline. It will be in the following way:

Copy
{
    "enabled": true,
    "description": "Promote cell phones",
    "routes": [
        {
            "path": "/query-boost-example",
            "method": "POST",
            "classify": {
                "category": "reactivesearch"
            }
        }
    ],
    "envs": {
        "category": "reactivesearch",
        "index": [
            "best-buy-dataset"
        ]
    }
}

We have defined the search route details along with some envs variables to configure the route category and search index. We would be using the best-buy-dataset index, you can browse the data at here.

Stages

Now that we have the pre setup out of the way, let's define the stages for the pipeline.

Authorization

We need to make sure that the requests made to this endpoint are authenticated. To do this, we can use the pre-built stage authorization. We can define it in the following way:

Copy
[
  {
    "id": "authorization",
    "use": "authorization",
    "continueOnError": false
  }
]

It's as simple as that, we don't need to do anything else, the rest will be taken care of by the pipeline.

Apply Facet

The following stage defines a facet to filter search results by department value as DIGITAL COMMUNICATIO.

Copy
[
  {
    "id": "addFilter",
    "use": "addFilter",
    "continueOnError": false,
    "description": "Filter results by department",
    "inputs": {
      "data": {
        "department.keyword": "DIGITAL COMMUNICATIO"
      }
    }
  }
]

Replace Words

The following stage replaces the iphone word in search query to iphone 11 apple so we can get more specific results for latest iphone series available in stock.

Copy
[
  {
    "id": "replaceWords",
    "use": "replaceWords",
    "continueOnError": false,
    "description": "Replaces iphone word to iphone 11 apple",
    "inputs": {
      "data": {
        "iphone": "iphone 11 apple"
      }
    }
  }
]

Boost Results by Score

In the following stage, we are boosting the documents for which categoryPath.name field is cell Phones or Prepaid Phones.

Copy
[
  {
    "id": "boostByScore",
    "use": "boost",
    "continueOnError": false,
    "inputs": {
      "dataField": "categoryPath.name",
      "value": [
        "cell Phones",
        "Prepaid Phones"
      ],
      "boostType": "score",
      "boostFactor": 0,
      "boostOp": "add"
    }
  }
]

Above stage will translate to the following ElasticSearch query:

Copy
{
  "query": {
    <query from search value...>
    "match": {
      "categoryPath.name": {
        "query": "cell Phones Prepaid Phones",
        "operator": "or"
      }
    }
  }
}

Other types of boosting

boost stage also supports geo and range based query boosting. Following is an example on how range based query boosting works:

Copy
[
  {
    "id": "boostByScore",
    "use": "boost",
    "continueOnError": false,
    "inputs": {
      "dataField": "categoryPath.name",
      "value": {
        "start": 23,
        "end": 45
      },
      "boostType": "score",
      "boostFactor": 0,
      "boostOp": "add"
    }
  }
]

Above stage translates to the following ElasticSearch query:

Copy
{
  "query": {
    <query from search value...>
    "range": {
      "categoryPath.name": {
        "gte": 23,
        "lte": 45
      }
    }
  }
}

Following is an example of boosting on a geo location field. Note that in order to boost on a geo field, the field type should be a supported location field.

Copy
[
  {
    "id": "boostByScore",
    "use": "boost",
    "continueOnError": false,
    "inputs": {
      "dataField": "location",
      "value": {
        "location": "22.3184816, 73.17065699999999",
        "unit": "mi",
        "distance": 45
      },
      "boostType": "score",
      "boostFactor": 0,
      "boostOp": "add"
    }
  }
]

Above stage translates to the following ElasticSearch query:

Copy
{
  "query": {
    <query from search value...>
    "geo_distance": {
      "distance": "45mi",
      "location": "22.3184816, 73.17065699999999"
    }
  }
}

Boost Results at Top (Promote Results)

The following stage would promote the documents at the top for which albumTitle field contains Galaxy Note10+. Since we want to promote maximum one document, so we have set the boostMaxDocs to 1.

Copy
[
  {
    "id": "promoteResults",
    "use": "boost",
    "continueOnError": false,
    "inputs": {
      "dataField": "albumTitle",
      "value": [
        "Galaxy Note10+"
      ],
      "boostType": "promote",
      "boostMaxDocs": 1
    }
  }
]

ReactiveSearch Query

We will use the pre-built stage reactivesearchQuery for this stage. We will be converting the RS Query to ES Query in this stage.

We can define this stage in the following way:

Copy
[
  {
    "use": "reactivesearchQuery",
    "continueOnError": false
  }
]

Elastic Search Query

In the final stage, we make the ES call and return the response accordingly. At this stage, the request body should be converted to the ES body so that ES can understand it.

We will be using the pre-built stage elasticsearchQuery at this stage.

We can define this stage in the following way:

Copy
[
  {
    "use": "elasticsearchQuery",
    "continueOnError": false
  }
]

Complete Pipeline

Now that all the stages are defined, let's take a look at the whole pipeline at once:

Copy
{
    "enabled": true,
    "description": "Promote cell phones",
    "routes": [
        {
            "path": "/query-boost-example",
            "method": "POST",
            "classify": {
                "category": "reactivesearch"
            }
        }
    ],
    "envs": {
        "category": "reactivesearch",
        "index": [
            "best-buy-dataset"
        ]
    },
    "stages": [
        {
            "id": "authorization",
            "use": "authorization",
            "continueOnError": false
        },
        {
            "id": "addFilter",
            "use": "addFilter",
            "continueOnError": false,
            "description": "Filter results by department",
            "inputs": {
                "data": {
                    "department.keyword": "DIGITAL COMMUNICATIO"
                }
            }
        },
        {
            "id": "replaceWords",
            "use": "replaceWords",
            "continueOnError": false,
            "description": "Replaces iphone word to iphone 11 apple",
            "inputs": {
                "data": {
                    "iphone": "iphone 11 apple"
                }
            }
        },
        {
            "id": "boostByScore",
            "use": "boost",
            "continueOnError": false,
            "inputs": {
                "dataField": "categoryPath.name",
                "value": [
                    "cell Phones",
                    "Prepaid Phones"
                ],
                "boostType": "score",
                "boostFactor": 0,
                "boostOp": "add"
            }
        },
        {
            "id": "promoteResults",
            "use": "boost",
            "continueOnError": false,
            "inputs": {
                "dataField": "albumTitle",
                "value": [
                    "Galaxy Note10+"
                ],
                "boostType": "promote",
                "boostMaxDocs": 1
            }
        },
        {
            "use": "reactivesearchQuery",
            "continueOnError": false
        },
        {
            "use": "elasticsearchQuery",
            "continueOnError": false
        }
    ]
}

Create the pipeline

Now that we have the whole pipeline defined, we can create the pipeline by hitting the ReactiveSearch instance.

The URL we will hit is: /_pipeline with a POST request.

The above endpoint expects a multipart/form-data body with the pipeline key containing the path to the pipeline file. All the scriptRef files can be passed as a separate key in the form data and will be parsed by the API automatically. Read more about this endpoint here

We can create the pipeline in the following request:

Below request assumes all the files mentioned in this guide are present in the current directory

Copy
curl -X POST 'CLUSTER_URL/_pipeline' -H "Content-Type: multipart/form-data" --form "pipeline=pipeline.json"

Testing the Pipeline

We can hit the pipeline endpoint now to see it working live. Use the following request to hit and get the response in the ReactiveSearch format.

Copy
curl -X POST 'CLUSTER_URL/query-boost-example' -H "Content-Type: application/json" -d '{"query": [{"id": "some ID", "value": "sudoku", "dataField": ["name_s"]}]}'

The above request should return a response with search results (considering there are results matching the search term) in the ReactiveSearch format.