Ranking Signals Quickstart

Objective’s AI-native search ranks search results by a relevance score. Ranking Signals enable you to adjust the score, and therefore the final ranking of search results at query-time through the new ranking_expr query parameter.

The most common use case for Ranking Signals is for query-time “boosting” or “de-boosting”. Boosting increases the rank of results based on specific criteria. Boosting is commonly used for personalization based on user preferences or other contextual information that depends on external factors outside of the core, intrinsic relevancy of your search system.

In this Quickstart, we'll walk through a common scenario in e-commerce. We'll use Ranking Signals to promote products within search results that a fictional user would be more likely to purchase.

We'll follow these steps:

  • Add data: Add e-commerce data to the Object Store.
  • Create Index: Build an Index on the data.
  • Search the Index: Search the index for "t-shirt".
  • Compare results: Add a Ranking Expression to the "t-shirt" search.

Setup

Begin by obtaining an API key and installing the SDK:

To get an API key, sign up.

pip3 install --upgrade objective-sdk requests

Adding Data

Next we will add Objects to the Object Store. The code below will download 10,000 example e-commerce products and upsert them to the Object Store. In this example we are building an e-commerce search platform:

import json
import requests
from objective import Objective
client = Objective(api_key='YOUR_API_KEY')

data = json.loads(requests.get("https://d11p8vtjlacpl4.cloudfront.net/demos/ecommerce/hm-10k.json").text)
batch_size = 100
for i in range(0, len(data), batch_size):
   batch = data[i:i+batch_size]
   client.objects.batch(
       operations=[{"method": "PUT", "object_id": str(obj.get("article_id")), "object": obj} for obj in batch]
   )
   print(f"\rUpserted {i+batch_size} objects...", end="", flush=True)

print("\nUpsert complete!")

Building an Index

Build a multimodal index out of the Objects pushed to the API with searchable and filterable fields:

import time
from objective import Objective

from objective.types.index_create_params import Configuration, ConfigurationIndexType, ConfigurationFields

client = Objective(api_key='YOUR_API_KEY')

index = client.indexes.create(
       configuration=Configuration(
           index_type=ConfigurationIndexType(
               name="multimodal",
           ),
           fields=ConfigurationFields(
               searchable={
                   "allow": [
                     "prod_name",
                     "colour_group_name",
                     "detail_desc",
                     "department_name",
                     "garment_group_name",
                     "index_group_name",
                     "perceived_colour_master_name",
                     "perceived_colour_value_name",
                     "product_type_name",
                     "product_group_name",
                     "section_name"
                   ],
               },
               crawlable={
                   "allow":[
                       "image_url"
                   ]
               },
               filterable={
                  "allow": [
                       "department_name",
                       "price"
                   ]
               },
               types={
                   "department_name": "string",
                   "price": "int",
               },
           )
       ),
   )

while not client.indexes.status(index_id=index.id).status.ready:
   time.sleep(1)

print(f"Index {index.id} is ready for queries!")

Create a Function for Displaying Results

Create a function called display_results. We'll use this to help visualize search results as we add ranking expressions.

def display_results(results):
    for obj in results['results']:
        from IPython.display import Image, display, HTML
        data = obj["object"]
        image_html = f'<div style="display: flex; align-items: center;">' \
                     f'<div style="flex: 1;"><img src="{data["image_url"]}" height="120" width="85"></div>' \
                     f'<div style="flex: 2;">' \
                     f'<p>ID: {obj["id"]}</p>' \
                     f'<p>Product Name: {data["prod_name"]}</p>' \
                     f'<p>{data["index_name"]} &raquo; {data["product_group_name"]} &raquo; {data["product_type_name"]}</p>' \
                     f'<p>Color: {data["colour_group_name"]} - {data["colour_group_code"]}</p>'\
                     f'<p>Rating: {data["rating"]}'\
                     f'</div></div>'
        
        display(HTML(image_html))

Query and Display Results for "t-shirt"

Let's start with a query for "t-shirt". After executing this search, we will add a ranking expression to it and then run it again in order to compare the 2 result sets.

results = client.indexes.search(
    index_id=index.id,
    query="t-shirt",
    object_fields="*"
)
display_results(results.to_dict())

Create a Ranking Expression to Boost Menswear

Let's assume that a user is searching the product catalog and that the user is a male who prefers shopping in the Menswear department. How can we express this with Ranking Signals?

In our data we have a field called field called index_group_name and it contains values like Menswearand Ladieswear. Let's construct a ranking expression that gives a 2% boost to all products in the Menswear department.

Why only 2%? - When using Ranking Signals, it is important to consider their impact on overall relevancy. For example. If the boost for Menswear is too strong, a search result for "ladies t-shirt" would be full of Menswear.

Let's rerun the t-shirt search with the 2% boost on Menswear. Then we'll examine the ranking expression in more detail.

ranking_expr = 'uncalibrated_relevance * if(object.index_group_name == "Menswear", 1.02, 1)'

results = client.indexes.search(
    index_id=index.id,
    query="t-shirt",
    object_fields="*",
    ranking_expr=ranking_expr
)

display_results(results.to_dict())

Let's take a closer look at the ranking expression:

uncalibrated_relevance * if(object.index_group_name == "Menswear", 1.02, 1)

uncalibrated_relevance is a primitive in Ranking Signals that represents the relevance score computed by the index before any ranking expression is applied. A ranking expression must return a new score.

The if(object.index_group_name == "Menswear", 1.02, 1) part of the expression is saying if the index_group_name equals Menswear, return 1.02, else return 1.

Finally, we are multiplying the result of the if statement and the uncalibrated_relevance.

To see more examples and a full list of the available functions in Ranking Expressions, see our docs.

Add Another Signal

Our objects also have a rating attribute with values from 1 to 5. Let's extend our ranking signal so that we give a 2% boost to any objects having a rating equal to or greater than 4. To accomplis this, we'll follow the same pattern as our Menswear boost but we will add it to the existing expression by multiplying them together. This means that any object that is in the Menswear category with a rating of over 4 or more will receive a 4% boost.

ranking_expr = 'uncalibrated_relevance * (if(object.index_group_name == "Menswear", 1.02, 1) * if(object.rating >= 4, 1.02, 1))'

results = client.indexes.search(
    index_id=index.id,
    query="t-shirt",
    object_fields="*",
    ranking_expr=ranking_expr
)
display_results(results.to_dict())

The top 10 results are now only populated with products that have a rating of 4 or better!

Congratulations! 🎉 You've just successfully learned how to use Ranking Signals to promote results based on extrinsic factors. Use Ranking Signals when you want to personalize or adjust search results based contextual data.

To learn more about Ranking Signals, see our docs.

Was this page helpful?