Nearest vectors, filtered, then walked: one record, one call
12 October 2026 · Semurg · 4 min read · Markdown
Verified: every command below ran on a fresh node of r11.78.20261012.bc0d94f (how)
Retrieval rarely stops at "the nearest vectors". You want the nearest ones from the right year, that mention the right word, and then what they are connected to. In most stacks that is a vector database, a filter in application code and a graph query, three systems and two round trips. In Semurg it is one record and one call.
This guide uses vectors you supply yourself, so it needs no embedding model on the node.
A record with everything on it
A hybrid record has an id, a vector, up to seven numeric attribute slots, a text and links to other records, each link with a relation number. Write a few at once:
export SEMURG_API_TOKEN=<your token>
curl -sS http://localhost:4000/v1/query \
-H "Authorization: Bearer $SEMURG_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"op":"hybrid_ingest","d":4,"records":[
{"id": 2097153, "vector": [1.0, 0.0, 0.0, 0.0],
"attrs": [{"slot": 0, "f16": 2016, "f24": 3}],
"text": "deep learning for graphs", "links": [{"dst": 2097154, "rel": 1}]},
{"id": 2097154, "vector": [0.9, 0.1, 0.0, 0.0],
"attrs": [{"slot": 0, "f16": 2018, "f24": 1}],
"text": "graphs on disk", "links": [{"dst": 2097155, "rel": 1}]},
{"id": 2097155, "vector": [0.0, 1.0, 0.0, 0.0],
"attrs": [{"slot": 0, "f16": 2012, "f24": 2}],
"text": "a history of databases", "links": []}]}'Expected: three records written, with how many vector, attribute and link containers that took
Nearest, filtered, then walked
Ask for the nearest records to a vector, keep only those whose text holds a word, and walk the links out from the hits:
export SEMURG_API_TOKEN=<your token>
curl -sS http://localhost:4000/v1/query \
-H "Authorization: Bearer $SEMURG_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"op":"hybrid_query","vector":[1.0,0.0,0.0,0.0],"k":2,"hops":1,"filter":[{"has_word":{"word":"graphs"}}]}'Expected: the nearest records whose text holds "graphs", each as a distance and an id, plus the nodes the walk visited
The answer lists the hits as distance and id pairs, the seeds the walk started from, and how many nodes and links the walk touched. The filters combine: an attribute range on a slot, for example {"attr_range":{"slot":0,"field":16,"lo":2015,"hi":2019}} for the values written as f16 above, a word in the text, or the presence of a link from a given record. A filter that cannot be completed is refused with 409 rather than answered short, and a word filter over records that carry no text is refused rather than answered empty.
Read any node's links
The links a record carries are ordinary edges, and any node's edges can be read directly with neighbour_ids, out, in or both.
Known issue on the published release: neighbour_ids answers absent for the id of a hybrid record, because a hybrid record keeps its parts at ids of their own and has no node at its own id, and the reader looked for one. The links are there: the walk in the query above follows them. The fix is in the release train; until then, walk a hybrid record's links with hybrid_query and hops, as above.
Plain vector search
When all you want is the nearest vectors, vector_top_k takes several query vectors at once as a base64 block of little-endian single-precision floats, row by row, with the number of queries q, the width d and k. It is exact by default; an ef asks for the indexed, approximate-reach answer instead, where the store has an index.
What it does not do today
- These are your vectors. Turning text into vectors on the node needs its embedding model; a node that agrees to downloads installs it once the signed catalogue lists it, and this guide does not need it.
- Reading the links in toward a node (
directionin) depends on the store having the reverse read built; where it does not, the answer sayscould_not_judgeand names why, rather than returning a short list.
When something is refused
422: a malformed body, and the message names the field.409: a filter that could not be completed, or a word filter over records without text.413: too many ids or queries in one call.401: the token is missing or wrong.
Next
- Semurg as a vector database: exact and indexed search on their own.
- Connecting over REST: the token and every route.
How these commands were checked
Our test farm ran each of the 2 command blocks marked runnable, exactly as written, with the answer checked against the expectation printed under it, against a fresh node of the published release r11.78.20261012.bc0d94f. A post is published only when every one of them passed there.