jsonion
Version:
A lightweight JSON file-based database with nested data access and manipulation capabilities.
558 lines (406 loc) • 11.6 kB
Markdown
# 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.