Expressions in Inference Rules

Info: Bloomreach provides Enterprise support for this feature to Bloomreach Experience customers. The release cycle for this feature may differ from the core product release cycle.

Overview

Inference Rules allow you to define how goal values are determined based on input variables. Business users can configure parameters in the Parameters field for flexibility, but the actual goal value is calculated by evaluating the expression in the Rules Expression field of an Inference Rules document.

Expressions in the Rules Expression field are executed by the internal JEXL engine. All expressions must follow JEXL syntax.

Alternatively, you can specify a Spring bean name or fully qualified Java class name (FQN) in the Custom Rules FQN field. This allows you to execute custom Java code that implements the com.onehippo.cms7.inference.engine.api.command.InferenceCommand interface. If the Custom Rules FQN field is set, it takes precedence over the expression in the Rules Expression field. The engine first attempts to retrieve a component from the HST-2 ComponentManager (HstServices.getComponentManager().getComponent(fqn)). If no component is found, it creates a new bean using the provided class name.

Goal Value and Classifications

The primary purpose of evaluating expressions in an inference rules document is to determine a goal value (for example, "news" or "events") from a goal variable (such as "content interest category name").

In the Relevance Module, data should be classified—whether categorical, interval, or otherwise—to make it intuitive for business users when applying characteristics in the authoring UI. For example:

  • If the goal variable is categorical (such as "content interest category name" or "visitor comes from a continent"), you can map these values directly or group them as needed.
  • If the goal variable is interval-based (such as "annual income"), classify it into ranges like "Low" (less than $30K), "Medium" (30–70K), or "High" (more than $70K).

If you do not classify the variable, business users must enter ad hoc expressions for each characteristic. This approach has several drawbacks:

  • It is less intuitive than selecting from predefined classified values.
  • It complicates processing of request logs and targeting data, as ad hoc expressions must be interpreted repeatedly.
  • It is more consistent and maintainable to define all possible goal values for a goal variable in the inference rules document. This approach makes the goal variable explicit and versioned for future improvements.

How Is a Goal Value Determined?

