Controlling facet ordering in Oracle Commerce Cloud

Gregory Eschbacher
Oracle Developers
Published in
7 min readDec 13, 2018

When rendering the product listing and search results pages, calls will be made to /ccstore/v1/search to retrieve both the search results and various other data, including faceted navigation, breadcrumbs, sort keys.

When it comes to rendering the list of facets (sometimes also called Guided Navigation, or faceted navigation) there are APIs available to control the ordering of those facets. For instance, you might wish to always have Category, then Brand, then Price displayed first, and then follow up with other facets.

With these APIs, we can accomplish a great deal of control over faceted navigation. We can re-order facets, we can suppress facets from showing up, we can create per-Collection orderings and more.

This article seeks to take many of the collected best-practices and tricks to provide useful blueprints to better control your site.

Summary of steps to set the default facet ordering

The overall process to set a site-wide default facet ordering will be as follows:

  • GET /gsadmin/v1/cloud/content/facets/default
  • Configure JSON for each facet we want to specifically order
  • Configure whether we want to show all other facets (or just the ones specifically listed)
  • PUT the updated JSON to /gsadmin/v1/cloud/content/facets/default
  • Preview the changes in your Preview storefront
  • Once it looks good, run partial indexing so the changes are promoted to the live Storefronts

Configuring JSON for facet ordering

If you haven’t configured this before, when you GET /gsadmin/v1/cloud/content/facets/default your JSON will look as follows:

{
"workflowState": "ACTIVE",
"ecr:lastModifiedBy": "admin",
"ecr:lastModified": "2018-11-13T14:14:26.726-05:00",
"priority": 100,
"ecr:createDate": "2018-11-13T14:14:26.726-05:00",
"ecr:type": "content-item",
"contentItem": {
"navigation": [],
"showAll": true,
"@type": "GuidedNavigation"
},
"triggers": [
{
"exactLocation": false
}
]
}

What we’re going to do is add more configuration into that contentItem section. Here is sample JSON that orders 3 facets:

{
"workflowState": "ACTIVE",
"ecr:lastModifiedBy": "admin",
"ecr:lastModified": "2018-11-13T14:14:26.726-05:00",
"priority": 100,
"ecr:createDate": "2018-11-13T14:14:26.726-05:00",
"ecr:type": "content-item",
"contentItem": {
"navigation": [],
"showAll": true,
"@type": "GuidedNavigation",
"navigation": [
{
"@type": "RefinementMenu",
"dimensionName": "product.category"
},
{
"@type": "RefinementMenu",
"dimensionName": "product.brand"
},
{
"@type": "RefinementMenu",
"dimensionName": "product.priceRange"
}
]
},
"triggers": [
{
"exactLocation": false
}
]
}

In the above JSON, we’re doing 4 things of note: We set Category, then Brand, then Price to be rendered. Finally, we have “showAll” : true set. With that option configured, all other facets (that apply) will be rendered after Price Range.

If “showAll” is set to false, only the facets specifically listed in the “navigation” array will be returned in the JSON response.

Previewing and promoting these changes

Once you have modified the JSON, you execute a PUT to /gsadmin/v1/cloud/content/facets/default .

At that point, you can preview these changes in your Preview storefront (by logging into OCC Admin and clicking the Preview button in the upper right).

