Multi-warehouse search

Most ecommerce businesses use several geographically distributed warehouses but the underlying warehousing infrastructure is transparent to the end users. They simply interact with the store, place an order and the order is delivered to them from one of the warehouses.

For some businesses however, the concept of the warehouse is an integral part of their business and the users are aware of the warehouse and frequently, they choose the warehouse that they want to use for their delivery. This will typically be B2B commerces, online groceries, or in general, business, where speed of delivery is of utmost importance.

The requirements for this scenario are usually:

  • User selects one of the available warehouses in the user interface (or the warehouse is selected automatically by the system)
  • Several aspects of the products, most notably availability of the products are considered in the context of the selected warehouse

The important aspect of a multi-warehouse solution is that it is not a simple display problem. The underlying models have to obey the warehouse-related product data when applying Ranking rules.

Luigi's Box Autocomplete and Search are designed to let you plug in your own attributes which drive the underlying ranking models and getting a warehouse-scoped ranking.

Availability & availability rank

By plugging in a warehouse-related availability attribute, you can instruct the ranking model to use that attribute for product availability when ranking.

Consider two products with the following data

// Product #1
{
  "title": "Product #1",
  "availability_warehouse_1": 1,  // available in warehouse 1
  "availability_warehouse_2": 0   // not available in warehouse 2
}

// Product #2
{
  "title": "Product #2",
  "availability_warehouse_1": 0,  // not available in warehouse 1
  "availability_warehouse_2": 1   // available in warehouse 2
}

You can then use the availability Context parameter to instruct the ranking model to consider product availability at warehouse 1 by setting context[availability_field]=availability_warehouse_1.

Since the default Ranking prefers available products, if you issue an API request with context[availability_field]=availability_warehouse_1, the API would return products in the order:

  • Product #1
  • Product #2

If you switch availability context to warehouse 2 and issue an API request with context[availability_field]=availability_warehouse_2, the API would return products in the order:

  • Product #2
  • Product #1

Similarly to context[availability_field], use context[availability_rank_field] to plug in the warehouse-scoped Availability rank attribute to use for ranking.

Boosting

The Boost attribute can be used to specify product boosting when indexing data. You can plug in a warehouse-scoped boost attribute to drive boosting per-warehouse.

Use the boosting context to set up different product promotions across different warehouses.

// Product #1
{
  "title": "Product #1",
  "availability_warehouse_1": 1,
  "availability_warehouse_2": 1,
  "boost_warehouse_1": 1          // Boost this product in warehouse 1
}

// Product #2
{
  "title": "Product #2",
  "availability_warehouse_1": 1,
  "availability_warehouse_2": 1,
  "boost_warehouse_2": 1           // Boost this product in warehouse 2
}

You can then use the boost Context parameter to instruct the ranking model to consider product boost at warehouse 1 by setting context[boost_field]=boost_warehouse_1.

If you issue an API request with context[boost_field]=boost_warehouse_1, the API would return products in the order:

  • Product #1
  • Product #2

If you switch boost context to warehouse 2 and issue an API request with context[boost_field]=boost_warehouse_2, the API would return products in the order:

  • Product #2
  • Product #1

Freshness (Introduced at)

The Introduced at attribute can be used to drive the concept of freshness in the ranking model. If the products are being introduced to different warehouses at different times, you can use the freshness Context parameter to adapt the ranking model to a specific warehouse.

// Product #1
{
  "title": "Product #1",
  "availability_warehouse_1": 1,
  "availability_warehouse_2": 1,
  "introduced_at_warehouse_1": "2023-11-30", // Newer at warehouse 1
  "introduced_at_warehouse_2": "2023-11-23"
}

// Product #2
{
  "title": "Product #2",
  "availability_warehouse_1": 1,
  "availability_warehouse_2": 1,
  "introduced_at_warehouse_1": "2023-11-23",
  "introduced_at_warehouse_2": "2023-11-30" // Newer at warehouse 2
}

If you issue an API request with context[freshness_field]=introduced_at_warehouse_1, the API would return products in the order:

  • Product #1
  • Product #2

If you switch boost context to warehouse 2 and issue an API request with context[freshness_field]=introduced_at_warehouse_2, the API would return products in the order:

  • Product #2
  • Product #1

Putting it all together

In practice, you will only rarely use all of the context fields at once. Start by identifying what are the attributes that should affect the ranking at the warehouse level and make sure that you are indexing these attribute at the product level for each warehouse. The examples here are using names which end with _warehouse_1 and _warehouse_2, but the naming convention is flexible. You can use warehouse names, such as _warehouse_berlin or _warehouse_london, you can use your internal ids, such as _warehouse_28179.

When making requests to Search or Autocomplete API, set the context fields to the warehouse fields storing data for the warehouse the user selected. In terms of code, you will most likely just interpolate the warehouse ID into the request, such as

`&context[availability_field]=availability_warehouse_${current_user_warehouse_id}&context[freshness_field]=introduced_at_warehouse_${current_user_warehouse_id}`

Feeds vs. API

This solution and the context fields in general are agnostic of the indexing method. The solution will work the same whether you are using Feeds or API to index the data.

Constraining the feedback loop

If by the nature of your business the users behave very differently across different warehouses, you can force Luigi's Box to create several ranking models for every warehouse. Note that this is a very advanced use case and you should consider the implications before you proceed with the integration. By splitting the model into several smaller models you are making each model slighly less robust, since it will get exposed to less behavioral data. You should be reasonably certain that the user behavior is different across the different warehouses that the split will lead to a benefit.

Creation of the models is driven by the context in the analytics data. Note that since contexts are an advanced feature, the context is currently only supported when pushing analytics events via API.

To create a separate ranking model for each of your warehouses, start by adding a context parameter to the analytics events.

  "context": {
    "warehouse": "Berlin"
  }

This will create a separate model for each warehouse in the background and each events will contribute to the warehouse-specific model.

To use the model in the retrieval APIs, specify the ctx key/value parameter in the API request. The ctx parameter must match one of the key/value context pairs that you use in analytics and which identify the model. In this specific example, you would use

&ctx[]=warehouse:Berlin

The model selection (the ctx parameter) is supported in:

To effectively scope each of the services to the specific warehouse, use the ctx parameter consistenly when calling the respective retrieval endpoints.