> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Query aggregation and processing

> How search queries are processed for analytics.

export const SearchRequest = () => <Tooltip tip="A search request is a single HTTP call to the Algolia Search API that can run one or more search operations. It can include multiple queries, for example, when querying several indices at once.">
    search request
  </Tooltip>;

export const SearchQuery = () => <Tooltip tip="The text users enter into a search box. In the Search API, this corresponds to the query parameter. A search query is often used with filters, facets, and other parameters, but these aren't part of the query text itself.">
    search query
  </Tooltip>;

To ensure relevant analytics data,
search queries are aggregated in the search analytics.

For example, if a user types "beatles":

Algolia runs seven search queries:
"b", "be", "bea", "beat", "beatl", beatle", and "beatles".

Algolia's analytics only counts the last <SearchQuery />, "beatles".

Algolia aggregates search queries based on:

* [Edit distance](https://wikipedia.org/wiki/Edit_distance)
* Timestamp (queries within 30 s are aggregated)
* User ID

By default, the user ID is the user's IP address.
For more accurate analytics, explicitly [set a user token](/doc/guides/search-analytics/guides/usertoken)

## Query aggregation and backend search

If you make a <SearchRequest /> from your backend,
the user ID for all searches would be your server's IP address,
which leads to inaccurate analytics.
To obtain accurate analytics, you must provide unique user IDs.

To provide unique user IDs, select one of these options:

* [Set a user token for analytics](/doc/guides/search-analytics/guides/usertoken)
* Forward user IP addresses

### Forward user IP addresses

If you make search requests from your backend servers,
and you don't want to explicitly set user tokens,
you can add the `X-Forwarded-For` header to forward your users' IP addresses to Algolia.

<CodeGroup>
  ```cs C# theme={"system"}
  var response = await client.SearchSingleIndexAsync<Hit>(
    "INDEX_NAME",
    new SearchParams(new SearchParamsString { }),
    new RequestOptionBuilder().AddExtraHeader("x-forwarded-for", "XX.XXX.XXX.XXX").Build()
  );
  ```

  ```dart Dart theme={"system"}
  final response = await client.searchSingleIndex(
    indexName: "INDEX_NAME",
    searchParams: SearchParamsString(),
    requestOptions: RequestOptions(
      headers: {
        'x-forwarded-for': 'XX.XXX.XXX.XXX',
      },
    ),
  );
  ```

  ```go Go theme={"system"}
  response, err := client.SearchSingleIndex(client.NewApiSearchSingleIndexRequest(
    "INDEX_NAME").WithSearchParams(search.SearchParamsStringAsSearchParams(
    search.NewEmptySearchParamsString())), search.WithHeaderParam("x-forwarded-for", "XX.XXX.XXX.XXX"))
  if err != nil {
    // handle the eventual error
    panic(err)
  }
  ```

  ```java Java theme={"system"}
  SearchResponse response = client.searchSingleIndex(
    "INDEX_NAME",
    new SearchParamsString(),
    Hit.class,
    new RequestOptions().addExtraHeader("x-forwarded-for", "XX.XXX.XXX.XXX")
  );
  ```

  ```js JavaScript theme={"system"}
  const response = await client.searchSingleIndex(
    { indexName: 'indexName', searchParams: {} },
    {
      headers: { 'x-forwarded-for': 'XX.XXX.XXX.XXX' },
    },
  );
  ```

  ```kotlin Kotlin theme={"system"}
  var response =
    client.searchSingleIndex(
      indexName = "INDEX_NAME",
      searchParams = SearchParamsString(),
      requestOptions =
        RequestOptions(headers = buildMap { put("x-forwarded-for", "XX.XXX.XXX.XXX") }),
    )
  ```

  ```php PHP theme={"system"}
  $response = $client->searchSingleIndex(
      'INDEX_NAME',
      [],
      [
          'headers' => [
              'x-forwarded-for' => 'XX.XXX.XXX.XXX',
          ],
      ]
  );
  ```

  ```python Python theme={"system"}
  response = client.search_single_index(
      index_name="INDEX_NAME",
      search_params={},
      request_options={
          "headers": loads("""{"x-forwarded-for":"XX.XXX.XXX.XXX"}"""),
      },
  )
  ```

  ```ruby Ruby theme={"system"}
  response = client.search_single_index(
    "INDEX_NAME",
    Algolia::Search::SearchParamsString.new,
    {:header_params => {"x-forwarded-for" => "XX.XXX.XXX.XXX"}}
  )
  ```

  ```scala Scala theme={"system"}
  val response = Await.result(
    client.searchSingleIndex(
      indexName = "INDEX_NAME",
      searchParams = Some(
        SearchParamsString(
        )
      ),
      requestOptions = Some(
        RequestOptions
          .builder()
          .withHeader("x-forwarded-for", "XX.XXX.XXX.XXX")
          .build()
      )
    ),
    Duration(100, "sec")
  )
  ```

  ```swift Swift theme={"system"}
  let response: SearchResponse<Hit> = try await client.searchSingleIndex(
      indexName: "INDEX_NAME",
      searchParams: SearchSearchParams.searchParamsString(SearchParamsString()),
      requestOptions: RequestOptions(
          headers: ["x-forwarded-for": "XX.XXX.XXX.XXX"]
      )
  )
  ```
</CodeGroup>

## Revenue transactions

To track revenue associated with a search,
events must include the following properties:

* `eventSubtype` with the value `purchase`
* `objectData`
* `queryID`

Events without the `eventSubtype` or `queryID` properties,
events with query IDs that don't correspond to searches, and
events with `eventSubtype` set to `addToCart`
won't be included in revenue metrics.

For more information, see the [Insights API reference](/doc/rest-api/insights).

<Note>
  All items in the `objectData` array of a single event are part of a single transaction, even if they have different query IDs.
  The revenue associated with each item in the `objectData` array will be associated with a search according to the item-level query ID.
  or with the event-level query ID if none is provided for an item.
</Note>

The following event would be included in the [Average order value](/doc/guides/search-analytics/concepts/metrics#average-order-value),
[Purchase Rate](/doc/guides/search-analytics/concepts/metrics#purchase-rate),
and [Revenue](/doc/guides/search-analytics/concepts/metrics#revenue) charts,
as well as the respective search or hit in the [Searches](/doc/guides/search-analytics/concepts/metrics#searches) or
[Results](/doc/guides/search-analytics/concepts/metrics#results) tables for the given `queryID` or `objectIDs`.

```json JSON icon=braces theme={"system"}
{
  "eventType": "conversion",
  "objectIDs": ["black-t-shirt-xxl", "pink-jeans-xl"],
  "eventSubtype": "purchase"
  "objectData": [
    {
      // revenue data for 'black-t-shirt-xxl'
      "queryID": "43b15df305339e827f0ac0bdc5ebcaa7", // optional
      "price": 19.99,   // required (the sale price is the per item list price minus discount)
      "discount": 4.01, // optional (defaults to 0)
      "quantity": 2     // optional (defaults to 1)
    },
    {
      // revenue data for 'pink-jeans-xl'
      "queryID": "43b15df305339e827f0ac0bdc5ebcaa7",
      "price": 39.99,
      "discount": 10.01,
      "quantity": 1
    }
  ],
  "currency": "USD" // required
  // ...
}
```
