Rules engine conditions: Working draft spec


#1

Rules engine: draft spec and terminology

At this point, we are just building a “conditions” engine and UI for users. Our current thinking is to take inspiration from other similar systems, e.g.:

Conditions

A condition is made up of one or more configured expressions. An example condition might look like this:

attended (more than) (3) (conference) events (in years 2004, 2005 and 2006)
(or)
    Has (gold) level membership
    (and)
    visited the website (more than) (20) times (within the last 60 days)
    (and) 
    spent (more than) ($500) (within the last 10 days)

Indentation is used to imply logical groupings, avoiding the need for additional and confusing “add sub-group” UIs.

Conditions can be saved globally in the system against a unique name. These conditions can then be used later on in context specific rules. i.e. a rules to show some specific editorial content when condition X is true.

Expressions

Expressions are a programmer configurable line item that can be used in a condition. Expressions consist of:

  • A human readable and translatable label
  • A human readable and translatable “expression string” with placeholders for configurable fields
  • Zero or more configurable fields, used by administrators to fine tune the expression
  • Logical code handler that returns a boolean value based on the current payload + configured fields

See “Example expression” for an idea of how developers can build expressions.

Context

Context for conditions and expressions dictate where in the system they can be used. Possible contexts (not exhaustive):

  • Form validation (form builder, etc.)
  • User (content personalisation and marketing automation)
  • Workflow
  • Specific objects (to be used as filters in datamanager??)

When creating a condition, users can choose a context. This will limit the possible expressions used in the condition based on the developer-configured contexts against individual expressions.

Expressions can be configured with one or more contexts to which they can be applied.

Expression fields

Expression fields allow users to configure a rule condition expression in detail. For example, a user will want to configure the “number of days” field in the following expression:

Logged in user (has) visited the site in the last (10) days

In the above example, the user could also configure the “has” field - possible options being has / has not.

Expression field types

Types of expression field dictate how a user configures that field. For example, a “date” field will present the user with a date picker, whereas a “timeperiod” field will present the user with an advanced date range dialog with options such as “within the last x days”, “from date 1 to date 2”, etc.

Expression types can also receive configuration options. e.g. a “boolean” type can receive a “variant” option to dictate how it is displayed (has/has not, is/is not, etc.)

Expression field type reference

Operator

The operator type will allow an expression to be configured with >, <, ==, contains, etc. It expects a datatype configuration option to dictate what options are available. Valid types:

  • Numeric (greater than,less than,equals,less than or equal to,greater than or equal to)
  • String (equal to, contains, starts with, ends with)
  • Item (all, any, none)
  • Date (after, on or after, before, on or before, on )

Boolean modifier

With a configured variant that dicates which of the following options the user sees:

* is / is not
* has / has not
* was / was not
* will / will not

Condition

UI to choose from previously saved conditions. This allows a condition to be made up of other conditions.

Object

The object type allows a user to choose one or more object records. it expects an “object” configuration item to dicate which object to choose from. Example expression:

Logged in user has booked the (Annual Conference) event

The configured object here would be “event” and the user has chosen the “Annual Conference” event from an event picker.

Number

Simple number picker

Date

Simple date picker

Month

Simple picker

Year

Simple picker

Day of week

Simple picker

Day of month

Simple picker

Time period

Complex picker with different options for relative dates, etc.

Plain string

User can enter some text

Option list

User can picker from a programmer-supplied list of options

Example expression (handler)

This handler could live at /handlers/rules/expressions/UserGroup.cfc. Its metadata indicating all the field configuration, etc. Corresponding i18n .properties files would provide labelling and text for the expression using convention.

/**
 * Rules expression handler for "User (is/is not) a member of (any/all/none) of the groups (groups)"
 * Defines two contexts: user (logged in user/user in general) and event_booking (user who made a booking)
 *
 */
component {

    property name="userService" inject="userService";

    /**
     * Rules engine expression for checking whether
     * or not the current user belongs (or not) to the
     * configured user group(s).
     * \n
     * Expression appears like: "User {_is} a member of {_any} of the groups {groups}"
     *
     * @groups.fieldType  object
     * @groups.object     user_group
     * @expressionContext user
     *
     */
    private boolean function user(
          required struct  payload
        , required any     groups
        , required boolean _is
        , required string  _any
    ) {
        var isMember = userService.userBelongsToGroups(
              userId = arguments.payload.user.id ?: ""
            , groups = arguments.groups
            , scope  = arguments._any // all / any / none
        );

        return arguments._is ? isMember : !isMember;
    }


    /**
     * Rules engine expression for checking whether
     * or not the event booking user belongs (or not) to the
     * configured user group(s).
     * \n
     * Expression appears like: "Booking user {_is} a member of {_any} of the groups {groups}"
     *
     * @groups.fieldType  object
     * @groups.object     user_group
     * @expressionContext event_booking
     *
     */
    private boolean function eventBooking(
          required struct  payload
        , required any     groups
        , required boolean _is
        , required string  _any
    ) {
        var isMember = userService.userBelongsToGroups(
              userId = argument.payload.event_booking.booked_by_user ?: ""
            , groups = arguments.groups
            , scope  = arguments._any // all / any / none
        );

        return arguments._is ? isMember : !isMember;
    }
}

#2

Looks great, Dom.
Does the indentation mean the ability of nesting conditions without additional brackets?


#3

Yes exactly that @Thomas. I will make that clear in the spec.

Edit: added the following:


#4

+1 from my side. It looks well thought. Currently no idea for improvements, will have to think more about it. Great work so far.


#5

I added an example expression handler that a developer could create to fulfill both the logic and configuration of an expression.


#6

Awesome! No idea for improvement, but already a lot of ideas to use it. Great enhancement!


#7

Epic stuff, I was talking about this the other day, would it be possible to have widgets within the content displayed upon the affirmation of a rule?


#8

Yes absolutely. That will be the first application of conditions that we will make. a content widget that:

  • Specifies a condition
  • Specifies content to display on matching the condition
  • Optionally specifies content to display when the condition is not matched

#9

That’s great news, to clarify within the content displayed can a widget be placed? widget-ception?


#10

Yes, exactly. Planning also to create a ‘content’ widget type that should reduce “widget-ception” (for those not familiar, “widget-ception” == a widget with rich content item that contains widgets that contain widgets that contain widgets, etc.)

The new type would show the rich content area directly in the main editor (much like Confluence content macros). This could dramatically reduce widget-ception for these types of content widget.


#11

Phantastic! I would love to see that in action!


#12

Looks great Dom, I wondered if the rules engine could be extended to work for A/B testing experiments or would this need to be a separate spec.

Some things to consider would be

Time based considerations

  • Run experiment for x number of hours/days

Sample size considerations

  • Show content A/B to Total number of visitors
  • Show content A/B to x Percentage of visitors
  • Show same content for return visitors to avoid confusion

Measurement

  • Would need a way of recording what content was being served for which action, maybe could be handled with google tag manager or similar

#13

Yes Luke, that would be great and would be an application of the rules engine conditions system. i.e. we could/should build an ‘experiments’ content widget or somesuch that makes use of the rules engine and particular rule expressions to achieve this. i.e. an expression could be:

visitor is one of a random (40%) of visitors

But yes, separate from the core system.