Loop54

Developer Guide - Faceted Search

Introduction

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:

  1. 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.
    Note that there is no guarantee that the facets you specify will be returned in the response.
  2. 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.

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.

Examples

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);
C# source code on Github: FacetingController.cs
// 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);
Java source code on Github: FacetingController.java
// 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"}})
  }
);
JavaScript source code on Github: faceting.js
$request = $connector->search('food');

$request->resultsOptions()
    ->addDistinctFacet('Organic', 'Organic')
    ->addDistinctFacet('Category', 'Category');
PHP source code on Github: Simple.php

Filtering With Facets

Similar to the previous example we ask that the response contains Manufacturer and Category and in addition Organicand Price. Notice that the Manufacturer facet is now applied with a list containing the value specificManufacturer. This has the effect the 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);
C# source code on Github: FacetingController.cs
// 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);
Java source code on Github: FacetingController.java
// 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)});

JavaScript source code on Github: faceting.js
$request->resultsOptions()
    ->addDistinctFacet('Manufacturer', 'Manufacturer', ['Early']);
PHP source code on Github: Simple.php

Range Facets

We’re now applying the RangFacet by passing a parameter consisting of a maxand 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);
C# source code on Github: FacetingController.cs
// 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);
Java source code on Github: FacetingController.java
// 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)});
JavaScript source code on Github: faceting.js
$request->resultsOptions()
    ->addRangeFacet('Price', 'Price', 10, 60);
PHP source code on Github: Simple.php

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 
        }
    }
}
C# source code on Github: CategoryListingController.cs
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 
    }
  }
}
Java source code on Github: CategoryListingController.java
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 
      }
    }
  }
}

JavaScript source code on Github: categorylisting.js
$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;
        }
    }
}
PHP source code on Github: Simple.php