Skip to main content
Version: 1.1 (Latest)

Indexes

Indexes allow queries to quickly locate data without having to look through each document in a collection.

By default, every collection has an index on the _docID property and on @primary relationship fields. The @index directive allows you to set up further indexes on selected properties when creating a collection.

DefraDB supports two types of indexes:

Ordered indexes

Syntax

Syntax – Ordered @index directive
@index(ordered: {
name: String,
unique: Boolean,
direction: ORDERING,
includes: [{ field: String, direction: ORDERING }]
})
  • name – Index name.
    Default: concatenation of collection name, field names, direction.
  • unique – Enforce uniqueness constraint (i.e. no two documents can have the same value for the given fields).
    Default: false.
  • direction – Order direction. This affects the default sorting of results when querying documents.
    Valid values: ASC or DESC.
    Default: ASC.
  • includes – List of fields the index is created on (not required when the directive is used on a field).
tip

The syntax @index(kind: ordered) is a shorthand for creating an ordered index with the default values.

Index single fields

To create an index for a specific field, use the @index directive on the field when creating the collection.

Index the "title" property using default values
type Book {
title: String @index(kind: ordered)
}
Index multiple properties, individually, overriding defaults for "name"
type Book {
title: String @index(ordered: {name: "book_title"})
plot: String @index(ordered: {name: "book_plot"})
}
Index a relationship property
type Book {
title: String
author: Person @primary @index(kind: ordered)
}

type Person {
name: String
books: [Book]
}

Unique indexes

An indexed unique field ensures that no two documents have the same value for one field. Multiple documents can have the null value for a unique field.

Index the title property and enforce value uniqueness
type Book {
title: String @index(ordered: {unique: true})
}
note

Unique indexes are used under the hood to enforce one-to-one relationships. The index must not be dropped, or the 1:1 nature of the relationship will not be fulfilled anymore.

Index multiple fields (composite)

To create an index on the combination of multiple fields (composite index), use the @index directive at the collection level.

Index for (genre, author)
type Book @index(ordered: {
includes: [{ field: "genre" }, { field: "author" }]
}) {
genre: String
author: String
}

The order of fields in includes defines the structure of the index. An index defined on fields genre and author gives no performance benefit to queries filtering only on author. Think of a composite index storing references to documents in a hierarchical structure defined by its order:

Fiction/David Foster Wallace/docID1
Fiction/David Foster Wallace/docID2
Fiction/George Orwell/docID3
Fiction/George Orwell/docID4
Physics/Richard Feynman/docID5
...

Although there is a partial benefit to queries filtering only on genre, there is no benefit if a query skips fields.

JSON fields

If a JSON field is indexed, queries can traverse the JSON structure and filter by its inner properties. See Filter documents -> JSON fields.

Scalar types (ex. integers) are normalized to DefraDB types (ex. int64).

Vector indexes

Vector indexes speed up similarity queries. You can create vector indexes on fields of type [Float32!]. Pairing a vector index with the @embedding directive is possible but not required: the index works even if you manually manage the generation of embeddings.

Syntax

Syntax – Vector @index directive
@index(vector: {
name: String,
dimensions: Int!,
alg: String,
hnsw: {
metric: String,
M: Int,
efConstruction: Int,
efSearch: Int
}
})
  • name – Index name.
    Default: concatenation of collection name, field name.
  • dimensions – Number of vector entries.
  • alg – Algorithm backing the vector index.
    Possible values: hnsw (default).
  • hnsw – Configuration map if alg is hnsw.
    • metric – Metric to calculate vector distances.
      Possible values: COSINE (default), EUCLIDEAN, DOT.
    • M – Maximum number of connections per node. Higher values improve graph quality (recall) at the cost of memory and build time. Default: 16.
    • efConstruction – Build-time exploration factor. Higher values improve graph quality (recall) at the cost of build time. Default: 128.
    • efSearch – Query-time exploration factor. Higher values improve graph quality (recall) at the cost of query latency; can be overridden per query. Default: 64.

Examples

Create a vector index on book title and plot
type Book {
title: String!
plot: String
about_v: [Float32!] @index(vector: {dimensions: 768, hnsw: {metric: COSINE}})
}

Index operations

Show indexes

Display available indexes with the CLI command defradb client index list. You can restrict the list to a specific collection with the optional flag --collection <name>.

List indexes for collection "Book"
defradb client index list --collection Book

Delete indexes

Delete an index with the CLI command defradb client index delete.

Delete index "book_plot" from collection "Book"
defradb client index delete --name book_plot --collection Book

Performance considerations

More indexes is not better. The right indexes is better.

An index can improve read performance, but it will decrease write performance, because every document update/create must also update the relevant indexes.

  • Create indexes based on your query patterns. If you are tempted to index every field, remember that there's no free lunch and that the overhead (in storage and write speed) likely outweighs the benefit.
  • Use unique indexes only when necessary. Because they require extra validation, their performance impact is more significant.