Type “a heist that goes wrong in a snowy town” and get back real films, with posters, in a few milliseconds. The vector search behind it runs inside MySQL, with no separate vector database.
Movie Finder is the new demo app that ships with MyVector. It searches up to 1,035,695 TMDB movies by meaning, not keywords. One docker compose up gives you the whole thing: MySQL 9.7 with the MyVector component, a loader, and a small web app.
Try it live: https://demo.myvector.online/

We built it to answer the questions people ask us most. Does vector search in MySQL hold up at a million rows? Can I mix it with ordinary WHERE filters? What happens when I insert a new row? The demo shows the answers on the page, with the SQL that ran under every result.
Live: What you can do on the page
Every feature on the page maps to a MyVector capability, and every result panel shows its SQL.
- Describe a movie in your own words and get the nearest films. Ask for “a boy wizard at a school of magic” and Harry Potter comes back first.
- Filter by genre, year, rating and language. These are plain SQL predicates combined with the vector search, and the page tells you which filtered-search path ran and why.
- Rank by similarity alone, or with a small boost for films many people have rated, so the famous match beats an obscure one at almost the same distance. Both are ordinary
ORDER BYexpressions. - More like this: a film’s stored vector becomes the next query, in pure SQL.
- HNSW vs exact: run both side by side and compare speed and recall.
Add a movie: a plain(Not on live demo)INSERT, searchable within about a second, with no index rebuild.- Under the hood: live index details from
myvector_index_status, a bar showing where each query’s time went, and a speed-vs-recall chart that sweepsef_searchfrom 10 to 640 against an exact scan.
How it works

The app embeds only your query text; every movie’s vector already sits in MySQL, so a search is one SQL round trip. The movie vectors come with the dataset, made by nomic-embed-text-v1.5 from each film’s title, tagline, and overview. The app uses the same model locally on the CPU, so there is no API key, and nothing leaves your machine.
New rows need no rebuild. Adding a movie is a plain INSERT; MyVector’s binlog listener picks it up and adds it to the HNSW index, usually within a second.
The SQL
The whole index is declared in a column comment. This is the movies table’s vector column:
embedding VARBINARY(3080) COMMENT
'MYVECTOR COLUMN type=HNSW,dim=768,size=...,M=16,ef=100,dist=Cosine,online=Y,idcol=id,threads=N'
The loader inserts the rows, then builds the HNSW index with one call:
CALL mysql.myvector_index_build('movies.movies.embedding', 'id');
A search turns the query vector into a nearest-first list of ids with myvector_ann_set(), then joins back to the table. JSON_TABLE keeps the order:
SELECT m.title, myvector_distance(m.embedding, @q, 'Cosine') AS distance
FROM (SELECT myvector_ann_set('movies.movies.embedding', 'id', @q,
'nn=10,ef_search=100') AS js) src,
JSON_TABLE(src.js, '$[*]' COLUMNS (rank_no FOR ORDINALITY, id INT PATH '$')) nn
JOIN movies.movies m ON m.id = nn.id
ORDER BY nn.rank_no;
“More like this” needs no embedding at all. It reads a film’s stored vector into the query variable:
SELECT embedding INTO @q FROM movies.movies WHERE id = @movie_id;
Filters pick one of two paths. The app counts each filter on its own index and takes the smallest count as an upper bound:
- 50,000 matches or fewer: it passes the matching keys as the fifth argument of
myvector_ann_set, so HNSW searches only among them. - More than that: it calls
MYVECTOR_ANN_FILTERED, which takes the nearest candidates and keeps those that pass the filter.
Run it yourself
To run your own copy, from a clone of the repository:
cd examples/movie-finder
MOVIES=100k docker compose up # then open http://localhost:8080
The first start downloads the TMDB data, about 7 GB, once. MOVIES picks how many films to load, most-voted first: 10k, 100k (the default) or full. Measured on a 16-core Arm (Neoverse-N1) Linux host with MySQL 9.7.2:
| Step | 10k | 100k | full (1,035,695) |
|---|---|---|---|
| Insert | 7 s | 41 s | 407 s |
| Build the HNSW index (16 threads) | 29 s | 33 s | 545 s |
| HNSW search | 3–5 ms | 4–7 ms | 5–20 ms |
| Exact search (scans every row) | 30 ms | 280 ms | 3 s warm |
| Recall@10, HNSW vs exact | 100% | 100% | 100% |
At a million movies, HNSW answers in 5–20 ms where a full scan takes 3 seconds, with the same top 10. Recall is for the unfiltered test query; a broad genre filter (Drama) at full size gave 80% at ef_search 100, and the page lets you raise ef_search to trade time for recall. For the full profile, give Docker about 12 GB of memory and set MYSQL_BUFFER_POOL=6G.
Running it on a remote server? The page and MySQL listen on localhost only. Forward the port instead of opening it: ssh -N -L 8080:127.0.0.1:8080 <server>.
Version note: the demo uses features newer than the v1.26.9 images: filtered search, MYVECTOR_ANN_FILTERED, per-query ef_search, and online updates that survive a restart. Until the next release ships, the README shows how to build a local image from main.
Data: the movie metadata and posters come from TMDB via a Hugging Face mirror. Your machine downloads it; it is not in the repository or any image. This is a non-commercial demo, not endorsed by TMDB.
Try it and tell us
Try the live demo first. To run your own, start with MOVIES=10k for a quick first look, then go to full to watch a million rows answer in milliseconds. The demo guide has screenshots and the other demos; the Movie Finder README has the full details.
If a query surprises you, or you want a feature the page doesn’t show yet, open an issue on GitHub. A star helps other MySQL users find the project.