> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://www.comet.com/docs/opik/llms.txt.

# Opik Query Language (OQL)

OQL provides a powerful, SQL-like syntax for filtering data in Opik. It's used with various SDK methods like `searchPrompts()`, `searchTraces()`, and `searchThreads()` to find exactly the data you need using expressive filter conditions.

## Operators

### String Operators

```typescript
// Exact match
'name = "greeting-prompt"';

// Not equal
'name != "old-prompt"';

// Contains substring (case-insensitive)
'name contains "greeting"';

// Does not contain substring
'name not_contains "deprecated"';

// Starts with prefix
'name starts_with "prod-"';

// Ends with suffix
'name ends_with "-v2"';
```

### Comparison Operators

```typescript
// Greater than (alphabetical for strings, numeric for numbers)
'name > "m"'; // Names starting with n-z
'duration > 300'; // Threads longer than 300 seconds

// Less than (alphabetical for strings, numeric for numbers)
'name < "m"'; // Names starting with a-l
'number_of_messages < 5'; // Threads with fewer than 5 messages

// Greater than or equal
'feedback_scores.quality >= 0.8';

// Less than or equal
'duration <= 600';
```

### Numeric Operators

```typescript
// Equality
'number_of_messages = 3';

// Not equal
'duration != 0';

// Check if field is empty
'feedback_scores.quality is_empty';

// Check if field is not empty
'feedback_scores.quality is_not_empty';
```

### DateTime Operators

```typescript
// Use ISO 8601 format for dates
'start_time >= "2024-01-01T00:00:00Z"';
'end_time < "2024-12-31T23:59:59Z"';
```

### Enum Operators

Used for fields with a fixed set of values, such as `environment`:

```typescript
// Exact match
'environment = "production"';

// Not equal
'environment != "development"';

// Match any of a set of values
'environment in ("production", "staging")';

// Exclude a set of values
'environment not_in ("development", "local")';
```

### List Operators

```typescript
// Check if list contains value
'tags contains "production"';

// Check if list does not contain value
'tags not_contains "experimental"';
```

## Combining Conditions

Use `AND` to combine multiple filter conditions. All conditions must be true for a result to match:

```typescript
// Multiple conditions with AND
'tags contains "production" AND name contains "greeting" AND created_by = "user@example.com"';

// Complex multi-field filtering
'name starts_with "prod-" AND tags contains "stable" AND name not_contains "deprecated"';
```

> **Note**
>
> Currently, only `AND` logic is supported. `OR` logic is not available in OQL.

## Examples

### Searching Prompts

```typescript
import { Opik } from "opik";

const client = new Opik();

// Filter by single tag
const prod = await client.searchPrompts('tags contains "production"');

// Combine name pattern and tag
const approved = await client.searchPrompts(
  'name starts_with "prod-" AND tags contains "qa-approved"'
);

// Multiple conditions
const results = await client.searchPrompts(
  'created_by = "user@example.com" AND tags contains "production" AND name not_contains "deprecated"'
);
```

### Searching Threads

```typescript
import { Opik } from "opik";

const client = new Opik();

// Filter by status
const activeThreads = await client.searchThreads({
  projectName: "my-project",
  filterString: 'status = "active"'
});

// Filter by duration and message count
const longConversations = await client.searchThreads({
  projectName: "my-project",
  filterString: 'duration > 300 AND number_of_messages >= 5'
});

// Filter by feedback score
const highQualityThreads = await client.searchThreads({
  projectName: "my-project",
  filterString: 'feedback_scores.quality > 0.8'
});

// Filter by empty feedback score
const unratedThreads = await client.searchThreads({
  projectName: "my-project",
  filterString: 'feedback_scores.quality is_empty'
});

// Filter by environment
const prodThreads = await client.searchThreads({
  projectName: "my-project",
  filterString: 'environment = "production"'
});

// Filter by multiple environments
const stagingOrProdThreads = await client.searchThreads({
  projectName: "my-project",
  filterString: 'environment in ("production", "staging")'
});

// Filter by tags and metadata
const importantProdThreads = await client.searchThreads({
  projectName: "my-project",
  filterString: 'tags contains "important" AND metadata.environment = "production"'
});

// Filter by date range
const recentThreads = await client.searchThreads({
  projectName: "my-project",
  filterString: 'start_time >= "2024-01-01T00:00:00Z"'
});
```

### Searching Traces

```typescript
import { Opik } from "opik";

const client = new Opik();

// Filter by metadata
const traces = await client.searchTraces({
  projectName: "my-project",
  filterString: 'metadata.model = "gpt-5"'
});

// Filter by feedback scores
const goodTraces = await client.searchTraces({
  projectName: "my-project",
  filterString: 'feedback_scores.accuracy > 0.9'
});

// Filter by environment
const prodTraces = await client.searchTraces({
  projectName: "my-project",
  filterString: 'environment = "production"'
});

// Combine environment with other filters
const goodProdTraces = await client.searchTraces({
  projectName: "my-project",
  filterString: 'environment = "production" AND feedback_scores.accuracy > 0.9'
});
```

> **Note**
>
> The same OQL syntax works across all search methods (`searchPrompts()`, `searchTraces()`, `searchThreads()`).
> Specific resource types may support additional fields - see their respective documentation for available columns.

## Syntax Rules

### String Values

Always wrap string values in double quotes:

```typescript
// ✅ Correct - double quotes around values
await client.searchPrompts('name = "my-prompt"');
await client.searchPrompts('tags contains "production"');

// ❌ Incorrect - missing quotes
await client.searchPrompts("name = my-prompt"); // Will fail

// ❌ Incorrect - single quotes
await client.searchPrompts("name = 'my-prompt'"); // Will fail
```

### Error Handling

```typescript
try {
  const results = await client.searchPrompts("invalid syntax ===");
} catch (error) {
  console.error("Invalid OQL syntax:", error.message);
}
```

## Best Practices

1. **Use descriptive tag hierarchies** - Structure tags like `"production"`, `"staging"`, `"team-alpha"` for effective filtering
2. **Use naming conventions** - Implement consistent naming patterns (e.g., `"prod-"` prefix) to enable powerful filtering
3. **Handle errors** - Always wrap OQL queries in try-catch blocks to handle syntax errors gracefully