Drivine Proposition Repository
Graph-backed PropositionRepository over Drivine / Neo4j.
Filtering, ordering, limiting, vector search, and entity (HAS_MENTION) predicates all push into the database via the high-level GraphObjectManager DSL — no whole-store scans. A few operations the DSL can't express drop to hand-written Cypher: the findClusters correlation, the cascade-aware bulk clear (queries/clear_propositions.cypher), the dedup lookup, and the batch re-embed.
Embeddings are derived from Proposition.embeddableValue and owned here, not by the model mapper.
v1 notes:
findClusters runs as a single correlated Cypher statement (one round trip) rather than the interface default's vector query per candidate.
Detached re-saves with changed mentions may leave orphan Mention nodes (load-then-save avoids it).
Constructors
Properties
Whether this backend filters graph edges by source authority on its own.
This backend confines every entity-axis walk to a supplied context in its own Cypher (a p.contextId = $ctx predicate on each hop), so the portable facade routes context-scoped queries straight down here instead of falling back to the proposition-edge path.
Which backend provides this repository. Implementations override to advertise their kind; used to select/flip between in-memory and persistent backends.
Whether this particular instance can really subtract. Implementing the interface is a type-level promise; this is the runtime truth for an implementor whose ability depends on configuration. A caller checks this before trusting the type.
Whether this particular instance can actually answer these queries. Implementing the interface is a type-level promise; this is the runtime truth. A decorator that forwards to a backend it only discovers at construction time reports here whether the backend it got can answer. Callers check this before trusting an empty result.
Whether this particular instance can actually run vector search. Implementing the interface is a type-level promise; this is the runtime truth. A store that can be wired without an embedder (so it satisfies the type but has nothing to embed with) should override this to report whether it was given one. Lets a caller — e.g. PropositionStoreTemplate.supportsVector — distinguish "configured for vectors" from "vector search will quietly return empty".
Functions
Append provenance to a proposition (deduplicated); never removes existing entries.
Delete all propositions in the given context.
Delete all propositions whose context id starts with the given prefix.
Remove all provenance from a proposition.
Get the total count of propositions.
Abstractions of a proposition, pushed down: the default in GraphTraversalCapable scans the whole store, so match on the stored sourceIds list directly — WHERE $propositionId IN p.sourceIds, the same shape as findByGrounding.
Get all propositions.
Get all propositions, optionally guaranteeing loaded provenance. With withProvenance = true every result has its Proposition.provenanceEntries populated (at extra read cost); the default delegates to the lean findAll — fine for backends that always carry provenance (e.g. in-memory).
Find all propositions ordered by effective confidence (highest first). Applies time-based decay to confidence scores.
Find propositions in the given context.
Java-friendly variant of findByContextId that accepts a plain string.
Find propositions created within a time range.
Find propositions from a time range, ordered by effective confidence as of a point in time. Useful for temporal analysis: "What was most confidently true during Q1?"
Find propositions with effective confidence above a threshold.
Find all propositions that mention a specific entity.
Find all propositions grounded by a specific chunk.
Find a proposition by its ID.
Find propositions at or above the specified abstraction level. Level 0 = raw observations, 1+ = abstractions.
Find propositions last touched within a time range. "Touched" means any update — content or administrative — so this anchors on Proposition.lastTouched (the later of contentRevised/metadataRevised), not the decay anchor alone.
Find propositions in contextId with evidence from any revision of sourceKey.
Find propositions in contextId with evidence from exactly ref's source key and revision.
Find all propositions with the given status.
Single correlated statement: select candidates DB-side via query, then within that set run the vector index once per seed using the seed's own embedding, keeping seed.id < m.id so each pair appears once. No N+1 round trips; membership and dedup stay server-side.
All pinned propositions in contextId.
Find propositions in contextId with revisionless evidence whose locator key equals locator's key.
Find propositions similar to the given text using vector similarity.
Find propositions similar to the given text with similarity scores.
Vector similarity search with an additional PropositionQuery filter applied to results.
Find the source propositions that a given proposition was abstracted from. Resolves the proposition's Proposition.sourceIds to actual propositions.
Case-insensitive keyword-overlap probe pushed into the DB. The typed DSL has no case-insensitive CONTAINS and no list-comprehension, and there's no full-text index (see luceneSyntaxNotes), so this drops to hand-written Cypher: size([t IN $tokens WHERE toLower(p.text) CONTAINS t]) > 0, ordered by that overlap then effective confidence. Only the structured filters this statement understands are handled; a base carrying anything else (entity, level, temporal, non-default decay, …) falls back to the portable default, which still pushes base's filters through query.
Native entity neighbourhood: GraphProjectionCypher.neighborhood walks the entity projection in Neo4j and hands back each reachable entity, its shortest hop distance, and the proposition ids on a shortest final hop into it. We only hydrate those via propositions (via the lean view) — the traversal itself never leaves the database. A non-null contextId confines the walk to that context (every hop's proposition must match); null is unscoped. Called directly (bypassing the facade's own ceiling), this bounds the walk at GraphProjectionCypher.MAX_DEPTH.
Same walk as above, but bounded by the caller's own maxDepth ceiling instead of the store's hard cap — this is the overload com.embabel.dice.query.graph.GraphQuery actually calls, so a facade configured with a smaller (or larger) ceiling than GraphProjectionCypher.MAX_DEPTH gets a walk that honors it, clamped at that hard cap.
Native shortest path: GraphProjectionCypher.pathBetween returns the shortest entity sequence (up to GraphProjectionCypher.MAX_DEPTH hops) and the connecting proposition ids; empty when the two entities are unreachable. Same-entity is the trivial one-node path, as in the portable facade. A non-null contextId confines every hop to that context; null is unscoped. Called directly (bypassing the facade's own ceiling), this bounds the walk at GraphProjectionCypher.MAX_DEPTH.
Same walk as above, but bounded by the caller's own maxDepth ceiling instead of the store's hard cap — this is the overload com.embabel.dice.query.graph.GraphQuery actually calls, so a facade configured with a smaller (or larger) ceiling than GraphProjectionCypher.MAX_DEPTH gets a walk that honors it, clamped at that hard cap.
Pin a proposition so it resists reclamation: pinned propositions are skipped by the decay collector and the sweep policy, are decay-exempt in the default status policy, and are not auto-retired by contradiction resolution.
The provenance entries of a proposition, or an empty list if it has none or does not exist.
Resolves the diamond: both PropositionStore and VectorSearchCapable declare query. This explicitly routes to the base store's implementation; concrete stores can still override for backend-level filtering.
Query propositions, optionally guaranteeing loaded provenance. With withProvenance = true every result has its Proposition.provenanceEntries populated (at extra read cost); the default delegates to the lean query. See the provenance read contract on PropositionStore.findAll.
Re-embed every proposition by writing a fresh vector onto each node. Lighter than the interface default (which re-saves the whole view): it SETs only embedding, leaving mentions and other properties untouched. The @VectorIndex-declared index is owned by Drivine, so a same-dimension re-embed needs no index DDL here.
Save with exact-text dedup. Parallel chunk extraction mints the same fact as two propositions with identical (contextId, text) but different ids; a bare MERGE-by-id persists both, leaving duplicate rows. The stripe lock is held across the transaction COMMIT — the find-then-insert runs inside txTemplate, which commits before the lock is released — so a concurrent sibling on the same stripe cannot read pre-commit and slip a duplicate past the existence check.
Save multiple propositions.
Save multiple propositions and report which stored proposition each one landed on.
Take a whole fold off the proposition in one statement, without reading it first.
Take exactly the evidence named by provenanceRefs off propositionId, leaving the rest of its evidence, grounding and source ids alone.
Batch SET in one round trip instead of the SPI default's per-id find-then-save. Runs in its own Propagation.REQUIRES_NEW transaction: callers commonly invoke this from inside a readOnly = true query transaction (e.g. Memory's eager load), which cannot itself take a write. Empty ids is a no-op — no need to open a transaction for nothing.
Clear a proposition's pin, returning it to normal reclamation.
The lineage behind the proposition with the given id, assembled from its durable fields.
Native lineage: read the proposition's own durable fields (provenance, grounding, reinforcement, status, temporal) and resolve its abstraction sources via findSources. A non-null contextId treats a proposition in another context as absent (null), matching the portable facade's scoped lineage. Null when no such proposition exists.