neo4j-contrib/neo4j-skills3 files

Neo4j Vector Index Skill

Create and manage Neo4j vector indexes, run vector similarity search (ANN/kNN),

Specification
Skill ID
neo4j-contrib/neo4j-skills/neo4j-vector-index-skill
Publisher
neo4j-contrib
Repository
neo4j-skills
Installs
0
Files
3
Requires
Neo4j >= 2025.01; SEARCH clause requires 2026.01+
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

neo4j-contrib/neo4j-skills/neo4j-vector-index-skillInstalls these files
  • README.md
  • SKILL.md
  • references/hybrid-search.md

What this skill tells the agent

When to Use

  • Creating a vector index (CREATE VECTOR INDEX) on nodes or relationships
  • Running vector similarity / nearest-neighbor search
  • Storing embeddings on graph nodes during ingestion
  • Indexing/querying embeddings already written by GDS algorithms
  • Choosing similarity function, dimensions, HNSW params, or quantization
  • Using SEARCH clause (2026.01+) or db.index.vector.queryNodes() (2025.x)
  • Batch-updating embeddings after model change
  • Combining vector results with immediate graph neighborhood (full retrieval_query pipelines → neo4j-graphrag-skill)
  • Hybrid search that combines vector results with fulltext or other ranked sources

When NOT to Use

  • GraphRAG pipelines (VectorCypherRetriever, HybridCypherRetriever, retrieval_query) → neo4j-graphrag-skill
  • Fulltext-only / keyword-only search (FULLTEXT INDEX, db.index.fulltext.queryNodes) → neo4j-cypher-skill
  • Computing GDS graph embeddings (FastRP, Node2Vec, GraphSAGE) → neo4j-gds-skill
  • Index admin (list all indexes, drop range/text/lookup indexes) → neo4j-cypher-skill

Pre-flight — Determine Version

Drives syntax choice:

CALL dbms.components() YIELD versions RETURN versions[0] AS neo4j_version
VersionUse
2026.01 or higherSEARCH clause (in-index filtering, preferred)
2025.xdb.index.vector.queryNodes() procedure (deprecated 2026.04 — use SEARCH when on 2026.x)

Step 1 — Create Vector Index

Node index (single label):

CYPHER 25
CREATE VECTOR INDEX chunk_embedding IF NOT EXISTS
FOR (c:Chunk) ON (c.embedding)
OPTIONS {
  indexConfig: {
    `vector.dimensions`: 1536,
    `vector.similarity_function`: 'cosine',
    `vector.quantization.type`: 'SCALAR',
    `vector.hnsw.m`: 16,
    `vector.hnsw.ef_construction`: 100
  }
}

Node index with filterable properties [2026.01+] — WITH declares which properties can be used in SEARCH ... WHERE:

CYPHER 25
CREATE VECTOR INDEX chunk_embedding IF NOT EXISTS
FOR (c:Chunk) ON (c.embedding)
WITH [c.source, c.lang, c.published_year]  // stored as metadata; filterable in SEARCH WHERE
OPTIONS { indexConfig: { `vector.dimensions`: 1536, `vector.similarity_function`: 'cosine' } }

Multi-label index with filterable properties [2026.01+]:

CYPHER 25
CREATE VECTOR INDEX doc_embedding IF NOT EXISTS
FOR (n:Document|Article) ON n.embedding
WITH [n.author, n.published_year, n.lang]
OPTIONS { indexConfig: { `vector.dimensions`: 1536, `vector.similarity_function`: 'cosine' } }

Relationship index:

CYPHER 25
CREATE VECTOR INDEX rel_embedding IF NOT EXISTS
FOR ()-[r:HAS_CHUNK]-() ON (r.embedding)
OPTIONS { indexConfig: { `vector.dimensions`: 768, `vector.similarity_function`: 'cosine' } }

`WITH` property types — only scalar types allowed: INTEGER, FLOAT, STRING, BOOLEAN, DATE, ZONED DATETIME, LOCAL DATETIME, ZONED TIME, LOCAL TIME, DURATION. Not allowed: LIST, POINT, or the vector property itself.

Index config reference:

