Chromia
Start Building

13 / 15Module 4: A deep dive into the codeAdvanced

Link your data to the Vector DB with Rell

This lesson shows you how to wire your movie data into the Vector DB extension with Rell. You will:

  • Keep movie metadata next to its vectors
  • Insert movies and their vectors in batches
  • Retrieve detailed search results through Rell query templates

Each vector is tied to a movie's rowid. You can reuse this pattern for any other domain.

Movie entity structure

The main entity stores the movie metadata you need:

rell
entity movie {    wiki_id: integer;    title: text;    plot: text;    release_date: text;    box_office: text;    runtime: text;    languages: text;    countries: text;    genres: text;}

You use these fields to filter search results and display them.

Adding vectors alongside movies

Each vector points to a movie through the movie's rowid, which is its primary key. The following code adds movies and vectors together in a single operation.

movie_input struct

The client must send movies in this format:

rell
struct movie_input {    wiki_id: integer;    title: text;    plot: text;    release_date: text;    box_office: text;    runtime: text;    languages: text;    countries: text;    genres: text;    vector: text;}

add_movies: batch insert movies and vectors

Rather than sending movies one at a time, call this operation once with a full list of movies and their vectors:

rell
val CONTEXT_MOVIE = 0; // vector context ID for moviesoperation add_movies(movies: list<movie_input>) {    val vector_ids = list<vector_db_data>();    for (movie in movies) {        val created = create movie(            wiki_id = movie.wiki_id,            title = movie.title,            plot = movie.plot,            release_date = movie.release_date,            box_office = movie.box_office,            runtime = movie.runtime,            languages = movie.languages,            countries = movie.countries,            genres = movie.genres         );        vector_ids.add(vector_db_data(movie.vector, created.rowid.to_integer()));    }    store_vectors(CONTEXT_MOVIE, vector_ids);}

All vectors are written in one batch through store_vectors, which improves performance on large datasets.

Vector context

CONTEXT_MOVIE sets the context for this group of vectors.
A context ID marks a separate vector domain. For example, movies, legal documents, and support tickets could each have their own context.

Because of this, you can apply the same embedding model to several kinds of data and keep their results separate.

You choose the integer ID yourself. The demo assigns 0 to movies; in your own project, assign other IDs to other content types.

Querying search results

After your data and vectors are stored on-chain, you can run semantic searches with Rell query templates. Results include similarity scores and can be narrowed with optional filters.

get_movies_with_distance: include similarity score

rell
struct movie_with_distance {    movie: struct<movie>;    distance: decimal;}query get_movies_with_distance(closest_results: list<object_distance>): list<movie_with_distance> {    val closest_result_ids = closest_results @ {} ( @set(rowid(.id)) );    val movies_map = movie @ { .rowid in closest_result_ids } (        @map(.rowid.to_integer(), .to_struct())    );    val results = list<movie_with_distance>();    for (closest_result in closest_results) {        results.add(movie_with_distance(            movies_map[closest_result.id],            closest_result.distance        ));    }    return results;}

For each match, this template returns the complete movie metadata together with its distance from the query vector.

get_movies_with_filter: filter by genre

rell
query get_movies_with_filter(    closest_results: list<object_distance>,    genre_filter: text): list<movie_with_distance> {    val closest_result_ids = closest_results @ {} ( @set(rowid(.id)) );    val movies_map = movie @ { .rowid in closest_result_ids } (        @map(.rowid.to_integer(), .to_struct())    );    val results = list<movie_with_distance>();    for (closest_result in closest_results) {        val m = movies_map[closest_result.id];        if (m.genres.contains(genre_filter)) {            results.add(movie_with_distance(m, closest_result.distance));        }    }    return results;}

Here the results are restricted to one genre. You can change the filter to use other fields, such as release date, language, or runtime.

Summary

  • Movie metadata lives in an entity, and vectors reference it through rowid
  • add_movies inserts movies and vectors in one operation
  • store_vectors writes every vector in a single batch
  • Rell query templates return detailed search results with distance scores and optional filters

With this foundation, you can run semantic vector search on-chain and extend it to your own domain and data.

Written for this site from Chromia’s public course. Code samples are the course’s own.