Multimodal and hybrid queries

ShrikeDB uses a shared entity identity to connect data stored in different modalities. A query can retrieve candidates from a vector index, constrain them with graph relationships, hydrate payloads from KV, or relate observations in SpaceKey without copying each modality into a separate database.

Model one entity across modalities

Choose a stable UID at ingestion time and use it consistently:

Modality Stored representation Join value

Property graph

Node properties and typed relationships

Node UID

RDF

Subject, predicate, and object terms

A stable IRI or mapped entity UID

Vector

Embedding in a named index

Vector entry entity ID

Key-value

JSON, bytes, or application record

KV key derived from the UID

SpaceKey

Timestamped position and optional attributes

Observation entity ID

Lakehouse

Reviewed projection over Parquet columns

Mapped source identifier

The modalities remain independently indexed. Shared identity makes the handoff explicit and avoids fuzzy joins on names or labels.

Vector candidates, graph constraints, payload output

CALL vector.knn("case_notes_v2", $embedding, 80, {ef: 240})
YIELD node AS case, score
MATCH (case:Case)-[:ASSIGNED_TO]->(team:Team)
CALL kv.get("default", case) YIELD value AS case_record
RETURN case, team, score, case_record
LIMIT 20

Execution has three stages: vector candidate retrieval, one-hop graph expansion, and key-value hydration. Use the case UID as the vector entity ID and the public PutKeyValue key. The KV read uses the default namespace and occurs after the row limit. See Multimodal queries for setup.

Graph-first preparation

When graph context determines the query embedding, fetch that context first:

MATCH (account:Account)-[:HAS_PROFILE]->(profile:Profile)
WHERE account.status = "review"
RETURN account, profile.embedding AS embedding
LIMIT 20

Use each returned embedding in a vector search request, then query the resulting UIDs for graph context. This makes the number of vector searches explicit and lets the application bound it before execution.

Query-time derived relationships

Cypher can return correlated evidence without inserting edges. Keep observed records and derived associations distinct in the result:

MATCH (a:Account)-[:SENT]->(p:Payment)<-[:RECEIVED]-(b:Account)
WHERE a <> b AND p.amount >= $threshold
RETURN a, b, p.id AS payment_id, p.amount AS amount

The row records the association and the payment that supports it. Materialize a relationship only through an explicit, reviewed write workflow.

SPARQL CONSTRUCT provides the corresponding result-graph pattern:

PREFIX ex: <https://example.com/schema/>

CONSTRUCT {
  ?a ex:sharedHighValueFlow ?b .
}
WHERE {
  ?payment ex:sender ?a ;
           ex:receiver ?b ;
           ex:amount ?amount .
  FILTER (?amount >= 100000)
}

The constructed triples form the query result graph. They do not modify the stored RDF graph.

Space and time as a candidate source

SpaceKey can find entity observations in a region and time interval. Use the returned entity IDs to request graph context or payloads. A typical application:

  1. queries observations in a bounded region and time range;

  2. groups or correlates returned entity IDs;

  3. requests graph ownership, communication, or transaction context;

  4. returns a result graph describing the derived association.

SpaceKey indexing and graph indexing remain separate. Writing latitude and longitude as node properties does not automatically append a SpaceKey observation. See Geospatial and spatiotemporal indexing for coordinate systems, time bounds, and query surfaces.

RDF and property-graph views

Use SPARQL when the question is naturally expressed through RDF terms, named graphs, optional graph patterns, or ontology vocabulary. Use Cypher when typed relationship traversal, graph analytics procedures, or direct vector/KV composition is central.

An application may expose both views of the same domain. Establish the mapping between an RDF identifier and the graph UID during ingestion, and test round trips with representative identifiers. Do not infer equivalence from display names.

Keep snapshot boundaries coherent

Vector search supports an explicit snapshot version. The vector/graph/KV Cypher composition above reads the current published views and does not offer a historical cross-modal snapshot. For a sequence of direct RPCs, record the version returned by each stage and pass a snapshot version where that API accepts one.

Transactions group supported KV, graph, and vector writes before commit. See Transactions, durability, and snapshots for write and publication behavior.

Control intermediate cardinality

Hybrid plans become expensive when one stage multiplies rows before the next stage narrows them.

  • Bound path lengths and specify relationship types.

  • Filter seed rows before a lateral procedure.

  • Request enough vector candidates for post-filtering, but measure each increase in k.

  • Hydrate KV payloads after structural and similarity filters.

  • Return the fields the client uses rather than entire nodes and payloads.

  • Use PROFILE to identify the stage where rows expand.

Preserve query semantics while tuning. Moving a predicate across OPTIONAL MATCH, changing DISTINCT, or replacing a trail with a walk can change null handling, multiplicity, and path counts.

Design checklist

  1. Select the authoritative identifier and use it in every participating modality.

  2. Version vector indexes by embedding model.

  3. Keep raw observations separate from query-derived associations.

  4. Define whether each step returns candidates, exact matches, or payloads.

  5. Pin or record the snapshot when reproducibility matters.

  6. Measure candidate counts and output counts at every stage.

  7. Compare hybrid results with independent small fixtures before scaling the workload.