Skip to main content

Data Modeling

The first step is to define database models as part of your application code. These data models are then converted to appropriate objects, such as collections, on the backend.

Declaring Models

With the Tigris TypeScript client you can define your data models using Decorated classes or Interfaces. The use of Decorators is recommended and is covered in this section. If you prefer to use Interfaces, you can refer to the Interfaces section.

Below we describe how the database models can be defined.

Tigris client uses @TigrisCollection("collection_name") decorator on a class to identify a Collection schema. Requires a string input as the name of this collection.

@TigrisCollection("catalog")
class Catalog {
@PrimaryKey({ order: 1, autoGenerate: true })
id?: string;

@SearchField()
@Field()
name: string;

@Field()
price: number;

@SearchField({ sort: true, facet: true })
@Field()
brand: string;

@SearchField({ facet: true })
@Field({ elements: TigrisDataTypes.STRING })
labels: Array<string>;

@Field(TigrisDataTypes.INT32)
popularity: number;

@Field({ timestamp: "createdAt" })
entryDate: Date;

@Field({ default: false })
available: boolean;
}

Following decorators are available to annotate Class and its properties:

DecoratorDescripton
@TigrisCollection()Annotates a class as Tigris Collection.
@PrimaryKey()Annotates a class propery as Collection's primary key.
@Field()Annotates a class property as Collection's field. The field can be of any type including primitives, classes, objects and arrays.
@SearchField()Annotates a class property as searchable. This field is automatically synchronized in the search index created for the Collection.

@TigrisCollection()

The @TigrisCollection() decorator is used to annotate a class as a collection. It requires a string input as the name of Collection. |

@TigrisCollection("catalog")
export class Catalog {}

@PrimaryKey()

A primary key uniquely identifies a document in the collection and enforces the unique constraint. In the absence of a user-defined primary key, it is auto-generated.

The example below demonstrates how primary key is defined.

The @PrimaryKey() decorator accepts an optional data type as the first parameter and also the following options:

OptionTypeRequiredUsage
orderintegerRequired for collections with multiple primary keysRepresents the order of the field in the primary key.
autogeneratebooleanFalseRepresents that the values for this field will be automatically generated by the Tigris server.
// Model with a single primary key does not need to specify order
@TigrisCollection("catalog")
export class Catalog {
@PrimaryKey({ autogenerate: true })
id?: number;

@Field()
name: string;
}

// Model with multiple primary keys requires order to be specified for each of the fields
@TigrisCollection("catalog")
export class Catalog {
@PrimaryKey({ order: 1, autogenerate: true })
id?: number;

@PrimaryKey({ order: 2 })
name: string;
}

Composite Primary Key

Composite primary keys are also supported but in case of composite keys order of the fields is important. The example below demonstrates how the order of the fields are defined in case of a composite primary key

@TigrisCollection("catalog")
export class Catalog {

@PrimaryKey(TigrisDataTypes.INT32, { order: 1, autogenerate: true })
id?: number;

@PrimaryKey({order: 2})
name: string;
}

@Field()

The @Field() decorator is used to annotate a class property as a field in the collection. The decorater accepts an optional type parameter. It also accepts options to further describe the field.

OptionTypeRequiredUsage
defaultintegerFalseThe default value of the field if no value is specified when writing the document to the collection.
autogeneratebooleanFalseRepresents that the values for this field will be automatically generated by the Tigris server.
maxLengthintegerFalseThe maximum length of the string fields. If a value is inserted that is bigger than the insert operation is rejected.
elementsTigrisDataTypesRequired for Array fieldsThe type of the elements in the array.
timestampstringFalseThis is only applicable to fields of Date type. Can be one of createdAt or updatedAt.
@TigrisCollection("products")
export class Product {
@PrimaryKey({ order: 1, autogenerate: true })
id?: number;

@PrimaryKey({ order: 2 })
name: string;

@Field({ default: 1 })
quantity: number;

@Field({ maxLength: 128, default: "" })
description: string;

@Field({ elements: TigrisDataTypes.STRING })
tags: Array<string>;

@Field({ timestamp: "createdAt" })
createdAt?: Date;

@Field({ timestamp: "updatedAt" })
updatedAt?: Date;
}

