Package Exports
- tiny-graph-db
- tiny-graph-db/src/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (tiny-graph-db) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
TinyGraphDB
A tiny, no-external-dependencies, disk-based graph database for Node.js with rich query, traversal, batch ops, batch cosine similarity, and semantic filtering.
- Persist node-&-relation graphs in a JSON file
- Query, traverse, mutate, and semantically search graphs in JavaScript
- Cosine similarity search of nodes & edges via vector embeddings for AI/semantic-graph use cases
- Batch and hierarchical traversals, semantic+traditional queries, and stats
- Full API for CRUD, batch, similarity, statistics, import/export, and traversal
Table of Contents
Features
- ✅ Persistent storage
All nodes & edges auto-saved to a JSON file - 🔍 Search: name, metadata, ID, relation endpoints, and semantic/meta comparison
- 🧮 Cosine Similarity queries for embeddings in metadata (nodes or relations)
- 🔄 Graph Traversal, walk/batch from node, relation, or metadata; supports direction/depth/name filters
- ⬇️ Batch update/delete by search criteria (see below)
- 📈 Stats: node count, edge count, average degree
- 🔄 Import/export: snapshot/restore full graph
- ⚡ Fast, super lightweight, perfect for graph semantic search, retrieval-augmented generation, etc.
Installation
npm install tiny-graph-dbQuick Start
const TinyGraphDB = require('tiny-graph-db');
const db = new TinyGraphDB();
// Add nodes with embeddings
const nodeA = db.addNode('Paper A', { type: 'paper', embedding: [0.2, 0.1, 0.5] });
const nodeC = db.addNode('Concept X', { type: 'concept', embedding: [0.25, 0.1, 0.55] });
const nodeP = db.addNode('Author', { type: 'person', embedding: [0.9, 0.8, 0.7] });
const rel1 = db.addRelation('mentions', nodeA.id, nodeC.id, { confidence: 0.92 });
const rel2 = db.addRelation('authored_by', nodeA.id, nodeP.id, { confidence: 1.0 });
// Node search by metadata
console.log('All concepts:', db.searchNodes({ metadata: { type: 'concept' } }));
// Cosine similarity search
const qv = [0.2, 0.1, 0.52];
const similar = db.searchNodesByCosineSimilarity(qv, { threshold: 0.99 });
console.log('Semantically closest nodes:', similar);
// Traverse outgoing links from nodeA up to depth 2
const walk = db.traverseFromNode(nodeA.id, { maxDepth: 2, directions: ['outgoing'] });
console.log('Traversal:', walk);
// Batch update: update all "concept" nodes
db.updateBySearch('node', { metadata: { type: 'concept' } }, { metadata: { reviewed: true } });
// Batch delete: remove all relations with low confidence
db.deleteBySearch('relation', { metadata: { confidence: { lt: 0.95 } } });
// Save (usually auto, but explicit call)
db.flushToDisk();API
Constructor
new TinyGraphDB(filePath?: string)- filePath: Path to JSON file (default:
'./graph_data.json').
Node Operations
| Method | Description | Returns |
|---|---|---|
addNode(name, metadata = {}, flush = true) |
Create node with name/metadata | Node object |
getNode(nodeId) |
Look up node by ID | Node or undefined |
getAllNodes() |
Get all nodes | Node[] |
updateNode(nodeId, {name?, metadata?}) |
Update name/metadata | Updated node |
deleteNode(nodeId) |
Remove node and all its relations | Deleted node object |
deleteBySearch('node', conditions) |
Batch delete by search | Array of removed |
Relation Operations
| Method | Description | Returns |
|---|---|---|
addRelation(name, fromNodeId, toNodeId, metadata = {}, flush = true) |
Create edge between nodes | Relation object |
getRelation(relationId) |
Fetch edge by ID | Relation or undefined |
getAllRelations() |
Get all edges | Relation[] |
updateRelation(relationId, {name?, metadata?}) |
Update name/metadata | Updated relation |
deleteRelation(relationId) |
Remove relation | Deleted relation object |
deleteBySearch('relation', conditions) |
Batch delete by search | Array of removed |
Query & Search
searchNodes(conditions: SearchConditions): Node[]
searchRelations(conditions: SearchConditions): Relation[]conditions:
name: string | RegExp |{ contains: string }id,fromNodeId,toNodeIdmetadata:{ [key]: ... }supports:- equality, comparison:
{ eq, ne, gt, gte, lt, lte, contains, startsWith, endsWith, in } - cosine similarity:
{ cosineSimilarity: { queryEmbedding, threshold } }
- equality, comparison:
cosineSimilarity(top-level):{ queryEmbedding, embeddingKey, threshold }
Cosine Similarity Search
searchNodesByCosineSimilarity(queryEmbedding: number[], options?): Array
searchRelationsByCosineSimilarity(queryEmbedding: number[], options?): Array
cosineSimilarity(vecA: number[], vecB: number[]): numberqueryEmbedding: Numeric vector- Options:
embeddingKey: metadata key for vector (default:'embedding')threshold: similarity threshold (default: 0.5)limit: max results (default: 10)
Example
db.searchNodesByCosineSimilarity([0.1, 0.2, 0.3], { threshold: 0.8, limit: 3 });Graph Traversal
| Method | Description | Returns |
|---|---|---|
traverseFromNode(startNodeId, options) |
Walks from a node, following edges (see below) | Array of [fromNode, relation, toNode] |
traverseFromRelation(startRelationId, maxDepth?) |
Starts traversal from a relation | Same as above |
traverseFromMetadata(metadataConditions, maxDepth?) |
Begins traverse from nodes/relations that match metadata | Same as above |
Options for traverseFromNode:
maxDepth: limit depth (Infinityby default)directions:['outgoing','incoming']relationName: (optional) filter by relation name
Example
db.traverseFromNode(nodeId, { maxDepth: 2, directions: ['outgoing'] });Result: Array of [fromNode, relation, toNode] triplets in visit order.
Batch Update / Delete
Update by search
updateBySearch('node' | 'relation', searchConditions, { name?, metadata? }): Array
// Example:
db.updateBySearch('node', { metadata: { genre: 'sci-fi' } }, { name: 'SF Novel' });Delete by search
deleteBySearch('node' | 'relation', searchConditions): Array
// Example:
db.deleteBySearch('relation', { metadata: { confidence: { lt: 0.9 } } });GraphRAG & Hierarchical Traversal
Hybrid search and traversal for retrieval-augmented-graph (RAG) and LLM flows
searchAndTraverse(queryEmbedding, options?): ArraySupports:
- Cosine similarity search + regular filters, for nodes/relations
- For each initial match, traverses up to N hops, directionally (optionally, end traversal on node only)
- Returns rich hierarchical JSON
Options:
embeddingKey,threshold,limit- see cosine similarityhops: Number of hops to traverse (default: 3)nodeFilters,relationFilters: Additional filterssearchNodes,searchRelations: Whether to include nodes, edges, or bothdirections: e.g.,['outgoing', 'incoming']endOnNode: bool (whether to always finish traversal on nodes)
Example:
const tree = db.searchAndTraverse([0.2, 0.1, 0.5], {
hops: 2,
searchNodes: true,
searchRelations: false,
nodeFilters: { metadata: { type: 'paper' } },
});
console.log(tree);
// Output: array of hierarchical trees, each rooted on an initial (semantic) hit, with outgoing/incoming relations, connected nodes/edges & so forthImport / Export
exportData(): { nodes: Node[], relations: Relation[] }
importData(data: { nodes, relations }): voidExport produces the full graph dataset as JSON-serializable data. Import wipes and loads supplied graph, then persists.
Utility
getNeighbors(nodeId): All neighbor nodes, with edge and direction- Returns: Array of
{ node, relation, direction }
- Returns: Array of
getStats():{ nodeCount, relationCount, avgDegree }flushToDisk(): Explicit save to disk (auto after every mutation unless usingflush = falseparam on add)rebuildNodeRelationsIndex(): Internal; rebuilds edge indices (auto-run after import)
Examples
1. Traditional Search
const book1 = db.addNode('Dune', { genre: 'sci-fi', pages: 412, published: 1965 });
const book2 = db.addNode('Foundation', { genre: 'sci-fi', pages: 255, published: 1951 });
const author1 = db.addNode('Frank Herbert', { nationality: 'US' });
// Find all US authors:
db.searchNodes({ metadata: { nationality: 'US' } });
// Find all books published pre-1960:
db.searchNodes({ metadata: { published: { lt: 1960 } } });2. Cosine Similarity Search
const doc = db.addNode('Graph Vector', { embedding: [0.2, 0.4, 0.6] });
// Find similar to [0.2, 0.41, 0.67]:
db.searchNodesByCosineSimilarity([0.2, 0.41, 0.67], { threshold: 0.95 });3. Traversals
// Walk two hops out from a node
const walk = db.traverseFromNode(doc.id, { maxDepth: 2, directions: ['outgoing'] });
// Start traversal from a relation
const traverseRels = db.traverseFromRelation(rel1.id, 3);
// Traverse from all nodes with type "paper":
db.traverseFromMetadata({ type: 'paper' }, 2);4. Batch Update & Delete
// Tag all "concept" nodes as reviewed
db.updateBySearch('node', { metadata: { type: 'concept' } }, { metadata: { reviewed: true } });
// Delete all weak relations
db.deleteBySearch('relation', { metadata: { confidence: { lt: 0.8 } } });5. Hybrid "search and traverse" (GraphRAG pattern)
// Retrieve node (by semantic match) then its 2-hop subgraph
const rag = db.searchAndTraverse([0.25, 0.1, 0.5], { hops: 2 });
console.log(JSON.stringify(rag, null, 2));6. Utilities
console.log('Stats:', db.getStats());
console.log('Neighbors of nodeA:', db.getNeighbors(nodeA.id));
// Export/import
const json = db.exportData();
db.importData(json);Performance Benchmarks
| Function | Time (ms) | Ops/sec |
|---|---|---|
| getNode() | 0.0001 | 8,473,743 |
| traverseFromNode() | 0.0072 | 138,175 |
| searchNodes() | 0.1728 | 5,787 |
| searchNodesByCosineSimilarity() | 0.3456 | 2,893 |
Run benchmarks: node src/benchmark.js 1000 2000 5 or npm run benchmark -- 1000 2000 5
Contributing
- Fork the repo
- Create a branch:
git checkout -b feat/my-feature - Commit & push, then open a PR
Please file bugs/requests using GitHub Issues.
License
MIT License (see LICENSE)
Built with ♥ by freakynit