Bolty.Policy (Bolty v0.4.0)

Copy Markdown View Source

Resolved driver behaviour for a single connection.

Resolved from the negotiated Bolt version (and optionally the HELLO response metadata) at connection time, then stashed on the connection state and threaded into every pack/unpack/message call. Code pattern-matches on policy fields and never reads a Bolt or server version directly.

Policy is an internal distillation of negotiated facts, not a user-facing configuration surface. Users influence policy by passing connection options (e.g. constraining :versions at negotiation).

Summary

Types

Named Cypher features the server understands, beyond what the coarse cypher_5/cypher_25 language selectors imply.

DateTime encoding dialect. Bolty only ever negotiates Bolt 5.0+, which always uses the evolved struct tags (0x49/0x69) — :evolved is the only value.

HELLO wire field name for disabled notification categories/classifications.

Policy fields a caller may assert by hand via the :capabilities connection option — the server-capability flags only.

t()

Functions

Applies caller-asserted capability overrides over a resolved policy.

Types

cypher_feature()

@type cypher_feature() :: :disjoint_by | :vector_search_in_predicate | :vector_hfq

Named Cypher features the server understands, beyond what the coarse cypher_5/cypher_25 language selectors imply.

A curated set, not an exhaustive one: Cypher gains features every calendar release as it converges on GQL, and a boolean struct field per feature would neither scale nor be worth the permanent API surface. Membership is the check (:disjoint_by in policy.cypher_features), so naming a new feature is one atom here plus one row in the docs.

Every member is a Cypher 25 feature: it errors under a CYPHER 5 prefix even on a server that lists it, so gate on cypher_25 as well when emitting an explicit language selector.

  • :disjoint_byDISJOINT BY (expr, …) | AUTO | NONE batch scheduling on CALL { … } IN CONCURRENT TRANSACTIONS, which prevents lock contention in parallel writes (Neo4j ≥ 2026.06).

  • :vector_search_in_predicateIN predicates in vector search filters (SEARCH n IN (VECTOR INDEX … WHERE n.prop IN […] LIMIT …)), Neo4j ≥ 2026.06. Equality filters predate this; only the IN form is new, and on 2026.05 it fails with an internal server error rather than cleanly, so gating matters.
  • :vector_hfq — Hi-Fidelity Quantized vector search: the vector.quantization.type and vector.default_search_expansion_factor index options on CREATE VECTOR INDEX (Neo4j ≥ 2026.06). A preview feature in 2026.06, expected GA in the next release — the availability boundary holds either way, but the option semantics may still move.

datetime()

@type datetime() :: :evolved

DateTime encoding dialect. Bolty only ever negotiates Bolt 5.0+, which always uses the evolved struct tags (0x49/0x69) — :evolved is the only value.

notifications_field()

@type notifications_field() ::
  :notifications_disabled_categories | :notifications_disabled_classifications

HELLO wire field name for disabled notification categories/classifications.

  • :notifications_disabled_categories — Bolt ≤ 5.5
  • :notifications_disabled_classifications — Bolt 5.6+ (spec rename)

overridable()

@type overridable() :: :cypher_5 | :cypher_25 | :dynamic_labels | :cypher_features

Policy fields a caller may assert by hand via the :capabilities connection option — the server-capability flags only.

The wire-level dimensions (datetime, notifications_field, gql_errors, vectors) are negotiated facts about the Bolt connection, not opinions, and overriding one would just corrupt the wire; they are rejected.

t()

@type t() :: %Bolty.Policy{
  cypher_25: boolean(),
  cypher_5: boolean(),
  cypher_features: MapSet.t(cypher_feature()),
  datetime: datetime(),
  dynamic_labels: boolean(),
  gql_errors: boolean(),
  notifications_field: notifications_field(),
  vectors: boolean()
}

Functions

override(policy, capabilities)

@spec override(
  t(),
  keyword()
) :: {:ok, t()} | {:error, Bolty.Error.t()}

Applies caller-asserted capability overrides over a resolved policy.

Bolty infers Cypher capabilities from the HELLO server string, which only works for a server that both is Neo4j and reports its real calendar release. A Bolt server that emulates Cypher 25 without following Neo4j's calendar — or that wears a pinned Neo4j agent string while implementing a different subset — cannot be inferred correctly in either direction, so the caller gets to state the truth instead:

Bolty.start_link(capabilities: [cypher_25: true, cypher_features: [:disjoint_by]])

Each given value replaces the inferred one rather than merging, so an override can withdraw a wrongly-inferred capability as well as add a missing one. Anything not named keeps its inferred value.

Returns {:error, %Bolty.Error{}} for an unknown or non-overridable field, so a typo surfaces at connect rather than silently doing nothing.