Once the changes look good, you will need to promote them to Production. In a previous article ( https://medium.com/oracledevs/understanding-indexing-for-search-in-oracle-cloud-commerce-e4829109c03a ), we discussed ways of promoting content. A common trick is to make a trivial change to a product (such as modifying the long description to add a space) and Publishing. After publishing occurs, indexing will occur, during which time content is promoted to the live Storefronts.

Modifying Facet Ordering at a certain location

The above steps detailed how to modify facet ordering as default configuration. If you make the changes above, you will have successfully set the facet order across your whole site.

However, you can modify the facet ordering on a per-location basis. For instance, when navigating “Men’s Shoes”, you might want to put Size first, followed by Brand. Perhaps for “Kid’s Shoes”, you’ve found Price is the most important, and would like that to be rendered first.

This can be accomplished with modifications to the JSON above. It is a bit more complicated, and it involves creating additional “rules” in the JSON format above.

First, let’s discuss how rules work before we move onto creating additional rules for facet ordering.

Understanding Rules

The content above is located as /gsadmin/v1/cloud/content/facets/default , and is a single rule (identified as “default”) in a content collection folder called “facets”.

If we do a GET on /gsadmin/v1/cloud/content/facets, we can see our one rule “default” located there. This rule was created for you out of the box.

We can define more rules to control facet ordering by POST /gsadmin/v1/cloud/content/facets/<insertRuleName> . Rule names should be alphanumeric values with no names or special characters. (For the /gsadmin endpoints, generally we use POST to create content, PUT to update content).

Understanding which rule gets activated

At query time, the navigation state (including refinements, search terms, date/time, and other information) is processed by a rules engine. Multiple rules can activate at a given location. For instance, the “default” rule above will activate everywhere since it has no triggers defined.

If multiple rules apply for the given navigation state, then it comes down to which has the lower numeric value for priority. (The default rule above has a value of 100).

Understanding Rule Priorities

Each rule has a numeric value for priority. Lower values take precedence over higher values.

Therefore, you want to have your generic rules (such as the “default”) have a high numeric value so that you can fit more rules underneath it.

Understanding Triggers

There are few types of trigger for when a rule can be activated. The two main types are keyword triggers and selecting refinements. For now, we’ll discuss only refinement triggers as that makes the most sense for managing facets. (Creating a facet order per keyword would be very burdensome to manage).

Triggers for refinements are based on numeric IDs called “dimension value IDs”. These currently appear in the URL as the N= parameter. For instance, navigating on Brand=Acme, we might see an dimension value ID in the URL like N=439439. If we then navigated on Price=$10–$20, and that has an ID of 23943, the URL might look like &N=439439+23943.

It is those dimension value IDs that we will use to define triggers.

Here is a rule defined with a refinement trigger in place:

{
"workflowState": "ACTIVE",
"ecr:lastModifiedBy": "admin",
"ecr:lastModified": "2018-11-13T14:14:26.726-05:00",
"priority": 50,
"ecr:createDate": "2018-11-13T14:14:26.726-05:00",
"ecr:type": "content-item",
"contentItem": {
"navigation": [],
"showAll": false,
"@type": "GuidedNavigation",
"navigation": [
{
"@type": "RefinementMenu",
"dimensionName": "product.brand"
},
{
"@type": "RefinementMenu",
"dimensionName": "product.priceRange"
},
{
"@type": "RefinementMenu",
"dimensionName": "product.category"
}
]
},
"triggers": [
{
"exactLocation": false,
"dvalIDs" : ["591400359"]
}
]
}

There are few interesting pieces of configuration to note:

  1. We have specified the dimension value ID 591400359, which in my application is for Category = Video Games.
  2. We have set “showAll” = false. This means the only refinements that will be displayed are the 3 listed (brand, price, Category)
  3. We have set the priority to 50, which is lower than the default rule’s value of 100. If we set the priority to 101 or higher, than the default rule would trigger even if we navigated on Category=Video Games
  4. We have “exactLocation” set to false. This means the rule will activate when we navigate on Category = Video Games AND also if we navigated on collections below Video Games AND also if we navigated on other refinements, such as Brand or Price after first navigating on Video Games.

Understanding Exact Location in the triggers

The “exactLocation” option can be tricky to understand, so let’s discuss it by example. Say a shopper has navigated on Category = Video Games and Brand = Sony. If you define a rule trigger on Category=Video Games with “exactLocation” = true, then the rule would not activate for this shopper. The shopper is not at the exact location that rule was defined for. (They are at Sony + Video Games, not Video Games exactly).

With “exactLocation” = false, that rule would activate.

There’s one more consideration. Imagine the shopper navigated on Category = Video Games, and then navigated further on a sub-collection called Video Game Consoles. With “exactLocation” = false, the rule would activate (since we’re not exactly at “Video Games”, but the sub-collection).

The “exactLocation” option can then be used in a few interesting ways. You can establish a rule at a higher location in a hierarchy (such as the Category facet) and have it affect nodes lower in the hierarchy. Or you can define rules to activate only at very specific locations.

When combined with rule priorities, exactLocation can be very powerful to create a series of generic rules, which get overridden by more specific rules. Your generic rules would have high numeric priority values with exactLocation=false.

A trick for more easily retrieving dimension value IDs
(Note: These tricks work for versions 18.5 and higher)

Determining the dimension value ID for a given facet value (like Category=Video Games) can be a bit tricky, but there are a few tricks for determining this.

First, there is a Dimension Value service that can list facet values and their IDs. For instance, to list all of the values in the Category facet, you can execute GET /gsadmin/v1/cloud/dimvals/product.category/children?maxDepth=-1

That same service can also translate an ID into its text value. For instance, if you see 3643890729 in your URL, you can execute GET /gsadmin/v1/cloud/dimvals/3643890729 and it will tell you the name of the facet and facet value.

Second, in the JSON returned by the /ccstore/v1/search endpoint, there is a block of JSON called the searchEventSummary. In there is the list of “facetFilters”. For instance, in my application, navigating on Brand = Acme brings this back:

...
"searchEventSummary": {
"facetFilters": [
{
"id": "591400359",
"dimensionName": "product.brand",
"spec": "Acme"
}
],
...

Suppressing Facets from being returned

Combining the features discussed above, we can now leverage facet ordering to suppress facets from being returned.

For instance, we can configure a default rule to only bring back a handful of facets (so as to not overwhelm the shopper). For instance, if a customer clicks on Electronics from the megamenu at the top, you might have 40 different facets (Screen Size, Connector Type, Megapixels, Battery Size, RAM Size, etc) across all of the various sub-categories of Electronics. It would be better (for this application anyway) to start off with a core set of facets like Category, Brand, Price, Rating, etc instead of a giant list of 40.

First, we would give that default rule a high numeric value for priority. Next, we would specifically list the facets we care about, and set “showAll” = false and configure no triggers.

Then, for certain categories, we define rules where we either list more facets or turn “showAll” = true. Continuing our Electronics example from this section, we might create a rule with a dimension value ID for Smartphones, exactLocation=false, showAll=true. Now only the facets that pertain to Smartphones would be displayed. If we wanted to push Brand to the top, we would add that facet to the list in “navigation”.

Conclusions

This article demonstrates a large number of configuration options and tricks for controlling your site, as well as helping you understand the concepts of rules, dimension value IDs, triggers and more.

--

--

Gregory Eschbacher
Oracle Developers

technologist, Oracle Cloud architect, commerce specialist