//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
Apache Unomi gathers information about users actions, information that is processed and stored by Unomi services. The
collected information can then be used to personalize content, derive insights on user behavior, categorize the user
profiles into segments along user-definable dimensions or acted upon by algorithms.

=== Items and types

Unomi structures the information it collects using the concept of `Item` which provides the base information (an
identifier and a type) the context server needs to process and store the data. Items are persisted according to their
type (structure) and identifier (identity). This base structure can be extended, if needed, using properties in the
form of key-value pairs.

These properties are further defined by the `Item`’s type definition which explicits the `Item`’s structure and
semantics. By defining new types, users specify which properties (including the type of values they accept) are
available to items of that specific type.

Unomi defines default value types: `date`, `email`, `integer` and `string`, all pretty self-explanatory. While you can
think of these value types as "primitive" types, it is possible to extend Unomi by providing additional value types.

Additionally, most items are also associated to a scope, which is a concept that Unomi uses to group together related
items. A given scope is represented in Unomi by a simple string identifier and usually represents an application or set
of applications from which Unomi gathers data, depending on the desired analysis granularity. In the context of web
sites, a scope could, for example, represent a site or family of related sites being analyzed. Scopes allow clients
accessing the context server to filter data to only see relevant data.

_Base `Item` structure:_

[source,json]
----
{
  "itemType": <type of the item>,
  "scope": <scope>,
  "itemId": <item identifier>,
  "properties": <optional properties>
}
----

Some types can be dynamically defined at runtime by calling to the REST API while other extensions are done via Unomi
plugins. Part of extending Unomi, therefore, is a matter of defining new types and specifying which kind of Unomi
entity (e.g. profiles) they can be affected to. For example, the following JSON document can be passed to Unomi to
declare a new property type identified (and named) `tweetNb`, tagged with the `social` tag, targeting profiles and
using the `integer` value type.

_Example JSON type definition:_

[source,json]
----
{
    "itemId": "tweetNb",
    "itemType": "propertyType",
    "metadata": {
        "id": "tweetNb",
        "name": "tweetNb",
        "systemTags": ["social"]
    },
    "target": "profiles",
    "type": "integer"
}
----

____

Unomi defines a built-in scope (called `systemscope`) that clients can use to share data across scopes.
____

=== Events

Users' actions are conveyed from clients to the context server using events. Of course, the required information
depends on what is collected and users' interactions with the observed systems but events minimally provide a type, a
scope and source and target items. Additionally, events are timestamped. Conceptually, an event can be seen as a
sentence, the event's type being the verb, the source the subject and the target the object.

_Event structure:_

[source,json]
----
{
    "eventType": <type of the event>,
    "scope": <scope of the event>,
    "source": <Item>,
    "target": <Item>,
    "properties": <optional properties>
}
----

Source and target can be any Unomi item but are not limited to them. In particular, as long as they can be described
using properties and Unomi’s type mechanism and can be processed either natively or via extension plugins, source and
target can represent just about anything. Events can also be triggered as part of Unomi’s internal processes for example
when a rule is triggered.

Events are sent to Unomi from client applications using the JSON format and a typical page view event from a web site
could look something like the following:

_Example page view event:_

[source,json]
----
{
    "eventType": "view",
    "scope": "ACMESPACE",
    "source": {
        "itemType": "site",
        "scope": "ACMESPACE",
        "itemId": "c4761bbf-d85d-432b-8a94-37e866410375"
    },
    "target": {
        "itemType": "page",
        "scope": "ACMESPACE",
        "itemId": "b6acc7b3-6b9d-4a9f-af98-54800ec13a71",
        "properties": {
            "pageInfo": {
            "pageID": "b6acc7b3-6b9d-4a9f-af98-54800ec13a71",
            "pageName": "Home",
            "pagePath": "/sites/ACMESPACE/home",
            "destinationURL": "http://localhost:8080/sites/ACMESPACE/home.html",
            "referringURL": "http://localhost:8080/",
            "language": "en"
        },
        "category": {},
        "attributes": {}
      }
    }
}
----

=== Profiles

By processing events, Unomi progressively builds a picture of who the user is and how they behave. This knowledge is
embedded in `Profile` object. A profile is an `Item` with any number of properties and optional segments and scores.
Unomi provides default properties to cover common data (name, last name, age, email, etc.) as well as default segments
to categorize users. Unomi users are, however, free and even encouraged to create additional properties and segments to
better suit their needs.

Contrary to other Unomi items, profiles are not part of a scope since we want to be able to track the associated user
across applications. For this reason, data collected for a given profile in a specific scope is still available to any
scoped item that accesses the profile information.

It is interesting to note that there is not necessarily a one to one mapping between users and profiles as users can be
captured across applications and different observation contexts. As identifying information might not be available in
all contexts in which data is collected, resolving profiles to a single physical user can become complex because
physical users are not observed directly. Rather, their portrait is progressively patched together and made clearer as
Unomi captures more and more traces of their actions. Unomi will merge related profiles as soon as collected data
permits positive association between distinct profiles, usually as a result of the user performing some identifying
action in a context where the user hadn’t already been positively identified.

