UNPKG

jsonion

Version:

A lightweight JSON file-based database with nested data access and manipulation capabilities.

558 lines (406 loc) 11.6 kB
# JSONion 🚀 A powerful and intuitive JSON-based database for Node.js with deep nested data access capabilities. ## Features ✨ - 📁 **File-based JSON storage** - Simple and portable - 🎯 **Deep path access** - Easy navigation through nested objects - 🔍 **Advanced search** - Find data at any depth - 📦 **Array operations** - Manipulate nested arrays effortlessly - 🔄 **Auto-save** - Automatic data persistence - 🛡️ **Type-safe** - Full TypeScript support - 🚀 **Zero dependencies** - Lightweight and fast ## Installation ```bash npm install jsonion ``` ## Quick Start ```typescript import { JSONion } from 'jsonion'; // Create or open a database const db = new JSONion('mydata.json'); // Simple operations db.set('user', { name: 'Ahmed', age: 25 }); const user = db.get('user'); // Deep path access db.setPath('user.profile.address.city', 'Casablanca'); const city = db.getPath('user.profile.address.city'); // Returns: 'Casablanca' ``` ## Core API ### Basic Operations ```typescript // Set a value db.set('key', value); // Get a value const value = db.get('key'); // Check if key exists db.has('key'); // true/false // Delete a key db.del('key'); // Get all keys db.keys(); // ['key1', 'key2', ...] // Get all data db.all(); // { key1: value1, key2: value2, ... } // Clear all data db.clear(); // Count entries db.count(); // 10 ``` ## Deep Path Access 🎯 The most powerful feature of JSONion - access deeply nested data with ease! ### Get Path Access nested data using dot notation and array indices: ```typescript // Simple nested object db.setPath('user.name', 'Ahmed'); db.getPath('user.name'); // 'Ahmed' // Deep nesting db.setPath('config.database.connections[0].host', 'localhost'); db.getPath('config.database.connections[0].host'); // 'localhost' // Complex paths db.getPath('users[0].posts[2].comments[5].text'); ``` ### Set Path Create or update values at any depth (automatically creates missing paths): ```typescript // Creates the entire structure if it doesn't exist db.setPath('user.profile.settings.theme', 'dark'); // Works with arrays db.setPath('products[0].price', 99.99); db.setPath('users[0].roles[1]', 'admin'); ``` ### Has Path Check if a path exists: ```typescript db.hasPath('user.profile.verified'); // true/false db.hasPath('config.database.host'); // true/false ``` ### Delete Path Remove data at a specific path: ```typescript db.deletePath('user.oldField'); db.deletePath('settings.deprecated.feature'); ``` ### Update Path Update existing values (only if path exists): ```typescript // Simple update db.updatePath('user.age', 26); // Update with function db.updatePath('product.price', (oldPrice) => oldPrice * 1.1); ``` ## Advanced Search 🔍 ### Find Deep Search for a key-value pair at any level: ```typescript // Find all occurrences of email db.findDeep('email', 'ahmed@test.com'); // Returns: [ // { path: 'users[0].email', value: 'ahmed@test.com' }, // { path: 'contacts[3].email', value: 'ahmed@test.com' } // ] // Find all active statuses db.findDeep('status', 'active'); ``` ### Pluck Path Extract values using wildcards: ```typescript // Get all user names db.pluckPath('users[*].name'); // Returns: ['Ahmed', 'Sara', 'Omar'] // Get all prices db.pluckPath('products[*].price'); // Returns: [29.99, 49.99, 19.99] // Get nested values db.pluckPath('orders[*].user.country'); // Returns: ['MA', 'EG', 'MA'] ``` ### Find In Path Search within arrays with conditions: ```typescript // Find with function db.findInPath('users', (user) => user.age > 18); // Find with object matching db.findInPath('products', { available: true, category: 'electronics' }); // Find with operators db.findInPath('products', { price: { $lt: 100, $gt: 50 }, stock: { $gte: 10 } }); ``` **Supported operators:** - `$lt` - Less than - `$lte` - Less than or equal - `$gt` - Greater than - `$gte` - Greater than or equal - `$ne` - Not equal - `$in` - Value in array - `$nin` - Value not in array ## Array Operations 📦 ### Push To Path Add items to nested arrays: ```typescript // Add a post to user's posts db.pushToPath('user.posts', { title: 'New Post', content: 'Content here', date: new Date() }); // Add a tag db.pushToPath('article.tags', 'javascript'); ``` ### Pull From Path Remove items from arrays: ```typescript // Remove posts with specific ID const removed = db.pullFromPath('user.posts', (post) => post.id === 5); console.log(`Removed ${removed} items`); // Remove old notifications db.pullFromPath('notifications', (n) => n.read === true); ``` ### Update In Path Update specific items in arrays: ```typescript // Update with object condition db.updateInPath('users', { id: 1 }, { status: 'active', lastLogin: new Date() } ); // Update with function condition db.updateInPath('products', (product) => product.stock < 10, { lowStock: true } ); ``` ## Data Manipulation 🔄 ### Merge Path Merge objects without losing existing data: ```typescript // Merge into existing object db.mergePath('user.profile', { bio: 'New bio', avatar: 'avatar.jpg' }); // Keeps existing fields like 'name', 'email', etc. ``` ### Copy Path Copy data from one path to another: ```typescript // Create a copy db.copyPath('template.config', 'user.config'); // Duplicate an item db.copyPath('products[0]', 'products[10]'); ``` ### Move Path Move data (copy + delete original): ```typescript // Rename a path db.movePath('user.oldSettings', 'user.settings'); // Reorganize data db.movePath('temp.data', 'permanent.data'); ``` ## Complete Example ```typescript import { JSONion } from 'jsonion'; const db = new JSONion('blog.json'); // Create a blog post structure db.setPath('posts[0]', { id: 1, title: 'Getting Started with JSONion', author: { name: 'Ahmed', email: 'ahmed@example.com' }, tags: ['javascript', 'database'], comments: [] }); // Add a comment db.pushToPath('posts[0].comments', { user: 'Sara', text: 'Great article!', date: new Date() }); // Find all posts by Ahmed const ahmedPosts = db.findInPath('posts', (post) => post.author.name === 'Ahmed' ); // Get all post titles const titles = db.pluckPath('posts[*].title'); // Update post db.updatePath('posts[0].title', 'JSONion: A Complete Guide'); // Search for email anywhere const emailLocations = db.findDeep('email', 'ahmed@example.com'); // Merge author data db.mergePath('posts[0].author', { bio: 'Software Developer', website: 'ahmed.dev' }); // Always flush before exit db.flush(); ``` ## Database Management ### Manager API ```typescript import { Manager } from 'jsonion'; const manager = new Manager('./databases'); // Create a new database const db = manager.create('users.json', { initialized: true }); // Open existing database const db2 = manager.open('products.json'); // Check if database exists if (manager.exists('config.json')) { // ... } // List all databases const files = manager.list(); // ['users.json', 'products.json'] // Delete a database manager.delete('old.json'); // Rename a database manager.rename('old.json', 'new.json'); // Copy a database manager.copy('template.json', 'new-project.json'); // Get database info const info = manager.info('users.json'); // { path: '...', size: 1024, entries: 50, modified: Date } // Merge multiple databases manager.merge( ['db1.json', 'db2.json', 'db3.json'], 'merged.json' ); ``` ## Advanced Features ### Find with Predicate ```typescript // Find entries matching condition const adults = db.find((value, key) => { return value.age && value.age >= 18; }); // Find entries (returns [key, value] pairs) const entries = db.findEntries((value, key) => value.verified === true); ``` ### Batch Operations ```typescript // Get multiple values const [name, email, age] = db.getMany('name', 'email', 'age'); // Check multiple keys if (db.hasAll('username', 'email', 'password')) { // All keys exist } // Delete multiple keys const deleted = db.deleteMany('temp1', 'temp2', 'cache'); console.log(`Deleted ${deleted} keys`); ``` ### Import/Export ```typescript // Export to JSON string const jsonString = db.export(); // Import from object db.import({ key1: 'value1', key2: 'value2' }, true); // merge // Import from JSON string db.import('{"key": "value"}', false); // replace ``` ## Best Practices 💡 ### 1. Always flush before exiting ```typescript process.on('SIGINT', () => { db.flush(); process.exit(0); }); ``` ### 2. Use path notation for nested data ```typescript // ❌ Bad const user = db.get('user'); user.profile.settings.theme = 'dark'; db.set('user', user); // ✅ Good db.setPath('user.profile.settings.theme', 'dark'); ``` ### 3. Use wildcards for bulk operations ```typescript // Get all values at once const allEmails = db.pluckPath('users[*].email'); // Instead of looping const users = db.get('users'); const emails = users.map(u => u.email); ``` ### 4. Use operators for complex queries ```typescript // Clean and readable db.findInPath('products', { price: { $gte: 10, $lte: 100 }, category: { $in: ['electronics', 'gadgets'] } }); ``` ## API Reference ### Database Class | Method | Description | |--------|-------------| | `set(key, value)` | Set a key-value pair | | `get(key)` | Get a value by key | | `del(key)` | Delete a key | | `has(key)` | Check if key exists | | `keys()` | Get all keys | | `all()` | Get all data | | `clear()` | Clear all data | | `count()` | Count entries | | `update(key, value)` | Update existing key | | `find(predicate)` | Find values matching predicate | | `flush()` | Force save to disk | | `reload()` | Reload from disk | ### Deep Access Methods | Method | Description | |--------|-------------| | `getPath(path)` | Get value at path | | `setPath(path, value)` | Set value at path | | `hasPath(path)` | Check if path exists | | `deletePath(path)` | Delete path | | `updatePath(path, value)` | Update path | | `findDeep(key, value)` | Find key-value at any depth | | `pluckPath(pattern)` | Extract values with wildcards | | `findInPath(path, predicate)` | Find in array at path | | `pushToPath(path, value)` | Add to array at path | | `pullFromPath(path, predicate)` | Remove from array | | `updateInPath(path, find, update)` | Update array item | | `mergePath(path, data)` | Merge object at path | | `copyPath(from, to)` | Copy path | | `movePath(from, to)` | Move path | ## TypeScript Support JSONion is written in TypeScript and includes full type definitions: ```typescript interface User { name: string; email: string; age: number; } // Type-safe operations db.set<User>('user', { name: 'Ahmed', email: 'ahmed@test.com', age: 25 }); const user = db.get<User>('user'); // Type-safe path access const name = db.getPath<string>('user.name'); const age = db.getPath<number>('user.age'); ``` ## Error Handling ```typescript import { JSONionError } from 'jsonion'; try { db.setPath('user.name', 'Ahmed'); } catch (error) { if (error instanceof JSONionError) { console.error(`Operation: ${error.operation}`); console.error(`Message: ${error.message}`); } } ``` ## Performance Tips - Use `flush()` manually for critical operations - Batch related operations together - Use path methods instead of get-modify-set cycles - Use `reload()` if external changes are made ## License MIT ## Contributing Contributions are welcome! Please feel free to submit a Pull Request. ## Support For issues and questions, please open an issue on GitHub. --- Made with ❤️ for developers who love simple, powerful tools.