Overview
Optimizely Personalization allows you to discover and take action on your customers' browsing behavior.
In this context, browsing behavior is encoded by event objects, documented below. You will need to become familiar with structure of these event objects in order to write effective behavioral queries.
A behavioral query describes how a single customer's events can be converted into a meaningful value. Each query is specified using a JSON object. The format of these query objects is documented below.
Query Objects
Behavioral queries are specified using JSON objects. The Use Cases section describes how you can evaluate these queries.
Each "step" of query construction creates a new, separate property in the top-level query object.
An empty query object evaluates to the list of all events that have been generated by the current visitor.
Example Javascript
// Query for all events. { "version": "0.2" } // Result: [ { "type": "pageview", "name": "AB_landing_page", "category": "landing_page", "tags": { "theme": "urban_explorer" }, "session_index": 1, "time": 1111111111000 }, { "type": "pageview", "name": "AB_product_page", "category": "product_detail", "tags": { "price": 12800, "product_name": "Scout Backpack" }, "session_index": 1, "time": 1111111115000 }, { "type": "click", "name": "AB_add_to_cart", "category": "add_to_cart", "tags": { "price": 12800, "product_name": "Scout Backpack", "quantity": 1 }, "session_index": 1, "time": 1111111119000 }, { "type": "pageview", "name": "AB_product_page", "category": "product_detail", "tags": { "price": 14700, "product_name": "Derby Tier Backpack" }, "session_index": 0, "time": 2222222222000 } ]
1. version
You must include a version number in each of your query objects. This ensures that your query will always be evaluated the same, even if Optimizely introduces a backwards-incompatibile query format in the future.
Example Javascript
// Minimal query object. { "version": "0.2" }
2. filter
You can filter
the results by passing in an array of filters, each comprising a field
, comparator
, and value
. This narrows down the query to those events that match (all) filters.
Note that you can filter by ["age"]
even though age
is not an actual event field. This is particularly useful if you want to select events that were generated in the last N days.
These comparators are usable on all fields:
"eq"
: Requires the field value to roughly equal the filter'svalue
. For strings, this is case-insensitive, as well as leading- and trailing-whitespace-insensitive."is"
: Requires the field value to exactly equal the filter'svalue
."in"
: Requires the field value to be contained in the filter'svalue
, which must be an["array", "of", "acceptable", "values", "such as", 2, "and", true]
. For strings, this is case-insensitive, as well as leading- and trailing-whitespace-insensitive."contains"
: Requires the field value, which must be an array, to contain the filter'svalue
according toindexOf
. For strings, this is case-insensitive."exists"
: Requires the field value to be defined; the filter need not specify avalue
. This is only useful for tags, since top-level fields are defined for every event.
The following string comparators can be used on string fields like type
, name
, category
and also on string tags:
"regex"
: Requires the field value to match the filter'svalue
, which must be either a case-insensitive RegExp"pattern"
, or a["pattern", "flags"]
array
The following number comparators can be used on numeric fields like time
, age
and also on numeric tags like revenue
. These comparators automatically reject non-numeric field values.
"gt"
: Requires the field value to be greater than the filter'svalue
, which must be a number."gte"
: Requires the field value to be greater than or equal to the filter'svalue
, which must be a number."lt"
: Requires the field value to be less than the filter'svalue
, which must be a number."lte"
: Requires the field value to be less than or equal to the filter'svalue
, which must be a number."between"
: Requires the field value to be in the inclusive interval specified by the filter'svalue
, which must be an array of two numbers.
If comparator
is omitted, it defaults to "eq"
.
value
can only be omitted when you have specified the "exists"
comparator.
Example Javascript
// Query for click events. { "version": "0.2", "filter": [ { "field": ["type"], "value": "click" } ] } // Result: [ { "type": "click", "name": "AB_add_to_cart", "category": "add_to_cart", "tags": { "price": 12800, "product_name": "Scout Backpack", "quantity": 1 }, "session_index": 1, "time": 1111111119000 } ] // Query for events that demonstrate interest in the "Scout Backpack". { "version": "0.2", "filter": [ { "field": ["tags", "product_name"], "value": "Scout Backpack" } ] } // Result: [ { "type": "pageview", "name": "AB_product_page", "category": "product_detail", "tags": { "price": 12800, "product_name": "Scout Backpack" }, "session_index": 1, "time": 1111111115000 }, { "type": "click", "name": "AB_add_to_cart", "category": "add_to_cart", "tags": { "price": 12800, "product_name": "Scout Backpack", "quantity": 1 }, "session_index": 1, "time": 1111111119000 } ] // Query for events where a price was known and was at least $135.00. { "version": "0.2", "filter": [ { "field": ["tags", "price"], "comparator": "gte", "value": 13500 } ] } // Result: [ { "type": "pageview", "name": "AB_product_page", "category": "product_detail", "tags": { "price": 14700, "product_name": "Derby Tier Backpack" }, "session_index": 0, "time": 2222222222000 } ] // Query for pageview events that happened between 7 and 14 days ago. { "version": "0.2", "filter": [ { "field": ["type"], "value": "pageview" }, { "field": ["age"], "comparator": "between", "value": [7*24*60*60*1000, 14*24*60*60*1000] } ] } // Result: [ { "type": "pageview", "name": "AB_product_page", "category": "product_detail", "tags": { "price": 14700, "product_name": "Derby Tier Backpack" }, "session_index": 0, "time": 2222222222000 } ] // Query for events that happened in the current session. { "version": "0.2", "filter": [ { "field": ["session_index"], "value": 0 } ] } // Result: [ { "type": "pageview", "name": "AB_product_page", "category": "product_detail", "tags": { "price": 14700, "product_name": "Derby Tier Backpack" }, "session_index": 0, "time": 2222222222000 } ]
3. sort by time
You can sort
events by ["time"]
, either "ascending"
or "descending"
.
Example Javascript
// Query for events, sorted from newest to oldest. { "version": "0.2", "sort": [ { "field": ["time"], "direction": "descending" } ] } // Result: [ { "type": "pageview", "name": "AB_product_page", "category": "product_detail", "tags": { "price": 14700, "product_name": "Derby Tier Backpack" }, "session_index": 0, "time": 2222222222000 }, ..., { "type": "pageview", "name": "AB_landing_page", "category": "landing_page", "tags": { "theme": "urban_explorer" }, "session_index": 1, "time": 1111111111000 }, ]
Example Javascript
// Query for tag values, sorted from most recent to least recent. { "version": "0.2", "sort": [ { "field": ["time"], "direction": "descending" } ], "pick": { "field": ["tags", "product_name"] } } // Result: [ "Derby Tier Backpack", "Scout Backpack", "Scout Backpack" ]
5. sort by frequency
If field values are being picked out of events, you can sort
those values by ["frequency"]
, either "ascending"
or "descending"
.
This deduplicates the picked values and sorts them based on how frequently each one was found in the filtered events. This will also override any sort that may have been performed on the underlying events.
Unlike conventional field identifiers, ["frequency"]
does not correspond to a real event field.
Example Javascript
// Query for unique tag values sorted from most frequent to least frequent. { "version": "0.2", "pick": { "field": ["tags", "product_name"] }, "sort": [ { "field": ["frequency"], "direction": "descending" } ] } // Result: [ "Scout Backpack", "Derby Tier Backpack" ]
6. reduce
You can reduce
a list of values into a single value using an aggregator
.
These aggregators are usable on all types of values:
"nth"
: Reduce the list by choosing the nth value and ignoring the rest."n"
is specified separately, and is 0-indexed, so you should specify0
if you want the first value. This aggregator is only meaningful when values have been sorted."count"
: Reduce the list by resolving to the number of values in the list. There is no need to sort or pick when using this aggregator.
The following mathematical aggregators are usable on numeric fields like time
, age
and also on numeric tags like revenue
:
"sum"
: Reduce the list by computing the sum of the numeric values."avg"
: Reduce the list by computing the average of the numeric values."max"
: Reduce the list by choosing the largest of the numeric values."min"
: Reduce the list by choosing the smallest of the numeric values.
Non-numeric values are ignored when evaluating a mathematical aggregator, as if those values didn't exist at all. This ensures, for example, that an "avg"
computation is not diluted through zero-filling of undefined
values. Note that JavaScript numbers like NaN
, +Infinity
, and -Infinity
are still recognized and can severely affect the result of the aggregation.
Example Javascript
// Query for the single most recent event. { "version": "0.2", "sort": [ { "field": ["time"], "direction": "descending" } ], // Reduce a list of sorted events into a single event. "reduce": { "aggregator": "nth", "n": 0 } } // Result: { "type": "pageview", "name": "AB_product_page", "category": "product_detail", "tags": { "price": 14700, "product_name": "Derby Tier Backpack" }, "session_index": 0, "time": 2222222222000 } // Query for the average price across all product page views. { "version": "0.2", "filter": [ { "field": ["type"], "value": "pageview" } ], "pick": { "field": ["tags", "price"], }, // Reduce a list of picked field values into a single value. "reduce": { "aggregator": "avg" } } // Result: 13750
Event Objects
Events are one of the core concepts of Optimizely Personalization. In the context of customer behavior, these events are exposed as JavaScript objects.
Fields
Each event object has the following fields:
type
:'pageview'
,'click'
, or'custom'
name
: A page name, click event name, or custom event name. If you filter byname
, you probably want to filter bytype
as wellcategory
: A category name. All events with a givenname
andtype
will necessarily have the samecategory
.- Various
tags
: All events from a given page will have those pages' tag values, although additional or overridden tag values may be present on custom events. session_index
: identifier for the session in which the event occurred. The sessions are indexed such that the current session is0
, the previous session is1
, and each subsequentsession_index
is one more than the session which follows it (chronologically).time
: The time at which the event occurred (number of milliseconds after January 1, 1970).
Each field can be a number, a boolean, or a string.
Tag fields may also be undefined
or some arbitrary JSON value, so be prepared for anything if you're retrieving tag values using the query API.
Example Javascript
// A single event { "type": "pageview", "name": "full_product_page", "category": "product_detail", "tags": { "product_sku": "428977", "product_desc": "Clamshell Button 12mm", "product_cat": "button" }, "session_index": 2, "time": 1447046231000 }
Field Identifers
When defining a behavioral query, you may need to refer to a particular event field. You can do this using field identifiers.
For top-level fields like time
, the identifier is an array containing the name of the field.
["type"]
: Identifies thetype
field.["name"]
: Identifies thename
field.["category"]
: Identifies thecategory
field.["time"]
: Identifies thetime
field.
For tag fields, the identifier is an array containing the string "tags"
and then the name of the tag.
["tags", "material"]
: Identifies thematerial
tag.["tags", "color"]
: Identifies thecolor
tag.["tags", "revenue"]
: Identifies the specialrevenue
tag. Its value is extracted in Optimizely's analytics backend and used to compute advanced statistics.
When filtering, you can also refer to an event's age
. age
is not a real event field so it is never actually included in event objects. Compare with time
.
["age"]
: Identifies the amount of time since an event occurred (number of millseconds before the time at which the query is executed).
Use Cases
JavaScript API
When running Optimizely Personalization, your web page can evaluate a behavioral query on demand using the query API.
Notes:
- This type of behavioral query can evaluate to a complex value like a list or an event object, in addition to a simple value like a number, boolean, or string.
- You analyze even more complex behavior by making multiple calls to the JavaScript API.
- You can also use the JavaScript API to debug behavioral queries that you're planning to use in your Custom Behavioral Attributes.