-
Notifications
You must be signed in to change notification settings - Fork 1
features_temporal_graphs
Zeitabhängige Graph-Traversals mit Zeitfiltern.
- 📋 Übersicht
- ✨ Features
- 🚀 Schnellstart
- 📖 Detaillierte Dokumentation
- 💡 Best Practices
- 🔧 Troubleshooting
- 📚 Siehe auch
- 📝 Changelog
Themis' Temporal Graph implementation adds time-awareness to graph edges, enabling historical queries and point-in-time graph traversals. This is critical for tracking relationship evolution, knowledge graph versioning, and time-series network analysis.
Key Features:
-
Temporal Edges: Edges with
valid_fromandvalid_totimestamps - Point-in-Time Queries: Traverse graph as it existed at specific timestamp
- Historical Analysis: Track how relationships changed over time
- Flexible Validity: Unbounded intervals supported (null = forever/since beginning)
- Efficient Filtering: Temporal checks integrated into BFS/Dijkstra algorithms
Temporal Edge Schema:
struct Edge {
std::string id;
std::string _from; // Source node
std::string _to; // Target node
double _weight = 1.0; // Edge weight for pathfinding
// Temporal fields (optional)
std::optional<int64_t> valid_from; // Start of validity (ms since epoch)
std::optional<int64_t> valid_to; // End of validity (ms since epoch)
}Validity Semantics:
-
valid_from = null: Edge valid since beginning of time -
valid_to = null: Edge valid indefinitely into future -
valid_from = T1, valid_to = T2: Edge valid during interval [T1, T2] - Both
null: Edge always valid (eternal)
Temporal Filter Logic:
bool isValid(query_timestamp, valid_from, valid_to) {
if (valid_from.has_value() && query_timestamp < valid_from) {
return false; // Not yet valid
}
if (valid_to.has_value() && query_timestamp > valid_to) {
return false; // No longer valid
}
return true; // Valid at query time
}Breadth-first search with temporal filtering.
Signature:
std::pair<Status, std::vector<std::string>> bfsAtTime(
std::string_view startPk,
int64_t timestamp_ms,
int maxDepth = 3
) const;Parameters:
-
startPk: Starting node primary key -
timestamp_ms: Query timestamp (milliseconds since epoch) -
maxDepth: Maximum traversal depth
Returns:
-
Status: Success/error status -
std::vector<std::string>: Reachable nodes (BFS order)
Example:
// Query: Which nodes could Alice reach in January 2023?
auto [st, nodes] = graph_mgr->bfsAtTime("Alice", 1672531200000, 5);
if (st.ok) {
for (const auto& node : nodes) {
std::cout << "Reachable: " << node << "\n";
}
}Shortest path with temporal filtering.
Signature:
std::pair<Status, PathResult> dijkstraAtTime(
std::string_view startPk,
std::string_view targetPk,
int64_t timestamp_ms
) const;Returns:
-
PathResult.path: Nodes from start to target -
PathResult.totalCost: Total path cost (sum of weights)
Example:
// Query: Shortest path from Alice to CompanyX in 2022?
auto [st, path] = graph_mgr->dijkstraAtTime("Alice", "CompanyX", 1640995200000);
if (st.ok) {
std::cout << "Path cost: " << path.totalCost << "\n";
for (const auto& node : path.path) {
std::cout << node << " -> ";
}
}Planned AQL syntax for temporal queries:
// Find all documents cited by Doc1 in 2022
FOR v IN 1..3 OUTBOUND 'Doc1' citations
FILTER e.valid_from <= @timestamp AND e.valid_to >= @timestamp
RETURN v
// Shortest path at specific time
FOR p IN SHORTEST_PATH 'Alice' TO 'CompanyX' GRAPH employment_graph
FILTER PATH.ALL(e, e.valid_from <= @timestamp AND e.valid_to >= @timestamp)
RETURN p
Test 1: NoFilter_AcceptsAll ✅
- Filter with
timestamp = nullaccepts all edges - Validates unbounded interval support
Test 2: WithTimestamp_FiltersCorrectly ✅
- Edges before
valid_from→ rejected - Edges after
valid_to→ rejected - Edges during validity period → accepted
- Unbounded intervals handled correctly
Test 3: BoundaryConditions ✅
- Query at exact
valid_from→ accepted - Query at exact
valid_to→ accepted - Edge valid only at query time → accepted
Test 4: NoTemporalEdges_ReturnsAllNeighbors ✅
- Graph without temporal constraints behaves normally
- All nodes reachable regardless of timestamp
Test 5: FiltersByValidFrom ✅
- Edge A→B valid from 2022 onwards
- Query at 2021: Only A reachable
- Query at 2023: A→B→C reachable
Test 6: FiltersByValidTo ✅
- Edge A→B valid until 2022
- Query at 2021: Full graph accessible
- Query at 2023: A isolated (edge expired)
Test 7: FiltersByValidRange ✅
- Edge A→B valid from 2021 to 2023
- Query at 2020: A isolated
- Query at 2022: Full graph
- Query at 2024: A isolated
Test 8: MultiplePathsOverTime ✅
- Complex scenario: paths change over time
- Period 2020-2021: A→B→D
- Period 2022-2023: A→C→D
- Period 2024+: Both paths active
Test 9: IsolatedNodeAfterExpiration ✅
- All outgoing edges expire
- Node becomes isolated after expiration time
Test 10: FindsShortestPathAtTime ✅
- Two paths: A→B→D (cost 2) and A→C→D (cost 6)
- Before C→D becomes valid: uses A→B→D
- After both paths valid: still uses shorter path
Test 11: PathChangesOverTime ✅
- Path A→B→D valid 2020-2022 (cost 3)
- Path A→C→D valid 2023+ (cost 2)
- Algorithm correctly switches to cheaper path when available
Test 12: NoPathAtTime ✅
- All edges to target expired
- Returns error: "Kein Pfad gefunden"
Test 13-15: Input Validation ✅
- Empty node names → error
- Negative depth → error
- Proper error messages returned
Test 16: MaxDepthZero_ReturnsOnlyStart ✅
- Depth limit of 0 returns only starting node
Test 17: EmploymentHistory ✅
- Models: Alice worked at CompanyA (2020-2022), CompanyB (2023+)
- Query 2021: Alice→CompanyA
- Query 2023: Alice→CompanyB
Test 18: KnowledgeGraphEvolution ✅
- Document citation network evolves over time
- Citations added/retracted
- Historical queries return correct citation graph state
Track employee-company relationships over time:
// Create temporal employment edge
auto e1 = createTemporalEdge(
"emp1",
"Alice",
"CompanyA",
toTimestamp(2020, 1, 1), // started Jan 2020
toTimestamp(2022, 12, 31) // ended Dec 2022
);
graph_mgr->addEdge(e1);
// Query: Where did Alice work in 2021?
auto [st, nodes] = graph_mgr->bfsAtTime("Alice", toTimestamp(2021, 6, 1), 1);
// Returns: ["Alice", "CompanyA"]Track evolving knowledge and citations:
// Citation retracted in 2023
auto cite = createTemporalEdge(
"cite1",
"Paper1",
"Paper2",
toTimestamp(2020, 1, 1),
toTimestamp(2023, 1, 1) // retracted
);
// Query: What did Paper1 cite in 2021?
auto [st, citations] = graph_mgr->bfsAtTime("Paper1", toTimestamp(2021, 1, 1), 1);
// Returns: ["Paper1", "Paper2"]
// Query: What does Paper1 cite in 2024?
auto [st2, citations2] = graph_mgr->bfsAtTime("Paper1", toTimestamp(2024, 1, 1), 1);
// Returns: ["Paper1"] (citation retracted)Model friendships, follows, and connections over time:
// Alice followed Bob from 2020-2022, then unfollowed
auto follow = createTemporalEdge(
"follow1",
"Alice",
"Bob",
toTimestamp(2020, 1, 1),
toTimestamp(2022, 12, 31)
);
// Query: Who could Alice reach in 2021?
auto [st, network] = graph_mgr->bfsAtTime("Alice", toTimestamp(2021, 1, 1), 2);
// Returns: Alice's network including Bob
// Query: Who can Alice reach in 2023?
auto [st2, network2] = graph_mgr->bfsAtTime("Alice", toTimestamp(2023, 1, 1), 2);
// Returns: Alice's network excluding BobTrack network topology changes:
// Router1 -> Router2 link active 2020-2022
// Router1 -> Router3 link active 2023+
// Query shortest path at different times
auto [st1, path1] = graph_mgr->dijkstraAtTime("Router1", "Server1", toTimestamp(2021, 1, 1));
auto [st2, path2] = graph_mgr->dijkstraAtTime("Router1", "Server1", toTimestamp(2024, 1, 1));
// Paths differ based on network topology at query timeTime Complexity:
- BFS at time: O(V + E * T) where T = edge load time
- Dijkstra at time: O((V + E) * log V * T)
Edge Load Overhead: Each edge requires:
- RocksDB Get (~1-2ms for SSD)
- Deserialization (~0.1ms)
- Temporal filter check (~0.001ms)
Optimization Strategies:
-
Batch Edge Loading:
// Instead of individual Gets, use MultiGet std::vector<std::string> edge_keys; for (auto& adj : adjacency) { edge_keys.push_back(makeGraphEdgeKey(adj.edgeId)); } auto edges = db_.multiGet(edge_keys); // Single batch call
-
Temporal Index:
// Secondary index: valid_at_timestamp -> [edge_ids] // Enables fast "give me all edges valid at time T" Key: "temporal_index:2023-01-01:edge1" -> ""
-
Caching:
// Cache edge validity info (avoid deserialization) struct EdgeValidityCache { std::string edge_id; std::optional<int64_t> valid_from; std::optional<int64_t> valid_to; };
- No Temporal Aggregation: Cannot query "How many times did this edge exist?"
- No Event Streams: Cannot subscribe to temporal changes
- Single Timestamp Queries: No interval queries (e.g., "valid anytime during 2020-2022")
- No Temporal Joins: Cannot correlate temporal patterns across graphs
-
Temporal Range Queries:
// Find all edges that were ever valid during interval auto edges = graph_mgr->getEdgesValidDuring(t_start, t_end);
-
Temporal Aggregations:
// How long was this edge valid? auto duration = graph_mgr->getTotalValidDuration(edge_id);
-
AQL Integration:
FOR v IN 1..3 OUTBOUND 'Doc1' citations OPTIONS {timestamp: @query_time} RETURN v -
Temporal Predicates:
// Find nodes reachable at ANY point during 2022 FOR v IN 1..3 OUTBOUND 'Alice' friends FILTER e.valid_from <= '2022-12-31' AND e.valid_to >= '2022-01-01' RETURN DISTINCT v -
Change Stream:
// Subscribe to temporal edge changes graph_mgr->watchTemporalChanges(callback);
Located in include/index/temporal_graph.h:
struct TemporalFilter {
std::optional<int64_t> timestamp_ms;
bool isValid(std::optional<int64_t> valid_from,
std::optional<int64_t> valid_to) const {
if (!timestamp_ms.has_value()) return true; // No filter
int64_t t = *timestamp_ms;
if (valid_from.has_value() && t < *valid_from) return false;
if (valid_to.has_value() && t > *valid_to) return false;
return true;
}
static TemporalFilter now();
static TemporalFilter at(int64_t timestamp_ms);
static TemporalFilter all(); // No temporal filtering
};Located in src/index/graph_index.cpp:
std::pair<Status, std::vector<std::string>>
GraphIndexManager::bfsAtTime(std::string_view startPk,
int64_t timestamp_ms,
int maxDepth) const {
TemporalFilter filter = TemporalFilter::at(timestamp_ms);
// Standard BFS with temporal edge filtering
for (const auto& info : adjacency) {
// Load edge to check validity
BaseEntity edge = BaseEntity::deserialize(edgeKey, *blob);
std::optional<int64_t> valid_from = edge.getFieldAsInt("valid_from");
std::optional<int64_t> valid_to = edge.getFieldAsInt("valid_to");
if (!filter.isValid(valid_from, valid_to)) {
continue; // Skip invalid edge
}
// Process valid neighbor
// ...
}
}# Run all temporal graph tests
cd build
.\Release\themis_tests.exe --gtest_filter="TemporalGraphTest.*"
# Expected output:
# [==========] Running 18 tests from 1 test suite.
# [ PASSED ] 18 tests.- ✅ TemporalFilter logic (3 tests)
- ✅ BFS temporal traversal (6 tests)
- ✅ Dijkstra shortest path (3 tests)
- ✅ Edge cases & validation (3 tests)
- ✅ Real-world scenarios (3 tests)
Total: 18 tests, 100% passing
No special configuration required - temporal edges work alongside regular edges.
Optional: Temporal Index (Future):
{
"graph": {
"temporal_index": true,
"temporal_cache_size_mb": 256
}
}Sprint B - Temporal Graphs: ✅ PRODUCTION READY
- ✅ Temporal edge support with
valid_from/valid_to - ✅ BFS at time implementation
- ✅ Dijkstra at time implementation
- ✅ 18 comprehensive Google Tests (all passing)
- ✅ Real-world scenario validation
- ✅ Documentation complete
Use Cases Validated:
- Employment history tracking
- Knowledge graph versioning
- Social network evolution
- Infrastructure change management
Next Steps:
- Temporal range queries
- AQL integration
- Temporal aggregations
- Change streams
- Architecture-ACCESS-MODEL-IMPLEMENTATION-SUMMARY
- Architecture-ADR-003-pg-dump-sql-parser
- Architecture-BASEENTITY-PRINCIPLE
- Architecture-CACHE-STORAGE-INTEGRATION
- Architecture-CMAKE-ARCHITECTURE
- Architecture-CMAKE-FLAGS-REFERENCE
- Architecture-CMAKE-MODULAR-ARCHITECTURE
- Architecture-CONCERNS-ARCHITECTURE-DIAGRAM
- Architecture-CONCERNS-IMPLEMENTATION-SUMMARY
- Architecture-CONTENT-MODEL
- Architecture-COPILOT-THEMISDB-GRAPH-RAG-BACKEND-ARCHITECTURE
- Architecture-CRYPTO-AND-KEYS
- Architecture-FEATURE-FLAGS-REFERENCE
- Architecture-GPU-ARCHITECTURE-REVIEW-TEMPLATE
- Architecture-HTTP-SHUTDOWN-HARDENING
- Architecture-MIGRATION-GUIDE-CONCERNS
- Architecture-MIGRATION-GUIDE-v13-v14
- Architecture-MODULARIZATION-GUIDE
- Architecture-MODULAR-ARCHITECTURE-ROADMAP
- Architecture-MODULE-ARCHITECTURE-INDEX
- Architecture-P1D01-ISSMPLUGIN-DESIGN-REVIEW
- Architecture-P1-D01-ISSMPLUGIN-DESIGN-REVIEW
- Architecture-P1-D08-MAMBA-GOVERNANCE-CONTRACT
- Architecture-P1-P2-IMPLEMENTATION-COMPLETION-INDEX
- Architecture-PHASE0-COMPLETION-ASSESSMENT
- Architecture-PHASE3-QUERYENGINE-DI-ARCHITECTURE
- Architecture-PHASE4-INDEX-MANAGER-DI
- Architecture-POSTGRESQL-WIRE-PROTOCOL
- Architecture-QUERYENGINE-IMPLEMENTATION-GUIDE
- Architecture-QUERY-SCHEDULING
- Architecture-RAFT-CONSENSUS-DESIGN
- Architecture-README
- Architecture-README-SSM-HYBRID-IMPLEMENTATION
- Architecture-REFACTORING-SUMMARY
- Architecture-RESOURCE-POOLING
- Architecture-SOURCE-DIRECTORY-GUIDE
- Architecture-THEMIS-CORE-GUIDE
- Architecture-UNIFIED-ACCESS-MODEL
- Architecture-WAL-GRPC-MTLS-CONFIGURATION
- Architecture-WIRE-PROTOCOL-RETRY
- Architecture-boltzmann-observability-draft
- Architecture-experimental-logarithmic-vector-storage
- Architecture-llm-wiki-mvp-adr
- Architecture-rewrite-engine-architecture
- Architecture-rope-api-architecture
- Architecture-ssm-gguf-mamba-status
- Architecture-ssm-hybrid-analysis
- Architecture-ssm-hybrid-rollout-plan
- Architecture-ssm-plugin-interface-design-review
- Architecture-transaction-coordinators
- Architecture-wiki-secondary-index
- Architecture-wire-protocol
- Governance-DISABLED-STUB-POLICY
- Governance-DOCS-PR-POLICY
- Governance-GA-PROMOTION-SIGN-OFF
- Governance-GITHUB-MILESTONES-SETUP
- Governance-MATURITY-CLAIM-VERIFICATION-CHECKLIST
- Governance-MATURITY-EVIDENCE-REGISTRY
- Governance-MERGE-GATE-BOT-CONFIG
- Governance-MERGE-GATE-STATUS-LIVE
- Governance-PHASE3-ENFORCEMENT-RUNBOOK
- Governance-PHASE-1-CLOSURE-REPORT
- Governance-PHASE-CLOSURE-POLICY
- Governance-PHASE-DEPENDENCY-GRAPH
- Governance-PLUGIN-SUBMODULE-ROLLBACK
- Governance-PRODUCTION-READY-2026-DELIVERY-PLAN
- Governance-PR-VERSION-TARGETING
- Governance-PR-VERSION-TARGETING-BACKFILL
- Governance-QUERY-MODULE-STATUS
- Governance-README
- Governance-RELEASE-PROMOTION-GATE-POLICY
- Governance-RELEASE-VALIDATION-CHECKLIST
- Governance-SECURITY-MODULE-5671-EVIDENCE-SUMMARY
- Governance-SHARDING-P6-RESIDUAL-RISK-ACCEPTANCE
- Governance-SOURCECODE-COMPLIANCE-GOVERNANCE
- Governance-UPDATES-DEVELOPMENT-STATUS-SIGN-OFF
- Governance-WAVE-C-IMPLEMENTATION-COMPLETE
- Module-acceleration-Roadmap
- Module-access-model-Roadmap
- Module-ai-Roadmap
- Module-analytics-Roadmap
- Module-api-Roadmap
- Module-aql-Roadmap
- Module-auth-Roadmap
- Module-base-Roadmap
- Module-cache-Roadmap
- Module-cdc-Roadmap
- Module-chaos-Roadmap
- Module-chimera-Roadmap
- Module-config-Roadmap
- Module-content-Roadmap
- Module-core-Roadmap
- Module-distributed-knowledge-Roadmap
- Module-distributed-tensor-Roadmap
- Module-document-Roadmap
- Module-ethics-ai-Roadmap
- Module-evaluation-Roadmap
- Module-execution-Roadmap
- Module-exporters-Roadmap
- Module-failover-Roadmap
- Module-geo-Roadmap
- Module-governance-Roadmap
- Module-gpu-Roadmap
- Module-graph-Roadmap
- Module-image-analysis-Roadmap
- Module-importers-Roadmap
- Module-index-Roadmap
- Module-ingestion-Roadmap
- Module-llama-cpp-Roadmap
- Module-llm-Roadmap
- Module-llm-streaming-Roadmap
- Module-llm-wiki-Roadmap
- Module-maintenance-Roadmap
- Module-metadata-Roadmap
- Module-network-Roadmap
- Module-observability-Roadmap
- Module-onnx-clip-Roadmap
- Module-performance-Roadmap
- Module-plugins-Roadmap
- Module-process-Roadmap
- Module-projects-Roadmap
- Module-prompt-engineering-Roadmap
- Module-query-Roadmap
- Module-rag-Roadmap
- Module-replication-Roadmap
- Module-retrieval-Roadmap
- Module-rpc-grpc-Roadmap
- Module-scheduler-Roadmap
- Module-scraper-Roadmap
- Module-search-Roadmap
- Module-security-Roadmap
- Module-server-Roadmap
- Module-sharding-Roadmap
- Module-stable-diffusion-Roadmap
- Module-storage-Roadmap
- Module-temporal-Roadmap
- Module-tensor-Roadmap
- Module-themis-Roadmap
- Module-timeseries-Roadmap
- Module-toolbox-Roadmap
- Module-training-Roadmap
- Module-transaction-Roadmap
- Module-updates-Roadmap
- Module-user-storage-encrypted-Roadmap
- Module-utils-Roadmap
- Module-vector-search-Roadmap
- Module-voice-Roadmap
- Module-whisper-Roadmap