Search Modeling
The first step is to define search models as part of your application code. The data conforming to these models is stored as JSON documents and indexed in Tigris to facilitate searches.
You do not need to have all the fields present in your document, defined in the search model, only the fields that you need to be searchable. When you store documents in the search index, we store the entire document including the fields that are not defined in the search model
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 search models can be defined.
Tigris client uses @TigrisSearchIndex("search-index-name")
decorator on a class
to identify a schema (search model) for the index. Requires a string
input as the name of this search index.
export const CATALOG_INDEX_NAME = "catalog";
@TigrisSearchIndex(CATALOG_INDEX_NAME)
class Catalog {
@SearchField()
name: string;
@SearchField()
price: number;
@SearchField()
brand: string;
@SearchField()
labels: string;
@SearchField(TigrisDataTypes.INT32)
popularity: number;
@SearchField()
entryDate: Date;
@SearchField()
available: boolean;
}
For the properties in the class to be made searchable, they need to be annotated
with the @SearchField()
annotation. The data type is determined using
"reflection" if the optional type
parameter is not specified. A @SearchField()
decorator accepts optional parameters to further refine how the document field is indexed.
Option | Type | Default | Usage |
---|---|---|---|
searchIndex | boolean | True | To enable/disable indexing on a field. Any non-indexed field won't be searchable. |
sort | boolean | False | To enable/disable sorting on a field. Not supported for Arrays and Objects without a predefined schema. |
facet | boolean | False | To enable/disable faceting on a field. Not supported for Array of Objects. |
dimensions | number | Required for vector fields | The number of dimensions of the vector embeddings that will be stored. |
elements | TigrisDataTypes | Required for Array fields | The type of the elements in the array. |
id | boolean | False | To have this field's value used as the document ID for documents in the search index. The field must be of string type. |
@TigrisSearchIndex("catalog")
export class Catalog {
@SearchField({ id: true })
catalogId: string;
@SearchField({ sort: true })
name: string;
@SearchField({ sort: true })
price: number;
@SearchField({ facet: true })
brand: string;
@SearchField({ elements: TigrisDataTypes.STRING, facet: true })
@Field({ elements: TigrisDataTypes.STRING })
tags: Array<string>;
@SearchField({ dimensions: 1536 })
vector: number[];
}
Documents that you store in a search index can have fields that are not annotated
with @SearchField()
. These fields are not searchable. A common use-case is
where you have a document with ten fields and you only want five of the fields
to be searchable but would like to store the entire document.
Additionally, it is possible to turn sorting or faceting on or off for the model in the search index at any time without the need to drop the index.
The Tigris TypeScript client repository has more examples for defining complex schemas.
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[];
Indexing Embedded Documents
Tigris supports rich document structures. For example, you can embed related data in a single document. Embedded models allow application to fetch data with fewer queries, thus reducing query activity and increasing efficiency.
Below is an example of embedded model. We first define the ProductItem
type
and then embed it inside the Order
type.
export class ProductItem {
@SearchField()
productId: string;
@SearchField()
quantity: number;
}
@TigrisSearchIndex("orders")
export class Order {
@SearchField()
userId: bigint;
@SearchField()
orderTotal: number;
@SearchField({ elements: ProductItem })
productItems: Array<ProductItem>
}
Arrays in documents
Including arrays in your schema requires elements
option to identify type of elements when using
@SearchField()
decorator.
@TigrisSearchIndex("cities")
export class City {
@SearchField()
name: string;
@SearchField({ elements: TigrisDataTypes.STRING })
neighborhoods: Array<string>;
}
Create a Search Index
Tigris provides an easy to use way to create or modify search indexes.
const client = new Tigris();
const search = client.getSearch();
const catalog = await search.createOrUpdateIndex<Catalog>(
Catalog
);
id
field in a document
Every indexed document in Tigris has an id
field of string
type to uniquely
identify it. The id
can be used to get, replace or delete a document in search
index. id
is immutable.
Tigris assigns an auto-generated id
to each document. However, there might be
cases where you want to supply your own ID, for example to avoid duplicate
documents to be indexed. In such cases you can annotate a field that can uniquely
identify a document with @SearchField({ id: true })
as shown below.
@SearchField({ id: true })
catalogId: string;