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

# Searching Data Objects

> Learn how to search and retrieve master data objects using the Pretectum API

This guide walks you through searching for data objects in Pretectum using the API. You will learn how to authenticate, construct search queries, and handle the results.

## Overview

Pretectum stores your master data as "data objects" organized within a hierarchical structure:

* **Business Areas**: High-level organizational categories (e.g., Customer, Product, Supplier)
* **Schemas**: Define the structure and fields for data objects within a business area
* **Datasets**: Collections of data objects that share the same schema

The Search API allows you to find data objects across this structure using full-text search and filters.

## Before You Begin

To use the Search API, you need:

<Steps>
  <Step title="Create an API Key">
    In the Pretectum app, go to **Configuration → API Keys** and create a key. It is shown once, at
    creation, so copy it then. Ask your tenant administrator if you do not have access to that screen.
  </Step>

  <Step title="Understand Your Data">
    Familiarize yourself with the business areas, schemas, and datasets in your tenant. This knowledge helps you construct effective search queries.
  </Step>

  <Step title="Set Up Your Environment">
    Ensure you have a way to make HTTP requests from your application. This guide includes examples using cURL, JavaScript, and Python.
  </Step>
</Steps>

## Step 1: Create an API Key

Create a key in the Pretectum app under **Configuration → API Keys**. A key looks like this:

```
pre_Ab3dEf5gHi7jKl9mNo1pQr3sTu5vWx7yZa9bCd1e
```

<Warning>
  The key is shown once, at the moment it is created. Pretectum stores only a hash of it and cannot
  show it again. Copy it before closing the dialog.
</Warning>

Keep it in a secret manager or an environment variable. A key is a long-lived credential: it does not
expire unless you give it an expiry date, and there is no token to refresh.

```bash theme={null}
export PRETECTUM_API_KEY="pre_your_api_key"
```

## Step 2: Search for Data Objects

With your key, you can now search for data objects. Include it in the `Authorization` header.

### Basic Search

Search for data objects containing a specific term:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.pretectum.io/v1/dataobjects/search?query=John" \
    -H "Authorization: pre_your_api_key"
  ```

  ```javascript JavaScript theme={null}
  async function searchDataObjects(apiKey, query) {
    const params = new URLSearchParams({ query });

    const response = await fetch(
      `https://api.pretectum.io/v1/dataobjects/search?${params}`,
      {
        headers: {
          'Authorization': apiKey
        }
      }
    );

    return response.json();
  }

  const results = await searchDataObjects(apiKey, 'John');
  console.log(`Found ${results.total} matching records`);
  ```

  ```python Python theme={null}
  def search_data_objects(api_key, query, **filters):
      params = {'query': query, **filters}

      response = requests.get(
          'https://api.pretectum.io/v1/dataobjects/search',
          params=params,
          headers={'Authorization': api_key}
      )

      return response.json()

  results = search_data_objects(api_key, 'John')
  print(f"Found {results['total']} matching records")
  ```
</CodeGroup>

### Search with Filters

Narrow your search by specifying business area, schema, or dataset.

<Tip>
  To get the list of business area names available to your application, use the [List Business Areas](/api-reference/business-areas/list) endpoint. See the [Working with Business Areas](/guides/business-areas/overview) guide for complete examples.
</Tip>

<Tip>
  To get the list of schema names within a business area, use the [List Schemas](/api-reference/schemas/list) endpoint. See the [Working with Schemas](/guides/schemas/overview) guide for complete examples.
</Tip>

<Tip>
  To get the list of dataset names within a schema, use the [List Datasets](/api-reference/datasets/list) endpoint. See the [Working with Datasets](/guides/datasets/overview) guide for complete examples.
</Tip>

```bash theme={null}
# Search within a specific business area
curl -X GET "https://api.pretectum.io/v1/dataobjects/search?query=John&businessArea=Customer" \
  -H "Authorization: pre_your_api_key"

# Search within a specific schema
curl -X GET "https://api.pretectum.io/v1/dataobjects/search?query=John&businessArea=Customer&schema=Individual%20Customer" \
  -H "Authorization: pre_your_api_key"

# Search within a specific dataset
curl -X GET "https://api.pretectum.io/v1/dataobjects/search?query=John&dataSet=US%20Customers" \
  -H "Authorization: pre_your_api_key"
