5.16 elasticsearch — a search index as a relevance source
An elasticsearch producer joins an Elasticsearch index or alias — self-managed or hosted —
by RELEVANCE: the anchor’s text is the query, each hit becomes a virtual node linked to the
anchor it was found for, and the retrieval provenance rides on the edge. It is the relevance
sibling of sparql/cypher: the realm declares WHICH cluster, index and retrieval; no query
author writes a search body.
- name: contractSearch kind: elasticsearch # alias: elastic index: contracts # an index or alias; wildcards allowed (docs-*) connection: endpoint: "https://cluster.example.org:443" apiKeyEnv: ACME_ES_API_KEY # env var; never a credential in YAML. Omit for an open cluster timeoutSeconds: 30 retrieval: mode: hybrid # hybrid (default) | bm25 | semantic | knn fields: [title, body] # the lexical leg semanticField: body_semantic # a self-embedding field; required by every mode but bm25 rankWindowSize: 50 rankConstant: 60 rerankInferenceId: my-reranker # optional; needs rerankField rerankField: body hits: k: 5 minScore: 0.0 idField: contractNumber # optional: a business key as identity, instead of the document id project: { counterparty: "party.name" } cache: { kind: ttl, seconds: 300 }Query it like any relevance edge, selecting it with via where a target is reachable more than
one way:
MATCH (me:AssistantUser)-[:TRACKS]->(t:ResearchTopic)MATCH (t)-[rel:RELEVANT_TO {via: 'elasticsearch'}]->(d:IndexedDocument)RETURN d.title, rel.score, rel.docId, rel.indexORDER BY rel.score DESCThe contract:
- One search per anchor, composed cluster-side. Hybrid retrieval is a single request the
cluster plans and fuses — not several searches merged afterwards — so a fused ranking is the
cluster’s, and
kis what comes back per anchor. hybridis the default and fuses lexical with semantic by reciprocal rank.bm25is purely lexical and needs no semantic field, no inference and no model access — the mode that runs against any cluster.semanticandknnquery a self-embedding field by TEXT: the index owns the embedding, so nothing is vectorized outside the cluster.- Every hit is citable. Each match carries
score,docId,index,modeandrankon the EDGE, not on the node. So a document relevant to two anchors keeps a distinct score and rank for each — and an answer built on retrieved documents can cite the exact document in the exact index it came from. - Identity is the document id, or a business key you name. With
hits.idFielddeclared, that field IS the identity and a hit lacking it is DROPPED and reported — never silently keyed by the document id instead, which would split one document across two identities. The document id remains available asdocIdprovenance either way. minScorefilters at the source. Fused (RRF) scores are small and not comparable to BM25 scores; leave the floor at 0 forhybridunless you have measured the distribution.- Reranking is opt-in. A declared reranker re-scores the retrieval it wraps. It is the one stage that requires a deployed model, which is why it is off by default.
- Honesty. A failed search — network, HTTP error, unparseable or non-search response — is a
warningsentry plus an empty result, never a silent 0 that reads as “you have no such documents”. A rejected key (HTTP 401) is reported as an authentication problem, distinct from a generic failure. A declaredapiKeyEnvthat is unset is reported BEFORE the cluster is called, because an unset variable and a refused key have different fixes. A retrieval that cannot be composed as declared — a semantic mode with nosemanticField, a reranker with no field — is reported by name as an authoring error rather than degraded to a weaker search that would return plausible, quietly worse results.
Cost every consumer should know: one live search per anchor, against a cluster the realm does not control. A fan-out over many anchors is many searches — bound it with a narrowing match before the hop, and declare a TTL cache for repeated questions.

