Skip to main content
Version: 1.0 RC1 (Latest)

Collections

Collections represent groups of documents with similar structures, like tables for SQL databases. The world is chaos, but at least there's collections. Every document created in DefraDB belongs to a collection.

A collection has a name (ex. Book) and a number of typed fields (ex. title: String).

Example – Collection "Book"
type Book {
title: String
plot: String
rating: Float
}

Field types

  • Int: Signed 32‐bit integer.
  • Float (alias Float64): Signed double-precision floating-point value.
  • Float32: Signed single-precision floating-point value.
  • String: UTF‐8 character sequence.
  • Boolean: true or false.
  • ID: Unique identifier.
  • DateTime: ISO-8601 time (ex. 2017-07-23T03:46:56.647Z).
  • JSON: JSON data (ex. { privacy: { is: "sexy" } }). Query filters extend to JSON inner properties if the field is indexed.
  • Blob: Hex string (ex. 00FF).
  • List: Array of another type (ex. [String]). Lists can not be nested.

Non-null fields

An exclamation mark ! after a type (ex. Int!) specifies that it should be non-null. Also supported with lists:

  • Int! – Non-null Int.
  • [Int!] – List of non-null Int.
  • [Int]! – Non-null list of (possibly null) Int.
  • [Int!]! – Non-null list of non-null Int.

Default values

Specify a default value for a field with the @default directive. The default value and the field must be of the same data type. UTC_NOW is a special value for the current timestamp.

active: Boolean @default(bool: true)
age: Int @default(int: 0)
status: String @default(string: "draft")
creation: DateTime @default(dateTime: UTC_NOW)
expiry: DateTime @default(dateTime: "2026-06-19T00:00:00Z")
meta: JSON @default(json: "{}")
thumb: Blob @default(blob: "ff0099")

Create collections

Create a collection with the CLI command defradb client collection add.

Create collection "Book"
defradb client collection add '
type Book {
title: String!
plot: String
rating: Float
}
'
Result
[
{
"Name": "Book",
"VersionID": "bafyreiasg2fk5b4jmqzoajyjqhmd2m2jmnzlvqnar6llqs5hu34uyh2tdy",
"CollectionID": "bafyreiasg2fk5b4jmqzoajyjqhmd2m2jmnzlvqnar6llqs5hu34uyh2tdy",
"CollectionSet": null,
"Query": null,
"PreviousVersion": null,
"Fields": [
{
"FieldID": "bafyreihqzhiz3iwro4jozp6kphq4sosg6ccoqcbiaf7rg5dmvea7aux55a",
"Name": "_docID",
"Kind": 1,
"Typ": 0,
"RelationName": null,
"IsPrimary": false,
"DefaultValue": null,
"Size": 0
},
{
"FieldID": "bafyreibxx5wzp4iagt3jifid2r7hfzvbtzp2fuq26vku6t6ptk3ppwgxl4",
"Name": "plot",
"Kind": 11,
"Typ": 1,
"RelationName": null,
"IsPrimary": false,
"DefaultValue": null,
"Size": 0
},
{
"FieldID": "bafyreibbxpehr5radbbkkmsau5uuscoif4dxu6j3ef4by6f445fyx7pl3y",
"Name": "rating",
"Kind": 6,
"Typ": 1,
"RelationName": null,
"IsPrimary": false,
"DefaultValue": null,
"Size": 0
},
{
"FieldID": "bafyreifhl4p32tbcum4353gigaz7cqrribrgxlbzbps7ec24i5ydxoxewm",
"Name": "title",
"Kind": 26,
"Typ": 1,
"RelationName": null,
"IsPrimary": false,
"DefaultValue": null,
"Size": 0
}
],
"Indexes": [],
"EncryptedIndexes": [],
"Policy": null,
"IsActive": true,
"IsMaterialized": true,
"IsBranchable": false,
"IsEmbeddedOnly": false,
"IsPlaceholder": false,
"VectorEmbeddings": []
}
]

Relationships

To create a relationship between two types, define a field having the other side of the relationship as type. The way in which you define relationships depends on their kind:

  • One-to-one – Each document of type A is linked to one document of type B, and viceversa.
  • One-to-many – Each document of type A is linked to one document of type B. Each document of type B is linked to one or more documents of type A.
  • Many-to-many – Each document of type A is linked to one or more documents of type B, and viceversa.

This section shows how to create relationships. For information on how to populate and query them, see Create documents with relationships and Query the database.

note

As all other fields, relationship fields can be null. For example, defining a one-to-one relationship doesn't guarantee that each document of type A will be linked to a document of type B: a document can leave the relationship undefined.

One-to-one

One-to-one relationships are such that each document of type A is linked to one and only one document of type B. In practice, type A defines a field of type B, and type B defines a field of type A.

For example, each Husband is married to one Wife and viceversa. At least disregarding avant-garde polyamorous relationships.

Type "Husband" with 1:1 relationship with "Wife"
type Husband {
name: String
wife: Wife
}

type Wife {
name: String
husband: Husband @primary
}

The type holding the @primary directive stores a direct pointer to the other end of the relationship, resulting in faster queries. @primary fields also get automatically indexed. In the example above, Wife contains a (implicit) field _husbandID, so retrieving a wife's husband is quick. On the other hand, documents of type Husband do not contain any pointer to the relative Wife, so a collection scan is needed to retrieve a husband's wife. Wifes are just harder to find. Which side should have the @primary directive depends on your query patterns.