=== Sessions

A session represents a time-bounded interaction between a user (via their associated profile) and a Unomi-enabled
application. A session represents the sequence of actions the user performed during its duration. For this reason,
events are associated with the session during which they occurred. In the context of web applications, sessions are
usually linked to HTTP sessions.

=== Segments

Segments are used to group profiles together, and are based on conditions that are executed on profiles to determine if
they are part of a segment or not. This also means that a profile may enter or leave a segment based on changes in their
properties, making segments a highly dynamic concept.

Here is an example of a simple segment definition registered using the REST API:

[source]
----
curl -X POST http://localhost:8181/cxs/segments \
--user karaf:karaf \
-H "Content-Type: application/json" \
-d @- <<'EOF'
{
  "metadata": {
    "id": "leads",
    "name": "Leads",
    "scope": "systemscope",
    "description": "You can customize the list below by editing the leads segment.",
    "readOnly":true
  },
  "condition": {
    "type": "booleanCondition",
    "parameterValues": {
      "operator" : "and",
      "subConditions": [
        {
          "type": "profilePropertyCondition",
          "parameterValues": {
            "propertyName": "properties.leadAssignedTo",
            "comparisonOperator": "exists"
          }
        }
      ]
    }
  }
}
EOF
----

For more details on the conditions and how they are structured using conditions, see the next section.

=== Conditions

Conditions are a very useful notion inside of Apache Unomi, as they are used as the basis for multiple other objects.
Conditions may be used as parts of:

- Segments
- Rules
- Queries
- Campaigns
- Goals
- Profile filters

A condition is composed of two basic elements:

- a condition type identifier
- a list of parameter values for the condition, that can be of any type, and in some cases may include sub-conditions

A condition type identifier is a string that contains a unique identifier for a condition type. Example condition types
may include `booleanCondition`, `eventTypeCondition`, `eventPropertyCondition`, and so on. Plugins may implement new
condition types that may implement any logic that may be needed. The parameter values are simply lists of objects that
may be used to configure the condition. In the case of a `booleanCondition` for example one of the parameter values will
be an `operator` that will contain values such as `and` or `or` and a second parameter value called `subConditions`
that contains a list of conditions to evaluate with that operator. The result of a condition is always a boolean
value of true or false.

Apache Unomi provides quite a lot of built-in condition types, including boolean types that make it possible to
compose conditions using operators such as `and`, `or` or `not`. Composition is an essential element of building more
complex conditions.

Here is an example of a complex condition:

[source,json]
----
{
  "condition": {
    "type": "booleanCondition",
    "parameterValues": {
      "operator":"or",
      "subConditions":[
        {
          "type": "eventTypeCondition",
          "parameterValues": {
            "eventTypeId": "sessionCreated"
          }
        },
        {
          "type": "eventTypeCondition",
          "parameterValues": {
            "eventTypeId": "sessionReassigned"
          }
        }
      ]
    }
  }
}
----

As we can see in the above example we use the boolean `or` condition to check if the event type is of type `sessionCreated`
or `sessionReassigned`.

For a more complete list of available conditions, see the <<Built-in conditions>> reference section.

=== Rules

image::unomi-rule-engine.png[Unomi Rule Engine]

Apache Unomi has a built-in rule engine that is one of the most important components of its architecture. Every time
an event is received by the server, it is evaluated against all the rules and the ones matching the incoming event will
be executed. You can think of a rule as a structure that looks like this:

    when
        conditions
    then
        actions

Basically when a rule is evaluated, all the conditions in the `when` part are evaluated and if the result matches
(meaning it evaluates to `true`) then the actions will be executed in sequence.

The real power of Apache Unomi comes from the fact that `conditions` and `actions` are fully pluggeable and that plugins
may implement new conditions and/or actions to perform any task. You can imagine conditions checking incoming event data
against third-party systems or even against authentication systesm, and actions actually pulling or pushing data to third-party
systems.

For example the Salesforce CRM connector is simply a set of actions that pull and push data into the CRM. It is then
just a matter of setting up the proper rules with the proper conditions to determine when and how the data will be
pulled or pushed into the third-party system.

==== Actions

Actions are executed by rules in a sequence, and an action is only executed once the previous action has finished
executing. If an action generates an exception, it will be logged and the execution sequence will continue unless in the
case of a Runtime exception (such as a NullPointerException).

Actions are implemented as Java classes, and as such may perform any kind of tasks that may include calling web hooks,
setting profile properties, extracting data from the incoming request (such as resolving location from an IP address),
or even pulling and/or pushing data to third-party systems such as a CRM server.

Apache Unomi also comes with built-in actions. You may find the list of built-in actions in the <<Built-in actions>> section.

=== Request flow

Here is an overview of how Unomi processes incoming requests to the `ContextServlet`.

image::unomi-request.png[Unomi request overview]