The Expressional Inference Rule Engine evaluates the expression defined in the inference rule document and checks the return value. If the script returns a non-null value, the engine converts it to a String (using Object#toString()) and uses it as the determined goal value.

Goal values must ultimately be classified or categorical values defined by business analysts. This approach simplifies both conceptual understanding and data processing.

Important: The expression script must return a non-null value to determine a goal value. If the script returns null, no targeting data is stored for that evaluation.

Example script:

// If the request URI contains '/news', set the goal value to 'news'. if ($.request.requestURI.contains("/news")) { return "news"; } // If the request URI contains '/events', set the goal value to 'events'. else if ($.request.requestURI.contains("/events")) { return "events"; } // Otherwise, return null to indicate no targeting data. return null;

This script implements the following logic:

  • If the request URI contains "/news", the goal value is "news".
  • If the request URI contains "/events", the goal value is "events".
  • Otherwise, no goal value is determined.

Built-in Objects

The Expressional Inference Rule Engine provides several built-in objects for use in expressions:

VariableDescriptionType
$Root object containing parameters, attributes, and other objectscom.onehippo.cms7.inference.engine.api.model.GenericBuiltinModel
$.loggerLogger object for writing logs from expressionscom.onehippo.cms7.inference.engine.api.model.GenericLoggerModel
$.requestRequest object with properties and methods for HttpServletRequest and HstRequestContextcom.onehippo.cms7.inference.engine.api.model.GenericRequestContextModel
$.timeProvides the current date and timecom.onehippo.cms7.inference.engine.api.model.GenericTimeModel
$.collectorContextProvides context attributes during Targeting Collector executioncom.onehippo.cms7.inference.engine.api.model.GenericCollectorContextModel

Each object exposes specific properties and methods, described below.

$ (Root Object)

/** * Built-in root object in inference rules expressions. */ public interface GenericBuiltinModel extends GenericModel { public Set<String> getParameterNames(); public String getParameter(String name); public String[] getParameterValues(String name); public GenericLoggerModel getLogger(); public GenericRequestContextModel getRequest(); public GenericTimeModel getTime(); public boolean hasCollectorContext(); public GenericCollectorContextModel getCollectorContext(); public Iterable<String> getAttributeNames(); public boolean hasAttribute(String name); public Object getAttribute(String name); public void setAttribute(String name, Object value); public void removeAttr(String name); }

$.logger

/** * Logger object in inference rules expressions, wrapping the SLF4J Logger interface. */ public interface GenericLoggerModel extends GenericModel { public String getName(); public boolean isTraceEnabled(); public void trace(String msg); public void trace(String format, Object arg); public void trace(String format, Object arg1, Object arg2); public void trace(String format, Object... arguments); public void trace(String msg, Throwable t); public boolean isDebugEnabled(); public void debug(String msg); public void debug(String format, Object arg); public void debug(String format, Object arg1, Object arg2); public void debug(String format, Object... arguments); public void debug(String msg, Throwable t); public boolean isInfoEnabled(); public void info(String msg); public void info(String format, Object arg); public void info(String format, Object arg1, Object arg2); public void info(String format, Object... arguments); public void info(String msg, Throwable t); public boolean isWarnEnabled(); public void warn(String msg); public void warn(String format, Object arg); public void warn(String format, Object... arguments); public void warn(String format, Object arg1, Object arg2); public void warn(String msg, Throwable t); public boolean isErrorEnabled(); public void error(String msg); public void error(String format, Object arg); public void error(String format, Object arg1, Object arg2); public void error(String format, Object... arguments); public void error(String msg, Throwable t); }

$.request

/** * ServletRequest representation object that wraps HttpServletRequest and HstReuqestContext instances. */ public interface GenericServletRequestModel extends GenericModel { public String getParameter(String name); public List<String> getParameterNames(); public List<String> getParameterValues(String name); public String getScheme(); public String getServerName(); public int getServerPort(); public String getRemoteAddr(); public Locale getLocale(); public String getAuthType(); public Map<String, Cookie> getCookies(); public String getHeader(String name); public long getDateHeader(String name); public int getIntHeader(String name); public List<String> getHeaders(String name); public List<String> getHeaderNames(); public String getMethod(); public String getPathInfo(); public String getPathTranslated(); public String getContextPath(); public String getQueryString(); public String getRemoteUser(); public java.security.Principal getUserPrincipal(); public String getRequestURI(); public String getRequestURL(); } /** * HstRequestContext representation object in inference rules expressions, * extending GenericServletRequestModel. */ public interface GenericRequestContextModel extends GenericServletRequestModel { public GenericContentBeanModel getContent(); }

$.time

/** * Date/time representation object in inference rules expressions, to return the current datetime information. */ public interface GenericTimeModel extends GenericModel { public Date getTime(); public long getTimeInMillis(); public TimeZone getTimeZone(); public int getYear(); public int getMonth(); public int getDate(); public int getWeekOfYear(); public int getWeekOfMonth(); public int getDayOfMonth(); public int getDayOfWeek(); public int getDayOfWeekInMonth(); public int getDayOfYear(); public int getHourOfDay(); public boolean isPm(); public int getHour(); public int getMinute(); public int getSecond(); public int getMillisecond(); }

$.collectorContext

/** * Targeting Collector execution context representation object. */ public interface GenericCollectorContextModel extends GenericModel { public boolean isNewVisitor(); public boolean isNewVisit(); public Map<String, Object> getExtraData(); public Object getRequestLevelGoalValue(); public void setRequestLevelGoalValue(Object requestLevelGoalValue); public double getPersonaEvaluationScore(); public void setPersonaEvaluationScore(double personaEvaluationScore); }

Built-in Function Namespaces

The Expressional Inference Rule Engine provides several built-in function namespaces for use in expressions:

Function NamespaceDescriptionType
string:String utilities, e.g., string:split("Hello, World!", ",")org.apache.commons.lang.StringUtils
arrays:Array access utilities, e.g., arrays:length(arr); arrays:get(arr, 0)com.onehippo.cms7.inference.engine.core.util.ArraysUtils
array:Array utilities from Commons Langorg.apache.commons.lang.ArrayUtils
locale:Locale utilities from Commons Langorg.apache.commons.lang.LocaleUtils
date:Date utilities from Commons Langorg.apache.commons.lang.time.DateUtils
dateformat:Date formatting utilities from Commons Langorg.apache.commons.lang.time.DateFormatUtils
durationformat:Time duration utilities from Commons Langorg.apache.commons.lang.time.DurationFormatUtils
number:Number utilities from Commons Langorg.apache.commons.lang.math.RandomUtils
random:Random number utilities from Commons Langorg.apache.commons.lang.math.RandomUtils
collection:java.util.Collection utilities from Commons Langorg.apache.commons.collections.CollectionUtils
enumaration:java.util.Enumeration utilities from Commons Langorg.apache.commons.collections.EnumerationUtils
iterator:java.util.Iterator utilities from Commons Langorg.apache.commons.collections.IteratorUtils
list:java.util.List utilities from Commons Langorg.apache.commons.collections.ListUtils
map:java.util.Map utilities from Commons Langorg.apache.commons.collections.MapUtils
set:java.util.Set utilities from Commons Langorg.apache.commons.collections.SetUtils
counter:Counting utilities, e.g., incrementing a counter by key in a mapcom.onehippo.cms7.inference.engine.api.util.CounterUtils
resourcebundle:java.util.ResourceBundle utilities, including HST-2 Dynamic Resource Bundlescom.onehippo.cms7.inference.engine.core.util.ResourceBundleUtils
regex:Regular expression utilities for compiling regex and glob expressionscom.onehippo.cms7.inference.engine.core.util.RegexUtils
json:JSON utilities for parsing to net.sf.json.JSONObject or net.sf.json.JSONArraycom.onehippo.cms7.inference.engine.core.util.JsonUtils
yaml:YAML utilities for parsing to org.yaml.snakeyaml.nodes.Nodecom.onehippo.cms7.inference.engine.core.util.YamlUtils
geolocation:GEO location utilities for finding location by client IP addresscom.onehippo.cms7.inference.engine.core.util.GenericGeoLocationUtils

Storing Extra Targeting Data

In some scenarios, you may need to store additional data alongside the goal value. For example, if you want to track "the most frequent content interest category" for each visitor, you need to maintain a counter map for each category and determine the most frequent one.

Example request sequence:

Request SequenceInterest Category of this RequestInterest Category Counter MapGoal value determined
1"news"{ "news": 1 }"news"
2"events"{ "news": 1, "events": 1 }"news" or "events"
3"events"{ "news": 1, "events": 2 }"events"
4"unknown"{ "news": 1, "events": 2, "unknown": 1 }"events"

If you do not store the counter map in the targeting data store, you cannot determine the designed goal value.

To store extra data (such as the counter map), add it to $.collectorContext.extraData (type: java.util.Map). Any data stored in $.collectorContext.extraData is automatically persisted and retrieved from the targeting data store.

Example Script

The following example script determines the "most frequent content interest category" for a visitor. This example is also available in the demo project.

// The primary goal data to infer from various inputs. var interestType = "unknown"; // Example input variables: current request URI and/or the 'Referer' HTTP header. var requestURI = $.request.requestURI; var referer = $.request.getHeader("Referer"); // Map request URIs to goal values using configured parameters. for (var paramName : $.parameterNames) { if (paramName.startsWith("goal.uri.mapping.")) { var paramValue = $.getParameter(paramName); var pair = string:split(paramValue, " :"); var type = arrays:get(pair, 0); var uri = arrays:get(pair, 1); if (requestURI.contains(uri) or referer.contains(uri)) { interestType = type; break; } } } // If the interestType goal value was determined and the collector context is available, // update the counter map and store it in extra data. if ($.hasCollectorContext()) { // Set the request-level goal value before determining the max counter value. $.collectorContext.setRequestLevelGoalValue(interestType); $.logger.debug("requestLevelGoalValue: {}", interestType); // Retrieve or initialize the counter map. var counterMap = $.collectorContext.extraData.get("counterMap"); if (counterMap == null) { counterMap = counter:newMap(); $.collectorContext.extraData.put("counterMap", counterMap); } interestType = counter:incrementAndGetMaxKey(counterMap, interestType); $.logger.debug("counterMap: {}", counterMap); } // Example of adding or reading extra attributes. var fooConnector = $.getAttribute("fooMarketingConnector"); var account = fooConnector != null ? fooConnector.getAccount() : null; if (account != null) { $.logger.debug("Account : " + account); } // Log and return the primary goal value. $.logger.debug("interestType return: {}", interestType); return interestType;

Using an InferenceCommand Bean Instead of JEXL Expressions (Optional)

You can also specify a Spring bean name or fully qualified Java class name (FQN) in the Custom Rules FQN field to execute custom Java code that implements the com.onehippo.cms7.inference.engine.api.command.InferenceCommand interface. If the Custom Rules FQN field is set, it takes precedence over the expression in the Rules Expression field. The engine first looks up the component in the HST-2 ComponentManager (HstServices.getComponentManager().getComponent(fqn)). If not found, it creates a new bean using the provided class name.

To simplify implementation, extend com.onehippo.cms7.inference.engine.api.command.AbstractInferenceCommand in your command class. This base class provides utility methods to access built-in objects. For example, AbstractInferenceCommand#getBuiltin() returns the GenericBuiltinModel instance (referenced as $ in JEXL expressions).

Example:

/* * Copyright 2017 BloomReach B.V. (http://www.bloomreach.com) */ package com.onehippo.cms7.inference.engine.demo.integration; import

[Content truncated]

Share Feedback
Page: /build/service-plugins/inference-engine/expressions-in-inference-rules
Section: Build
Category *