DrivinePropositionRepository

class DrivinePropositionRepository(graphObjectManager: <Error class: unknown class>, persistenceManager: <Error class: unknown class>, embeddingService: <Error class: unknown class>, transactionManager: <Error class: unknown class>, vectorIndexName: String = VECTOR_INDEX) : PropositionRepository, GraphQueryCapable, SourceRevisionQueryCapable, ProvenanceSubtractionCapable

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

Link copied to clipboard
constructor(graphObjectManager: <Error class: unknown class>, persistenceManager: <Error class: unknown class>, embeddingService: <Error class: unknown class>, transactionManager: <Error class: unknown class>, vectorIndexName: String = VECTOR_INDEX)

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard

Whether this backend filters graph edges by source authority on its own.

Link copied to clipboard
open override val honorsContextFilter: Boolean

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.

Link copied to clipboard
Link copied to clipboard
open override val storeType: PropositionStoreType

Which backend provides this repository. Implementations override to advertise their kind; used to select/flip between in-memory and persistent backends.

Link copied to clipboard

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.

Link copied to clipboard

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.

Link copied to clipboard

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

Link copied to clipboard
open override fun addProvenance(propositionId: String, entries: List<ProvenanceEntry>): Proposition?

Append provenance to a proposition (deduplicated); never removes existing entries.

Link copied to clipboard
open override fun clearAll(): Int

Delete every proposition.

Link copied to clipboard
open override fun clearByContext(contextId: String): Int

Delete all propositions in the given context.

Link copied to clipboard
open override fun clearByContextPrefix(contextIdPrefix: String): Int

Delete all propositions whose context id starts with the given prefix.

Link copied to clipboard
open override fun clearProvenance(propositionId: String): Proposition?

Remove all provenance from a proposition.

Link copied to clipboard
open override fun count(): Int

Get the total count of propositions.

open override fun count(query: PropositionQuery): Int

Filtered count pushed into the DB: same where block as query, counted server-side instead of materialising and sizing the rows. Non-default decay can't push onto the materialised effectiveConfidence column, so that case falls back to counting query's live-decay result.

Link copied to clipboard
open override fun delete(id: String): Boolean

DELETE_ORPHAN (not DELETE_ALL) so shared :Source nodes survive unless this was their last reference.

Link copied to clipboard
open override fun findAbstractionsOf(propositionId: String): List<Proposition>

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.

Link copied to clipboard
open override fun findAll(): List<Proposition>

Get all propositions.

open override fun findAll(withProvenance: Boolean): List<Proposition>

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).

Link copied to clipboard

Find all propositions ordered by effective confidence (highest first). Applies time-based decay to confidence scores.

Link copied to clipboard
open override fun findByContextId(contextId: <Error class: unknown class>): List<Proposition>

Find propositions in the given context.

Link copied to clipboard
open fun findByContextIdValue(contextIdValue: String): List<Proposition>

Java-friendly variant of findByContextId that accepts a plain string.

Link copied to clipboard

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?"

Link copied to clipboard

Find propositions with effective confidence above a threshold.

Link copied to clipboard
open override fun findByEntity(entityIdentifier: <Error class: unknown class>): List<Proposition>

Find all propositions that mention a specific entity.

Link copied to clipboard
open override fun findByGrounding(chunkId: String): List<Proposition>

Find all propositions grounded by a specific chunk.

Link copied to clipboard
open override fun findById(id: String): Proposition?

Find a proposition by its ID.

Link copied to clipboard
open override fun findByMinLevel(minLevel: Int): List<Proposition>

Find propositions at or above the specified abstraction level. Level 0 = raw observations, 1+ = abstractions.

Link copied to clipboard

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.

Link copied to clipboard
open override fun findBySourceKey(contextIdValue: String, sourceKey: String): List<Proposition>

Find propositions in contextId with evidence from any revision of sourceKey.

Link copied to clipboard
open override fun findBySourceRevision(contextIdValue: String, ref: SourceRevisionRef): List<Proposition>

Find propositions in contextId with evidence from exactly ref's source key and revision.

Link copied to clipboard
open override fun findByStatus(status: PropositionStatus): List<Proposition>

Find all propositions with the given status.

Link copied to clipboard
open override fun findClusters(similarityThreshold: <Error class: unknown class>, topK: Int, query: PropositionQuery): List<<Error class: unknown class><Proposition>>

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.

Link copied to clipboard
open fun findPinned(contextId: <Error class: unknown class>): List<Proposition>

All pinned propositions in contextId.

