Files
gh-enviodev-envio-plugins-p…/skills/hyperindex-development/references/database-indexes.md
2025-11-29 18:26:05 +08:00

3.8 KiB

Database Indexes & Query Optimization

Optimize query performance with strategic indexing.

Why Indexes Matter

Data Size Without Indexes With Indexes
1,000 rows ~10ms ~1ms
100,000 rows ~500ms ~2ms
1,000,000+ rows 5+ seconds ~5ms

Single-Column Indexes

Use @index directive on frequently queried fields:

type Transaction {
  id: ID!
  userAddress: String! @index
  tokenAddress: String! @index
  amount: BigInt!
  timestamp: BigInt! @index
}

Use when:

  • Frequently filter by a field
  • Sort results by a field
  • Field has many different values (high cardinality)

Composite Indexes

For multi-field queries, use entity-level @index:

type Transfer @index(fields: ["from", "to", "tokenId"]) {
  id: ID!
  from: String! @index
  to: String! @index
  tokenId: BigInt!
  value: BigInt!
  timestamp: BigInt!
}

Creates:

  1. Individual indexes on from and to
  2. Composite index on from + to + tokenId

Use when:

  • Query multiple fields together
  • "Find transfers from X to Y for token Z"

Multiple Composite Indexes

type NFTListing
  @index(fields: ["collection", "status", "price"])
  @index(fields: ["seller", "status"]) {
  id: ID!
  collection: String! @index
  tokenId: BigInt!
  seller: String! @index
  price: BigInt!
  status: String! @index  # "active", "sold", "cancelled"
  createdAt: BigInt! @index
}

Supports:

  • Active listings for collection, sorted by price
  • Listings by seller with status
  • Recently created listings

Automatic Indexes

HyperIndex auto-indexes:

  • All ID fields
  • All @derivedFrom fields

No manual indexing needed for these.

Common Index Patterns

Token Transfers

type TokenTransfer {
  id: ID!
  token_id: String! @index
  from: String! @index
  to: String! @index
  amount: BigInt!
  blockNumber: BigInt! @index
  timestamp: BigInt! @index
}

DEX Swaps

type Swap @index(fields: ["pair", "timestamp"]) {
  id: ID!
  pair_id: String! @index
  sender: String! @index
  amountIn: BigInt!
  amountOut: BigInt!
  timestamp: BigInt! @index
}

User Activity

type UserAction @index(fields: ["user", "actionType", "timestamp"]) {
  id: ID!
  user: String! @index
  actionType: String! @index
  timestamp: BigInt! @index
  amount: BigInt!
}

Performance Tradeoffs

Write Impact

Index Level Write Slowdown Read Speed
No indexes Baseline Slowest
Few targeted 5-10% Fast
Many indexes 15%+ Fastest

Blockchain data is read-heavy - indexes usually worth it.

Storage

  • Each index: 2-10 bytes per row
  • Consider for very large tables (millions+ rows)

Query Optimization Tips

Fetch Only What You Need

# Good
query {
  Transfer(where: { token: { _eq: "0x123" } }, limit: 10) {
    id
    amount
  }
}

# Bad - unnecessary fields
query {
  Transfer(where: { token: { _eq: "0x123" } }, limit: 10) {
    id
    from
    to
    amount
    timestamp
    blockNumber
    transactionHash
    # ... more fields
  }
}

Always Paginate

query {
  Transfer(
    where: { token: { _eq: "0x123" } }
    limit: 20
    offset: 40  # Page 3
  ) {
    id
    amount
  }
}

Filter on Indexed Fields

# Fast - userAddress is indexed
query {
  Transaction(where: { userAddress: { _eq: "0x..." } }) { ... }
}

# Slow - amount is not indexed
query {
  Transaction(where: { amount: { _gt: "1000" } }) { ... }
}

Index Checklist

When designing schema:

  • Index fields used in where clauses
  • Index fields used in order_by
  • Add composite indexes for multi-field queries
  • Consider cardinality (high variety = good index candidate)
  • Don't over-index write-heavy entities
  • Test query performance with realistic data volumes