# Work with Chromia through the Python client · Semantic search on Chromia using Vector DB · Learn Chromia

> This lesson shows you how to upload your movie data to your Chromia chain and run semantic vector searches against it with the Postchain Python client.

Source: https://chromia.com/learn/vector-db-movie-demo/code-deep-dive/python-client/

Semantic search on Chromia using Vector DBLesson 14 of 15

1. [01Searching movies semantically on Chromia](https://chromia.com/learn/vector-db-movie-demo/introduction/)

Module 1: Project setup

1. [02Overview](https://chromia.com/learn/vector-db-movie-demo/setup/)
2. [03Choose an embedding model](https://chromia.com/learn/vector-db-movie-demo/setup/embedding-model/)
3. [04Configure your Rell module](https://chromia.com/learn/vector-db-movie-demo/setup/configure.rell/)
4. [05Deploy your Rell module](https://chromia.com/learn/vector-db-movie-demo/setup/deploy-rell-module/)
5. [06Finalize your Python environment](https://chromia.com/learn/vector-db-movie-demo/setup/finalize-python-env/)

Module 2: Running the data pipeline

1. [07Overview](https://chromia.com/learn/vector-db-movie-demo/data-pipeline/)
2. [08Preprocess movie data](https://chromia.com/learn/vector-db-movie-demo/data-pipeline/preprocess/)
3. [09Generate sentence embeddings](https://chromia.com/learn/vector-db-movie-demo/data-pipeline/generate-embeddings/)
4. [10Store vectors on the blockchain](https://chromia.com/learn/vector-db-movie-demo/data-pipeline/upload-vectors/)

1. [11Search the vector database](https://chromia.com/learn/vector-db-movie-demo/search/)

Module 4: A deep dive into the code

1. [12Overview](https://chromia.com/learn/vector-db-movie-demo/code-deep-dive/)
2. [13Link your data to the Vector DB with Rell](https://chromia.com/learn/vector-db-movie-demo/code-deep-dive/rell-interface/)
3. [14Work with Chromia through the Python client](https://chromia.com/learn/vector-db-movie-demo/code-deep-dive/python-client/)

1. [15Use cases and extensions](https://chromia.com/learn/vector-db-movie-demo/use-cases/)

[Learn](https://chromia.com/learn/)[Semantic search on Chromia using Vector DB](https://chromia.com/learn/vector-db-movie-demo/)

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

# Work with Chromia through the Python client

This lesson shows you how to upload your movie data to your Chromia chain and run semantic vector searches against it with the [Postchain Python client](https://docs.chromia.com/build/clients/postchain-clients/python-client/).

The steps are:

* Set up the Python client
* Send movie data to the chain in batches through the `add_movies` operation
* Turn a **user query** into a vector with the **same model** you used for the upload
* Search the chain through **Rell query templates**

Complete [the setup module](https://chromia.com/learn/vector-db-movie-demo/setup/) first if your `.env` file isn't configured or the required dependencies aren't installed.

## 1. Initialize the Python client

Load your environment variables and create a client that points to your deployed chain:

python

```
from postchain_client_py import BlockchainClient
from dotenv import load_dotenv
import os, json

load_dotenv()

async def get_client():
    settings = {
        "node_url_pool": json.loads(os.getenv("NODE_URL_POOL", "[]")),
        "blockchain_rid": os.getenv("BLOCKCHAIN_RID")
    }
    return await BlockchainClient.create(settings)
```

## 2. Upload movies and vectors

You send movie data to the chain through the `add_movies` operation defined in your Rell module. It takes a **list of flat movie structs**, and each struct holds the movie's metadata plus its vector as a string.

A single movie entry looks like this. Every field is a string except `wiki_id`, which is an integer:

python

```
[
    12345,
    "The Matrix",
    "A man discovers reality is a simulation...",
    "1999-03-31",
    "466000000",
    "136",
    "English",
    "USA",
    "Science Fiction, Action",
    "[0.1, 0.2, ...]"
]
```

Send the entries in batches:

python

```
from postchain_client_py.blockchain_client import Operation, Transaction
from secp256k1 import PrivateKey

async def store_batch(client, batch):
    private_key = PrivateKey(bytes.fromhex(os.getenv("PRIV_KEY")), raw=True)
    public_key = private_key.pubkey.serialize()

    operation = Operation(op_name="add_movies", args=[batch])
    transaction = Transaction(
        operations=[operation],
        signers=[public_key],
        blockchain_rid=os.getenv("BLOCKCHAIN_RID")
    )

    signed = await client.sign_transaction(transaction, private_key.private_key)
    receipt = await client.send_transaction(signed, do_status_polling=True)

    if receipt.status != 0:
        raise Exception(f"Upload failed: {receipt.message}")
```

Each entry matches the `movie_input` struct in Rell. Internally, the operation saves both the metadata and the vectors by calling `store_vectors(...)`.

## 3. Run a semantic search

The query template `get_movies_with_distance` in your Rell module returns each matching movie's metadata together with its vector distance.

Call it from Python:

python

```
async def search_movies(query_vector, max_results=10):
    payload = {
        "context": 0,
        "q_vector": query_vector,
        "max_distance": "1.0",
        "max_vectors": max_results,
        "query_template": {"type": "get_movies_with_distance"}
    }
    client = await get_client()
    return await client.query("query_closest_objects", payload)
```

## 4. Filter search results by genre

To restrict results to a genre, call the `get_movies_with_filter` template instead. It behaves the same way but takes one extra argument:

python

```
async def search_with_filter(query_vector, genre, max_results=10):
    payload = {
        "context": 0,
        "q_vector": query_vector,
        "max_distance": "1.0",
        "max_vectors": max_results,
        "query_template": {
            "type": "get_movies_with_filter",
            "args": {"genre_filter": genre}
        }
    }
    client = await get_client()
    return await client.query("query_closest_objects", payload)
```

## 5. Create the query vector

Generate the query vector with the **same model** you chose during preprocessing, for example:

python

```
from sentence_transformers import SentenceTransformer
import os

model = SentenceTransformer(os.getenv("EMBEDDING_MODEL"))

def create_query_vector(prompt):
    raw_vector = model.encode(prompt)
    return "[" + ", ".join(f"{float(v)}" for v in raw_vector) + "]"
```

Ensure model consistency

The embedding model for your queries must be **exactly the same** as the one you used to create the vectors uploaded to Chromia. If the dimensionality or encoding differs, queries either fail or return irrelevant matches.

## 6. Interpret the results

A query returns a list of matching movies. Each item includes the full metadata and a similarity score:

python

```
{
    "movie": {
        "wiki_id": 12345,
        "title": "The Matrix",
        "plot": "...",
        ...
    },
    "distance": "0.042"
}
```

* `movie` holds all the Rell metadata for the matched entry
* `distance` measures how far the stored vector is from your query vector; the smaller the value, the more similar the movie

## Summary

* Call `add_movies` to upload movie metadata and vectors together as a batch operation
* Retrieve movies by vector similarity, optionally narrowed down by genre
* Use the same embedding model throughout to get accurate results

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

[← Previous**Link your data to the Vector DB with Rell**](https://chromia.com/learn/vector-db-movie-demo/code-deep-dive/rell-interface/)[Next →**Use cases and extensions**](https://chromia.com/learn/vector-db-movie-demo/use-cases/)

On this page

* [1. Initialize the Python client](#1-initialize-the-python-client)
* [2. Upload movies and vectors](#2-upload-movies-and-vectors)
* [3. Run a semantic search](#3-run-a-semantic-search)
* [4. Filter search results by genre](#4-filter-search-results-by-genre)
* [5. Create the query vector](#5-create-the-query-vector)
* [6. Interpret the results](#6-interpret-the-results)
* [Summary](#summary)