ParameterTypeDefaultNotes
vector.dimensionsINTEGER 1–4096noneRequired; must match embedding model exactly
vector.similarity_functionSTRING'cosine''cosine' or 'euclidean'
vector.quantization.typeSTRING'scalar''none', 'scalar', 'binary' [2026.06+, GA 2026.07]; reduces storage; binary smallest (1 bit per dimension), most aggressive; needs vector-2.0+ (5.18+)
vector.quantization.enabledBOOLEANtrueDeprecated 2026.06 — use vector.quantization.type; false without vector.quantization.type: 'none' fails index creation before 2026.07
vector.default_search_expansion_factorFLOAT 1.0–10000.01.0 none / 1.5 scalar / 3.0 binary (was 2.0 before 2026.07)[2026.06+, GA 2026.07]; value >1.0 on quantized vectors enables automatic rescoring with full-precision vectors (High-Fidelity Quantized search, HFQ); not settable at query time; existing indexes keep their stored value until rebuilt
vector.hnsw.mINTEGER 1–51216HNSW graph connections; higher = better recall, more memory
vector.hnsw.ef_constructionINTEGER 1–3200100Build-time candidates; higher = better recall, slower build

Provider is not settable in Cypher 25 — Neo4j picks the most feature-rich one (vector-2026.06 on 2026.06+, required for binary quantization + rescoring). Check with SHOW VECTOR INDEXES YIELD name, indexProvider. Changing quantization requires dropping and recreating the index.

Unquantized vectors: set vector.quantization.type: 'none' alone — on 2026.06, vector.quantization.enabled: false without it errors (fixed 2026.07).

Similarity function choice:

Use caseFunction
Normalized embeddings (OpenAI, Cohere, Voyage, Google)'cosine'
Unnormalized / raw distance matters'euclidean'

Index providers — latest selected automatically; not specifiable in Cypher 25. Check with SHOW VECTOR INDEXES YIELD name, indexProvider:

ProviderQuantization support
vector-2026.07High-Fidelity Quantized search for scalar and binary
vector-2026.06scalar and binary
vector-2.0 (5.18+)scalar

Changing quantization type or expansion factor requires index re-create + re-population.


Step 2 — Wait for Index ONLINE

Index builds asynchronously — do NOT query until ONLINE:

SHOW VECTOR INDEXES YIELD name, state, populationPercent
WHERE name = 'chunk_embedding'
RETURN name, state, populationPercent

Poll every 5s until state = 'ONLINE' and populationPercent = 100.0. If state = 'FAILED' → stop, check logs.

Shell poll (cypher-shell):

until cypher-shell -u neo4j -p "$NEO4J_PASSWORD" \
  "SHOW VECTOR INDEXES YIELD name, state WHERE name='chunk_embedding' RETURN state" \
  | grep -q ONLINE; do
  sleep 5
done

Step 3 — Ingest Embeddings

Batch UNWIND pattern (use for > 100 nodes — never one-node-per-transaction):

from neo4j import GraphDatabase

driver = GraphDatabase.driver(uri, auth=(user, password))

def embed_batch(texts: list[str]) -> list[list[float]]:
    response = openai_client.embeddings.create(
        model="text-embedding-3-small", input=texts
    )
    return [r.embedding for r in response.data]

def store_embeddings(records: list[dict], batch_size: int = 500):
    expected_dim = 1536  # must match vector.dimensions
    texts = [r["text"] for r in records]
    embeddings = embed_batch(texts)
    for emb in embeddings:
        assert len(emb) == expected_dim, f"Dim mismatch: {len(emb)} != {expected_dim}"
    rows = [{"id": r["id"], "embedding": emb}
            for r, emb in zip(records, embeddings)]
    for i in range(0, len(rows), batch_size):
        driver.execute_query(
            "UNWIND $rows AS row MATCH (c:Chunk {id: row.id}) SET c.embedding = row.embedding",
            rows=rows[i:i+batch_size]
        )

❌ Never create index after embeddings are already stored — always create index first. ✅ Create index → poll ONLINE → ingest embeddings.


Step 4 — Run Vector Search

SEARCH clause (2026.01+, preferred)

CYPHER 25
MATCH (c:Chunk)
  SEARCH c IN (
    VECTOR INDEX chunk_embedding
    FOR $queryEmbedding
    LIMIT 10
  ) SCORE AS score
RETURN c.text, score
ORDER BY score DESC

With in-index filter [2026.01+] — properties must be declared in WITH at index creation:

// Index must have been created with: WITH [c.source, c.lang, c.published_year]
CYPHER 25
MATCH (c:Chunk)
  SEARCH c IN (
    VECTOR INDEX chunk_embedding
    FOR $queryEmbedding
    WHERE c.source = $source AND c.lang = 'en' AND c.published_year >= 2024
    LIMIT 10
  ) SCORE AS score
RETURN c.text, c.source, score
ORDER BY score DESC

Filtering strategy — choose one:

StrategyWhen to useTradeoff

Use this skill in a project

Open any RiverX project, search the Skills panel for neo4j-contrib/neo4j-skills/neo4j-vector-index-skill, and install it in one click.

Use this skill