Omnigraph
Search

Vector Search

Find semantically similar nodes using nearest-neighbor search over embeddings.

Vector search finds nodes that are semantically similar to a query vector. It uses the current Omnigraph vector index implementation — an ivf_flat index with L2 distance — over a Vector(N) field.

Schema setup

Declare a Vector(N) property with @index to enable vector search. N is the embedding dimension.

node Person {
    name:      String @key
    bio:       String @index
    embedding: Vector(1536) @index
}

node Document {
    title:     String @key
    content:   String
    embedding: Vector(768) @index
}

The @index annotation on a Vector(N) field causes Omnigraph to build the current vector index for that column. Without it, nearest() queries on that field are rejected at compile time.

If you want embeddings to be auto-generated from another field, use @embed(source_property):

node Person {
    name:      String @key
    bio:       String @index
    embedding: Vector(1536) @embed(bio) @index
}

nearest() — vector similarity ranking

nearest(field, vector) appears in the order block and ranks results by nearest-neighbor distance to the query vector. Because nearest() requires a limit clause, pair them together.

query similar_people($vec: Vector(1536)) {
    match {
        $p: Person
    }
    return { $p.name, $p.bio }
    order { nearest($p.embedding, $vec) }
    limit 10
}

Passing vector parameters

Vectors are passed as JSON arrays in the --params argument:

omnigraph read --uri ./my-graph \
    --query queries.gq \
    --name similar_people \
    --params '{"vec": [0.021, -0.003, 0.118]}'

In practice, you generate the query vector from an embedding model and pass it as a parameter.

Via the HTTP API:

curl -X POST http://localhost:8080/read \
    -H "Content-Type: application/json" \
    -d '{
        "query_source": "query similar_people($vec: Vector(1536)) { match { $p: Person } return { $p.name, $p.bio } order { nearest($p.embedding, $vec) } limit 10 }",
        "query_name": "similar_people",
        "params": {"vec": [0.021, -0.003, 0.118]},
        "branch": "main"
    }'

Filtering before ranking

Combine a match filter with nearest() in order to narrow the candidate set before ranking.

query similar_active_people($vec: Vector(1536)) {
    match {
        $p: Person { status: "active" }
    }
    return { $p.name, $p.bio }
    order { nearest($p.embedding, $vec) }
    limit 10
}

This first filters to active people, then ranks the filtered set by vector similarity.

Combining with traversal

Vector search composes with graph traversal. You can find semantically similar nodes and then follow edges to related entities:

query similar_people_at_company($vec: Vector(1536), $company: String) {
    match {
        $p: Person
        $p worksAt $c: Company { name: $company }
    }
    return { $p.name, $p.bio, $c.name }
    order { nearest($p.embedding, $vec) }
    limit 10
}

This finds people who work at a specific company, ranked by how similar their embedding is to the query vector.

On this page