Faceting
Facets offer a simple way to allow users to filter their search results. Faceting can be applied to any attribute set in the product feed.
Adding and Setting Facets
The following two things are important to keep in mind when working with facets:
- The first is to specify which facets should be returned in the response, for example to be shown in the frontend. This is done by adding the facets in question to the request. This gives you control over which facets to show. See Adding facets for more details.
- Secondly you can allow users to filter the search request based on facets. This is called applying a facet and it is done by including a list of the attribute values that the request should be filtered on. More on this under Filtering With Facets.
Note
There is no guarantee that the facets you specify will be returned in the response.
Datatype
The datatype of facets must be the same as the datatype of the attribute value in the product feed.
Dynamic Faceting
Dynamic faceting means that the Loop54 engine automatically determines which facets, out of the list of specified attributes, are most relevant to show. The relevant facets are chosen based on the context of the search result.
When can Facets be Applied
Facets can be applied to the following calls:
- Search
- Category listings
- Related products
The examples on this page show how to add facets to a search call. The same logic can be applied to category listings by replacing the search
call with getEntitiesByAttribute
call and for Java and C# create the request
object as an instance of GetEntitiesByAttributeRequest
instead of SearchRequest
.
Adding Facets
This example shows how to specify the facets to include in the response. In this case, we specify Manufacturer
and Category
. The response will contain those facets if they are relevant and present for the products in the results along with a count of the number of results that applying the facet would yield.
// Search with multiple facets
SearchRequest request = new SearchRequest(query);
//Add facets to the search request
request.ResultsOptions.AddDistinctFacet<string>("Manufacturer");
request.ResultsOptions.AddDistinctFacet<string>("Category");
SearchResponse response = _loop54Client.Search(request);
// Search with multiple facets
SearchRequest request = new SearchRequest(query);
// Add facets to the search request
request.resultsOptions.addDistinctFacet("Manufacturer");
request.resultsOptions.addDistinctFacet("Category");
SearchResponse response = loop54Client.search(request);
// Search with multiple facets
// Add facets to the search request
var response = client.search(
query,
{
facets: ["Manufacturer","Category"].map(function(f){return {name:f,attributeName:f,type:"distinct"}})
}
);
$request = $connector->search('food');
$request->resultsOptions()
->addDistinctFacet('Organic', 'Organic')
->addDistinctFacet('Category', 'Category');
Filtering With Facets
Similar to the previous example we ask that the response contains Manufacturer
and Category
and in addition Organic
and Price
. Notice that the Manufacturer
facet is now applied with a list containing the value specificManufacturer
. This has the effect that the returned result will be filtered on the attribute Manufacturer
matching the string contained in the variable specificManufacturer
. Note also that the facet Price
is a different kind of facet, namely a RangeFacet covered by our next example.
// Search with a distinct facet applied
// The use-case here is e.g. when the user clicks on a specific manufacturer in the search result facet list
SearchRequest request = new SearchRequest(query);
//Add facets to the search request
//And select a specific facet value to filter on
request.ResultsOptions.AddDistinctFacet<string>("Manufacturer", new List<string>() { specificManufacturer });
request.ResultsOptions.AddDistinctFacet<string>("Organic");
request.ResultsOptions.AddDistinctFacet<string>("Category");
request.ResultsOptions.AddRangeFacet<double>("Price");
SearchResponse response = _loop54Client.Search(request);
// Search with a distinct facet applied
// The use-case here is e.g. when the user clicks on a specific manufacturer in the search result facet list
List<String> selectedManufacturers = new ArrayList<>();
selectedManufacturers.add("MeatNStuff");
SearchRequest request = new SearchRequest(query);
// Add facets to the search request
// And select a specific facet value to filter on
request.resultsOptions.addDistinctFacet("Manufacturer", selectedManufacturers);
request.resultsOptions.addDistinctFacet("Organic");
request.resultsOptions.addDistinctFacet("Category");
request.resultsOptions.addRangeFacet("Price");
SearchResponse response = loop54Client.search(request);
// Search with a distinct facet applied
// The use-case here is e.g. when the user clicks on a specific manufacturer in the search result facet list
// Add facets to the search request
// And select a specific facet value to filter on
var selectedFacets = {
"Manufacturer": [specificManufacturer],
"Category": [],
"Organic": []
};
var distinctFacets = ["Manufacturer", "Category", "Organic"].map(function(f){return {name:f,attributeName:f,type:"distinct",selected:selectedFacets[f]}});
var rangeFacets = ["Price"].map(function(f){return {name:f,attributeName:f,type:"range"}});
var response = client.search(query, {facets: distinctFacets.concat(rangeFacets)});
$request->resultsOptions()
->addDistinctFacet('Manufacturer', 'Manufacturer', ['Early']);
Range Facets
We’re now applying the RangFacet by passing a parameter consisting of a max
and min
value that defines the range within which the attributes of returned products must be.
// Search with a range facet
// The use-case here is e.g. when the user selects a specific price range in the search result facet list
SearchRequest request = new SearchRequest(query);
//Add facets to the search request
//And select a specific range for a certain facet
request.ResultsOptions.AddDistinctFacet<string>("Manufacturer");
request.ResultsOptions.AddDistinctFacet<string>("Organic");
request.ResultsOptions.AddDistinctFacet<string>("Category");
request.ResultsOptions.AddRangeFacet<double>("Price", new RangeFacetSelectedParameter<double>() { Min = 10, Max = 60 });
SearchResponse response = _loop54Client.Search(request);
// Search with a range facet
// The use-case here is e.g. when the user selects a specific price range in the search result facet list
SearchRequest request = new SearchRequest(query);
// Add facets to the search request
// And select a specific range for a certain facet
request.resultsOptions.addDistinctFacet("Manufacturer");
request.resultsOptions.addDistinctFacet("Organic");
request.resultsOptions.addDistinctFacet("Category");
request.resultsOptions.addRangeFacet("Price", new RangeFacetParameter.RangeFacetSelectedParameter<Double>() {{ min = 10.0; max = 60.0; }}, null);
SearchResponse response = loop54Client.search(request);
// Search with a range facet
// The use-case here is e.g. when the user selects a specific price range in the search result facet list
//Add facets to the search request
//And select a specific range for a certain facet
var distinctFacets = ["Manufacturer", "Category", "Organic"].map(function(f){return {name:f,attributeName:f,type:"distinct"}});
var rangeFacets = ["Price"].map(function(f){return {name:f,attributeName:f,type:"range",selected:{min: 10, max: 60}}});
var response = client.search(query, { facets: distinctFacets.concat(rangeFacets)});
$request->resultsOptions()
->addRangeFacet('Price', 'Price', 10, 60);
Rendering Facets
Finally, this example shows how to render the facets returned in a response. Note that the code starts with a list of desired facets to show and that it then checks for the existence of those facets in the response. Importantly, it does not assume the existence of any facets in the response.
List<string> distinctFacetsToDisplay = new List<string>() { "Manufacturer", "Category", "Organic" };
foreach (string attributeName in distinctFacetsToDisplay)
{
var facet = response.Results.GetDistinctFacetByName(attributeName);
if (facet != null)
{
var facetItems = facet.Items;
if (facetItems.Any())
Debug.WriteLine(attributeName + ": ");
foreach (var facetItem in facetItems)
{
Debug.WriteLine(facetItem.GetItem<string>() + ": " + facetItem.Count); // Write the facet name and the number of products in the facet
}
}
}
List<String> distinctFacetsToDisplay = new ArrayList<String>();
distinctFacetsToDisplay.add("Manufacturer");
distinctFacetsToDisplay.add("Category");
distinctFacetsToDisplay.add("Organic");
for(String attributeName : distinctFacetsToDisplay)
{
DistinctFacet facet = response.results.getDistinctFacetByName(attributeName);
if (facet != null)
{
List<DistinctFacetItem> facetItems = facet.getItems();
if (facet.hasValues())
System.out.println(attributeName + ": ");
for (DistinctFacetItem facetItem : facetItems)
{
System.out.println(facetItem.getItem(String.class) + ": " + facetItem.count); // Write the facet name and the number of products in the facet
}
}
}
var distinctFacetsToDisplay = ["Manufacturer", "Category", "Organic"];
if(data.results && data.results.facets.length > 0)
{
for (var i in distinctFacetsToDisplay)
{
var facet = data.results.facets.find(function(f) { return f.type == "distinct" && f.name == distinctFacetsToDisplay[i]; });
if(facet)
{
var facetItems = facet.items;
if (facetItems && facetItems.length > 0)
console.log(distinctFacetsToDisplay[i] + ": ");
for (var j in facetItems)
{
console.log(facetItems[j].item + ": " + facetItems[j].count); // Write the facet name and the number of products in the facet
}
}
}
}
$distinctFacetsToDisplay = ['Manufacturer', 'Category', 'Organic'];
$facets = $response->getFacets();
foreach ($facets as $facet) {
if (in_array($facet['name'], $distinctFacetsToDisplay)) {
echo $facet['name'] . ': ' . PHP_EOL;
foreach ($facet['items'] as $option) {
echo ' [' . ($option->selected ? 'X' : ' ') . '] '
. $option->item . ' (' . $option->count . ')' . PHP_EOL;
}
}
}