diff --git a/.github/vale/styles/Vocab/OpenSearch/Products/accept.txt b/.github/vale/styles/Vocab/OpenSearch/Products/accept.txt index 16e7562dcc7..e856adceb75 100644 --- a/.github/vale/styles/Vocab/OpenSearch/Products/accept.txt +++ b/.github/vale/styles/Vocab/OpenSearch/Products/accept.txt @@ -88,6 +88,7 @@ Simple Schema for Observability Tableau TorchScript Tribuo +User Behavior Insights VisBuilder Winlogbeat Zstandard \ No newline at end of file diff --git a/_search-plugins/index.md b/_search-plugins/index.md index fca30667ee8..7b58d1820a5 100644 --- a/_search-plugins/index.md +++ b/_search-plugins/index.md @@ -70,6 +70,8 @@ OpenSearch provides the following search relevance features: - [Querqy]({{site.url}}{{site.baseurl}}/search-plugins/querqy/): Offers query rewriting capability. +- [User Behavior Insights]({{site.url}}{{site.baseurl}}/search-plugins/ubi/): Link user application behavior to their queries for improving search relevance insights. + ## Search results OpenSearch supports the following commonly used operations on search results: diff --git a/_search-plugins/ubi/data-structures.md b/_search-plugins/ubi/data-structures.md new file mode 100644 index 00000000000..e0a7376e2b3 --- /dev/null +++ b/_search-plugins/ubi/data-structures.md @@ -0,0 +1,179 @@ +--- +layout: default +title: UBI client data structures +parent: User behavior insights +has_children: false +nav_order: 7 +--- + +# Sample client data structures +The client data structures can be used to create events that follow the [UBI event schema specification](https://github.com/o19s/opensearch-ubi), +which is described in further detail [here]({{site.url}}{{site.baseurl}}/search-plugins/ubi/schemas/). + +The developer provides an implementation for the following functions: +- `getClientId()` +- `getQueryId()` + +_Optionally_: +- `getSessionId()` +- `getPageId()` + + + +```js +/********************************************************************************************* + * Ubi Event data structures + * The following structures help ensure adherence to the UBI event schema + *********************************************************************************************/ + + + +export class UbiEventData { + constructor(object_type, id=null, description=null, details=null) { + this.object_id_field = object_type; + this.object_id = id; + this.description = description; + this.object_detail = details; + } +} +export class UbiPosition{ + constructor({ordinal=null, x=null, y=null, trail=null}={}) { + this.ordinal = ordinal; + this.x = x; + this.y = y; + if(trail) + this.trail = trail; + else { + const trail = getTrail(); + if(trail && trail.length > 0) + this.trail = trail; + } + } +} + + +export class UbiEventAttributes { + /** + * Tries to prepopulate common event attributes + * The developer can add an `object` that the user interacted with and + * the site `position` information relevant to the event + * + * Attributes, other than `object` or `position` can be added in the form: + * attributes['item1'] = 1 + * attributes['item2'] = '2' + * + * @param {*} attributes: object with general event attributes + * @param {*} object: the data object the user interacted with + * @param {*} position: the site position information + */ + constructor({attributes={}, object=null, position=null}={}) { + if(attributes != null){ + Object.assign(this, attributes); + } + if(object != null && Object.keys(object).length > 0){ + this.object = object; + } + if(position != null && Object.keys(position).length > 0){ + this.position = position; + } + this.setDefaultValues(); + } + + setDefaultValues(){ + try{ + if(!this.hasOwnProperty('dwell_time') && typeof TimeMe !== 'undefined'){ + this.dwell_time = TimeMe.getTimeOnPageInSeconds(window.location.pathname); + } + + if(!this.hasOwnProperty('browser')){ + this.browser = window.navigator.userAgent; + } + + if(!this.hasOwnProperty('page_id')){ + this.page_id = window.location.pathname; + } + if(!this.hasOwnProperty('session_id')){ + this.session_id = getSessionId(); + } + + if(!this.hasOwnProperty('page_id')){ + this.page_id = getPageId(); + } + + if(!this.hasOwnProperty('position') || this.position == null){ + const trail = getTrail(); + if(trail.length > 0){ + this.position = new UbiPosition({trail:trail}); + } + } + // ToDo: set IP + } + catch(error){ + console.log(error); + } + } +} + + + +export class UbiEvent { + constructor(action_name, {message_type='INFO', message=null, event_attributes={}, data_object={}}={}) { + this.action_name = action_name; + this.client_id = getClientId(); + this.query_id = getQueryId(); + this.timestamp = Date.now(); + + this.message_type = message_type; + if( message ) + this.message = message; + + this.event_attributes = new UbiEventAttributes({attributes:event_attributes, object:data_object}); + } + + /** + * Use to suppress null objects in the json output + * @param key + * @param value + * @returns + */ + static replacer(key, value){ + if(value == null || + (value.constructor == Object && Object.keys(value).length === 0)) { + return undefined; + } + return value; + } + + /** + * + * @returns json string + */ + toJson() { + return JSON.stringify(this, UbiEvent.replacer); + } +} +``` +{% include copy.html %} + +# Sample usage + +```js +export async function logUbiMessage(event_type, message_type, message){ + let e = new UbiEvent(event_type, { + message_type:message_type, + message:message + }); + logEvent(e); +} + +export async function logDwellTime(action_name, page, seconds){ + console.log(`${page} => ${seconds}`); + let e = new UbiEvent(action_name, { + message:`On page ${page} for ${seconds} seconds`, + event_attributes:{dwell_seconds:seconds}, + data_object:TimeMe + }); + logEvent(e); +} +``` +{% include copy.html %} diff --git a/_search-plugins/ubi/documentation.md b/_search-plugins/ubi/documentation.md new file mode 100644 index 00000000000..751aaaae54a --- /dev/null +++ b/_search-plugins/ubi/documentation.md @@ -0,0 +1,48 @@ +--- +layout: default +title: UBI plugin management +parent: User behavior insights +has_children: false +nav_order: 2 +--- + + +# OpenSearch User Behavior Insights + +This *repository* contains the OpenSearch plugin for the User Behavior Insights (UBI) capability. This plugin +facilitates persisting client-side events (e.g. item clicks, scroll depth) and OpenSearch queries for the purpose of analyzing the data +to improve search relevance and user experience. + +[Link to repo plugin's documentation](https://github.com/o19s/opensearch-ubi) + +## Quick start + +We need a Quick Start!!! + +## UBI store + +The plugin has a concept of a "store", which is a logical collection of the events and queries. A store consists of two indexes. +One index is used to store events, and the other index is for storing queries. + +### OpenSearch data mappings +UBI has 2 primary indexes: +- **UBI Queries** stores all queries and results. +- **UBI Events** store that the UBI client writes events to. +*Follow the [schema deep dive]({{site.url}}{{site.baseurl}}/search-plugins/ubi/schemas/) to understand how these two indexes make UBI into a causal framework for search.* + +## Plugin API + + +### Associating a query with client-side events + +The plugin passively listens to query requests passing through OpenSearch. Without any extra information, +the plugin cannot associate a query with the client-side events associated with the query. (What user clicked on what to make this query?) + +To make this association, queries need to have a header value that indicates the user ID. + + +### Example queries + +[Sample SQL queries]({{site.url}}{{site.baseurl}}/search-plugins/ubi/sql-queries/) + +[Sample OpenSearch queries]({{site.url}}{{site.baseurl}}/search-plugins/ubi/dsl-queries/) diff --git a/_search-plugins/ubi/dsl-queries.md b/_search-plugins/ubi/dsl-queries.md new file mode 100644 index 00000000000..353711b230b --- /dev/null +++ b/_search-plugins/ubi/dsl-queries.md @@ -0,0 +1,97 @@ +--- +layout: default +title: UBI queries using OpenSearch +parent: User behavior insights +has_children: false +nav_order: 8 +--- + +# Sample UBI using OpenSearch DSL queries + + + +```json +GET .ubi_log_events/_search +{ + "size":0, + "aggs":{ + "event_types":{ + "terms": { + "field":"action_name", + "size":10 + } + } + } +} +``` +{% include copy.html %} + +returns +```json +{ + "took": 1, + "timed_out": false, + "_shards": { + "total": 1, + "successful": 1, + "skipped": 0, + "failed": 0 + }, + "hits": { + "total": { + "value": 10000, + "relation": "gte" + }, + "max_score": null, + "hits": [] + }, + "aggregations": { + "event_types": { + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0, + "buckets": [ + { + "key": "brand_filter", + "doc_count": 3084 + }, + { + "key": "product_hover", + "doc_count": 3068 + }, + { + "key": "button_click", + "doc_count": 3054 + }, + { + "key": "product_sort", + "doc_count": 3012 + }, + { + "key": "on_search", + "doc_count": 3010 + }, + { + "key": "type_filter", + "doc_count": 2925 + }, + { + "key": "login", + "doc_count": 2433 + }, + { + "key": "logout", + "doc_count": 1447 + }, + { + "key": "new_user_entry", + "doc_count": 207 + } + ] + } + } +} +``` +{% include copy.html %} + +These can be performed on the OpenSearch Dashboards/Query Workbench: +[http://chorus-opensearch-edition.dev.o19s.com:5601/app/opensearch-query-workbench](http://chorus-opensearch-edition.dev.o19s.com:5601/app/opensearch-query-workbench) diff --git a/_search-plugins/ubi/index.md b/_search-plugins/ubi/index.md new file mode 100644 index 00000000000..e4d662fd68f --- /dev/null +++ b/_search-plugins/ubi/index.md @@ -0,0 +1,45 @@ +--- +layout: default +title: User behavior insights +has_children: true +nav_order: 90 +redirect_from: + - /search-plugins/ubi/ +--- +# Overview + +**Introduced 2.15** +{: .label .label-purple } + +**References UBI Draft Specification X.Y.Z** +{: .label .label-purple } + +User Behavior Insights, or UBI, is a community plugin for capturing client-side events and queries for the purposes of improving search relevance and user experience. +It is a causal system, linking a user's query to all subsequent user interactions with your application until they perform another search. + + +* An machine readable [schema](https://github.com/o19s/ubi) that faciliates interoperablity of the UBI specification. +* An OpenSearch [plugin](https://github.com/o19s/opensearch-ubi) that facilitates the storage of client-side events and queries. +* A client-side JavaScript [ example ]({{site.url}}{{site.baseurl}}/search-plugins/ubi/data-structures/) reference implementation that shows how to capture events and send those events to the OpenSearch UBI plugin. + + + +| Explanation & Reference | Description +| :--------- | :------- | +| [UBI Request/Response Specification](https://github.com/o19s/ubi/)
**References UBI Draft Specification X.Y.Z** | Schema standard for making UBI requests and responses | +| [UBI OpenSearch Schema Documentation]({{site.url}}{{site.baseurl}}/search-plugins/ubi/schemas/) | Documentation on the individual Query and Event stores for OpenSearch | +| `query_id` Data Flow | To remove? | + + +| Tutorials & How-to Guides | Description +| :--------- | :------- | +| [UBI Plugin Admin]({{site.url}}{{site.baseurl}}/search-plugins/ubi/documentation/) | How to install and use the UBI Plugin | +| [ JavaScript client structures ]({{site.url}}{{site.baseurl}}/search-plugins/ubi/data-structures/) | Sample JavaScript structures for populating the Event store | +| [UBI SQL queries ]({{site.url}}{{site.baseurl}}/search-plugins/ubi/sql-queries/) | How to write analytic queries for UBI data in SQL | +| [UBI Dashboard Tutorial]({{site.url}}{{site.baseurl}}/search-plugins/ubi/ubi-dashboard-tutorial/) | Teaches you how to build an OpenSearch dashboard with UBI data | +| ... | teaches how to do something | + + +Documentation adapted using concepts from [Diátaxis](https://diataxis.fr/) +{: .tip } + diff --git a/_search-plugins/ubi/query_id.md b/_search-plugins/ubi/query_id.md new file mode 100644 index 00000000000..8a6d055dea3 --- /dev/null +++ b/_search-plugins/ubi/query_id.md @@ -0,0 +1,99 @@ +{% comment %} +# *********** +TODO: decide whether this page is necessary since schemas.md contains most of the same information +in easier terms +--- +layout: default +title: UBI data flow +parent: User behavior insights +has_children: false +nav_order: 7 +--- +# *********** +{% uncomment %} + +# Basic UBI flow +**Executive Summary**: Once a user performs search, that search is tied to a `query_id`. Then any following user events until the next search are logged and indexed by the search's `query_id`. If the user finds something of interest, that something's identifier (`object_id` or `key_value`) is logged in the event store with the `query_id`. + +### UBI roles +- **UBI Plugin**: in charge of initiating an UBI store, saving all incoming queries associated with an UBI store, and logging the UBI events directly. +- **Search Client**: in charge of searching and recieving a `query_id` from the **UBI Plugin**. This `query_id` is then passed to the **UBI Client** +- **UBI Client**: in charge of logging user events, such as `onClick`, passing the data to the **UBI Plugin**, with the appropriate `query_id`, if the event follows a search event that produced a `query_id`. +If the user interacts with an *object* (book, product, document) that was returned from their search, that `object_id` is logged, tying the `query_id` to the `object_id` + + + +{% comment %} +This mermaid source is converted into an png under +.../images/ubi/query_id.png + + + +```mermaid +%%{init: {'theme':'base', + 'themeVariables': { + 'background': '#ffffff', + 'secondaryColor': '#3cb371', + 'actorTextColor': '#2a00ff', + 'actorBorder': '#ff6347' + } + +}}%% +sequenceDiagram + autonumber + actor U as User-123 + activate Search Client + U->>Search Client: "Show me cute puppies" + box rgb(255, 247, 146) Cute Things App + participant Search Client + participant UBI Client + end + box rgb(121, 247, 255) OpenSearch Cluster + participant OS as OpenSearch + participant UBI Plugin + end + activate OS + Search Client->>OS: "puppies" {X-ubi-store: cute-things-index, X-ubi-user-id: user-123} + note right of OS: Hits:
[ pug: {id:321, name:JohnBoy},
beagle: {id:456, name:Reagle},
poodle: {id:785, name:Noodle},
...] + deactivate Search Client + activate UBI Plugin + note right of UBI Plugin: Saving User-123's QUERIES and HITS
with query_id: 15c182f2-... + OS->> UBI Plugin: save user query and hits to the QUERY store + UBI Plugin-->>OS: User-123's query_id is 15c182f2-05db-4f4f-814f-46dc0de6b9ea + activate Search Client + OS-->>Search Client: [ pug: {id:321, name:JohnBoy},
beagle: {id:456, name:Reagle},
poodle: {id:785, name:Noodle},...
query_id: 15c182f2-05db-4f4f-814f-46dc0de6b9ea + note over U: "Hmmm, pug, beagle or poodle?" + deactivate UBI Plugin + deactivate OS + Search Client->> UBI Client: The new query_id is 15c182f2-05db-4f4f-814f-46dc0de6b9ea + activate UBI Client + U->>UBI Client: item_onClick, user_id:User-123,
query_id: 15c182f2-05db-4f4f-814f-46dc0de6b9ea + deactivate Search Client + activate UBI Plugin + note over U: "This pug, JohnBoy,
is really cute!" + note right of UBI Plugin: Saving User-123's EVENTS linked to
JohnBoy and query_id: 15c182f2-... + UBI Client->> UBI Plugin: save item_onClick, query_id & user_id to the EVENT store + U->>UBI Client: item_Adopt, user_id:User-123,
query_id: 15c182f2-05db-4f4f-814f-46dc0de6b9ea + note over U: Imma adopt him! + UBI Client->> UBI Plugin: save item_Adopt, query_id & user_id to the EVENT store + deactivate UBI Client + deactivate UBI Plugin +``` +{% endcomment %} + + + + +# The *Cute Things Animal Rescue* + +1) `User-123` searches for a cute puppy to adopt. +2) The *Cute Things App* relays the search to an OpenSearch Cluster where the `UBI Plugin` is installed and listening for queries on the *cute-things-index* +A number of puppies up for adoption are found. +3) The *hits* and the query is saved in the **query** store, linked to the user, session and `query_id` +4) If no `query_id` is sent in from the client in a request header, a new `query_id` is generated and used to index the data, and then returned in the response. +5) OpenSearch's response headers will have the `query_id` indexed. +6) The application saves this id for the `UBI Client` to log all user events to be associated with that search. +7) `User-123` clicks on the pug to learn more about JohnBoy. +8) The `UBI Plugin` saves this click event, `query_id`, `user_id` and the specific puppy's identifying information. +9) `User-123` initiates the adoption process for the pug, JohnBoy. +10) The `UBI Plugin` saves this adoption event, `query_id`, `User-123` and the puppy's identifying information. diff --git a/_search-plugins/ubi/schemas.md b/_search-plugins/ubi/schemas.md new file mode 100644 index 00000000000..de0c7a5992c --- /dev/null +++ b/_search-plugins/ubi/schemas.md @@ -0,0 +1,223 @@ +--- +layout: default +title: UBI index schemas +parent: User behavior insights +has_children: false +nav_order: 7 +--- + +# Key User Behavior Insights concepts +**User Behavior Insights** (UBI) **Logging** is really a matter of linking and indexing queries, results to user interactions (events) with your application. +## Key ID's +UBI is not functional unless the links between the following fields are consistently maintained within your UBI-enabled application: + +- [`client_id`](#client_id) represents a unique user with their client application. +- [`object_id`](#object_id) represents an id for whatever item the user is searching for, such as `epc`, `isbn`, `ssn`, `handle`. + +- [`object_id_field`](#object_id) tells us the type of `object_id`, i.e. the actual labels: "epc", "isbn", "ssn", or "handle" for each `object_id`. + +- [`query_id`](#query_id) is a unique id for the raw query language executed and the resultant `object_id`'s (_hits_) that the query returned. +- [`action_name`](#action_name), though not technically an *id*, the `action_name` tells us what exact user action (such as `click` or `add_to_cart`, `watch`, `view`, `purchase`) that was taken (or not) with a given `object_id`. + +To summarize: the `query_id` signals the beginning of a `client_id`'s *Search Journey* every time a user queries the search index, the `action_name` tells us how the user is interacting with the query results within the application, and [`event_attributes.object.object_id`](#object_id) is referring to the precise query result that the user interacts with. + +## Important UBI roles +- **Search Client**: in charge of searching, and then recieving *objects* from some document index in OpenSearch. + (1, 2, *5* and 7, in following sections) +- **User Behavior Insights** plugin: if activated in the `ext.ubi` stanza of the search request, manages the **UBI Queries** store in the background, indexing each underlying, technical, DSL, index query with a unique [`query_id`](#query_id) along with all returned resultant [`object_id`](#object_id)'s, and then passing the `query_id` back to the **Search Client** so that events can be linked to this query. + (3, 4 and *5*, in following sections) +- **objects**: are whatever items the user is searching for with the queries. Activating UBI involves mapping your real-world objects (using it's `isbn`, `ssn`) to the [`object_id`](#object_id) fields in the schemas. +- The **Search Client**, if separate from the **UBI Client**, forwards the indexed [`query_id`](#query_id) to the **UBI Client**. +   *Note:* We break out the roles of *search* and *UBI event indexing* here, but many implementations will likely use the same OpenSearch client instance for both roles of searching and index writing. +  (6, following section) +- The **UBI Client** then indexes all user events with this [`query_id`](#query_id) until a new search is performed, and a new `query_id` is generated by **User Behavior Insights** and passed back to the **UBI Client** +- If the **UBI Client** interacts with a result *object*, such as `onClick`, that [`object_id`](#object_id), `onClick` [`action_name`](#action_name) and `query_id` are all indexed together, signalling the causal link between the *search* and the *object*. + (8 and 9, following section) + + + + + +{% comment %} +The mermaid source is converted into an png under +.../images/ubi/ubi-schema-interactions.png + + +```mermaid +graph LR +style L fill:none,stroke-dasharray: 5 5 +subgraph L["`*Legend*`"] + style ss height:150px + subgraph ss["Standard Search"] + direction LR + + style ln1a fill:blue + ln1a[ ]--->ln1b[ ]; + end + subgraph ubi-leg["UBI data flow"] + direction LR + + ln2a[ ].->|"`**UBI interaction**`"|ln2b[ ]; + style ln1c fill:red + ln1c[ ]-->|query_id flow|ln1d[ ]; + end +end +linkStyle 0 stroke-width:2px,stroke:#0A1CCF +linkStyle 2 stroke-width:2px,stroke:red +``` +```mermaid +%%{init: { + "flowchart": {"htmlLabels": false}, + + } +}%% +graph TB + +User--1) raw search string-->Search; +Search--2) search string-->Docs +style OS stroke-width:2px, stroke:#0A1CCF, fill:#62affb, opacity:.5 +subgraph OS[OpenSearch Cluster fa:fa-database] + style E stroke-width:1px,stroke:red + E[( UBI Events )] + style Docs stroke-width:1px,stroke:#0A1CCF + style Q stroke-width:1px,stroke:red + Docs[(Document Index)] -."3) {DSL...} & [object_id's,...]".-> Q[( UBI Queries )]; + Q -.4) query_id.-> Docs ; +end + +Docs -- "5) return both query_id & [objects,...]" --->Search ; +Search-.6) query_id.->U; +Search --7) [results, ...]--> User + +style *client-side* stroke-width:1px, stroke:#D35400 +subgraph "`*client-side*`" + style User stroke-width:4px, stroke:#EC636 + User["`**User**`" fa:fa-user] + App + Search + U + style App fill:#D35400,opacity:.35, stroke:#0A1CCF, stroke-width:2px + subgraph App[       UserApp fa:fa-store] + style Search stroke-width:2px, stroke:#0A1CCF + Search( Search Client ) + style U stroke-width:1px,stroke:red + U( UBI Client ) + end +end + +User -.8) selects object_id:123.->U; +U-."9) index event:{query_id, onClick, object_id:123}".->E; + +linkStyle 1,2,0,6 stroke-width:2px,fill:none,stroke:#0A1CCF +linkStyle 3,4,5,8 stroke-width:2px,fill:none,stroke:red +``` +{% endcomment %} + +## UBI stores +There are 2 separate stores for UBI: +### 1) **UBI queries** +All underlying query information and results ([`object_id`](#object_id)'s) are stored in the **UBI Queries** store, and remains largely invisible in the background. +The only obvious difference will be in the `ubi` stanza of the JSON response, *which could cause index bloat if one forgets that this is enabled*. + +**UBI Queries** [schema](https://github.com/o19s/opensearch-ubi/tree/2.14.0/src/main/resources/queries-mapping.json): +Since UBI manages the **UBI Queries** store, the developer should never have to write directly to this store (except for importing data). + +- `timestamp` (events and queries) +   A UNIX timestamp of when the query was received + +- `query_id` (events and queries) +   A unique ID of the query provided by the client or generated automatically. The same query text issued multiple times would generate different `query_id`. + +- `client_id` (events and queries) +   A user/client ID provided by the client application + +- `query_response_objects_ids` (queries) +   This is an array of the `object_id`'s. This *could* be the same id as the `_id` but is meant to be the externally valid id of document/item/product. + + + +### 2) **UBI events** +This is the event store that the client side directly indexes events to, linking the event [`action_name`](#action_name), [`object_id`](#object_id)'s and [`query_id`](#query_id)'s together with any other important event information. +Since this schema is dynamic, the developer can add any new fields and structures (such as *user* information, *geo-location* information) at index time that are not in the current **UBI Events** [schema](https://github.com/o19s/opensearch-ubi/tree/2.14.0/src/main/resources/events-mapping.json): + +

+ +- `application` +   (size 100) - name of the application tracking UBI events (e.g. `amazon-shop`, `ABC-microservice`) + +

+ +- `action_name` +   (size 100) - any name you want to call your event such as `click`, `watch`, `purchase`, and `add_to_cart`, but one could map these to any common *JavaScript* events, or debugging events. +   _TODO: How to formalize? A list of standard ones and then custom ones._ + +

+ +- `query_id` +   (size 100) - ID for some query. +  Either the client provides this, or the `query_id` is generated at index time by the **UBI Plugin**. + +

+ +- `client_id` +   A user/client ID provided by the client application +  The `client_id` must be consistent in both the **UBI Queries** and **UBI Events** indexes. + +- `timestamp`: +   UTC-based, UNIX epoch time. + +- `message_type` +   (size 100) - originally thought of in terms of ERROR, INFO, WARN, but could be anything else useful such as `QUERY` or `CONVERSION`. +   Can be used to group `action_name` together in logical bins. + +- `message` +   (size 256) - optional text message for the log entry. For example, with a `message_type` of `INFO`, people might expect an informational or debug type text for this field, but a `message_type` of `QUERY`, we would expect the text to be more about what the user is searching on. + + +- `event_attributes`'s +  structure that describes any important context about the event. Within it, it has 2 primary structures `position` and `object`, as well as being extensible to add anymore relevant, custom, information about the event can be stored such as timing informaiton, individual user or session information, etc. +   Since this has a dynamic mapping, the index _could_ become bloated with many new fields + {: .warning} + + - **`event_attributes.position`** +   structure that contains information on the location of the event origin, such as screen *x,y* coordinates, or the *n-th* object out of 10 results, .... + + - `event_attributes.position.ordinal` +   tracks the *n*th item within a list that a user could select, click (i.e. selecting the 3rd element could be event{`onClick, results[4]`}) + + - `event_attributes.position.{x,y}` +   tracks x and y values, that the client defines + + - `event_attributes.position.page_depth` +   tracks page depth of results + + - `event_attributes.position.scroll_depth` +   tracks scroll depth of page results + + - `event_attributes.position.trail` +   text field for tracking the path/trail that a user took to get to this location + + - **`event_attributes.object`**, which contains identifying information of the object returned from the query that the user interacts with (i.e.: a book, a product, a post). + The `object` structure has two ways to refer to the object, with `object_id` being the id that links prior queries to this object: + + - `event_attributes.object.internal_id` is a unique id that OpenSearch can use to internally to index the object, think the `_id` field in the indexes. + +

+ + - `event_attributes.object.object_id` +   is the id that a user could look up and find the object instance within the **document corpus**. Examples include: `ssn`, `isbn`, `ean`. Variants need to be incorporated in the `object_id`, so for a t-shirt that is red, you would need SKU level as the `object_id`. + Initializing UBI requires mapping from the **Document Index**'s primary key to this `object_id` +

+ + - `event_attributes.object.object_id_field` +   indicates the type/class of object _and_ the ID field of the search index. + + - `event_attributes.object.description` +   optional description of the object + + - `event_attributes.object.object_detail` +   optional text for further data object details + + - *extensible fields*: +  any new fields by any other names in the `object` that one indexes will dynamically expand this schema to that use-case. + {: .warning} diff --git a/_search-plugins/ubi/sql-queries.md b/_search-plugins/ubi/sql-queries.md new file mode 100644 index 00000000000..991799ed8c7 --- /dev/null +++ b/_search-plugins/ubi/sql-queries.md @@ -0,0 +1,342 @@ +--- +layout: default +title: UBI queries with sql +parent: User behavior insights +has_children: false +nav_order: 7 +--- + +# Sample UBI SQL queries +These can be performed on the OpenSearch Dashboards/Query Workbench: +[http://chorus-opensearch-edition.dev.o19s.com:5601/app/opensearch-query-workbench](http://chorus-opensearch-edition.dev.o19s.com:5601/app/opensearch-query-workbench) + +## Queries with zero results +Although it's trivial on the server side to find queries with no results, we can also get the same answer by querying the event side. +### Server-side +```sql +select + count(0) +from ubi_queries +where query_response_objects_ids is null +order by user_id +``` + +### Client event-side +```sql +select + count(0) +from .ubi_log_events +where action_name='on_search' and event_attributes.data.data_detail.query_data.query_response_objects_ids is null +order by timestamp +``` + +Both client and server-side queries should return the same number. + + + +## Trending queries +```sql +select + message, count(0) Total +from ubi_events +where + action_name='on_search' +group by message +order by Total desc +``` + + +Message|Total +|---|---| +Virtual flexibility systematic|143 +Virtual systematic flexibility|89 +discrete desk service Cross group|75 +Blanditiis quo sint repudiandae a sit.|75 +Optio id quis alias at.|75 +Consectetur enim sunt laborum adipisci occaecati reiciendis.|70 +Like prepare trouble consider.|68 +User Behavior Insights|65 +cheapest laptop with i9|64 +Cross group discrete service desk|63 +Laptop|61 +Amet maxime numquam libero ipsam amet.|59 +fastest laptop|54 +Voluptas iusto illum eum autem cum illum.|51 +fortalece relaciones e-business|2 +evoluciona comercio electrónico en tiempo real|2 +incentiva canales escalables|1 +incentiva ancho de banda inalámbrica|1 +implementa sistemas de siguiente generación|1 +implementa marcados eficientes|1 + + + +## Event type distribution counts +To make a pie chart like widget on all the most common events: +```sql +select + action_name, count(0) Total +from ubi_events +group by action_name +order by Total desc +``` +action_name|Total +|---|---| +on_search|3199 +brand_filter|3112 +button_click|3150 +type_filter|3149 +product_hover|3132 +product_sort|3115 +login|2458 +logout|1499 +new_user_entry|208 + +### Events associated with queries +Since the `query_id` is set during client-side search, all events that are associated with a query will have that same `query_id`. +To make a pie chart like widget on the most common events preceded by a query: +```sql +select + action_name, count(0) Total +from .ubi_log_events +where query_id is not null +group by action_name +order by Total desc +``` +action_name|Total +|---|---| +on_search|1329 +brand_filter|669 +button_click|648 +product_hover|639 +product_sort|625 +type_filter|613 +logout|408 + + +## Sample search journey + +Find a search in the query log: +```sql +select * +from ubi_queries +where query_id ='1065c70f-d46a-442f-8ce4-0b5e7a71a892' +order by timestamp +``` + +(In this generated data, the `query` field is plain text; however in the real implementation the query will be in the internal DSL of the query and parameters.) + + +query_response_id|query_id|user_id|query|query_response_objects_ids|session_id|timestamp +---|---|---|---|---|---|--- +1065c70f-d46a-442f-8ce4-0b5e7a71a892|1065c70f-d46a-442f-8ce4-0b5e7a71a892|155_7e3471ff-14c8-45cb-bc49-83a056c37192|Blanditiis quo sint repudiandae a sit.|8659955|fa6e3b1c-3212-44d2-b16b-690b4aeddbba_1975|2027-04-17 10:16:45 + + +In the event log +Search for the events that correspond to the query above, `1065c70f-d46a-442f-8ce4-0b5e7a71a892`. + +```sql +select + query_id, action_name, message_type, message, event_attributes.data.data_id, event_attributes.data.description, session_id, user_id +from .ubi_log_events +where query_id = '1065c70f-d46a-442f-8ce4-0b5e7a71a892' +order by timestamp +``` + + + +query_id|action_name|message_type|message|event_attributes.data.data_id|event_attributes.data.description|session_id|user_id +---|---|---|---|---|---|---|--- +1065c70f-d46a-442f-8ce4-0b5e7a71a892|product_hover|INQUERY|Focused logistical policy|1692104|HP LaserJet Color CP3525dn Printer Colour 600 x 1200 DPI A4|fa6e3b1c-3212-44d2-b16b-690b4aeddbba_1975|155_7e3471ff-14c8-45cb-bc49-83a056c37192 +1065c70f-d46a-442f-8ce4-0b5e7a71a892|brand_filter|INFO||||fa6e3b1c-3212-44d2-b16b-690b4aeddbba_1975|155_7e3471ff-14c8-45cb-bc49-83a056c37192 +1065c70f-d46a-442f-8ce4-0b5e7a71a892|type_filter|INFO|Multi-tiered client-server software|||fa6e3b1c-3212-44d2-b16b-690b4aeddbba_1975|155_7e3471ff-14c8-45cb-bc49-83a056c37192 +1065c70f-d46a-442f-8ce4-0b5e7a71a892|product_sort|PURCHASE||77499830|SES Creative Charm bracelets|fa6e3b1c-3212-44d2-b16b-690b4aeddbba_1975|155_7e3471ff-14c8-45cb-bc49-83a056c37192 +1065c70f-d46a-442f-8ce4-0b5e7a71a892|logout|ERROR||||fa6e3b1c-3212-44d2-b16b-690b4aeddbba_1975|155_7e3471ff-14c8-45cb-bc49-83a056c37192 +1065c70f-d46a-442f-8ce4-0b5e7a71a892|on_search|QUERY|Blanditiis quo sint repudiandae a sit.|1065c70f-d46a-442f-8ce4-0b5e7a71a892||fa6e3b1c-3212-44d2-b16b-690b4aeddbba_1975|155_7e3471ff-14c8-45cb-bc49-83a056c37192 +1065c70f-d46a-442f-8ce4-0b5e7a71a892|product_purchase|REJECT||1377181|Matrox G55-MDDE32LPDF graphics card GDDR|fa6e3b1c-3212-44d2-b16b-690b4aeddbba_1975|155_7e3471ff-14c8-45cb-bc49-83a056c37192 +1065c70f-d46a-442f-8ce4-0b5e7a71a892|brand_filter|WARN||||fa6e3b1c-3212-44d2-b16b-690b4aeddbba_1975|155_7e3471ff-14c8-45cb-bc49-83a056c37192 +1065c70f-d46a-442f-8ce4-0b5e7a71a892|product_sort|PURCHASE|Object-based upward-trending policy|137688|HERMA CD labels A4 Ø 116 mm white paper matt opaque 200 pcs.|fa6e3b1c-3212-44d2-b16b-690b4aeddbba_1975|155_7e3471ff-14c8-45cb-bc49-83a056c37192 +1065c70f-d46a-442f-8ce4-0b5e7a71a892|product_click|INQUERY||4534016|ASUS BX700 mouse Bluetooth Laser 1200 DPI Right-hand|fa6e3b1c-3212-44d2-b16b-690b4aeddbba_1975|155_7e3471ff-14c8-45cb-bc49-83a056c37192 +1065c70f-d46a-442f-8ce4-0b5e7a71a892|product_hover|INQUERY||78314263|Tripp Lite DMCCASTER flat panel mount accessory|fa6e3b1c-3212-44d2-b16b-690b4aeddbba_1975|155_7e3471ff-14c8-45cb-bc49-83a056c37192 +1065c70f-d46a-442f-8ce4-0b5e7a71a892|product_click|INQUERY||2073|HP LaserJet 5100tn 1200 x 1200 DPI A3|fa6e3b1c-3212-44d2-b16b-690b4aeddbba_1975|155_7e3471ff-14c8-45cb-bc49-83a056c37192 +1065c70f-d46a-442f-8ce4-0b5e7a71a892|button_click|WARN||||fa6e3b1c-3212-44d2-b16b-690b4aeddbba_1975|155_7e3471ff-14c8-45cb-bc49-83a056c37192 + + + +## User sessions +To look at more sessions from the same user above, `155_7e3471ff-14c8-45cb-bc49-83a056c37192`. +```sql +select + user_id, session_id, query_id, action_name, message_type, message, event_attributes.data.data_type, timestamp +from .ubi_log_events +where user_id ='155_7e3471ff-14c8-45cb-bc49-83a056c37192' +order by timestamp +``` + +Results are truncated to a few sessions: + + +user_id|session_id|query_id|action_name|message_type|message|event_attributes.data.data_type|timestamp +---|---|---|---|---|---|---|--- +user_id|session_id|query_id|action_name|message_type|message|event_attributes.data.data_type|timestamp +155_7e3471ff-14c8-45cb-bc49-83a056c37192|2465d7cf-7123-499c-a510-f5681db2bad8_1967||new_user_entry|INFO|||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|2465d7cf-7123-499c-a510-f5681db2bad8_1967||login|ERROR|iniciativa potenciada centrado en el usuario||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|2465d7cf-7123-499c-a510-f5681db2bad8_1967|11a40012-8e70-4cb8-afcb-b7d1214aa0b0|on_search|QUERY|Blanditiis quo sint repudiandae a sit.|query|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|f8500640-1a69-41f0-b2ea-c7b2d7af5ab1_1970||login|ERROR|Switchable actuating methodology||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|f8500640-1a69-41f0-b2ea-c7b2d7af5ab1_1970||product_purchase|INQUERY|Enterprise-wide high-level circuit|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|f8500640-1a69-41f0-b2ea-c7b2d7af5ab1_1970||product_purchase|REJECT||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|f8500640-1a69-41f0-b2ea-c7b2d7af5ab1_1970||product_sort|PURCHASE|Enhanced content-based protocol|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|f8500640-1a69-41f0-b2ea-c7b2d7af5ab1_1970||button_click|INFO|||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|f8500640-1a69-41f0-b2ea-c7b2d7af5ab1_1970||brand_filter|INFO|Automated solution-oriented firmware||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|f8500640-1a69-41f0-b2ea-c7b2d7af5ab1_1970||product_sort|PURCHASE||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|f8500640-1a69-41f0-b2ea-c7b2d7af5ab1_1970||button_click|INFO|sinergia dedicada mandatorio||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|f8500640-1a69-41f0-b2ea-c7b2d7af5ab1_1970||product_sort|PURCHASE||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|f8500640-1a69-41f0-b2ea-c7b2d7af5ab1_1970||product_click|INQUERY||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|f8500640-1a69-41f0-b2ea-c7b2d7af5ab1_1970||type_filter|INFO|actitud maximizada virtual||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|f8500640-1a69-41f0-b2ea-c7b2d7af5ab1_1970||product_sort|INQUERY||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|f8500640-1a69-41f0-b2ea-c7b2d7af5ab1_1970||brand_filter|ERROR|Re-contextualized zero administration complexity||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|8819a35a-7cd8-4365-8c29-6b07fe46f073_1968|71b2903a-9108-4829-96f1-a675e7a635d8|brand_filter|INFO|||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|8819a35a-7cd8-4365-8c29-6b07fe46f073_1968||login|INFO|Optional context-sensitive system engine||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|8819a35a-7cd8-4365-8c29-6b07fe46f073_1968||button_click|INFO|Sharable background knowledge user||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|8819a35a-7cd8-4365-8c29-6b07fe46f073_1968||product_hover|PURCHASE|política basado en necesidades multicanal|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|8819a35a-7cd8-4365-8c29-6b07fe46f073_1968||product_click|INQUERY||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|8819a35a-7cd8-4365-8c29-6b07fe46f073_1968||button_click|WARN|Customer-focused exuding policy||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|8819a35a-7cd8-4365-8c29-6b07fe46f073_1968||button_click|WARN|||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|8819a35a-7cd8-4365-8c29-6b07fe46f073_1968||product_sort|INQUERY|paradigma basado en el contexto opcional|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|8819a35a-7cd8-4365-8c29-6b07fe46f073_1968|71b2903a-9108-4829-96f1-a675e7a635d8|on_search|QUERY|what is ubi?|query|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|8819a35a-7cd8-4365-8c29-6b07fe46f073_1968|71b2903a-9108-4829-96f1-a675e7a635d8|product_purchase|INQUERY|Ergonomic 24/7 solution|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|8819a35a-7cd8-4365-8c29-6b07fe46f073_1968|71b2903a-9108-4829-96f1-a675e7a635d8|product_purchase|REJECT|Enhanced uniform methodology|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|8819a35a-7cd8-4365-8c29-6b07fe46f073_1968|71b2903a-9108-4829-96f1-a675e7a635d8|type_filter|WARN|Seamless didactic info-mediaries||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|8819a35a-7cd8-4365-8c29-6b07fe46f073_1968|71b2903a-9108-4829-96f1-a675e7a635d8|product_sort|REJECT|algoritmo direccional visionario|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|dc7984c6-11b1-40ad-b6a5-b96717139da2_1969||login|INFO|||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|dc7984c6-11b1-40ad-b6a5-b96717139da2_1969||button_click|WARN|Enterprise-wide 24hour focus group||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|dc7984c6-11b1-40ad-b6a5-b96717139da2_1969||type_filter|WARN|Balanced cohesive adapter||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|dc7984c6-11b1-40ad-b6a5-b96717139da2_1969||product_click|INQUERY|Ergonomic hybrid instruction set|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|dc7984c6-11b1-40ad-b6a5-b96717139da2_1969||product_purchase|PURCHASE||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|dc7984c6-11b1-40ad-b6a5-b96717139da2_1969||product_sort|INQUERY|Automated zero administration encoding|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|dc7984c6-11b1-40ad-b6a5-b96717139da2_1969||product_sort|INQUERY|conjunto de instrucciones multitarea de tamaño adecuado|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|dc7984c6-11b1-40ad-b6a5-b96717139da2_1969||type_filter|WARN|enfoque heurística opcional||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|dc7984c6-11b1-40ad-b6a5-b96717139da2_1969||button_click|INFO|Multi-channeled optimizing neural-net||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|dc7984c6-11b1-40ad-b6a5-b96717139da2_1969||product_hover|INQUERY||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|dc7984c6-11b1-40ad-b6a5-b96717139da2_1969||product_click|INQUERY|Programmable intangible product|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|dc7984c6-11b1-40ad-b6a5-b96717139da2_1969||product_hover|INQUERY||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|dc7984c6-11b1-40ad-b6a5-b96717139da2_1969||product_purchase|PURCHASE|Grass-roots client-server conglomeration|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|dc7984c6-11b1-40ad-b6a5-b96717139da2_1969||brand_filter|ERROR|Implemented real-time standardization||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|dc7984c6-11b1-40ad-b6a5-b96717139da2_1969||product_sort|PURCHASE|función modular progresivo|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971||login|INFO|||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971||type_filter|WARN|conglomeración maximizada seguro||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971||button_click|INFO|Focused regional portal||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971||button_click|INFO|definición sistémica virtual||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971||type_filter|INFO|||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971||type_filter|INFO|||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971||product_purchase|INQUERY||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971||product_hover|INQUERY|Seamless directional database|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971||product_hover|REJECT|aplicación dinámica robusto|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971||product_click|INQUERY|aplicación 4ta generación personalizable|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971||product_click|INQUERY|alianza holística administrado|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971|0e360673-46fe-4912-a10c-0ab90bbb0513|on_search|QUERY|what is ubi?|query|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971|0e360673-46fe-4912-a10c-0ab90bbb0513|product_purchase|REJECT|Diverse intermediate hardware|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971|0e360673-46fe-4912-a10c-0ab90bbb0513|product_click|INQUERY|Advanced contextually-based Graphical User Interface|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971|0e360673-46fe-4912-a10c-0ab90bbb0513|product_purchase|PURCHASE|Ergonomic mission-critical ability|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971|0e360673-46fe-4912-a10c-0ab90bbb0513|product_purchase|INQUERY|Visionary discrete groupware|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|86f9c1e4-428f-4520-acef-883770c0f541_1971|0e360673-46fe-4912-a10c-0ab90bbb0513|logout|WARN|Compatible composite process improvement||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|c83a0f59-1ae5-4f63-a45f-b0dcefc7a7d5_1972||login|INFO|Upgradable interactive analyzer||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|c83a0f59-1ae5-4f63-a45f-b0dcefc7a7d5_1972||product_hover|REJECT|Horizontal modular database|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|c83a0f59-1ae5-4f63-a45f-b0dcefc7a7d5_1972||product_click|INQUERY|Re-engineered interactive knowledge user|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|c83a0f59-1ae5-4f63-a45f-b0dcefc7a7d5_1972||product_hover|INQUERY|caja de herramientas holística orgánico|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|c83a0f59-1ae5-4f63-a45f-b0dcefc7a7d5_1972||brand_filter|INFO|Public-key neutral infrastructure||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|c83a0f59-1ae5-4f63-a45f-b0dcefc7a7d5_1972||brand_filter|INFO|software 24 horas programable||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|c83a0f59-1ae5-4f63-a45f-b0dcefc7a7d5_1972||product_click|INQUERY||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|c83a0f59-1ae5-4f63-a45f-b0dcefc7a7d5_1972||product_purchase|REJECT||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|c83a0f59-1ae5-4f63-a45f-b0dcefc7a7d5_1972||button_click|WARN|||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|c83a0f59-1ae5-4f63-a45f-b0dcefc7a7d5_1972||product_hover|REJECT||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|c83a0f59-1ae5-4f63-a45f-b0dcefc7a7d5_1972||type_filter|INFO|||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|c83a0f59-1ae5-4f63-a45f-b0dcefc7a7d5_1972||logout|INFO|Sharable discrete policy||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|5fb20967-14fe-49e1-93e4-5ab54b0d54a7_1973||login|ERROR|||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|5fb20967-14fe-49e1-93e4-5ab54b0d54a7_1973|898fdbfb-ee8f-4a21-a0e6-8acbc46e45f6|on_search|QUERY|Amet maxime numquam libero ipsam amet.|query|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|5fb20967-14fe-49e1-93e4-5ab54b0d54a7_1973|898fdbfb-ee8f-4a21-a0e6-8acbc46e45f6|product_sort|INQUERY||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|5fb20967-14fe-49e1-93e4-5ab54b0d54a7_1973|e3b7319c-2517-4375-bc66-7ff50bfd37f5|on_search|QUERY|Laptop|query|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|5fb20967-14fe-49e1-93e4-5ab54b0d54a7_1973|e3b7319c-2517-4375-bc66-7ff50bfd37f5|brand_filter|INFO|||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|5fb20967-14fe-49e1-93e4-5ab54b0d54a7_1973|e3b7319c-2517-4375-bc66-7ff50bfd37f5|product_sort|REJECT||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|5fb20967-14fe-49e1-93e4-5ab54b0d54a7_1973|e3b7319c-2517-4375-bc66-7ff50bfd37f5|brand_filter|INFO|capacidad 3ra generación multi-capas||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|5fb20967-14fe-49e1-93e4-5ab54b0d54a7_1973|e3b7319c-2517-4375-bc66-7ff50bfd37f5|button_click|INFO|base de trabajo nueva generación distribuido||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|5fb20967-14fe-49e1-93e4-5ab54b0d54a7_1973|e382687a-a853-460a-99e3-9fec9806875e|on_search|QUERY|Cross group discrete service desk|query|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|4aca7a9c-895f-481c-86a0-1419cec4fbcc_1974||login|INFO|Horizontal full-range framework||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|4aca7a9c-895f-481c-86a0-1419cec4fbcc_1974||button_click|INFO|Vision-oriented motivating matrix||2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|4aca7a9c-895f-481c-86a0-1419cec4fbcc_1974||product_hover|PURCHASE|Cross-platform cohesive product|product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|4aca7a9c-895f-481c-86a0-1419cec4fbcc_1974||product_sort|INQUERY||product|2027-04-17 10:16:45 +155_7e3471ff-14c8-45cb-bc49-83a056c37192|4aca7a9c-895f-481c-86a0-1419cec4fbcc_1974||brand_filter|INFO|Multi-layered next generation process improvement||2027-04-17 10:16:45 + + + +## List user sessions that logged out without any queries +- This query denotes users without a query_id. Note that this could happen if the client side is not passing the returned query to other events. + +```sql +select + user_id, session_id, count(0) EventTotal +from .ubi_log_events +where action_name='logout' and query_id is null +group by user_id, session_id +order by EventTotal desc +``` + + + +user_id|session_id|EventTotal +---|---|--- +100_15c182f2-05db-4f4f-814f-46dc0de6b9ea|1c36712c-44b8-4fdd-8f0d-fdfeab5bd794_1290|1 +175_e5f262f1-0db3-4948-b349-c5b95ff31259|816f94d6-8966-4a8b-8984-a2641d5865b2_2251|1 +175_e5f262f1-0db3-4948-b349-c5b95ff31259|314dc1ff-ef38-4da4-b4b1-061f62dddcbb_2248|1 +175_e5f262f1-0db3-4948-b349-c5b95ff31259|1ce5dc30-31bb-4759-9451-5a99b28ba91b_2255|1 +175_e5f262f1-0db3-4948-b349-c5b95ff31259|10ac0fc0-409e-4ba0-98e9-edb323556b1a_2249|1 +174_ab59e589-1ae4-40be-8b29-8efd9fc15380|dfa8b38a-c451-4190-a391-2e1ec3c8f196_2228|1 +174_ab59e589-1ae4-40be-8b29-8efd9fc15380|68666e11-087a-4978-9ca7-cbac6862273e_2233|1 +174_ab59e589-1ae4-40be-8b29-8efd9fc15380|5ca7a0df-f750-4656-b9a5-5eef1466ba09_2234|1 +174_ab59e589-1ae4-40be-8b29-8efd9fc15380|228c1135-b921-45f4-b087-b3422e7ed437_2236|1 +173_39d4cbfd-0666-4e77-84a9-965ed785db49|f9795e2e-ad92-4f15-8cdd-706aa1a3a17b_2206|1 +173_39d4cbfd-0666-4e77-84a9-965ed785db49|f3c18b61-2c8a-41b3-a023-11eb2dd6c93c_2207|1 +173_39d4cbfd-0666-4e77-84a9-965ed785db49|e12f700c-ffa3-4681-90d9-146022e26a18_2210|1 +173_39d4cbfd-0666-4e77-84a9-965ed785db49|da1ff1f6-26f1-49d4-bd0d-d32d199e270e_2208|1 +173_39d4cbfd-0666-4e77-84a9-965ed785db49|a1674e9d-d2dd-4da9-a4d1-dd12a401e8e7_2216|1 +172_875f04d6-2c35-45f4-a8ac-bc5b675425f6|cc8e6174-5c1a-48c5-8ee8-1226621fe9f7_2203|1 +171_7d810730-d6e9-4079-ab1c-db7f98776985|927fcfed-61d2-4334-91e9-77442b077764_2189|1 +16_581fe410-338e-457b-a790-85af2a642356|83a68f57-0fbb-4414-852b-4c4601bf6cf2_156|1 +16_581fe410-338e-457b-a790-85af2a642356|7881141b-511b-4df9-80e6-5450415af42c_162|1 +16_581fe410-338e-457b-a790-85af2a642356|1d64478e-c3a6-4148-9a64-b6f4a73fc684_158|1 + + + +Since some of these query-less logouts repeat with some users, here is a query to see which users do this the most: + +```sql +select + user_id, count(0) EventTotal +from .ubi_log_events +where action_name='logout' and query_id is null +group by user_id +order by EventTotal desc +``` + +user_id|EventTotal +---|--- +87_5a6e1f8c-4936-4184-a24d-beddd05c9274|8 +127_829a4246-930a-4b24-8165-caa07ee3fa47|7 +49_5da537a3-8d94-48d1-a0a4-dcad21c12615|6 +56_6c7c2525-9ca5-4d5d-8ac0-acb43769ac0b|6 +140_61192c8e-c532-4164-ad1b-1afc58c265b7|6 +149_3443895e-6f81-4706-8141-1ebb0c2470ca|6 +196_4359f588-10be-4b2c-9e7f-ee846a75a3f6|6 +173_39d4cbfd-0666-4e77-84a9-965ed785db49|5 +52_778ac7f3-8e60-444e-ad40-d24516bf4ce2|5 +51_6335e0c3-7bea-4698-9f83-25c9fb984e12|5 +175_e5f262f1-0db3-4948-b349-c5b95ff31259|5 +61_feb3a495-c1fb-40ea-8331-81cee53a5eb9|5 +181_f227264f-cabd-4468-bfcc-4801baeebd39|5 +185_435d1c63-4829-45f3-abff-352ef6458f0e|5 +100_15c182f2-05db-4f4f-814f-46dc0de6b9ea|5 +113_df32ed6e-d74a-4956-ac8e-6d43d8d60317|5 +151_0808111d-07ce-4c84-a0fd-7125e4e33020|5 +204_b75e374c-4813-49c4-b111-4bf4fdab6f26|5 +29_ec2133e5-4d9b-4222-aa7c-2a9ae0880ddd|5 +41_f64abc69-56ea-4dd3-a991-7d1fd292a530|5 diff --git a/_search-plugins/ubi/ubi-dashboard-tutorial.md b/_search-plugins/ubi/ubi-dashboard-tutorial.md new file mode 100644 index 00000000000..2ac113784d9 --- /dev/null +++ b/_search-plugins/ubi/ubi-dashboard-tutorial.md @@ -0,0 +1,83 @@ +--- +layout: default +title: UBI Dashboard Tutorial +parent: User behavior insights +has_children: false +nav_order: 7 +--- + +{% comment %} +# ************** +TODO: update images for new indices +# ************** +{% endcomment %} + + +# Build an analytic dashboard for UBI +Whether you've been collecting user events and queries for a while, or [you uploaded some sample events](https://github.com/o19s/chorus-opensearch-edition/blob/main/katas/003_import_preexisting_event_data.md), now you're ready to visualize them in the dashboard using User Behavior Insights (UBI). + + +## 1) Fire up the OpenSearch dashboards +Depending on your configuration: http://localhost:5601/app/home#/ +![Dashboard Home]({{site.url}}{{site.baseurl}}/images/ubi/home.png "Dashboards") + +## 2) Create an index pattern +http://localhost:5601/app/management/opensearch-dashboards/indexPatterns +![Index Patterns]({{site.url}}{{site.baseurl}}/images/ubi/index_pattern1.png "Index Patterns") +Index patterns are how OpenSearch dashboards access your indexes. In this case, we want to access the indexes that UBI creates, so that we can visualize your users' online, search behaviors. + +After you click on "Create index pattern" you'll see a list of indexes in your OpenSearch instance. The UBI stores may be hidden by default; so, be sure to click on "Include system and hidden indexes". +![Index Patterns]({{site.url}}{{site.baseurl}}/images/ubi/index_pattern2.png "Index Patterns") + +With wildcards you can group indexes into the same data source for your dashboard. So we'll lump both the query and event stores together as `ubi_*`. + +It will prompt you to filter on any `date` field in your schema, so that you can look at things like trending queries over the last 15 minutes. However, for your first dashboard, do not filter on any date field. +Index Patterns + + + Click on "Create index pattern", and you're ready to start building your dashboard pointing to your UBI store. + +## 3) Create a new dashboard +http://localhost:5601/app/opensearch_dashboards_overview#/ + +![First Dashboard]({{site.url}}{{site.baseurl}}/images/ubi/first_dashboard.png "First Dashboard") + +The screen will bring up an empty dashboard, ready for you to add some analytic widgets. We'll start with a pie chart. Click on "Create new" and select a pie chart visualization. Then select the index patter you made in Step 2. + +|Create new|Visualizations| +|---|---| +|![New Chart]({{site.url}}{{site.baseurl}}/images/ubi/new_widget.png "New Chart")|Pie Chart| + +Most of the visualization require some sort of aggregate function on an bucket/facet/aggregatable field (i.e. numeric or keyword). We'll add a `Terms` aggregation on the field `action_name` so that we can see the distribution across event names. Change the size to the number of slices you want to display. +![Pie Chart]({{site.url}}{{site.baseurl}}/images/ubi/pie.png "Pie Chart") + +Save that visualization and it will be added to your new dashboard. Now that you have a visualization on your dashboard, you can save your dashboard. + +## 4) Add a "tag cloud" vizualization to your dashboard +Let's add a word cloud for trending searches. Choose the Tag Cloud visualization of the terms in the `message` field where the JavaScript client logs the raw text that the user searches on. (Note: the true query, as processed by OpenSearch with filters, boosting, and so on will be in the `.{store}_queries` index, but what we are looking at is the `message` field of the `.{store}_events` index, where the JavaScript client captures what the user actually typed. ) +![Word Cloud]({{site.url}}{{site.baseurl}}/images/ubi/tag_cloud1.png "Word Cloud") + +**But there's a problem!** The `message` field is on *every* event --not only query/search events-- and can be used in anyway the client developer decides to use it; so, it can contain error messages, debug messages, click information, and so on. +We need to add a filter to only see search terms on query events. Since the developer gave a `message_type` of `QUERY` for each search event, we will filter on that message type to isolate the specific users' searches. +![Word Cloud]({{site.url}}{{site.baseurl}}/images/ubi/tag_cloud2.png "Word Cloud") + +You should now have two visualizations on your dashboard. +![UBI Dashboard]({{site.url}}{{site.baseurl}}/images/ubi/dashboard2.png "UBI Dashboard") + +## 5) Add one more visualization of a histogram of item clicks +To add a histogram, first, add a vertical bar chart. + +Vertical Bar Chart + +The data field we want to examine is `event_attributes.position.ordinal`, meaning the user clicked on the *n-th* item in a list. The y-axis will be the number of times that *n-th* was clicked. The x-axis will be the ordinal number itself that was clicked, using the `Histogram` aggregation. + +![Vertical Bar Chart]({{site.url}}{{site.baseurl}}/images/ubi/histogram.png "Vertical Bar Chart") + +## 6) Have fun slicing and dicing +For example, let's see how the click position changes when there is a purchase, by adding this filter `action_name:product_purchase`. +![Product Purchase]({{site.url}}{{site.baseurl}}/images/ubi/product_purchase.png "Product Purchase") + +Or let's see what event messages include "\*UBI\*" somewhere between the wildcards. +![UBI]({{site.url}}{{site.baseurl}}/images/ubi/ubi.png "UBI") + +You now have a basic dashboard that lets you look at the data. In the next kata we'll focus on some typical ecommerce driven scenarios. diff --git a/images/ubi/001_screens_side_by_side.png b/images/ubi/001_screens_side_by_side.png new file mode 100644 index 00000000000..b230204b5a5 Binary files /dev/null and b/images/ubi/001_screens_side_by_side.png differ diff --git a/images/ubi/dashboard2.png b/images/ubi/dashboard2.png new file mode 100644 index 00000000000..632d75ec7ea Binary files /dev/null and b/images/ubi/dashboard2.png differ diff --git a/images/ubi/first_dashboard.png b/images/ubi/first_dashboard.png new file mode 100644 index 00000000000..7f3d4fabab7 Binary files /dev/null and b/images/ubi/first_dashboard.png differ diff --git a/images/ubi/histogram.png b/images/ubi/histogram.png new file mode 100644 index 00000000000..70900cce91c Binary files /dev/null and b/images/ubi/histogram.png differ diff --git a/images/ubi/home.png b/images/ubi/home.png new file mode 100644 index 00000000000..7c009496ec2 Binary files /dev/null and b/images/ubi/home.png differ diff --git a/images/ubi/index_pattern1.png b/images/ubi/index_pattern1.png new file mode 100644 index 00000000000..ca69405c7ad Binary files /dev/null and b/images/ubi/index_pattern1.png differ diff --git a/images/ubi/index_pattern2.png b/images/ubi/index_pattern2.png new file mode 100644 index 00000000000..e21803e9060 Binary files /dev/null and b/images/ubi/index_pattern2.png differ diff --git a/images/ubi/index_pattern3.png b/images/ubi/index_pattern3.png new file mode 100644 index 00000000000..8137371a1ef Binary files /dev/null and b/images/ubi/index_pattern3.png differ diff --git a/images/ubi/new_widget.png b/images/ubi/new_widget.png new file mode 100644 index 00000000000..5ba188c6a2a Binary files /dev/null and b/images/ubi/new_widget.png differ diff --git a/images/ubi/pie.png b/images/ubi/pie.png new file mode 100644 index 00000000000..e6b0b7e3734 Binary files /dev/null and b/images/ubi/pie.png differ diff --git a/images/ubi/product_purchase.png b/images/ubi/product_purchase.png new file mode 100644 index 00000000000..e04c1f20616 Binary files /dev/null and b/images/ubi/product_purchase.png differ diff --git a/images/ubi/query_id.png b/images/ubi/query_id.png new file mode 100644 index 00000000000..7051c8abb8b Binary files /dev/null and b/images/ubi/query_id.png differ diff --git a/images/ubi/tag_cloud1.png b/images/ubi/tag_cloud1.png new file mode 100644 index 00000000000..d0c7380e51c Binary files /dev/null and b/images/ubi/tag_cloud1.png differ diff --git a/images/ubi/tag_cloud2.png b/images/ubi/tag_cloud2.png new file mode 100644 index 00000000000..b273a977ca1 Binary files /dev/null and b/images/ubi/tag_cloud2.png differ diff --git a/images/ubi/ubi-schema-interactions.png b/images/ubi/ubi-schema-interactions.png new file mode 100644 index 00000000000..f2319bb2c11 Binary files /dev/null and b/images/ubi/ubi-schema-interactions.png differ diff --git a/images/ubi/ubi-schema-interactions_legend.png b/images/ubi/ubi-schema-interactions_legend.png new file mode 100644 index 00000000000..91cae04c746 Binary files /dev/null and b/images/ubi/ubi-schema-interactions_legend.png differ diff --git a/images/ubi/ubi.png b/images/ubi/ubi.png new file mode 100644 index 00000000000..c24ff6f4ab6 Binary files /dev/null and b/images/ubi/ubi.png differ diff --git a/images/ubi/visualizations.png b/images/ubi/visualizations.png new file mode 100644 index 00000000000..9f061486866 Binary files /dev/null and b/images/ubi/visualizations.png differ diff --git a/images/ubi/visualizations2.png b/images/ubi/visualizations2.png new file mode 100644 index 00000000000..c54920023f0 Binary files /dev/null and b/images/ubi/visualizations2.png differ