Skip to main content
Documents are the individual records stored and indexed in your Orama database. Each document must conform to the schema and can optionally include an ID.

Document Structure

Documents are JavaScript objects that match your schema definition:
import { create, insert } from '@orama/orama'

const db = await create({
  schema: {
    title: 'string',
    description: 'string',
    price: 'number',
    inStock: 'boolean'
  }
})

const doc = {
  title: 'Wireless Headphones',
  description: 'High-quality Bluetooth headphones with noise cancellation',
  price: 129.99,
  inStock: true
}

await insert(db, doc)

Document IDs

Every document in Orama has a unique identifier.

Auto-Generated IDs

If you don’t provide an ID, Orama automatically generates one:
const docId = await insert(db, {
  title: 'Product A',
  price: 49.99
})

console.log(docId) // e.g., "1-6789"

Custom IDs

You can provide your own ID by including an id field:
await insert(db, {
  id: 'prod-12345',
  title: 'Product A',
  price: 49.99
})
Document IDs must be strings. Using other types will throw an error.

ID Requirements

From components/defaults.ts:25-35:
function getDocumentIndexId(doc: AnyDocument): string {
  if (doc.id) {
    if (typeof doc.id !== 'string') {
      throw createError('DOCUMENT_ID_MUST_BE_STRING', typeof doc.id)
    }
    return doc.id
  }
  return uniqueId()
}

Inserting Documents

Single Insert

Insert one document at a time:
import { insert } from '@orama/orama'

const id = await insert(db, {
  title: 'Laptop',
  price: 999.99,
  inStock: true
})

console.log('Inserted document:', id)

Bulk Insert

Insert multiple documents efficiently:
import { insertMultiple } from '@orama/orama'

const docs = [
  { title: 'Product 1', price: 10 },
  { title: 'Product 2', price: 20 },
  { title: 'Product 3', price: 30 }
]

const ids = await insertMultiple(db, docs)
console.log('Inserted IDs:', ids) // ['id1', 'id2', 'id3']
insertMultiple is optimized for bulk operations and is significantly faster than multiple insert calls.

Updating Documents

Update by ID

Update an existing document:
import { update } from '@orama/orama'

await update(db, 'prod-12345', {
  title: 'Updated Product Name',
  price: 59.99
})
Updates replace the entire document. Include all fields you want to keep.

Partial Updates

To update only specific fields, retrieve the document first:
import { search, update } from '@orama/orama'

// Get the current document
const result = await search(db, {
  term: '',
  where: { id: 'prod-12345' }
})

const currentDoc = result.hits[0].document

// Update only the price
await update(db, 'prod-12345', {
  ...currentDoc,
  price: 59.99
})

Removing Documents

Remove by ID

Remove a single document:
import { remove } from '@orama/orama'

await remove(db, 'prod-12345')

Bulk Remove

Remove multiple documents:
import { removeMultiple } from '@orama/orama'

const idsToRemove = ['id1', 'id2', 'id3']
await removeMultiple(db, idsToRemove)

Document Storage

Orama uses an internal document store to manage documents efficiently. From components/documents-store.ts:10-14:
interface DocumentsStore {
  sharedInternalDocumentStore: InternalDocumentIDStore
  docs: Record<InternalDocumentID, AnyDocument>
  count: number
}

Internal vs External IDs

Orama maintains two ID systems:
  1. External ID - The string ID you see (custom or auto-generated)
  2. Internal ID - Numeric ID used internally for performance
// External ID: "prod-12345"
// Internal ID: 42 (numeric, used in indexes)
This dual system provides:
  • Human-readable external IDs
  • Fast numeric lookups internally
  • Efficient memory usage

Retrieving Documents

By ID

Get a single document by its ID:
import { getByID } from '@orama/orama'

const doc = await getByID(db, 'prod-12345')
console.log(doc)
// { id: 'prod-12345', title: 'Product', price: 49.99, ... }
Retrieve documents through search:
import { search } from '@orama/orama'

const results = await search(db, {
  term: 'headphones',
  limit: 10
})

for (const hit of results.hits) {
  console.log(hit.id, hit.document)
}

Count Documents

Get the total number of documents:
import { count } from '@orama/orama'

const total = await count(db)
console.log('Total documents:', total)

Document Indexing

When you insert a document, Orama indexes it across multiple data structures:

Text Indexing (Radix Trees)

String fields are tokenized and indexed for full-text search:
// Document
{ title: "Wireless Bluetooth Headphones" }

// Tokenized into
["wireless", "bluetooth", "headphones"]

// Each token indexed in Radix tree for fast lookup

Numeric Indexing (AVL Trees)

Number fields are indexed in balanced AVL trees:
// Enables efficient range queries
where: { price: { gte: 50, lte: 100 } }

Boolean Indexing (Bool Nodes)

Boolean fields are indexed separately:
// Fast filtering
where: { inStock: true }

Enum Indexing (Flat Trees)

Enum fields use flat tree structures:
// Exact match filtering
where: { category: { eq: 'electronics' } }

Vector Indexing

Vector fields are indexed for similarity search:
schema: {
  embedding: 'vector[384]'
}

// Enables semantic search
mode: 'vector',
vector: {
  value: embeddingArray,
  property: 'embedding'
}

Document Hooks

Orama provides hooks to execute code during document operations:

Insert Hooks

const db = await create({
  schema: { /* ... */ },
  components: {
    beforeInsert: async (db, id, doc) => {
      console.log('About to insert:', id)
      // Validate, transform, or log
    },
    afterInsert: async (db, id, doc) => {
      console.log('Inserted:', id)
      // Update external systems, cache, etc.
    }
  }
})

Update Hooks

components: {
  beforeUpdate: async (db, id, doc) => {
    // Pre-update logic
  },
  afterUpdate: async (db, id, doc) => {
    // Post-update logic
  }
}

Remove Hooks

components: {
  beforeRemove: async (db, id, doc) => {
    // Pre-remove logic
  },
  afterRemove: async (db, id, doc) => {
    // Post-remove logic
  }
}

Best Practices

When providing custom IDs, use meaningful identifiers that relate to your domain:
id: 'user-john-doe-123'
id: 'product-electronics-45678'
id: 'order-2024-001'
Always use insertMultiple and removeMultiple for bulk operations:
// ✓ Good
await insertMultiple(db, docs)

// ✗ Bad (slow)
for (const doc of docs) {
  await insert(db, doc)
}
Remember that update replaces the entire document:
// ✓ Good - includes all fields
await update(db, id, {
  ...currentDoc,
  price: 59.99
})

// ✗ Bad - loses other fields
await update(db, id, {
  price: 59.99
})
Optional fields are supported. Documents can omit fields:
schema: {
  title: 'string',
  description: 'string',
  price: 'number'
}

// Valid - description is optional
await insert(db, {
  title: 'Product',
  price: 29.99
  // description omitted
})

Next Steps

Search

Learn how to search your documents

Filters

Filter documents with where clauses

Build docs developers (and LLMs) love