@index
The @index decorator creates a non-unique database index for query performance optimization.
Creates a non-unique index on the field in SurrealDB. This improves query performance for fields that are frequently filtered or sorted, without enforcing uniqueness.
Syntax
model Product {
id Record @id
name String @index
category String @index
price Float
}Behavior
- The index speeds up lookups but does not prevent duplicate values.
@indexalone does not satisfy the unique field requirement forfindUnique,updateUnique, ordeleteUnique— those methods require at least one@uniqueor@idfield in thewhereclause.
@index vs @unique
| Aspect | @index | @unique |
|---|---|---|
| Duplicate values | Allowed | Rejected by DB |
| Unique lookups | Not available | findUnique, updateUnique, deleteUnique |
| Use case | Performance optimization | Data integrity constraint |
Example
model Article {
id Record @id
title String
category String @index
author String @index
publishedAt Date @createdAt
}// Both queries benefit from the index
const articles = await client.db.Article.findMany({
where: { category: 'tech' },
});
const byAuthor = await client.db.Article.findMany({
where: { author: 'Alice' },
orderBy: { publishedAt: 'desc' },
});Null and Optional Fields
Since @index does not enforce uniqueness, null/NONE values have no special behavior — multiple records with the same value (including null) are always allowed.
Array Fields
@index can be applied to array fields. SurrealDB indexes each element individually, which speeds up CONTAINS-style queries:
model Article {
id Record @id
tags String[] @index
}// Per-element index speeds up CONTAINS lookups
const tagged = await client.db.Article.findMany({
where: { tags: { contains: 'typescript' } },
});@unique is not allowed on array fields, but @index is — making it the go-to choice for indexing array elements.
Object Fields
@index can be applied to fields within object definitions. Each embedding of the object in a model generates its own independent index using dot-notation paths:
object LocationInfo {
address String
country String @index
}
model Store {
id Record @id
location LocationInfo
warehouse LocationInfo?
}This generates two separate indexes:
store_location_country_indexonlocation.countrystore_warehouse_country_indexonwarehouse.country
Allowed On
| Construct | Allowed |
|---|---|
| Model fields | ✅ (any storable type, including arrays) |
| Object sub-fields | ✅ |
| Tuple elements | — |
| Relation fields | — |
Restrictions
- Mutually exclusive with
@unique— A field can have@indexor@unique, but not both. Use@uniquewhen you need both indexing and a uniqueness constraint. - Not on Relation fields — Relation fields are virtual (not stored), so they cannot be indexed.
- Not on tuple elements — Tuple elements don't support indexing.