@SearchField()

The @SearchField() decorator is used to annotate a class property as a searchable field in the collection. The field is automatically synchronized in the search index created for the Collection.

@TigrisCollection("products")
export class Product {
@PrimaryKey({ order: 1, autogenerate: true })
id?: number;

@SearchField({ sort: true })
@PrimaryKey({ order: 2 })
name: string;

@Field({ default: 1 })
quantity: number;

@SearchField()
@Field({ maxLength: 128, default: "" })
description: string;

@SearchField({ elements: TigrisDataTypes.STRING, facet: true })
@Field({ elements: TigrisDataTypes.STRING })
tags: Array<string>;

@SearchField({ dimensions: 1536 })
vector: number[];
}

A @SearchField() decorator accepts optional parameters to further refine how the document field is indexed.

OptionTypeDefaultUsage
sortbooleanFalseTo enable/disable sorting on a field. Not supported for Arrays and Objects without a predefined schema.
facetbooleanFalseTo enable/disable faceting on a field. Not supported for Array of Objects.
dimensionsnumberRequired for vector fieldsThe number of dimensions of the vector embeddings that will be stored.
elementsTigrisDataTypesRequired for Array fieldsThe type of the elements in the array.
info

Properties not decorated with @Field() or @PrimaryKey() will be ignored in schema. Hence, it is required that a class property be decorated to store it in Collection.

The Tigris TypeScript client repository has more examples for defining complex schemas.

Embedded Data Model

Tigris offers rich documents that enable embedding related data in a single document. Embedded models allow applications to complete database operations with fewer queries or updates, thus reducing query activity and increasing efficiency.

Below is an example of embedded data model. We first define the ProductItem type and then embed it inside the Order type.

export class ProductItem {
@Field()
productId: string;

@Field()
quantity: number;
}

@TigrisCollection("orders")
export class Order {
@PrimaryKey(TigrisDataTypes.INT64, { order: 1, autoGenerate: true })
orderId?: string;

@Field()
userId: bigint;

@Field()
orderTotal: number;

@Field({ elements: ProductItem })
productItems: Array<ProductItem>
}

Storing Vector Embeddings

An embedding is a vector representation of a document. Embeddings are generated by a machine learning model. The vector representation is a dense vector of floating-point numbers that can be used to find similar documents.

To store vector embeddings in Tigris, you need to define a field as follows:

@SearchField({ dimensions: 1536 })
vector: number[];

Arrays in documents

Including arrays in your schema requires elements option to identify type of elements when using @Field() decorator.

@TigrisCollection("cities")
export class City {
@PrimaryKey({ order: 1 })
name: string;

@SearchField({ elements: TigrisDataTypes.STRING, facet: true })
@Field({ elements: TigrisDataTypes.STRING })
neighborhoods: Array<string>;
}

Array of array(s)

When using classes to model your collection with nested arrays, additional depth option is required in @Field() annotation. depth identifies the depth of your nested array. E.g - field of type Array<Array<Array<Array<string>>>> would have depth = 4.

export class Cell {
@Field()
x: number;

@Field()
y: number;

@Field()
value: string;
}

@TigrisCollection("matrices")
export class Matrix {
@PrimaryKey({ order: 1 })
id: string;

@Field({ elements: Cell, depth: 3 })
cells: Array<Array<Array<Cell>>>;
}

Create a Collection

Creates a collection with the model and any new operation will see the changes.

const catalog = await db.createOrUpdateCollection<Catalog>(
Catalog
);

Drop a Collection

Drops a collection with the model and any new operation will see the changes.

await db.dropCollection(Catalog);

List Collections

Lists all the collections in the database.

await db.listCollections();

Drop all Collections

Drops all collections in the database.

await db.dropAllCollections(Catalog);

Taking advantage of Models being classes

The Tigris models are ES6 classes. You can easily add custom instance or class level methods as needed.

@TigrisCollection("author")
export class Author {
@Field()
firstname: string;

@Field()
lastname: string;

static classLevelMethod(): string {
return "foo";
}

instanceLevelMethod(): string {
return "bar";
}

getFullname(): string {
return [this.firstname, this.lastname].join(" ");
}
}