A TypeScript library for managing RDF semantic graphs using SQLite storage. This library provides a simple and efficient way to work with RDF data, supporting operations like importing, exporting, querying, and managing triples across multiple named graphs.
- đź’ľ SQLite Storage: Persistent storage of RDF triples using SQLite
- 📊 Named Graphs: Support for multiple named graphs within the same database
- 🔄 RDF Format Support: Import and export RDF data in Turtle format
- 🔍 Flexible Querying: Query triples by subject, predicate, object, or any combination
- 🎯 Type Safety: Full TypeScript support with comprehensive type definitions
- ✨ High Performance: Optimized SQLite indexes for fast querying
- đź”’ Connection Pooling: Efficient connection management for concurrent operations
- đź’ľ Backup & Restore: Automated backup functionality with restore capabilities
- đź§Ş Well Tested: Comprehensive test coverage with Jest
npm install @emergentbit/tripple-threadimport { GraphManager } from '@emergentbit/tripple-thread';
// Initialize with in-memory database for quick testing
const graph = new GraphManager();
await graph.init();
// Define common prefixes
const ex = 'http://example.org/';
const rdf = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
const rdfs = 'http://www.w3.org/2000/01/rdf-schema#';
const schema = 'http://schema.org/';
// Add some triples about a person
await graph.addTriple({
subject: `${ex}john`,
predicate: `${rdf}type`,
object: `${schema}Person`
});
await graph.addTriple({
subject: `${ex}john`,
predicate: `${schema}name`,
object: '"John Doe"'
});
await graph.addTriple({
subject: `${ex}john`,
predicate: `${schema}email`,
object: '"john@example.org"'
});
// Query all information about John
const results = await graph.query(
`${ex}john`, // subject
undefined, // any predicate
undefined // any object
);
console.log(results);
// Close when done
await graph.close();import { GraphManager } from '@emergentbit/tripple-thread';
const graph = new GraphManager({ dbPath: 'knowledge-base.sqlite' });
await graph.init();
// Add organization data to a specific graph
const orgGraph = 'http://example.org/graphs/organizations';
// Import organization data using Turtle format
const orgData = `
@prefix org: <http://example.org/org/> .
@prefix schema: <http://schema.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
org:emergentbit rdf:type schema:Organization ;
schema:name "EmergentBit" ;
schema:url "https://emergentbit.com" ;
schema:employee org:john .
org:john rdf:type schema:Person ;
schema:name "John Doe" ;
schema:jobTitle "Software Engineer" ;
schema:email "john@emergentbit.com" .
`;
await graph.importFromTurtle(orgData, orgGraph);
// Query all employees
const employees = await graph.query(
undefined, // any subject
'http://schema.org/employee', // predicate
undefined, // any object
orgGraph // from org graph
);
// Export the entire organization graph
const exportedTurtle = await graph.exportToTurtle(orgGraph);// Add data to different graphs for better organization
const peopleGraph = 'http://example.org/graphs/people';
const projectsGraph = 'http://example.org/graphs/projects';
// Add person data
await graph.addTriple({
subject: 'http://example.org/jane',
predicate: 'http://schema.org/name',
object: '"Jane Smith"',
graph: peopleGraph
});
// Add project data
await graph.addTriple({
subject: 'http://example.org/project1',
predicate: 'http://schema.org/name',
object: '"Project Alpha"',
graph: projectsGraph
});
// Link person to project
await graph.addTriple({
subject: 'http://example.org/project1',
predicate: 'http://schema.org/member',
object: 'http://example.org/jane',
graph: projectsGraph
});
// List all available graphs
const graphs = await graph.queryGraphs();
console.log('Available graphs:', graphs);
// Query across multiple graphs
const janeData = await Promise.all([
graph.query('http://example.org/jane', undefined, undefined, peopleGraph),
graph.query(undefined, 'http://schema.org/member', 'http://example.org/jane', projectsGraph)
]);// Initialize with backup enabled
const graph = new GraphManager({
dbPath: 'production.sqlite',
enableBackup: true,
backupPath: './backups',
maxConnections: 5
});
try {
await graph.init();
// Perform some operations
await graph.addTriple({
subject: 'http://example.org/product1',
predicate: 'http://schema.org/name',
object: '"Amazing Product"'
});
// Create timestamped backup
await graph.backup();
} catch (error) {
console.error('Error:', error);
// Restore from most recent backup if needed
const backupFile = './backups/production_20240304_120000.sqlite';
await graph.restoreFromBackup(backupFile);
} finally {
await graph.close();
}// Initialize with connection pool settings
const graph = new GraphManager({
dbPath: 'concurrent.sqlite',
maxConnections: 10
});
await graph.init();
try {
// Prepare batch of operations
const batch = Array(20).fill(null).map((_, i) => ({
subject: `http://example.org/item${i}`,
predicate: 'http://schema.org/position',
object: `"${i}"`
}));
// Execute concurrent operations
await Promise.all(
batch.map(triple => graph.addTriple(triple))
);
// Perform concurrent queries
const results = await Promise.all([
graph.query(undefined, 'http://schema.org/position', undefined),
graph.query('http://example.org/item1', undefined, undefined),
graph.query(undefined, undefined, '"10"')
]);
} finally {
await graph.close();
}interface GraphManagerOptions {
dbPath?: string; // Path to SQLite database (default: ':memory:')
enableBackup?: boolean; // Enable automated backups
backupPath?: string; // Path for backup files
maxConnections?: number; // Max concurrent connections (default: 10)
}async init(): Promise<void>- Initialize database and tablesasync close(): Promise<void>- Close all connections
async addTriple(triple: Triple, graph?: string): Promise<void>async deleteTriple(triple: Triple, graph?: string): Promise<void>async query(subject?: string, predicate?: string, object?: string, graph?: string): Promise<Triple[]>
async queryGraphs(): Promise<string[]>- List all graphsasync importFromTurtle(data: string, graph?: string): Promise<void>async exportToTurtle(graph?: string): Promise<string>
async backup(): Promise<void>async restoreFromBackup(path: string): Promise<void>
interface Triple {
subject: string; // URI of the subject
predicate: string; // URI of the predicate
object: string; // URI or literal value
graph?: string; // Optional graph name
}The library uses custom error types for better error handling:
try {
await graph.addTriple({
subject: 'invalid',
predicate: 'http://example.org/name',
object: '"John"'
});
} catch (error) {
if (error instanceof DatabaseError) {
// Handle database-specific errors
} else if (error instanceof ValidationError) {
// Handle validation errors
}
}-
Connection Management
- Always call
close()when done with the GraphManager - Use connection pooling for concurrent operations
- Consider using a try-finally block
- Always call
-
Data Validation
- URIs should be valid and properly formatted
- Literal values should be properly quoted
- Use appropriate datatype annotations for typed literals
-
Performance
- Use specific queries instead of broad ones when possible
- Batch operations for bulk imports
- Consider using named graphs to partition data
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- GitHub Issues: tripple-thread/issues
- Email: krispy@emergentbit.com
See CHANGELOG.md for a list of changes.
- Built with TypeScript
- Uses better-sqlite3 for SQLite operations
- RDF parsing powered by rdf-parse