```

### Advanced Query Syntax

The search query supports two types of searches: **full-text search** across all fields, and **field-specific search** for filtering by attribute values.

<AccordionGroup>
  <Accordion title="Full-Text Search">
    Search for terms across all indexed fields:

    ```bash theme={null}
    # Simple term search
    query=John

    # Multiple terms (implicit AND)
    query=John Smith

    # Phrase search for exact match
    query="John Smith"

    # Wildcard search
    query=Jo*
    ```
  </Accordion>

  <Accordion title="Boolean Operators">
    Combine terms with AND, OR, NOT operators:

    ```bash theme={null}
    # Find records with both terms
    query=John AND Smith

    # Find records with either term
    query=John OR Jane

    # Exclude records with a term
    query=John NOT Doe

    # Group conditions with parentheses
    query=(John OR Jane) AND Smith
    ```
  </Accordion>

  <Accordion title="Field-Specific Search (Attribute Filtering)">
    Filter by specific attribute values using `field:value` syntax:

    ```bash theme={null}
    # Search by first name
    query=firstName:John

    # Search by last name with multiple values
    query=lastName:(Smith OR Johnson)

    # Search by email domain
    query=email:*@example.com

    # Search nested fields with dot notation
    query=address.city:"New York"
    query=address.state:CA

    # Combine multiple field filters
    query=firstName:John AND address.state:NY

    # Check if field exists
    query=_exists_:email

    # Check if field is missing
    query=NOT _exists_:phone
    ```
  </Accordion>

  <Accordion title="Range Queries">
    Filter by numeric or date ranges:

    ```bash theme={null}
    # Inclusive range (18 to 65)
    query=age:[18 TO 65]

    # Greater than
    query=age:>21

    # Less than or equal
    query=price:<=100

    # Date range
    query=createdDate:[2024-01-01 TO 2024-12-31]

    # After a specific date
    query=updatedDate:>2024-01-01
    ```
  </Accordion>

  <Accordion title="Comparison Operators (eq, ne, gt, lt)">
    Use comparison operators with bracket notation for field names with spaces:

    ```bash theme={null}
    # Equals - find customers where First Name is "Tim"
    query=[First Name] eq "Tim"

    # Not equals - exclude inactive records
    query=[Status] ne "Inactive"

    # Greater than
    query=[Age] gt 21

    # Less than or equal
    query=[Price] le 500

    # Contains substring
    query=[Email] contains "example.com"

    # Starts with
    query=[Last Name] startswith "Sm"

    # Combine operators with and/or
    query=[First Name] eq "Tim" and [Last Name] eq "Smith"
    query=[State] eq "CA" or [State] eq "NY"

    # Complex conditions
    query=([State] eq "CA" or [State] eq "NY") and [Status] eq "Active"
    ```
  </Accordion>

  <Accordion title="Complex Query Examples">
    Combine multiple search patterns:

    ```bash theme={null}
    # Customer in California with name John
    query=firstName:John AND address.state:CA

    # Products in price range with specific category
    query=category:Electronics AND price:[100 TO 500]

    # Active customers with recent orders
    query=status:Active AND lastOrderDate:>2024-01-01

    # Multi-condition search
    query=(firstName:John OR firstName:Jane) AND address.state:NY AND NOT status:Inactive

    # Full-text combined with field filters
    query="premium customer" AND customerType:Enterprise
    ```
  </Accordion>
</AccordionGroup>

<Tip>
  For a complete reference of all query syntax options, see the [Search API Reference](/api-reference/dataobjects/search#search-query-syntax).
</Tip>

## Step 3: Handle the Response

The search response includes matching data objects and a total count:

```json theme={null}
{
  "hits": [
    {
      "_dataObjectId": "20240601120000123f1a2b3c4d5e6789012345678901234",
      "_businessAreaName": "Customer",
      "_schemaName": "Individual Customer",
      "_dataSetName": "US Customers",
      "First Name": "John",
      "Last Name": "Smith",
      "Email": "john.smith@example.com",
      "City": "New York",
      "State": "NY"
    }
  ],
  "total": 1
}
```

### Understanding the Response

| Field | Description |
| - | - |
| `hits` | Array of matching data objects |
| `total` | Total number of matches (use for pagination) |
| `_dataObjectId` | Unique identifier for each data object |
| `_businessAreaName` | The business area the object belongs to |
| `_schemaName` | The schema defining the object's structure |
| `_dataSetName` | The dataset containing the object |

Additional fields in each hit depend on the schema configuration for that data object.

## Step 4: Implement Pagination

For large result sets, retrieve data in pages:

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function searchWithPagination(apiKey, query, pageSize = 20) {
    let allResults = [];
    let from = 0;
    let total = 0;

    do {
      const params = new URLSearchParams({
        query,
        from: from.toString(),
        size: pageSize.toString()
      });

      const response = await fetch(
        `https://api.pretectum.io/v1/dataobjects/search?${params}`,
        {
          headers: { 'Authorization': apiKey }
        }
      );

      const data = await response.json();
      allResults = allResults.concat(data.hits);
      total = data.total;
      from += pageSize;

      console.log(`Retrieved ${allResults.length} of ${total} records`);
    } while (allResults.length < total);

    return allResults;
  }
  ```

  ```python Python theme={null}
  def search_with_pagination(api_key, query, page_size=20):
      all_results = []
      from_index = 0

      while True:
          response = requests.get(
              'https://api.pretectum.io/v1/dataobjects/search',
              params={
                  'query': query,
                  'from': from_index,
                  'size': page_size
              },
              headers={'Authorization': api_key}
          )

          data = response.json()
          all_results.extend(data['hits'])

          print(f"Retrieved {len(all_results)} of {data['total']} records")

          if len(all_results) >= data['total']:
              break

          from_index += page_size

      return all_results
  ```
</CodeGroup>

## Complete Example

Here is a complete example that authenticates and searches for data objects:

<CodeGroup>
  ```javascript JavaScript theme={null}
  const API_BASE = 'https://api.pretectum.io';

  class PretectumClient {
    constructor(apiKey) {
      this.apiKey = apiKey;
    }

    async search(query, options = {}) {
      const params = new URLSearchParams({ query, ...options });
      const response = await fetch(
        `${API_BASE}/dataobjects/search?${params}`,
        {
          headers: { 'Authorization': this.apiKey }
        }
      );

      if (!response.ok) {
        throw new Error(`Search failed: ${response.statusText}`);
      }

      return response.json();
    }
  }

  // Usage
  const client = new PretectumClient(process.env.PRETECTUM_API_KEY);

  const results = await client.search('John', {
    businessArea: 'Customer',
    size: 50
  });

  console.log(`Found ${results.total} customers named John`);
  results.hits.forEach(customer => {
    console.log(`- ${customer.firstName} ${customer.lastName}`);
  });
  ```

  ```python Python theme={null}
  import os
  import requests
  class PretectumClient:
      API_BASE = 'https://api.pretectum.io'

      def __init__(self, api_key):
          self.api_key = api_key

      def search(self, query, **options):
          params = {'query': query, **options}
          response = requests.get(
              f'{self.API_BASE}/dataobjects/search',
              params=params,
              headers={'Authorization': self.api_key}
          )
          response.raise_for_status()

          return response.json()


  # Usage
  client = PretectumClient(os.environ['PRETECTUM_API_KEY'])

  results = client.search('John', businessArea='Customer', size=50)

  print(f"Found {results['total']} customers named John")
  for customer in results['hits']:
      print(f"- {customer['firstName']} {customer['lastName']}")
  ```
</CodeGroup>

## Error Handling

Handle common error scenarios in your application:

| Error | Cause | Solution |
| - | - | - |
| `401 Unauthorized` | API key missing, invalid, inactive or expired | Check the key; create a new one if it was deleted or has expired |
| `403 Forbidden` | Missing permissions | Contact your tenant administrator |
| `400 Bad Request` | Invalid query syntax | Check your query parameters |

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function safeSearch(client, query, options) {
    try {
      return await client.search(query, options);
    } catch (error) {
      if (error.message.includes('401')) {
        // The API key was rejected. It does not expire on its own, so this means the key was
        // deleted, deactivated, or has passed an expiry date it was given. Retrying will not help.
        throw new Error('Pretectum API key rejected; check the key in Configuration -> API Keys');
      }
      throw error;
    }
  }
  ```

  ```python Python theme={null}
  def safe_search(client, query, **options):
      try:
          return client.search(query, **options)
      except requests.exceptions.HTTPError as e:
          if e.response.status_code == 401:
              # The API key was rejected. It does not expire on its own, so this means the key was
              # deleted, deactivated, or has passed an expiry date it was given. Retrying will not help.
              raise RuntimeError(
                  'Pretectum API key rejected; check the key in Configuration -> API Keys'
              ) from e
          raise
  ```
</CodeGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="API Reference" icon="code" href="/api-reference/dataobjects/search">
    View the complete Search API reference
  </Card>

  <Card title="API Keys" icon="key" href="/api-reference/authentication/api-keys">
    Learn more about authentication
  </Card>

  <Card title="Business Areas" icon="building" href="/guides/business-areas/overview">
    Learn how to work with business areas
  </Card>

  <Card title="List Business Areas API" icon="list" href="/api-reference/business-areas/list">
    Get available business area names for filtering
  </Card>

  <Card title="Schemas" icon="file-code" href="/guides/schemas/overview">
    Learn how to work with schemas
  </Card>

  <Card title="List Schemas API" icon="list" href="/api-reference/schemas/list">
    Get available schema names for filtering
  </Card>

  <Card title="Datasets" icon="database" href="/guides/datasets/overview">
    Learn how to work with datasets
  </Card>

  <Card title="List Datasets API" icon="list" href="/api-reference/datasets/list">
    Get available dataset names for filtering
  </Card>
</CardGroup>