Link copied to clipboard
open override fun findRevisionlessBySourceLocator(contextIdValue: String, locator: SourceLocator): List<Proposition>

Find propositions in contextId with revisionless evidence whose locator key equals locator's key.

Link copied to clipboard
open fun findSimilar(textSimilaritySearchRequest: <Error class: unknown class>): List<Proposition>

Find propositions similar to the given text using vector similarity.

Link copied to clipboard
open override fun findSimilarWithScores(textSimilaritySearchRequest: <Error class: unknown class>): List<<Error class: unknown class><Proposition>>

Find propositions similar to the given text with similarity scores.

open override fun findSimilarWithScores(textSimilaritySearchRequest: <Error class: unknown class>, query: PropositionQuery): List<<Error class: unknown class><Proposition>>

Vector similarity search with an additional PropositionQuery filter applied to results.

Link copied to clipboard
open fun findSources(proposition: Proposition): List<Proposition>

Find the source propositions that a given proposition was abstracted from. Resolves the proposition's Proposition.sourceIds to actual propositions.

Link copied to clipboard
open override fun keywordOverlap(base: PropositionQuery, tokens: List<String>, limit: Int): List<Proposition>

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.

Link copied to clipboard
open override fun neighborhood(entityId: String, depth: Int): GraphNeighborhood

The entity neighbourhood reachable from entityId within depth hops.

open override fun neighborhood(entityId: String, depth: Int, contextId: <Error class: unknown class>?): GraphNeighborhood

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.

open override fun neighborhood(entityId: String, depth: Int, contextId: <Error class: unknown class>?, maxDepth: Int): GraphNeighborhood

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.

Link copied to clipboard
open override fun pathBetween(entityIdA: String, entityIdB: String): List<GraphPath>

The paths connecting entityIdA to entityIdB.

open override fun pathBetween(entityIdA: String, entityIdB: String, contextId: <Error class: unknown class>?): List<GraphPath>

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.

open override fun pathBetween(entityIdA: String, entityIdB: String, contextId: <Error class: unknown class>?, maxDepth: Int): List<GraphPath>

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.

Link copied to clipboard
open fun pin(id: String): Proposition?

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.

Link copied to clipboard
open override fun provenanceOf(propositionId: String): List<ProvenanceEntry>

The provenance entries of a proposition, or an empty list if it has none or does not exist.

Link copied to clipboard
open override fun query(query: PropositionQuery): List<Proposition>

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.

open override fun query(query: PropositionQuery, withProvenance: Boolean): List<Proposition>

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.

Link copied to clipboard
open override fun reembedAll(): Int

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.

Link copied to clipboard
open override fun save(proposition: Proposition): Proposition

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.

Link copied to clipboard
open fun saveAll(propositions: Collection<Proposition>)

Save multiple propositions.

Link copied to clipboard

Save multiple propositions and report which stored proposition each one landed on.

Link copied to clipboard
open override fun setProvenance(propositionId: String, entries: List<ProvenanceEntry>): Proposition?

Authoritative provenance replace (unlike the append-only save). Wanted evidence is upserted first, edges outside entries are deleted by their storage identity, and only globally unreferenced :Source nodes are pruned. clearProvenance funnels here with an empty list.

Link copied to clipboard
open override fun subtractFoldedEvidence(propositionId: String, provenanceRefs: List<String>, grounding: Collection<String>, sourceIds: Collection<String>): Proposition?

Take a whole fold off the proposition in one statement, without reading it first.

Link copied to clipboard
open fun subtractProvenance(propositionId: String, provenanceRefs: List<String>): Proposition?

Take exactly the evidence named by provenanceRefs off propositionId, leaving the rest of its evidence, grounding and source ids alone.

Link copied to clipboard
open fun supportsType(type: String): Boolean
Link copied to clipboard
open fun <T> textSearch(request: <Error class: unknown class>, clazz: Class<T>): List<<Error class: unknown class><T>>
Link copied to clipboard
open override fun touchAccessed(ids: Collection<String>)

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.

Link copied to clipboard
open fun unpin(id: String): Proposition?

Clear a proposition's pin, returning it to normal reclamation.

Link copied to clipboard
open fun <T> vectorSearch(request: <Error class: unknown class>, clazz: Class<T>): List<<Error class: unknown class><T>>
Link copied to clipboard
open override fun whyExplain(propositionId: String): PropositionLineage?

The lineage behind the proposition with the given id, assembled from its durable fields.

open override fun whyExplain(propositionId: String, contextId: <Error class: unknown class>?): PropositionLineage?

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.