# Implementing tracking

{% hint style="info" %}
If you are using the [search-ui-widgets](https://docs.search.io/developer-documentation/fundamentals/search-ui-widgets "mention"), tracking works out of the box. The [React SDK](https://react.docs.search.io/tracking) also provides components that simplifies the implementation of tracking.
{% endhint %}

## Event Tracking

Event tracking is Search.io's preferred method of tracking, as it is easy to implement yet extremely flexible way of sending event data to Search.io as users interact with search results.

Common events are `click`, `add_to_cart`, `purchase`, etc. Search.io use this data to track search results as they move through a funnel, surface useful insights via reporting and to train the machine learning algorithm that powers the various AI capabilities.

There are no tokens to store or send, events are simply bound to the query (i.e. `query_id`) that generated the result set and a unique identifier for your result (i.e. the name of a field in [your schema](https://github.com/sajari/gitbook-developer-docs/blob/main/deep-dives/concepts-and-terminology.md#schema) with the *Unique* constraint).

This guide explains how events are bound to the `query_id` and result.

### Getting the `query_id` from the query

`query_id`'s are generated by adding a tracking object when [Querying](https://dev.search.io/docs/api/#operation/QueryCollection) a collection. Within the tracking object `type` must be set to `"EVENT"`, and `field` must be passed the field name which contains the unique identifier you have assigned to each record (i.e. a product ID may be appropriate for ecommerce scenarios).

```json
{
  "variables": {
    "q": "samsung tv"
  },
  "tracking": {
    "type": "EVENT",
    "field": "productId"
  }
}
```

The API response will contain the `query_id` for the search result set.

```json
{
  "query_id": "3640a94a-ea02-46b9-87db-39cabf22eb46",
  "results": [
    {
      "record": {
        "productId": "5577783",
        "name": "Samsung LED TV"
      }
    },
    {
      "record": {
        "productId": "5042100",
        "name": "Samsung - LED - 2160p - Smart - 4K"
      }
    }
  ]
}
```

### Storing and sending events back

If you're using the [search-ui-widgets](https://docs.search.io/developer-documentation/fundamentals/search-ui-widgets "mention") or our [React SDK](https://react.docs.search.io/tracking) this is handled automatically. Events are persisted in localStorage for 30 days and sent as soon as possible.

If you're caching the event data on the server side event data should be sent using the API [TrackEvent](https://dev.search.io/docs/api/#operation/TrackEvent).

### Interacting with search results

The most common user event to track is when a user clicks on a result from the search. The `result_id` field contains the unique `field` specified in the tracking object when querying the collection.

```json
{
  "type": "click",
  "query_id": "3640a94a-ea02-46b9-87db-39cabf22eb46",
  "result_id": "5577783"
}
```

As the user moves further down the purchase funnel you'll also want to track other events to indicate positive signal to train Search.io's machine learning that helps provide more relevant results to your users.

Common event `type`'s include "add\_to\_cart", "checkout" and "purchase".

```json
{
  "type": "add_to_cart",
  "query_id": "3640a94a-ea02-46b9-87db-39cabf22eb46",
  "result_id": "5577783"
}
```

### Following redirects

Where the user follows a redirect the event `type` is "redirect" and a `redirect_id` field should be set with the id of the redirect.

```json
{
  "type": "redirect",
  "query_id": "3640a94a-ea02-46b9-87db-39cabf22eb46",
  "redirect_id": "abc123"
}
```

#### Clicking a promotion

Where the user clicks a promotion banner the event `type` is "promotion\_click" and a `banner_id` field should be set with the id of the banner.

```json
{
  "type": "promotion_click",
  "query_id": "3640a94a-ea02-46b9-87db-39cabf22eb46",
  "banner_id": "abc123"
}
```

The `metadata` field can also be sent with arbitrary key/value pairs, but does not need to be defined and is not in scope for this guide.

## PosNeg Tracking

PosNeg tracking is a mechanism which customers can use to send their event data to Search.io as users interact with search results. Common events are click, add to basket, checkout, like, down vote etc. Search.io use this data to track search results as they move through a funnel, surface useful insights via reporting and to train the machine learning algorithm that powers the various AI capabilities.

The tokens are at the centre of PosNeg tracking as they allow Search.io to connect the event back to its original query. This guide explains how PosNeg tokens can be generated with a result set, stored, and finally sent back when an event occurs.

### Before you start: <a href="#before-you-start" id="before-you-start"></a>

If you are new to PosNeg tracking we recommend reading our [introduction](https://docs.search.io/documentation/fundamentals/reporting/event-tracking)

### Generating PosNeg tokens <a href="#generating-posneg-tokens" id="generating-posneg-tokens"></a>

PosNeg tokens are generated by adding a tracking object when [Querying](https://dev.search.io/docs/api/#operation/QueryCollection) a collection. Within the tracking object `type` must be set to `"POS_NEG"`, `query_id` must be assigned a unique identifier (more on this later), and `field` must be passed the field name which contains the unique identifier you have assigned to each record i.e. a product ID may be appropriate for ecommerce scenarios.

```json
{
  "variables": {
    "q": "samsung tv"
  },
  "tracking": {
    "type": "POS_NEG",
    "query_id": "4216691599",
    "field": "productId"
  }
}
```

The API response will contain PosNeg tokens for each search result. Each token allows us to tie an interaction back to a specific query.

```json
{
  "results": [
    {
      "record": {
        "productId": "5577783",
        "name": "Samsung LED TV"
      },
      "token": {
        "pos_neg": {
          "pos": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwdXJwb3NlIjoicG9zIiwiZGVzdGluYXRpb24iOiIiLCJ2YWxzIjp7ImNvbGxlY3Rpb24iOlsiYmVzdGJ1eSJdLCJjb21wYW55IjpbIjE1OTQxNTM3MTE5MDE3MjQyMjAiXSwiZmllbGQiOlsiaWQiXSwicHJvamVjdCI6WyIxNTk0MTUzNzExOTAxNzI0MjIwIl0sInEuaWQiOlsiNDIxNjY5MTU5OSJdLCJxLnNsIjpbIjEiXSwicS51aWQiOlsiNDIxNjY5MTU5OTEiXSwidmFsdWUiOlsiNTU3Nzc4MyJdfX0.5KKsQaXG1b6LaDwkaTh9tpSAJONFY3nbSj4ZJYJM1IM",
          "neg": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwdXJwb3NlIjoibmVnIiwiZGVzdGluYXRpb24iOiIiLCJ2YWxzIjp7ImNvbGxlY3Rpb24iOlsiYmVzdGJ1eSJdLCJjb21wYW55IjpbIjE1OTQxNTM3MTE5MDE3MjQyMjAiXSwiZmllbGQiOlsiaWQiXSwicHJvamVjdCI6WyIxNTk0MTUzNzExOTAxNzI0MjIwIl0sInEuaWQiOlsiNDIxNjY5MTU5OSJdLCJxLnNsIjpbIjEiXSwicS51aWQiOlsiNDIxNjY5MTU5OTEiXSwidmFsdWUiOlsiNTU3Nzc4MyJdfX0.QraRedkZhcEAFwrAA90Rd63DCD3eKPtbL1JrKnMkbkY"
        }
      }
    },
    {
      "record": {
        "productId": "5042100",
        "name": "Samsung - LED - 2160p - Smart - 4K"
      },
      "token": {
        "pos_neg": {
          "pos": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwdXJwb3NlIjoicG9zIiwiZGVzdGluYXRpb24iOiIiLCJ2YWxzIjp7ImNvbGxlY3Rpb24iOlsiYmVzdGJ1eSJdLCJjb21wYW55IjpbIjE1OTQxNTM3MTE5MDE3MjQyMjAiXSwiZmllbGQiOlsiaWQiXSwicHJvamVjdCI6WyIxNTk0MTUzNzExOTAxNzI0MjIwIl0sInEuaWQiOlsiNDIxNjY5MTU5OSJdLCJxLnNsIjpbIjIiXSwicS51aWQiOlsiNDIxNjY5MTU5OTEiXSwidmFsdWUiOlsiNTA0MjEwMCJdfX0.JkAbTpQ0XiDbHxanLQOLv5niMwu6QT8L1BmbAromMjs",
          "neg": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwdXJwb3NlIjoibmVnIiwiZGVzdGluYXRpb24iOiIiLCJ2YWxzIjp7ImNvbGxlY3Rpb24iOlsiYmVzdGJ1eSJdLCJjb21wYW55IjpbIjE1OTQxNTM3MTE5MDE3MjQyMjAiXSwiZmllbGQiOlsiaWQiXSwicHJvamVjdCI6WyIxNTk0MTUzNzExOTAxNzI0MjIwIl0sInEuaWQiOlsiNDIxNjY5MTU5OSJdLCJxLnNsIjpbIjIiXSwicS51aWQiOlsiNDIxNjY5MTU5OTEiXSwidmFsdWUiOlsiNTA0MjEwMCJdfX0.qhyTVZOWzeWzTmolyaMxsmMz1yHQySeoQc-S9PIfEBc"
        }
      }
    }
  ]
}
```

### Multiple Queries, same Search: <a href="#multiple-queries-same-search" id="multiple-queries-same-search"></a>

A search from an end user perspective is often more than the action of typing text into a search bar. It can become a sequence of actions where the user reaches a refined set of search results. Actions such as refining by a product size, price range, stock availability or tweaking the search text.

Search.io uses `query_id` to determine the difference between a single discrete query and multiple queries which should be collapsed into a single search. This means that multiple queries can use the same id, in which case they will be considered a part of the same search.

Customers using our API for PosNeg tracking must define their own conditions for triggering a new search by sending a new `query_id`. You’re free to use whatever method you prefer to generate a unique string for `query_id`. If you already have independent search session logic in place these ID’s may also be suitable to use for `query_id`

`sequence` an integer which should increment by 1 with each successive query that falls within the same search (`query_id`).

If you would prefer Search.io to manage `query_id` and `sequence` for you then take a look at our [UI libraries.](https://docs.search.io/developer-documentation/fundamentals/api-and-client-libraries)

#### Why is it important to define a search? <a href="#why-is-it-important-to-define-a-search" id="why-is-it-important-to-define-a-search"></a>

Defining a search provides a fuller picture of search performance and enriches the quality of the data used for machine learning and business analysis.

> **Note**
>
> Here are some recommended events to define a new search:
>
> * The query text has been cleared after being non-empty i.e. from a delete
> * The first 3 characters of the query text have changed i.e. from direct replacement
> * The previous search returned no results
>
> When any of these are true, a new `query_id` is generated, therefore a new search is introduced in Search.io's analytics system.

#### Search Example: <a href="#search-example" id="search-example"></a>

A user starts a new search by typing “Samsung TV” into the search bar. Below is an example of the *Query collection* call where `query_id` must be passed a unique ID and `sequence` set to 0.

```json
{
  "variables": {
    "q": "samsung tv"
  },
  "tracking": {
    "type": "POS_NEG",
    "query_id": "4216691599",
    "sequence": 0,
    "field": "productId"
  }
}
```

The user then adds a price filter. As this is the same search to the Query collection, the call should contain the same `query_id` whilst the `sequence` is incremented to 1.

```json
{
  "variables": {
      "q" : "samsung tv"
  },
  "tracking": {
    "type": "POS_NEG",
    "filter": price < 1000,
    "query_id": "4216691599",
    "sequence": 1,
    "field": "productId"
  }
}
```

The user then searches for “tv cabinet”. This should be treated as a new search. The Query collection call should contain a new unique `query_id` and `sequence` should be reset to 0.

```json
{
  "variables": {
    "q": "tv cabinet"
  },
  "tracking": {
    "type": "POS_NEG",
    "query_id": "5643627563",
    "sequence": 0,
    "field": "id"
  }
}
```

### Storing and sending tokens back <a href="#storing-and-sending-tokens-back" id="storing-and-sending-tokens-back"></a>

PosNeg tokens can be used more than once. This means that a single token can be used to send positive signals as a user moves through a funnel. Additionally, tokens can be stored for long durations in order to attribute search queries with results. For example, if a user performs a search and finds a good result but doesn't actually purchase the product until much later.

Tokens can be stored using standard methods such as cookies, localStorage or cached server side.

When an event occurs like a user clicking a search result the token associated with the record for that specific search result should be sent using the API [SendEvent](https://dev.search.io/docs/api/#operation/SendEvent)

An appropriate name such as ‘click’ should be assigned to the `name` field. `metadata` and `weight` do not need to be defined and are not in scope for this guide.

```json
{
  "name": "click",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwdXJwb3NlIjoicG9zIiwiZGVzdGluYXRpb24iOiIiLCJ2YWxzIjp7ImNvbGxlY3Rpb24iOlsiYmVzdGJ1eSJdLCJjb21wYW55IjpbIjE1OTQxNTM3MTE5MDE3MjQyMjAiXSwiZmllbGQiOlsiaWQiXSwicHJvamVjdCI6WyIxNTk0MTUzNzExOTAxNzI0MjIwIl0sInEuaWQiOlsiNDIxNjY5MTU5OSJdLCJxLnNsIjpbIjEiXSwicS51aWQiOlsiNDIxNjY5MTU5OTEiXSwidmFsdWUiOlsiNTU3Nzc4MyJdfX0.5KKsQaXG1b6LaDwkaTh9tpSAJONFY3nbSj4ZJYJM1IM"
}
```

If the user then purchases the product the same token should be sent with another API *SendEvent* call, but this time with the name "purchase".

```json
{
  "name": "purchase",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwdXJwb3NlIjoicG9zIiwiZGVzdGluYXRpb24iOiIiLCJ2YWxzIjp7ImNvbGxlY3Rpb24iOlsiYmVzdGJ1eSJdLCJjb21wYW55IjpbIjE1OTQxNTM3MTE5MDE3MjQyMjAiXSwiZmllbGQiOlsiaWQiXSwicHJvamVjdCI6WyIxNTk0MTUzNzExOTAxNzI0MjIwIl0sInEuaWQiOlsiNDIxNjY5MTU5OSJdLCJxLnNsIjpbIjEiXSwicS51aWQiOlsiNDIxNjY5MTU5OTEiXSwidmFsdWUiOlsiNTU3Nzc4MyJdfX0.5KKsQaXG1b6LaDwkaTh9tpSAJONFY3nbSj4ZJYJM1IM"
}
```

A user may have multiple products, each with a token from different searches, in their basket. On purchase the tokens must all be sent individually via API SendEvent.

> Common events to track for e-commerce are "click", "add-to-cart", "checkout" and "purchased".

### What do I do with neg tokens? <a href="#what-do-i-do-with-neg-tokens" id="what-do-i-do-with-neg-tokens"></a>

You only need to to return neg tokens where the user has taken a specific negative action e.g. down voting an item, marking a result as unhelpful etc.

Search.io automatically handles negatives in scenarios such as a user not interacting with any search results, or passing over higher ranked results to click on a lower ranked result.

### Leveraging machine learning <a href="#leveraging-machine-learning" id="leveraging-machine-learning"></a>

Now you’ve implemented PosNeg tracking hop over to our guide on [Dynamic Boosting](https://docs.search.io/documentation/fundamentals/search-settings/dynamic-boosting) so you can start leveraging machine learning!
