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:
-
queries observations in a bounded region and time range;
-
groups or correlates returned entity IDs;
-
requests graph ownership, communication, or transaction context;
-
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
PROFILEto 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
-
Select the authoritative identifier and use it in every participating modality.
-
Version vector indexes by embedding model.
-
Keep raw observations separate from query-derived associations.
-
Define whether each step returns candidates, exact matches, or payloads.
-
Pin or record the snapshot when reproducibility matters.
-
Measure candidate counts and output counts at every stage.
-
Compare hybrid results with independent small fixtures before scaling the workload.