note

One-to-one relationships are enforced via unique indexes under the hood. The index must not be dropped, or the 1:1 nature of the relationship will not be fulfilled anymore.

warning

There's no validation on the type when creating relationships across documents. It is the client's responsibility to validate that the Wife.husbandID is populated with the docID of a Husband document. It's up to you to marry humans.

One-to-many

One-to-many relationships link one document of type A with one document of type B, but allows documents of type B to be linked to multiple documents of type A. In practice, type A defines a field of type B, whereas type B defines a field of type [A] (list of A).

For example: each book has one author, whereas one person can author multiple books:

Type "Book" with 1:many relationship with "Person"
type Book {
title: String!
author: Person
}

type Person {
name: String!
authoredBooks: [Book]
}
warning

There's no validation on the type when creating relationships across documents. It is the client's responsibility to validate that the Book.authorID is populated with the docID of a Person document.

Many-to-many

Many-to-many relationships link multiple documents of one type to multiple documents of another type, allowing the same on the other side. To create many-to-many relationships, use two one-to-many relationships and a join type.

For example: a student can enroll in many courses, and a course can have many students enrolled:

Type "Student" with many:many relationship with "Course"
type Student {
name: String!
age: Int
enrollment: [Enrollment]
}

type Course {
title: String!
code: String!
enrollment: [Enrollment]
}

type Enrollment { # the join type
student: Student!
course: Course!
}

Multiple relationships of same type

A type defining multiple relationships to the same type requires extra directives to disambiguate their targets. For example, in the scenario in which a book has both an author and a reviewer, the following definitions would be ambiguous:

Invalid – Ambiguous definition of multiple relationships
type Book {
title: String
author: Person
reviewer: Person
}

type Person {
name: String
authoredBooks: [Book]
reviewedBooks: [Book]
}

At query time, the database cannot infer whether Person.authoredBooks is linked to Book.author or Book.reviewer. To clarify which fields should get paired, use the @relation(name: String) directive, coupling each relationship together with the same name:

Valid – Unambiguous definition of multiple relationships
type Book {
title: String
author: Person @relation(name: "author")
reviewer: Person @relation(name: "reviewer")
}

type Person {
name: String
authoredBooks: [Book] @relation(name: "author")
reviewedBooks: [Book] @relation(name: "reviewer")
}

Show collections

To see all collections available on an instance, use the CLI command defradb client collection describe.

Show all collections
defradb client collection describe
tip

Use the --name parameter to restrict the output to a specific collection.

Result
[
{
"Name": "Book",
"VersionID": "bafyreiasg2fk5b4jmqzoajyjqhmd2m2jmnzlvqnar6llqs5hu34uyh2tdy",
"CollectionID": "bafyreiasg2fk5b4jmqzoajyjqhmd2m2jmnzlvqnar6llqs5hu34uyh2tdy",
"CollectionSet": null,
"Query": null,
"PreviousVersion": null,
"Fields": [
{
"FieldID": "bafyreihqzhiz3iwro4jozp6kphq4sosg6ccoqcbiaf7rg5dmvea7aux55a",
"Name": "_docID",
"Kind": 1,
"Typ": 0,
"RelationName": null,
"IsPrimary": false,
"DefaultValue": null,
"Size": 0
},
{
"FieldID": "bafyreibxx5wzp4iagt3jifid2r7hfzvbtzp2fuq26vku6t6ptk3ppwgxl4",
"Name": "plot",
"Kind": 11,
"Typ": 1,
"RelationName": null,
"IsPrimary": false,
"DefaultValue": null,
"Size": 0
},
{
"FieldID": "bafyreibbxpehr5radbbkkmsau5uuscoif4dxu6j3ef4by6f445fyx7pl3y",
"Name": "rating",
"Kind": 6,
"Typ": 1,
"RelationName": null,
"IsPrimary": false,
"DefaultValue": null,
"Size": 0
},
{
"FieldID": "bafyreifhl4p32tbcum4353gigaz7cqrribrgxlbzbps7ec24i5ydxoxewm",
"Name": "title",
"Kind": 26,
"Typ": 1,
"RelationName": null,
"IsPrimary": false,
"DefaultValue": null,
"Size": 0
}
],
"Indexes": [],
"EncryptedIndexes": [],
"Policy": null,
"IsActive": true,
"IsMaterialized": true,
"IsBranchable": false,
"IsEmbeddedOnly": false,
"IsPlaceholder": false,
"VectorEmbeddings": []
}
]

Truncate collections

Truncating a collection means deleting all documents belonging to it, including their histories. It's an irreversible operation that clears the collection's contents entirely.

Truncate a collection with the CLI command defradb client collection truncate.

Truncate collection "Book"
defradb client collection truncate --name Book

Delete collections

The delete command takes one (or more) collection name and erases their schema from the database. Collections linked together through relationships must be deleted together.

Delete a collection with the CLI command defradb client collection delete, providing a comma-separated list of collection names.

Delete collections "Book" and "Person"
defradb client collection delete Book,Person
note

You can only delete empty collections. If a collection has data, or has had data (and thus has history), you need to truncate it first. Deleting documents is not equivalent to truncating the collection.