The Problem We Solved
You've got 500 documents spread across projects, clients, and years. A contract mentions a vendor. An email references that same vendor. A proposal quotes their pricing. A spreadsheet tracks their performance. These documents are connected—but your tools don't know it.
Most document systems treat files as isolated objects. Search finds keywords. Folders organize by hierarchy. But the real intelligence lives in the relationships: who wrote this? What does it reference? How does it connect to that other document?
We built AiFiler's knowledge graph to answer those questions in real time, without making you wait.
What We Built: The 8 Edge Types
The knowledge graph isn't a single monolithic structure. It's a directed graph where nodes are documents, and edges represent eight distinct relationship types. Each edge type captures a different kind of connection.
The 8 Edge Types:
- REFERENCES — Document A mentions Document B by name or ID
- CITES — Document A quotes or excerpts from Document B
- DEPENDS_ON — Document A requires information from Document B to make sense
- RELATES_TO — Documents share semantic similarity (AI-detected)
- AUTHORED_BY — Document A was created by User B
- TAGGED — Document A is labeled with Tag B
- CONTAINS — Document A has Document B as an attachment or nested file
- TEMPORAL — Document A was created within a time window of Document B
Why eight? Because real work involves multiple kinds of connections. A contract (DEPENDS_ON) pricing from a vendor list. An email (REFERENCES) a project folder. A report (CITES) data from a spreadsheet. An analyst (AUTHORED_BY) multiple related documents. Treating all of these as generic "related" loses information.
The Architecture: From Ingestion to Query
The knowledge graph flows through four stages: ingestion, indexing, storage, and retrieval.
Stage 1: Ingestion & Edge Detection
When you upload a document, AiFiler's file parsing system (lib/ingest/parseFile.ts) extracts text, metadata, and structure. That raw content flows to the intelligence system.
The intent heuristics engine (lib/intentHeuristics.ts) analyzes the document for edge signals:
- Explicit references: Does the document mention other files by name? ("See attached Q3_Budget.xlsx")
- Semantic similarity: Does the content relate to other documents in your workspace?
- Metadata signals: Who created it? When? What tags apply?
- Temporal proximity: Was it created near other documents on the same topic?
This isn't a single pass. The system runs heuristics in parallel:
Document Upload
↓
Parse (text, metadata, structure)
↓
Intent Heuristics (parallel analysis)
├─ Reference detector (REFERENCES, CITES)
├─ Semantic analyzer (RELATES_TO)
├─ Metadata extractor (AUTHORED_BY, TAGGED)
├─ Containment detector (CONTAINS)
└─ Temporal clusterer (TEMPORAL)
↓
Edge candidates → Confidence scoring
↓
Supabase (persistence)
Each edge gets a confidence score. A direct mention ("See Q3_Budget.xlsx") scores 0.95. An AI-detected semantic relationship might score 0.62. We store both—the query layer decides what to trust.
Stage 2: Indexing & Real-Time Updates
Edges don't live in isolation. They're indexed by source document, target document, edge type, and confidence. This allows fast lookups in both directions.
When you query "Show me everything related to the vendor contract," the system can instantly:
- Find all documents this contract REFERENCES
- Find all documents that REFERENCE this contract
- Find documents AUTHORED_BY the same person
- Find documents created in the same time window
We use Supabase's full-text search and composite indexes to keep these queries sub-100ms, even with thousands of edges.
The real trick: incremental updates. When you edit a document, we don't rebuild the entire graph. We:
- Detect what changed (new references? new tags?)
- Compute delta edges (what edges are new? what's obsolete?)
- Upsert only the deltas
- Invalidate downstream caches
This keeps the graph responsive without constant recomputation.
Stage 3: Storage
Edges live in a simple Supabase schema:
edges
├─ id (UUID)
├─ source_document_id (FK)
├─ target_document_id (FK)
├─ edge_type (ENUM: REFERENCES, CITES, DEPENDS_ON, ...)
├─ confidence (0.0-1.0)
├─ metadata (JSONB: { mention_count, positions, ... })
├─ created_at
└─ updated_at
Why JSONB metadata? Because different edge types need different context. A REFERENCES edge stores the positions in the document where the reference appears. A TEMPORAL edge stores the time delta. A RELATES_TO edge stores the semantic score.
We also maintain a reverse index (target → source) for fast "what references this?" queries.
Stage 4: Retrieval & The Universal Router
When you use Universal Command (Ctrl+Shift+A on desktop, or tap the command icon on mobile), you're querying the knowledge graph.
Type "Show me vendor contracts" and the system:
- Parses your intent (
lib/intelligence/universalRouter.ts) - Identifies you want documents with REFERENCES or DEPENDS_ON edges to vendor-related content
- Queries the graph for those edges
- Ranks results by confidence and recency
- Returns them in under 200ms
The intent handlers (lib/intelligence/intentHandlers.ts) — there are 87 of them — each know how to traverse the graph. A handler for "Show me what depends on this" traverses DEPENDS_ON edges. A handler for "Find related documents" traverses RELATES_TO edges.
Why Zero Latency?
"Zero latency" is marketing speak. We don't mean instant. We mean imperceptible delay—under 200ms for 95th percentile queries, even with 10,000+ edges.
We achieve this through three mechanisms:
1. Materialized Views Common queries (like "all documents authored by user X") are pre-computed and cached. When you open the Knowledge view, you're seeing a materialized view of the graph, not a live traversal.
2. SWR with localStorage Prefixing
The browser caches graph results with lib/hooks/useTableData (the legacy name; it works for all matrix data now). When you navigate between documents, the graph data is already in memory.
3. Confidence-Based Filtering We don't return every possible edge. Edges below a confidence threshold (default 0.5) are excluded from the default view. This reduces result set size and query time. You can lower the threshold in settings if you want to see weaker connections.
The Intelligence System Integration
The knowledge graph doesn't exist in isolation. It feeds the action executor (lib/intelligence/actionExecutor.ts), which powers batch operations and AI-assisted workflows.
When you select multiple documents and ask "Group these by vendor," the executor:
- Queries the graph for REFERENCES edges to vendor documents
- Groups the selected documents by which vendor they reference
- Creates a new folder structure or applies tags accordingly
This is why AiFiler can handle batch operations on 100 documents in seconds—the graph pre-computes the relationships.
What This Means For You
Faster discovery. Instead of searching for keywords, you can search for relationships. "Show me everything this client has touched" traverses AUTHORED_BY edges. "What depends on this contract?" traverses DEPENDS_ON edges.
Smarter batch operations. When you select 50 documents, AiFiler can understand their relationships and group, tag, or move them intelligently.
Real context in search. The search parser (lib/searchParser.ts) can now understand intent. When you search "vendor contracts," it's not just keyword matching—it's finding documents that REFERENCE vendor documents and are tagged as contracts.
Less manual organization. The TEMPORAL and RELATES_TO edges mean documents naturally cluster by topic and time. You don't have to force them into rigid folder hierarchies.
The Tradeoffs We Made
We chose breadth over depth. Eight edge types cover 90% of real workflows, but they're not exhaustive. We don't have an "CONTRADICTS" edge type (that would require semantic analysis at scale). We don't have a "SUPERSEDES" edge (that requires domain knowledge).
We also chose confidence over certainty. A RELATES_TO edge might be wrong—the documents might just happen to mention the same industry term. But we show it anyway, with a confidence score, and let you decide. This is better than missing a real connection.
Finally, we chose incremental indexing over batch recomputation. This means the graph is eventually consistent, not strongly consistent. If you edit a document at 3:01pm, the graph might not reflect that change until 3:02pm. For document management, that's acceptable.
What's Next
We're exploring two directions: temporal reasoning (understanding how documents relate across time) and multi-hop queries (finding documents that are three edges away from your search term). Both require more sophisticated indexing, but they'd unlock deeper intelligence.
For now, the eight-edge graph is doing what it was designed to do: connect your documents in ways that matter, without making you wait.
How to Use It Today
Open any document in AiFiler. On the right side, you'll see the Knowledge panel. Scroll to "Related Documents"—those are the RELATES_TO and REFERENCES edges. Click any related document to jump to it.
In Universal Command (Ctrl+Shift+A), try these searches:
- "Show me documents by [person name]" — traverses AUTHORED_BY edges
- "What references this?" — traverses incoming REFERENCES edges
- "Find related documents" — traverses RELATES_TO edges
The graph is always working behind the scenes. The more documents you upload, the richer the connections become.
Enjoyed this article?
Get more articles like this delivered to your inbox. No spam, unsubscribe anytime.