Variants
Do you have products that come in multiple:
- sizes
- colors
- region specific prices
- region specific stock levels
- … or all of the above?
These are all referred to as Variants.
A Variant can be visual - when we want to render this information on screen (such as the correct color). It can also be structural - for example, if we want to only show specific store pricing to a given customer.
This document is intended for clients who have products with visual variants and/or non-visual variant. The term variant refers to all items that roll up to a master SKU.
This document lays out the best practices for creating a compelling user experience when your catalog has a combination of variant and visually variant products.
The Variant Problem
Color variant
When a user comes to your store and searches for “red dress” they expect to see a result page full of red dresses.
Not yellow dresses, not red shoes. They do not want to see three identical products, one for each size.
In the above image, we can see a typical results page from a typical search tool that does not consider variants. All of the dresses in the low conversion section, actually have a red variant, or the word red in the product description. However, the master record is being displayed along with the master record image.
When the user sees a list of non-red dresses after a simple search for red dress, the trust in the website is greatly reduced. How can we expect a user to trust a site with their credit card if that site doesn’t understand this seemingly simple question?
While it is intuitively obvious to the end user that a search for red dress should bring back red dresses, the problem compounds when you consider the following flawed solutions:
- Model for variants explicitly as stand-alone records and handle switching between different kinds of searches using regular expressions: complicated ETL and front-end code that is fragile and requires continuous maintenance.
- Write front-end logic to handle variant reordering within one record - again, requires custom code and doesn’t take into account other factors of your search
- Don’t reflect the visual variants - will reduce conversion
Stock or price variant
Frequently we need to maintain the same product with different metrics such as price or stock that differ for different regions. We don’t want to expose all of the stock or price options to our customers, while still retaining accurate navigation.
For example, if we have multiple price points, we need the engine to automatically and quickly suggest the possible price ranges for any given search and the given customer’s price rate.
This is critical, as showing the wrong rate to the wrong customer is highly detrimental to the business - both as the risk of reducing margin, or undermining the customer confidence if what is shown does not match the expectations.
The typical solutions to this include either complicated ETL, multiple records (which has higher overhead costs), and typically additional logic that’s maintained within the front-end.
Our solution
This problem is solved by using our engine’s understanding of relevancy together with the most common way to store variants: as children of a parent.
We will look at how to have the engine do the heavy lifting for both visual and non-visual variants.
Visual model
{
"title": "shirt",
"visualVariants": [
{
"color": "red",
"size": ["M","XL"]
},
{
"color": "blue",
"size": ["S","M"]
}
]
}
Non visual model
{
"title": "shirt",
"pricingRate": [
{
"group": "corporate",
"price": "5.99"
},
{
"group": "individual",
"price": "8.99"
}
]
}
Configuration
The fieldDefinition
of ChildRecord
is what gives us the power to support any of the above requirements.
Approach: Visual variants
We can use the ChildRecord
model to automatically re-order subvariants within the record by relevancy. The kinds of business requirements this could meet is when we have multiple visual variants within one record.
This means that the front-end just needs to render the very first variant, and no business logic has to be stored in the rendering layer. The engine automatically ensures that the correct visual variant is first.
Once a field is denoted as a ChildRecord
with an action of order
, we can tell the engine to place the most relevant child records at the top:
Sample configuration
fieldDefinition: {name: "visualVariants", type: "ChildRecord", childAction: "Order"}
Benefits
This is exceptionally business friendly, as all the typical benefits of relevancy apply:
- Synonyms and spell corrections that the merchandiser created will apply. This means a merchandiser can create a synonym for “maroon, red” and the correct visual variant will be automatically ordered to the top.
- Biasing profiles will apply at child level: if we have multiple “red” variants of different fabrics, and a specific kind of fabric is biased for, this will affect the order.
The relevancy will be calculated using the entire sub-record, using the fields for refinement, or, if this was a search, using searchable fields defined within the sub-record. This relevancy will take into account the biasing profile that is being applied.
Example
For example, given the following record:
{
"title": "shirt",
"visualVariants": [
{
"color": "red",
"size": ["M","XL"]
},
{
"color": "blue",
"size": ["S","M"]
}
]
}
By default, searching for “blue” will bring back the full record, with the variants in the original upload order, as above.
By setting the childAction
on visualVariants
to Order
the engine will place the most relevant child records at the top:
{
"title": "shirt",
"visualVariants": [
{
"color": "blue",
"size": ["S","M"]
},
{
"color": "red",
"size": ["M","XL"]
}
]
}
Approach: Non visual variants
We can use the ChildRecord
model to filter to only the correct subvariant , so that only the correct pricing or stock count is returned.
The business use case is a situation where you could have multiple stores with different inventory counts, or pricing models in a B2B relationship, and you’d like to segregate some parts of the record from polluting the recall or relevancy.
The childAction
of Filter
will restrict the returned child records to just the matching ones, so that we only get the correct pricing back.
Sample configuration
fieldDefinition: {name: "pricingRate", type: "ChildRecord", childAction: "Filter"}
Benefits
We want to ensure that Corporate users only see their pricing, and Individuals only see theirs - and that there is no chance of an individual seeing the corporate rate.
By default, refining on pricingRate.group
:“individual” will bring back the full record, which we don’t want as it would return the corporate pricing within the record.
The childAction
of Filter
will restrict the returned child records to just the matching ones, so that we only get the correct pricing back.
This is exceptionally fast and allows us to have thousands of different models within 1 record, without any additional overhead on the creation of the navigation or any impact to the search response time.
Example
For example, given the following record:
{
"title": "shirt",
"pricingRate": [
{
"group": "corporate",
"price": "5.99"
},
{
"group": "individual",
"price": "8.99"
}
]
}
By setting the childAction
on pricingRate
to Filter
the engine will only return the filtered to price.
Using the data above, when refining on pricingRate.group
:“individual”, the record returned would be:
{
"title": "shirt",
"pricingRate": [
{
"group": "individual",
"price": "8.99"
}
]
}
instead of the full record.
This means dynamic navigation and front-end rendering will not show the other pricing rates.
Approach: Both
You can blend both if you have custom pricing and visual variants.
The configuration is typically:
- visual variant are the “parent”
- non-visual variants are nested under
Example
Let’s combine the above snippets into one record:
{
"title": "shirt",
"visualVariants": [
{
"color": "blue",
"size": ["S","M"],
"pricingRate": [
{
"group": "corporate",
"price": "5.99"
},
{
"group": "individual",
"price": "8.99"
}
]
},
{
"color": "red",
"size": ["M","XL"],
"pricingRate": [
{
"group": "corporate",
"price": "5.99"
},
{
"group": "individual",
"price": "8.99"
}
]
}
]
}
The ChildRecord
configuration for this record would look as such:
fieldDefinition: {name: "visualVariants", type: "ChildRecord", childAction: "Order"}
fieldDefinition: {name: "visualVariants.pricingRate", type: "ChildRecord", childAction: "Filter"}
Note how pricingRate
is now a nested field under visualVariants
.