Quickstart: Vector index for Virtuoso

Get started with Virtuoso's native vector index in 5 minutes — create a table, build an HNSW-backed index with the bundled granite embedding model, and run your first semantic search.

In this quickstart, you create a table, build a vector index backed by the bundled granite embedding model, and run your first semantic similarity search — all in under five minutes.

Prerequisites

Before you begin, make sure you have:

  • Virtuoso Open Source (single-server) built with the ONNX plugin (--enable-onnxruntime)
  • The vonnx plugin loaded in your virtuoso.ini (the virtuoso.ini that make install writes already loads it)

If you need help building Virtuoso with ONNX support, see the SQL vector index how-to for full build instructions.


Step 1: Verify the bundled model loaded

After starting Virtuoso, confirm the embedding model is registered and its handle is non-null (a null handle means the ONNX model failed to load):

SQL
SELECT model_name, handle
FROM DB.DBA.ONNX_MODELS;

You should see output similar to:

model_namehandle
granite-384-local1

Step 2: Create a table and insert sample data

Create a table with a single-column integer primary key and a text column, then insert a few sample rows:

SQL
CREATE TABLE DB.DBA.DOCS (
  id      INTEGER NOT NULL PRIMARY KEY,
  content LONG VARCHAR
);

INSERT INTO DB.DBA.DOCS VALUES (1, 'How do I Install Virtuoso on Linux?');
INSERT INTO DB.DBA.DOCS VALUES (2, 'How do I install virtuoso on Windows?');
INSERT INTO DB.DBA.DOCS VALUES (3, 'How do I install virtuoso on macOS?');

Step 3: Create a vector index

Create the vector index, binding it to the bundled model:

SQL
CREATE VECTOR INDEX DOCS_VEC
ON DB.DBA.DOCS (content)
USING 'granite-384-local'
WITH (
  dimensions      = 384,
  distance_metric = 'cosine'
);

When this statement returns, the index is built and searchable — existing rows are backfilled inline, so there is no separate loading step.


Step 4: Search with X_SIMILAR and VECTOR_DISTANCE

Now run your first semantic search. Pass the query as plain text — the server embeds it with the index's bound model and searches the HNSW graph for the closest matches:

SQL
SELECT TOP 10 id, content,
       VECTOR_DISTANCE(content, 'how to install') AS distance,
       X_SIMILAR(content, 'how to install')       AS score
FROM DB.DBA.DOCS
ORDER BY VECTOR_DISTANCE(content, 'how to install');

Example output:

idcontentdistancescore
1How do I Install Virtuoso on Linux?0.2290.886
2How do I install virtuoso on Windows?0.2500.875
3How do I install virtuoso on macOS?0.2830.858

All three rows are semantically related to the query, so they score well above the ~0.73 that unrelated text gets with this model (see below).

What about unrelated queries?

Try a query that has nothing to do with the indexed content:

SQL
SELECT TOP 10 id, content,
       VECTOR_DISTANCE(content, 'is the sun yellow') AS distance,
       X_SIMILAR(content, 'is the sun yellow')       AS score
FROM DB.DBA.DOCS
ORDER BY VECTOR_DISTANCE(content, 'is the sun yellow');

Example output:

idcontentdistancescore
2How do I install virtuoso on Windows?0.5300.735
1How do I Install Virtuoso on Linux?0.5300.735
3How do I install virtuoso on macOS?0.5370.731

Unrelated content does not land at a cosine distance of 1.0 with this model: even text with nothing in common sits at a distance of about 0.53 — a cosine similarity of about 0.47, not 0 — which X_SIMILAR reports as a score of about 0.73. Judge relevance by the gap between related and unrelated text (here 0.86–0.89 against ~0.73) rather than against an absolute 1.0, and set a threshold from your own corpus, e.g. WHERE X_SIMILAR(content, '...') > 0.8.


Clean up

When you are done, remove the index and table:

SQL
DROP INDEX DOCS_VEC;
DROP TABLE DB.DBA.DOCS;

Next steps

Reference:

PreviousVector index overview for Virtuoso NextTutorial: Semantic search over OpenLink FAQs with a SPARQL vector index