@pixagram/lacerta-db 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.min.js +7 -7
- package/dist/index.min.js +7 -7
- package/index.js +111 -11
- package/package.json +1 -1
- package/readme.md +1075 -949
- package/dist/lacertadb-interface.html +0 -1641
package/readme.md
CHANGED
|
@@ -1,1238 +1,1364 @@
|
|
|
1
|
-
# LacertaDB 0.6.
|
|
1
|
+
# LacertaDB 0.6.2
|
|
2
2
|

|
|
3
3
|
|
|
4
4
|
> 🦎 **LacertaDB** - A powerful, feature-rich browser-based document database with encryption, compression, and advanced querying capabilities.
|
|
5
5
|
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
|
-
[]()
|
|
8
8
|
[]()
|
|
9
|
+
[](https://www.npmjs.com/package/lacertadb)
|
|
10
|
+
[](https://bundlephobia.com/package/lacertadb)
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- [✨ Features](#-features)
|
|
13
|
-
- [🚀 Quick Start](#-quick-start)
|
|
14
|
-
- [📦 Installation](#-installation)
|
|
15
|
-
- [🎯 Basic Usage](#-basic-usage)
|
|
16
|
-
- [🔧 Core Concepts](#-core-concepts)
|
|
17
|
-
- [Database](#database)
|
|
18
|
-
- [Collections](#collections)
|
|
19
|
-
- [Documents](#documents)
|
|
20
|
-
- [🔍 Querying](#-querying)
|
|
21
|
-
- [Basic Queries](#basic-queries)
|
|
22
|
-
- [Advanced Queries](#advanced-queries)
|
|
23
|
-
- [Aggregation Pipeline](#aggregation-pipeline)
|
|
24
|
-
- [🔐 Security Features](#-security-features)
|
|
25
|
-
- [Encryption](#encryption)
|
|
26
|
-
- [Password Protection](#password-protection)
|
|
27
|
-
- [📎 Attachments](#-attachments)
|
|
28
|
-
- [⚡ Performance](#-performance)
|
|
29
|
-
- [Compression](#compression)
|
|
30
|
-
- [Performance Monitoring](#performance-monitoring)
|
|
31
|
-
- [Query Caching](#query-caching)
|
|
32
|
-
- [🔄 Data Migration](#-data-migration)
|
|
33
|
-
- [💾 Backup & Restore](#-backup--restore)
|
|
34
|
-
- [🛠️ Advanced Features](#️-advanced-features)
|
|
35
|
-
- [Batch Operations](#batch-operations)
|
|
36
|
-
- [Event System](#event-system)
|
|
37
|
-
- [Quick Store](#quick-store)
|
|
38
|
-
- [📊 Storage Management](#-storage-management)
|
|
39
|
-
- [🎨 API Reference](#-api-reference)
|
|
40
|
-
- [💡 Examples](#-examples)
|
|
41
|
-
- [🐛 Error Handling](#-error-handling)
|
|
42
|
-
- [📝 Best Practices](#-best-practices)
|
|
43
|
-
- [🤝 Contributing](#-contributing)
|
|
44
|
-
|
|
45
|
-
## ✨ Features
|
|
46
|
-
|
|
47
|
-
🎯 **Core Features**
|
|
48
|
-
- 📁 **Document-based storage** using IndexedDB
|
|
49
|
-
- 🔍 **MongoDB-like query syntax** for familiar operations
|
|
50
|
-
- 🔐 **AES-256-GCM encryption** with PBKDF2 key derivation
|
|
51
|
-
- 🗜️ **Built-in compression** using CompressionStream API
|
|
52
|
-
- 📎 **File attachments** via Origin Private File System (OPFS)
|
|
53
|
-
- ⚡ **High performance** with async operations and caching
|
|
54
|
-
|
|
55
|
-
🚀 **Advanced Capabilities**
|
|
56
|
-
- 📊 **Aggregation pipeline** for complex data processing
|
|
57
|
-
- 🔄 **Schema migration system** for version management
|
|
58
|
-
- 📈 **Performance monitoring** with real-time metrics
|
|
59
|
-
- 🎭 **Event-driven architecture** with hooks
|
|
60
|
-
- 💾 **Import/Export functionality** with encryption support
|
|
61
|
-
- 🔧 **Batch operations** for efficient bulk processing
|
|
62
|
-
|
|
63
|
-
## 🚀 Quick Start
|
|
12
|
+
[**🎮 PLAYGROUND**](https://codepen.io/Matias-Affolter/pen/ogjrQdB)
|
|
64
13
|
|
|
65
|
-
|
|
66
|
-
import * AS LACERTA from './lacertadb.js';
|
|
14
|
+
---
|
|
67
15
|
|
|
68
|
-
|
|
69
|
-
|
|
16
|
+
## 📖 Table of Contents
|
|
17
|
+
|
|
18
|
+
1. [Introduction](#introduction)
|
|
19
|
+
2. [Browser Compatibility](#browser-compatibility)
|
|
20
|
+
3. [Quick Start](#quick-start)
|
|
21
|
+
4. [Installation](#installation)
|
|
22
|
+
5. [Core Concepts](#core-concepts)
|
|
23
|
+
6. [API Reference](#api-reference)
|
|
24
|
+
- [LacertaDB Class](#lacertadb-class)
|
|
25
|
+
- [Database Class](#database-class)
|
|
26
|
+
- [Collection Class](#collection-class)
|
|
27
|
+
- [Document Class](#document-class)
|
|
28
|
+
- [Index Management](#index-management)
|
|
29
|
+
- [Encryption](#encryption)
|
|
30
|
+
7. [Advanced Features](#advanced-features)
|
|
31
|
+
8. [Real-World Examples](#real-world-examples)
|
|
32
|
+
9. [Performance Optimization](#performance-optimization)
|
|
33
|
+
10. [Migration Guide](#migration-guide)
|
|
34
|
+
11. [Troubleshooting](#troubleshooting)
|
|
35
|
+
12. [Error Handling](#error-handling)
|
|
36
|
+
13. [Comparison](#comparison)
|
|
37
|
+
14. [Contributing](#contributing)
|
|
38
|
+
15. [Changelog](#changelog)
|
|
70
39
|
|
|
71
|
-
|
|
72
|
-
const db = await lacerta.getDatabase('myApp');
|
|
40
|
+
---
|
|
73
41
|
|
|
74
|
-
|
|
75
|
-
const users = await db.createCollection('users');
|
|
42
|
+
## Introduction
|
|
76
43
|
|
|
77
|
-
|
|
78
|
-
const userId = await users.add({
|
|
79
|
-
name: 'Alice Johnson',
|
|
80
|
-
email: 'alice@example.com',
|
|
81
|
-
age: 28,
|
|
82
|
-
tags: ['developer', 'javascript']
|
|
83
|
-
});
|
|
44
|
+
LacertaDB is a high-performance, browser-based NoSQL database built on IndexedDB. It provides MongoDB-like query capabilities, automatic compression, encryption support, and advanced indexing strategies - all running entirely in the browser with zero backend dependencies.
|
|
84
45
|
|
|
85
|
-
|
|
86
|
-
const results = await users.query({
|
|
87
|
-
age: { $gte: 25 },
|
|
88
|
-
tags: { $in: ['developer'] }
|
|
89
|
-
});
|
|
46
|
+
### ✨ Key Features
|
|
90
47
|
|
|
91
|
-
|
|
92
|
-
|
|
48
|
+
- 🔐 **Database-level encryption** with AES-GCM-256
|
|
49
|
+
- 🗜️ **Automatic compression** using native browser APIs (60-80% size reduction)
|
|
50
|
+
- 🔍 **Multiple index types** (B-Tree, Hash, Text, Geo)
|
|
51
|
+
- ⚡ **Smart caching strategies** (LRU, LFU, TTL)
|
|
52
|
+
- 📊 **Aggregation pipeline** for complex queries
|
|
53
|
+
- 🔄 **Atomic batch operations**
|
|
54
|
+
- 📈 **Built-in performance monitoring**
|
|
55
|
+
- 💾 **OPFS support** for attachments
|
|
56
|
+
- 🌐 **Works offline** - no server required
|
|
57
|
+
- 📦 **Small bundle size** (~45KB gzipped)
|
|
93
58
|
|
|
94
|
-
|
|
59
|
+
### 🎯 Use Cases
|
|
95
60
|
|
|
96
|
-
|
|
61
|
+
- **PWAs & Offline-First Apps** - Full database functionality without connectivity
|
|
62
|
+
- **Client-Side Encryption** - Secure sensitive data before any network transmission
|
|
63
|
+
- **Local Data Processing** - Complex queries and aggregations without server round-trips
|
|
64
|
+
- **Browser Extensions** - Persistent storage with advanced querying
|
|
65
|
+
- **Prototyping** - Rapid development without backend setup
|
|
97
66
|
|
|
98
|
-
|
|
99
|
-
npm install @pixagram/lacertadb
|
|
100
|
-
# or
|
|
101
|
-
yarn add @pixagram/lacertadb
|
|
102
|
-
```
|
|
67
|
+
---
|
|
103
68
|
|
|
104
|
-
|
|
69
|
+
## Browser Compatibility
|
|
105
70
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
71
|
+
| Browser | Minimum Version | Notes |
|
|
72
|
+
|---------|----------------|--------|
|
|
73
|
+
| Chrome | 88+ | Full support including compression |
|
|
74
|
+
| Firefox | 90+ | Full support |
|
|
75
|
+
| Safari | 15.4+ | Full support |
|
|
76
|
+
| Edge | 88+ | Full support |
|
|
77
|
+
| Opera | 74+ | Full support |
|
|
78
|
+
| Chrome Mobile | 88+ | Full support |
|
|
79
|
+
| Safari iOS | 15.4+ | Storage limits may apply |
|
|
112
80
|
|
|
113
|
-
###
|
|
81
|
+
### Required Browser APIs
|
|
82
|
+
- IndexedDB API
|
|
83
|
+
- Web Crypto API
|
|
84
|
+
- CompressionStream API (optional, falls back gracefully)
|
|
85
|
+
- Origin Private File System (optional, for attachments)
|
|
114
86
|
|
|
115
|
-
|
|
116
|
-
- `@pixagram/turboserial` - High-performance serialization
|
|
117
|
-
- `@pixagram/turbobase64` - Optimized Base64 encoding
|
|
87
|
+
---
|
|
118
88
|
|
|
119
|
-
##
|
|
89
|
+
## Quick Start
|
|
120
90
|
|
|
121
|
-
###
|
|
91
|
+
### 🚀 5-Minute Setup
|
|
122
92
|
|
|
123
93
|
```javascript
|
|
94
|
+
import { LacertaDB } from 'lacertadb';
|
|
95
|
+
|
|
96
|
+
// 1. Initialize LacertaDB
|
|
124
97
|
const lacerta = new LacertaDB();
|
|
125
98
|
|
|
126
|
-
//
|
|
127
|
-
const db = await lacerta.getDatabase('
|
|
99
|
+
// 2. Get a database
|
|
100
|
+
const db = await lacerta.getDatabase('my-app');
|
|
128
101
|
|
|
129
|
-
// Create a collection
|
|
130
|
-
const
|
|
131
|
-
compressed: true, // Enable compression by default
|
|
132
|
-
encrypted: false // Encryption disabled by default
|
|
133
|
-
});
|
|
134
|
-
```
|
|
102
|
+
// 3. Create a collection
|
|
103
|
+
const users = await db.createCollection('users');
|
|
135
104
|
|
|
136
|
-
|
|
105
|
+
// 4. Add documents
|
|
106
|
+
await users.add({
|
|
107
|
+
name: 'John Doe',
|
|
108
|
+
email: 'john@example.com',
|
|
109
|
+
age: 30,
|
|
110
|
+
tags: ['developer', 'javascript']
|
|
111
|
+
});
|
|
137
112
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
price: 999.99,
|
|
143
|
-
inStock: true
|
|
113
|
+
// 5. Query documents
|
|
114
|
+
const developers = await users.query({
|
|
115
|
+
tags: { $in: ['developer'] },
|
|
116
|
+
age: { $gte: 25 }
|
|
144
117
|
});
|
|
145
118
|
|
|
146
|
-
//
|
|
147
|
-
await
|
|
148
|
-
{
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
password: 'secretPassword', // Encryption password
|
|
156
|
-
compressed: true, // Compress the document
|
|
157
|
-
permanent: true // Mark as permanent (won't be auto-deleted)
|
|
158
|
-
}
|
|
159
|
-
);
|
|
119
|
+
// 6. Use aggregation
|
|
120
|
+
const stats = await users.aggregate([
|
|
121
|
+
{ $match: { age: { $gte: 18 } } },
|
|
122
|
+
{ $group: {
|
|
123
|
+
_id: null,
|
|
124
|
+
avgAge: { $avg: '$age' },
|
|
125
|
+
total: { $count: {} }
|
|
126
|
+
}}
|
|
127
|
+
]);
|
|
160
128
|
```
|
|
161
129
|
|
|
162
|
-
###
|
|
130
|
+
### 🔐 Encrypted Database Setup
|
|
163
131
|
|
|
164
132
|
```javascript
|
|
165
|
-
//
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
// Get all documents
|
|
171
|
-
const allProducts = await products.getAll();
|
|
133
|
+
// Create an encrypted database
|
|
134
|
+
const secureDb = await lacerta.getSecureDatabase(
|
|
135
|
+
'secure-app',
|
|
136
|
+
'user-pin-123456' // User's PIN
|
|
137
|
+
);
|
|
172
138
|
|
|
173
|
-
//
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
});
|
|
139
|
+
// All operations are automatically encrypted
|
|
140
|
+
const secrets = await secureDb.createCollection('secrets');
|
|
141
|
+
await secrets.add({ apiKey: 'sk_live_...' });
|
|
177
142
|
```
|
|
178
143
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
### Database
|
|
182
|
-
|
|
183
|
-
The database is the top-level container for your collections.
|
|
184
|
-
|
|
185
|
-
```javascript
|
|
186
|
-
// Get existing or create new database
|
|
187
|
-
const db = await lacerta.getDatabase('appDB');
|
|
144
|
+
---
|
|
188
145
|
|
|
189
|
-
|
|
190
|
-
const collections = db.listCollections();
|
|
146
|
+
## Installation
|
|
191
147
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
148
|
+
### NPM
|
|
149
|
+
```bash
|
|
150
|
+
npm install lacertadb
|
|
151
|
+
```
|
|
196
152
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
bufferLimitKB: 40000, // Start cleanup at 40MB
|
|
201
|
-
freeSpaceEvery: 60000 // Run cleanup every minute
|
|
202
|
-
});
|
|
153
|
+
### Yarn
|
|
154
|
+
```bash
|
|
155
|
+
yarn add lacertadb
|
|
203
156
|
```
|
|
204
157
|
|
|
205
|
-
###
|
|
158
|
+
### CDN
|
|
159
|
+
```html
|
|
160
|
+
<script type="module">
|
|
161
|
+
import { LacertaDB } from 'https://cdn.jsdelivr.net/npm/lacertadb@latest/dist/index.min.js';
|
|
162
|
+
</script>
|
|
163
|
+
```
|
|
206
164
|
|
|
207
|
-
|
|
165
|
+
### Import Options
|
|
208
166
|
|
|
209
167
|
```javascript
|
|
210
|
-
//
|
|
211
|
-
|
|
168
|
+
// ES6 Modules
|
|
169
|
+
import { LacertaDB } from 'lacertadb';
|
|
170
|
+
|
|
171
|
+
// Import specific components
|
|
172
|
+
import {
|
|
173
|
+
LacertaDB,
|
|
174
|
+
SecureDatabaseEncryption,
|
|
175
|
+
PerformanceMonitor,
|
|
176
|
+
MigrationManager
|
|
177
|
+
} from 'lacertadb';
|
|
178
|
+
|
|
179
|
+
// CommonJS (Node.js)
|
|
180
|
+
const { LacertaDB } = require('lacertadb');
|
|
181
|
+
```
|
|
212
182
|
|
|
213
|
-
|
|
214
|
-
const existingPosts = await db.getCollection('posts');
|
|
183
|
+
---
|
|
215
184
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
185
|
+
## Core Concepts
|
|
186
|
+
|
|
187
|
+
### Database Structure
|
|
219
188
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
189
|
+
```
|
|
190
|
+
LacertaDB Instance
|
|
191
|
+
├── Database 1
|
|
192
|
+
│ ├── Collection A
|
|
193
|
+
│ │ ├── Document 1
|
|
194
|
+
│ │ ├── Document 2
|
|
195
|
+
│ │ └── Indexes
|
|
196
|
+
│ └── Collection B
|
|
197
|
+
└── Database 2
|
|
198
|
+
└── __private_keys__ (special collection for encrypted keys)
|
|
224
199
|
```
|
|
225
200
|
|
|
226
|
-
###
|
|
201
|
+
### Document Structure
|
|
227
202
|
|
|
228
|
-
|
|
203
|
+
Every document automatically includes:
|
|
229
204
|
|
|
230
205
|
```javascript
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
_id: 'doc_1234567890_abc', // Auto-generated unique ID
|
|
239
|
-
_created: 1704067200000, // Creation timestamp
|
|
240
|
-
_modified: 1704067200000, // Last modification timestamp
|
|
241
|
-
_permanent: false, // Deletion protection flag
|
|
242
|
-
};
|
|
206
|
+
{
|
|
207
|
+
_id: "doc_1234567890_abc", // Auto-generated unique ID
|
|
208
|
+
_created: 1234567890, // Creation timestamp
|
|
209
|
+
_modified: 1234567890, // Last modified timestamp
|
|
210
|
+
_permanent: false, // Protection from auto-cleanup
|
|
211
|
+
...yourData // Your document fields
|
|
212
|
+
}
|
|
243
213
|
```
|
|
244
214
|
|
|
245
|
-
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## API Reference
|
|
218
|
+
|
|
219
|
+
### 📦 LacertaDB Class
|
|
220
|
+
|
|
221
|
+
The main entry point for database operations.
|
|
246
222
|
|
|
247
|
-
|
|
223
|
+
| Method | Parameters | Returns | Description |
|
|
224
|
+
|--------|------------|---------|-------------|
|
|
225
|
+
| `getDatabase()` | `name: string`<br/>`options?: object` | `Promise<Database>` | Get or create a database |
|
|
226
|
+
| `getSecureDatabase()` | `name: string`<br/>`pin: string`<br/>`salt?: string`<br/>`config?: object` | `Promise<Database>` | Get encrypted database |
|
|
227
|
+
| `dropDatabase()` | `name: string` | `Promise<void>` | Delete a database |
|
|
228
|
+
| `listDatabases()` | - | `string[]` | List all databases |
|
|
229
|
+
| `createBackup()` | `password?: string` | `Promise<string>` | Create full backup |
|
|
230
|
+
| `restoreBackup()` | `data: string`<br/>`password?: string` | `Promise<object>` | Restore from backup |
|
|
231
|
+
| `destroy()` | - | `void` | Clean up all connections |
|
|
232
|
+
|
|
233
|
+
<details>
|
|
234
|
+
<summary><strong>Examples</strong></summary>
|
|
248
235
|
|
|
249
236
|
```javascript
|
|
250
|
-
//
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
237
|
+
// Initialize LacertaDB
|
|
238
|
+
const lacerta = new LacertaDB();
|
|
239
|
+
|
|
240
|
+
// Get a standard database
|
|
241
|
+
const db = await lacerta.getDatabase('myapp');
|
|
255
242
|
|
|
256
|
-
//
|
|
257
|
-
const
|
|
258
|
-
|
|
243
|
+
// Get an encrypted database with custom security
|
|
244
|
+
const secureDb = await lacerta.getSecureDatabase(
|
|
245
|
+
'sensitive-data',
|
|
246
|
+
'my-secure-pin-123456',
|
|
247
|
+
null, // Auto-generate salt
|
|
259
248
|
{
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
limit: 20, // Limit to 20 results
|
|
263
|
-
projection: { // Select fields
|
|
264
|
-
name: 1,
|
|
265
|
-
email: 1,
|
|
266
|
-
_id: 0
|
|
267
|
-
}
|
|
249
|
+
iterations: 200000, // Higher = more secure but slower
|
|
250
|
+
keyLength: 256
|
|
268
251
|
}
|
|
269
252
|
);
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
### Advanced Queries
|
|
273
253
|
|
|
274
|
-
|
|
254
|
+
// Create a full backup
|
|
255
|
+
const backup = await lacerta.createBackup('backup-password');
|
|
256
|
+
// Save to file or cloud
|
|
257
|
+
await saveToCloud(backup);
|
|
275
258
|
|
|
276
|
-
|
|
259
|
+
// Restore from backup
|
|
260
|
+
const backupData = await loadFromCloud();
|
|
261
|
+
const result = await lacerta.restoreBackup(backupData, 'backup-password');
|
|
262
|
+
console.log(`Restored: ${result.databases} databases, ${result.documents} documents`);
|
|
277
263
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
price: { $gte: 100, $lte: 500 }
|
|
282
|
-
});
|
|
264
|
+
// List all databases
|
|
265
|
+
const databases = lacerta.listDatabases();
|
|
266
|
+
console.log('Available databases:', databases);
|
|
283
267
|
|
|
284
|
-
//
|
|
285
|
-
|
|
286
|
-
|
|
268
|
+
// Clean up when done (important for SPAs)
|
|
269
|
+
window.addEventListener('beforeunload', () => {
|
|
270
|
+
lacerta.destroy();
|
|
287
271
|
});
|
|
288
272
|
```
|
|
289
273
|
|
|
290
|
-
|
|
274
|
+
</details>
|
|
291
275
|
|
|
292
|
-
|
|
293
|
-
// $and
|
|
294
|
-
await products.query({
|
|
295
|
-
$and: [
|
|
296
|
-
{ price: { $lt: 1000 } },
|
|
297
|
-
{ category: 'electronics' }
|
|
298
|
-
]
|
|
299
|
-
});
|
|
276
|
+
---
|
|
300
277
|
|
|
301
|
-
|
|
302
|
-
await users.query({
|
|
303
|
-
$or: [
|
|
304
|
-
{ age: { $gte: 65 } },
|
|
305
|
-
{ status: 'premium' }
|
|
306
|
-
]
|
|
307
|
-
});
|
|
278
|
+
### 🗄️ Database Class
|
|
308
279
|
|
|
309
|
-
|
|
310
|
-
await products.query({
|
|
311
|
-
discontinued: { $not: { $eq: true } }
|
|
312
|
-
});
|
|
313
|
-
```
|
|
280
|
+
Manages collections and database-level operations.
|
|
314
281
|
|
|
315
|
-
|
|
282
|
+
| Method | Parameters | Returns | Description |
|
|
283
|
+
|--------|------------|---------|-------------|
|
|
284
|
+
| `createCollection()` | `name: string`<br/>`options?: object` | `Promise<Collection>` | Create a new collection |
|
|
285
|
+
| `getCollection()` | `name: string` | `Promise<Collection>` | Get existing collection |
|
|
286
|
+
| `dropCollection()` | `name: string` | `Promise<void>` | Delete a collection |
|
|
287
|
+
| `listCollections()` | - | `string[]` | List all collections |
|
|
288
|
+
| `getStats()` | - | `object` | Get database statistics |
|
|
289
|
+
| `updateSettings()` | `settings: object` | `void` | Update database settings |
|
|
290
|
+
| `export()` | `format: 'json'│'encrypted'`<br/>`password?: string` | `Promise<string>` | Export database |
|
|
291
|
+
| `import()` | `data: string`<br/>`format: string`<br/>`password?: string` | `Promise<object>` | Import data |
|
|
292
|
+
| `storePrivateKey()` | `name: string`<br/>`key: string`<br/>`auth?: string` | `Promise<boolean>` | Store encrypted key |
|
|
293
|
+
| `getPrivateKey()` | `name: string`<br/>`auth?: string` | `Promise<string>` | Retrieve encrypted key |
|
|
294
|
+
| `clearAll()` | - | `Promise<void>` | Clear all collections |
|
|
295
|
+
|
|
296
|
+
#### Database Settings
|
|
297
|
+
|
|
298
|
+
| Setting | Type | Default | Description |
|
|
299
|
+
|---------|------|---------|-------------|
|
|
300
|
+
| `sizeLimitKB` | `number` | `Infinity` | Maximum database size |
|
|
301
|
+
| `bufferLimitKB` | `number` | `80% of limit` | Start cleanup threshold |
|
|
302
|
+
| `freeSpaceEvery` | `number` | `10000` | Cleanup interval (ms) |
|
|
303
|
+
|
|
304
|
+
<details>
|
|
305
|
+
<summary><strong>Examples</strong></summary>
|
|
316
306
|
|
|
317
307
|
```javascript
|
|
318
|
-
//
|
|
319
|
-
await
|
|
320
|
-
|
|
308
|
+
// Create collections
|
|
309
|
+
const users = await db.createCollection('users');
|
|
310
|
+
const posts = await db.createCollection('posts', {
|
|
311
|
+
compressed: true
|
|
321
312
|
});
|
|
322
313
|
|
|
323
|
-
//
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
quantity: { $gte: 2 }
|
|
329
|
-
}
|
|
330
|
-
}
|
|
314
|
+
// Configure database limits
|
|
315
|
+
db.updateSettings({
|
|
316
|
+
sizeLimitKB: 100000, // 100MB limit
|
|
317
|
+
bufferLimitKB: 80000, // Start cleanup at 80MB
|
|
318
|
+
freeSpaceEvery: 30000 // Check every 30 seconds
|
|
331
319
|
});
|
|
332
320
|
|
|
333
|
-
//
|
|
334
|
-
|
|
335
|
-
|
|
321
|
+
// Get database statistics
|
|
322
|
+
const stats = db.getStats();
|
|
323
|
+
console.log(`Database: ${stats.name}`);
|
|
324
|
+
console.log(`Size: ${(stats.totalSizeKB / 1024).toFixed(2)}MB`);
|
|
325
|
+
console.log(`Documents: ${stats.totalDocuments}`);
|
|
326
|
+
stats.collections.forEach(coll => {
|
|
327
|
+
console.log(` ${coll.name}: ${coll.documents} docs, ${coll.sizeKB}KB`);
|
|
336
328
|
});
|
|
337
|
-
```
|
|
338
329
|
|
|
339
|
-
|
|
330
|
+
// Export/Import database
|
|
331
|
+
const exportData = await db.export('encrypted', 'export-password');
|
|
332
|
+
await saveToFile(exportData, 'backup.lacerta');
|
|
340
333
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
await
|
|
344
|
-
email: { $regex: '@company\\.com$' }
|
|
345
|
-
});
|
|
334
|
+
// Later...
|
|
335
|
+
const importData = await loadFromFile('backup.lacerta');
|
|
336
|
+
const result = await db.import(importData, 'encrypted', 'export-password');
|
|
346
337
|
|
|
347
|
-
//
|
|
348
|
-
await
|
|
349
|
-
|
|
350
|
-
|
|
338
|
+
// Store private keys (requires encrypted database)
|
|
339
|
+
await secureDb.storePrivateKey(
|
|
340
|
+
'stripe-api',
|
|
341
|
+
'sk_live_abcd1234',
|
|
342
|
+
'myapp.com' // Additional authentication
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
// Retrieve private key
|
|
346
|
+
const apiKey = await secureDb.getPrivateKey(
|
|
347
|
+
'stripe-api',
|
|
348
|
+
'myapp.com'
|
|
349
|
+
);
|
|
351
350
|
```
|
|
352
351
|
|
|
353
|
-
|
|
352
|
+
</details>
|
|
354
353
|
|
|
355
|
-
|
|
354
|
+
---
|
|
356
355
|
|
|
357
|
-
|
|
358
|
-
// Sales analysis pipeline
|
|
359
|
-
const salesReport = await orders.aggregate([
|
|
360
|
-
// Stage 1: Filter orders
|
|
361
|
-
{ $match: { status: 'completed' } },
|
|
362
|
-
|
|
363
|
-
// Stage 2: Group by customer
|
|
364
|
-
{
|
|
365
|
-
$group: {
|
|
366
|
-
_id: '$customerId',
|
|
367
|
-
totalSpent: { $sum: '$amount' },
|
|
368
|
-
orderCount: { $count: {} },
|
|
369
|
-
avgOrderValue: { $avg: '$amount' }
|
|
370
|
-
}
|
|
371
|
-
},
|
|
372
|
-
|
|
373
|
-
// Stage 3: Sort by total spent
|
|
374
|
-
{ $sort: { totalSpent: -1 } },
|
|
375
|
-
|
|
376
|
-
// Stage 4: Limit to top 10
|
|
377
|
-
{ $limit: 10 },
|
|
378
|
-
|
|
379
|
-
// Stage 5: Project final shape
|
|
380
|
-
{
|
|
381
|
-
$project: {
|
|
382
|
-
customer: '$_id',
|
|
383
|
-
metrics: {
|
|
384
|
-
total: '$totalSpent',
|
|
385
|
-
orders: '$orderCount',
|
|
386
|
-
average: '$avgOrderValue'
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
]);
|
|
391
|
-
```
|
|
356
|
+
### 📁 Collection Class
|
|
392
357
|
|
|
393
|
-
|
|
358
|
+
Manages documents within a collection.
|
|
394
359
|
|
|
395
|
-
|
|
396
|
-
// Join with another collection
|
|
397
|
-
const ordersWithProducts = await orders.aggregate([
|
|
398
|
-
{
|
|
399
|
-
$lookup: {
|
|
400
|
-
from: 'products', // Foreign collection
|
|
401
|
-
localField: 'productId', // Field in orders
|
|
402
|
-
foreignField: '_id', // Field in products
|
|
403
|
-
as: 'productDetails' // Output array field
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
]);
|
|
407
|
-
```
|
|
360
|
+
#### Core Methods
|
|
408
361
|
|
|
409
|
-
|
|
362
|
+
| Method | Parameters | Returns | Description |
|
|
363
|
+
|--------|------------|---------|-------------|
|
|
364
|
+
| `add()` | `data: object`<br/>`options?: object` | `Promise<string>` | Add a document |
|
|
365
|
+
| `get()` | `id: string`<br/>`options?: object` | `Promise<object>` | Get a document |
|
|
366
|
+
| `update()` | `id: string`<br/>`updates: object`<br/>`options?: object` | `Promise<string>` | Update a document |
|
|
367
|
+
| `delete()` | `id: string`<br/>`options?: object` | `Promise<void>` | Delete a document |
|
|
368
|
+
| `getAll()` | `options?: object` | `Promise<object[]>` | Get all documents |
|
|
369
|
+
| `clear()` | `options?: object` | `Promise<void>` | Clear collection |
|
|
370
|
+
| `count()` | `filter?: object` | `Promise<number>` | Count documents |
|
|
410
371
|
|
|
411
|
-
|
|
372
|
+
#### Query Methods
|
|
412
373
|
|
|
413
|
-
|
|
374
|
+
| Method | Parameters | Returns | Description |
|
|
375
|
+
|--------|------------|---------|-------------|
|
|
376
|
+
| `query()` | `filter: object`<br/>`options?: object` | `Promise<object[]>` | Query documents |
|
|
377
|
+
| `aggregate()` | `pipeline: object[]` | `Promise<object[]>` | Run aggregation pipeline |
|
|
378
|
+
| `findOne()` | `filter: object` | `Promise<object│null>` | Find first matching document |
|
|
414
379
|
|
|
415
|
-
|
|
416
|
-
// Add encrypted document
|
|
417
|
-
await users.add(
|
|
418
|
-
{
|
|
419
|
-
ssn: '123-45-6789',
|
|
420
|
-
bankAccount: 'SECRET123',
|
|
421
|
-
salary: 75000
|
|
422
|
-
},
|
|
423
|
-
{
|
|
424
|
-
encrypted: true,
|
|
425
|
-
password: 'strong-password-here',
|
|
426
|
-
permanent: true // Protect from auto-deletion
|
|
427
|
-
}
|
|
428
|
-
);
|
|
380
|
+
#### Batch Operations
|
|
429
381
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
382
|
+
| Method | Parameters | Returns | Description |
|
|
383
|
+
|--------|------------|---------|-------------|
|
|
384
|
+
| `batchAdd()` | `documents: object[]`<br/>`options?: object` | `Promise<object[]>` | Add multiple documents |
|
|
385
|
+
| `batchUpdate()` | `updates: object[]`<br/>`options?: object` | `Promise<object[]>` | Update multiple documents |
|
|
386
|
+
| `batchDelete()` | `items: string[]│object[]` | `Promise<object[]>` | Delete multiple documents |
|
|
434
387
|
|
|
435
|
-
|
|
436
|
-
await users.update('user-id',
|
|
437
|
-
{ salary: 80000 },
|
|
438
|
-
{
|
|
439
|
-
encrypted: true,
|
|
440
|
-
password: 'strong-password-here'
|
|
441
|
-
}
|
|
442
|
-
);
|
|
443
|
-
```
|
|
388
|
+
#### Event Methods
|
|
444
389
|
|
|
445
|
-
|
|
390
|
+
| Method | Parameters | Returns | Description |
|
|
391
|
+
|--------|------------|---------|-------------|
|
|
392
|
+
| `on()` | `event: string`<br/>`callback: function` | `void` | Add event listener |
|
|
393
|
+
| `off()` | `event: string`<br/>`callback: function` | `void` | Remove event listener |
|
|
446
394
|
|
|
447
|
-
|
|
395
|
+
#### Cache Methods
|
|
448
396
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
397
|
+
| Method | Parameters | Returns | Description |
|
|
398
|
+
|--------|------------|---------|-------------|
|
|
399
|
+
| `configureCacheStrategy()` | `config: object` | `void` | Configure caching |
|
|
400
|
+
| `clearCache()` | - | `void` | Clear query cache |
|
|
452
401
|
|
|
453
|
-
|
|
454
|
-
await db.import(encryptedExport, 'encrypted', 'export-password');
|
|
402
|
+
#### Options Parameters
|
|
455
403
|
|
|
456
|
-
|
|
457
|
-
|
|
404
|
+
| Option | Type | Default | Description |
|
|
405
|
+
|--------|------|---------|-------------|
|
|
406
|
+
| `id` | `string` | auto-generated | Custom document ID |
|
|
407
|
+
| `compressed` | `boolean` | `true` | Enable compression |
|
|
408
|
+
| `permanent` | `boolean` | `false` | Prevent auto-deletion |
|
|
409
|
+
| `includeAttachments` | `boolean` | `false` | Include file attachments |
|
|
410
|
+
| `attachments` | `Array` | `[]` | File attachments to store |
|
|
411
|
+
| `force` | `boolean` | `false` | Force delete permanent docs |
|
|
412
|
+
| `limit` | `number` | - | Limit results |
|
|
413
|
+
| `skip` | `number` | - | Skip results |
|
|
414
|
+
| `sort` | `object` | - | Sort criteria |
|
|
415
|
+
| `projection` | `object` | - | Field projection |
|
|
458
416
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
```
|
|
417
|
+
<details>
|
|
418
|
+
<summary><strong>Examples</strong></summary>
|
|
462
419
|
|
|
463
|
-
|
|
420
|
+
```javascript
|
|
421
|
+
const users = await db.getCollection('users');
|
|
464
422
|
|
|
465
|
-
|
|
423
|
+
// Add document with custom ID
|
|
424
|
+
const userId = await users.add({
|
|
425
|
+
name: 'John Doe',
|
|
426
|
+
email: 'john@example.com',
|
|
427
|
+
age: 30,
|
|
428
|
+
skills: ['JavaScript', 'TypeScript', 'React']
|
|
429
|
+
}, {
|
|
430
|
+
id: 'user_john_doe',
|
|
431
|
+
permanent: true // Protect from auto-cleanup
|
|
432
|
+
});
|
|
466
433
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
const file1 = new File(['content'], 'document.pdf', { type: 'application/pdf' });
|
|
470
|
-
const file2 = new Blob(['image data'], { type: 'image/png' });
|
|
434
|
+
// Find one document
|
|
435
|
+
const john = await users.findOne({ email: 'john@example.com' });
|
|
471
436
|
|
|
472
|
-
//
|
|
473
|
-
const
|
|
474
|
-
{
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
data: new TextEncoder().encode('Custom file content')
|
|
483
|
-
}
|
|
484
|
-
]
|
|
437
|
+
// Query with multiple conditions
|
|
438
|
+
const seniorDevs = await users.query(
|
|
439
|
+
{
|
|
440
|
+
age: { $gte: 25, $lte: 45 },
|
|
441
|
+
skills: { $all: ['JavaScript', 'React'] }
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
sort: { age: -1 },
|
|
445
|
+
limit: 10,
|
|
446
|
+
projection: { name: 1, email: 1, skills: 1 }
|
|
485
447
|
}
|
|
486
448
|
);
|
|
487
449
|
|
|
488
|
-
//
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
}
|
|
450
|
+
// Complex aggregation pipeline
|
|
451
|
+
const skillStats = await users.aggregate([
|
|
452
|
+
{ $match: { age: { $gte: 18 } } },
|
|
453
|
+
{ $project: { skills: 1 } },
|
|
454
|
+
{ $unwind: '$skills' },
|
|
455
|
+
{ $group: {
|
|
456
|
+
_id: '$skills',
|
|
457
|
+
count: { $count: {} },
|
|
458
|
+
users: { $push: '$_id' }
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
{ $sort: { count: -1 } },
|
|
462
|
+
{ $limit: 5 }
|
|
463
|
+
]);
|
|
492
464
|
|
|
493
|
-
//
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
465
|
+
// Batch operations with error handling
|
|
466
|
+
const newUsers = [
|
|
467
|
+
{ name: 'Alice', email: 'alice@example.com' },
|
|
468
|
+
{ name: 'Bob', email: 'bob@example.com' }
|
|
469
|
+
];
|
|
470
|
+
|
|
471
|
+
const results = await users.batchAdd(newUsers, {
|
|
472
|
+
compressed: true,
|
|
473
|
+
permanent: false
|
|
497
474
|
});
|
|
498
|
-
```
|
|
499
475
|
|
|
500
|
-
|
|
476
|
+
results.forEach(result => {
|
|
477
|
+
if (result.success) {
|
|
478
|
+
console.log(`Added user: ${result.id}`);
|
|
479
|
+
} else {
|
|
480
|
+
console.error(`Failed to add user: ${result.error}`);
|
|
481
|
+
}
|
|
482
|
+
});
|
|
501
483
|
|
|
502
|
-
|
|
484
|
+
// Event listeners
|
|
485
|
+
users.on('beforeAdd', (data) => {
|
|
486
|
+
console.log('Adding document:', data);
|
|
487
|
+
});
|
|
503
488
|
|
|
504
|
-
|
|
489
|
+
users.on('afterDelete', (id) => {
|
|
490
|
+
console.log('Deleted document:', id);
|
|
491
|
+
});
|
|
505
492
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
493
|
+
// Add with file attachments
|
|
494
|
+
const profile = await users.add({
|
|
495
|
+
name: 'User with Avatar'
|
|
496
|
+
}, {
|
|
497
|
+
attachments: [
|
|
498
|
+
await OPFSUtility.prepareAttachment(avatarFile, 'avatar.jpg'),
|
|
499
|
+
await OPFSUtility.prepareAttachment(resumeFile, 'resume.pdf')
|
|
500
|
+
]
|
|
510
501
|
});
|
|
511
502
|
|
|
512
|
-
//
|
|
513
|
-
await
|
|
514
|
-
|
|
503
|
+
// Retrieve with attachments
|
|
504
|
+
const userWithFiles = await users.get(profile, {
|
|
505
|
+
includeAttachments: true
|
|
515
506
|
});
|
|
507
|
+
// userWithFiles._attachments will contain the file data
|
|
516
508
|
```
|
|
517
509
|
|
|
518
|
-
|
|
510
|
+
</details>
|
|
519
511
|
|
|
520
|
-
|
|
512
|
+
---
|
|
521
513
|
|
|
522
|
-
|
|
523
|
-
// Start monitoring
|
|
524
|
-
lacerta.performanceMonitor.startMonitoring();
|
|
514
|
+
### 🔍 Index Management
|
|
525
515
|
|
|
526
|
-
|
|
516
|
+
Create indexes for optimized queries.
|
|
527
517
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
518
|
+
| Method | Parameters | Returns | Description |
|
|
519
|
+
|--------|------------|---------|-------------|
|
|
520
|
+
| `createIndex()` | `field: string`<br/>`options: object` | `Promise<string>` | Create an index |
|
|
521
|
+
| `dropIndex()` | `name: string` | `Promise<void>` | Remove an index |
|
|
522
|
+
| `getIndexes()` | - | `Promise<object>` | Get index statistics |
|
|
523
|
+
| `verifyIndexes()` | - | `Promise<object>` | Verify and repair indexes |
|
|
524
|
+
| `reindexCollection()` | - | `Promise<void>` | Rebuild all indexes |
|
|
534
525
|
|
|
535
|
-
|
|
536
|
-
const tips = lacerta.performanceMonitor.getOptimizationTips();
|
|
537
|
-
tips.forEach(tip => console.log(`💡 ${tip}`));
|
|
526
|
+
#### Index Types and Use Cases
|
|
538
527
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
528
|
+
| Type | Use Case | Query Performance | Space Overhead |
|
|
529
|
+
|------|----------|-------------------|----------------|
|
|
530
|
+
| `btree` | Range queries, sorting | O(log n) | Medium |
|
|
531
|
+
| `hash` | Exact match queries | O(1) | Low |
|
|
532
|
+
| `text` | Full-text search | O(n) for terms | High |
|
|
533
|
+
| `geo` | Spatial queries | O(n log n) | Medium |
|
|
534
|
+
|
|
535
|
+
#### Index Options
|
|
542
536
|
|
|
543
|
-
|
|
537
|
+
| Option | Type | Default | Values | Description |
|
|
538
|
+
|--------|------|---------|--------|-------------|
|
|
539
|
+
| `type` | `string` | `'btree'` | `'btree'`, `'hash'`, `'text'`, `'geo'` | Index type |
|
|
540
|
+
| `unique` | `boolean` | `false` | - | Enforce uniqueness |
|
|
541
|
+
| `sparse` | `boolean` | `false` | - | Skip null values |
|
|
542
|
+
| `name` | `string` | field name | - | Custom index name |
|
|
543
|
+
| `collation` | `object` | null | - | Collation rules |
|
|
544
544
|
|
|
545
|
-
|
|
545
|
+
<details>
|
|
546
|
+
<summary><strong>Examples</strong></summary>
|
|
546
547
|
|
|
547
548
|
```javascript
|
|
548
|
-
//
|
|
549
|
-
|
|
549
|
+
// Create B-Tree index for range queries
|
|
550
|
+
await users.createIndex('age', {
|
|
551
|
+
type: 'btree',
|
|
552
|
+
name: 'age_index'
|
|
553
|
+
});
|
|
550
554
|
|
|
551
|
-
//
|
|
552
|
-
|
|
555
|
+
// Create compound index (nested fields)
|
|
556
|
+
await users.createIndex('address.city', {
|
|
557
|
+
type: 'hash',
|
|
558
|
+
name: 'city_index'
|
|
559
|
+
});
|
|
553
560
|
|
|
554
|
-
//
|
|
555
|
-
users.
|
|
556
|
-
|
|
561
|
+
// Create unique index on email
|
|
562
|
+
await users.createIndex('email', {
|
|
563
|
+
unique: true,
|
|
564
|
+
sparse: true // Allow multiple nulls
|
|
565
|
+
});
|
|
557
566
|
|
|
558
|
-
|
|
567
|
+
// Create text index for full-text search
|
|
568
|
+
await posts.createIndex('content', {
|
|
569
|
+
type: 'text',
|
|
570
|
+
name: 'content_search'
|
|
571
|
+
});
|
|
559
572
|
|
|
560
|
-
|
|
573
|
+
// Use text index
|
|
574
|
+
const searchResults = await posts.query({
|
|
575
|
+
content: { $text: 'javascript tutorial' }
|
|
576
|
+
});
|
|
561
577
|
|
|
562
|
-
|
|
563
|
-
|
|
578
|
+
// Create geo index for location queries
|
|
579
|
+
await stores.createIndex('location', {
|
|
580
|
+
type: 'geo'
|
|
581
|
+
});
|
|
564
582
|
|
|
565
|
-
//
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
return { ...doc, role: 'standard' };
|
|
583
|
+
// Query using geo index - find nearby
|
|
584
|
+
const nearbyStores = await stores.query({
|
|
585
|
+
location: {
|
|
586
|
+
$near: {
|
|
587
|
+
coordinates: { lat: 40.7128, lng: -74.0060 },
|
|
588
|
+
maxDistance: 5000 // 5km in meters
|
|
572
589
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
// Query using geo index - find within bounds
|
|
594
|
+
const storesInArea = await stores.query({
|
|
595
|
+
location: {
|
|
596
|
+
$within: {
|
|
597
|
+
minLat: 40.7,
|
|
598
|
+
maxLat: 40.8,
|
|
599
|
+
minLng: -74.1,
|
|
600
|
+
maxLng: -74.0
|
|
579
601
|
}
|
|
580
|
-
return doc;
|
|
581
602
|
}
|
|
582
603
|
});
|
|
583
604
|
|
|
584
|
-
//
|
|
585
|
-
await
|
|
605
|
+
// Get index statistics
|
|
606
|
+
const indexes = await users.getIndexes();
|
|
607
|
+
Object.entries(indexes).forEach(([name, info]) => {
|
|
608
|
+
console.log(`Index: ${name}`);
|
|
609
|
+
console.log(` Type: ${info.type}`);
|
|
610
|
+
console.log(` Size: ${info.size} entries`);
|
|
611
|
+
console.log(` Memory: ~${info.memoryUsage} bytes`);
|
|
612
|
+
});
|
|
586
613
|
|
|
587
|
-
//
|
|
588
|
-
await
|
|
614
|
+
// Verify and auto-repair indexes
|
|
615
|
+
const report = await users.verifyIndexes();
|
|
616
|
+
if (!report.healthy) {
|
|
617
|
+
console.log('Indexes repaired:', report.repaired);
|
|
618
|
+
}
|
|
589
619
|
```
|
|
590
620
|
|
|
591
|
-
|
|
621
|
+
</details>
|
|
592
622
|
|
|
593
|
-
|
|
623
|
+
---
|
|
594
624
|
|
|
595
|
-
|
|
596
|
-
// Export single database
|
|
597
|
-
const exportData = await db.export('json');
|
|
598
|
-
// Save exportData to file or send to server
|
|
625
|
+
### 🔐 Encryption
|
|
599
626
|
|
|
600
|
-
|
|
601
|
-
const importResult = await db.import(exportData, 'json');
|
|
602
|
-
console.log(`Imported ${importResult.documents} documents`);
|
|
627
|
+
Database-level encryption configuration.
|
|
603
628
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
629
|
+
| Method | Parameters | Returns | Description |
|
|
630
|
+
|--------|------------|---------|-------------|
|
|
631
|
+
| `initialize()` | `pin: string`<br/>`salt?: Uint8Array` | `Promise<string>` | Initialize encryption |
|
|
632
|
+
| `changePin()` | `oldPin: string`<br/>`newPin: string` | `Promise<string>` | Change encryption PIN |
|
|
633
|
+
| `destroy()` | - | `void` | Clear encryption keys |
|
|
634
|
+
| `exportMetadata()` | - | `object` | Export encryption config |
|
|
635
|
+
| `importMetadata()` | `metadata: object` | `boolean` | Import encryption config |
|
|
607
636
|
|
|
608
|
-
|
|
637
|
+
#### Encryption Configuration
|
|
609
638
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
// Restore from backup
|
|
617
|
-
const restoreResult = await lacerta.restoreBackup(systemBackup);
|
|
618
|
-
console.log(`Restored: ${restoreResult.databases} databases`);
|
|
619
|
-
console.log(`Total documents: ${restoreResult.documents}`);
|
|
620
|
-
```
|
|
639
|
+
| Option | Type | Default | Range | Description |
|
|
640
|
+
|--------|------|---------|-------|-------------|
|
|
641
|
+
| `iterations` | `number` | `100000` | 10000-1000000 | PBKDF2 iterations |
|
|
642
|
+
| `hashAlgorithm` | `string` | `'SHA-256'` | SHA-256/SHA-512 | Hash algorithm |
|
|
643
|
+
| `keyLength` | `number` | `256` | 128/192/256 | Key length in bits |
|
|
644
|
+
| `saltLength` | `number` | `32` | 16-64 | Salt length in bytes |
|
|
621
645
|
|
|
622
|
-
|
|
646
|
+
#### Static Methods
|
|
623
647
|
|
|
624
|
-
|
|
648
|
+
| Method | Parameters | Returns | Description |
|
|
649
|
+
|--------|------------|---------|-------------|
|
|
650
|
+
| `generateSecurePIN()` | `length: number` | `string` | Generate random PIN |
|
|
625
651
|
|
|
626
|
-
|
|
652
|
+
<details>
|
|
653
|
+
<summary><strong>Examples</strong></summary>
|
|
627
654
|
|
|
628
655
|
```javascript
|
|
629
|
-
//
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
656
|
+
// Initialize encrypted database with custom config
|
|
657
|
+
const secureDb = await lacerta.getSecureDatabase(
|
|
658
|
+
'secure-app',
|
|
659
|
+
'user-pin-123456',
|
|
660
|
+
null, // Auto-generate salt
|
|
661
|
+
{
|
|
662
|
+
iterations: 200000, // Higher = more secure but slower
|
|
663
|
+
keyLength: 256,
|
|
664
|
+
hashAlgorithm: 'SHA-512'
|
|
665
|
+
}
|
|
666
|
+
);
|
|
635
667
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
668
|
+
// Change PIN
|
|
669
|
+
const newSalt = await secureDb.changePin(
|
|
670
|
+
'old-pin-123456',
|
|
671
|
+
'new-pin-789012'
|
|
672
|
+
);
|
|
673
|
+
console.log('New salt (store securely):', newSalt);
|
|
640
674
|
|
|
641
|
-
//
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
console.log(`✅ Added: ${result.id}`);
|
|
645
|
-
} else {
|
|
646
|
-
console.log(`❌ Failed: ${result.error}`);
|
|
647
|
-
}
|
|
648
|
-
});
|
|
675
|
+
// Generate secure PIN
|
|
676
|
+
const securePin = SecureDatabaseEncryption.generateSecurePIN(8);
|
|
677
|
+
console.log('Generated PIN:', securePin);
|
|
649
678
|
|
|
650
|
-
//
|
|
651
|
-
const
|
|
652
|
-
|
|
653
|
-
{ id: 'doc2', data: { status: 'inactive' } }
|
|
654
|
-
];
|
|
655
|
-
await collection.batchUpdate(updates);
|
|
679
|
+
// Export encryption metadata (for backup)
|
|
680
|
+
const encryptionMeta = secureDb.encryption.exportMetadata();
|
|
681
|
+
// Store this securely with your backups
|
|
656
682
|
|
|
657
|
-
//
|
|
658
|
-
const
|
|
659
|
-
|
|
683
|
+
// Restore encryption with same config
|
|
684
|
+
const restoredDb = await lacerta.getSecureDatabase(
|
|
685
|
+
'secure-app',
|
|
686
|
+
userPin,
|
|
687
|
+
encryptionMeta.salt // Use stored salt
|
|
688
|
+
);
|
|
660
689
|
```
|
|
661
690
|
|
|
662
|
-
|
|
691
|
+
</details>
|
|
663
692
|
|
|
664
|
-
|
|
693
|
+
---
|
|
665
694
|
|
|
666
|
-
|
|
667
|
-
// Collection-level events
|
|
668
|
-
collection.on('beforeAdd', async (data) => {
|
|
669
|
-
console.log('Validating document...');
|
|
670
|
-
// Perform validation
|
|
671
|
-
});
|
|
695
|
+
## Advanced Features
|
|
672
696
|
|
|
673
|
-
|
|
674
|
-
console.log(`Document ${doc._id} added`);
|
|
675
|
-
// Send notification, update cache, etc.
|
|
676
|
-
});
|
|
697
|
+
### 🎯 Query Operators
|
|
677
698
|
|
|
678
|
-
|
|
679
|
-
console.log(`Updating ${docId}`);
|
|
680
|
-
});
|
|
699
|
+
#### Comparison Operators
|
|
681
700
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
701
|
+
| Operator | Description | Example |
|
|
702
|
+
|----------|-------------|---------|
|
|
703
|
+
| `$eq` | Equals | `{ age: { $eq: 25 } }` |
|
|
704
|
+
| `$ne` | Not equals | `{ status: { $ne: 'deleted' } }` |
|
|
705
|
+
| `$gt` | Greater than | `{ price: { $gt: 100 } }` |
|
|
706
|
+
| `$gte` | Greater than or equal | `{ age: { $gte: 18 } }` |
|
|
707
|
+
| `$lt` | Less than | `{ score: { $lt: 50 } }` |
|
|
708
|
+
| `$lte` | Less than or equal | `{ age: { $lte: 65 } }` |
|
|
686
709
|
|
|
687
|
-
|
|
688
|
-
const handler = (doc) => console.log(doc);
|
|
689
|
-
collection.on('afterAdd', handler);
|
|
690
|
-
collection.off('afterAdd', handler);
|
|
691
|
-
```
|
|
710
|
+
#### Array Operators
|
|
692
711
|
|
|
693
|
-
|
|
712
|
+
| Operator | Description | Example |
|
|
713
|
+
|----------|-------------|---------|
|
|
714
|
+
| `$in` | Value in array | `{ category: { $in: ['A', 'B'] } }` |
|
|
715
|
+
| `$nin` | Value not in array | `{ status: { $nin: ['banned'] } }` |
|
|
716
|
+
| `$all` | Array contains all | `{ tags: { $all: ['js', 'node'] } }` |
|
|
717
|
+
| `$elemMatch` | Array element match | `{ scores: { $elemMatch: { $gt: 80 } } }` |
|
|
718
|
+
| `$size` | Array size | `{ items: { $size: 3 } }` |
|
|
719
|
+
|
|
720
|
+
#### Logical Operators
|
|
694
721
|
|
|
695
|
-
|
|
722
|
+
| Operator | Description | Example |
|
|
723
|
+
|----------|-------------|---------|
|
|
724
|
+
| `$and` | Logical AND | `{ $and: [{ age: { $gte: 18 } }, { active: true }] }` |
|
|
725
|
+
| `$or` | Logical OR | `{ $or: [{ role: 'admin' }, { role: 'moderator' }] }` |
|
|
726
|
+
| `$not` | Logical NOT | `{ age: { $not: { $lt: 18 } } }` |
|
|
727
|
+
| `$nor` | Logical NOR | `{ $nor: [{ expired: true }, { deleted: true }] }` |
|
|
728
|
+
|
|
729
|
+
#### Element Operators
|
|
730
|
+
|
|
731
|
+
| Operator | Description | Example |
|
|
732
|
+
|----------|-------------|---------|
|
|
733
|
+
| `$exists` | Field exists | `{ email: { $exists: true } }` |
|
|
734
|
+
| `$type` | Field type | `{ age: { $type: 'number' } }` |
|
|
735
|
+
|
|
736
|
+
#### String Operators
|
|
737
|
+
|
|
738
|
+
| Operator | Description | Example |
|
|
739
|
+
|----------|-------------|---------|
|
|
740
|
+
| `$regex` | Regular expression | `{ name: { $regex: '^John' } }` |
|
|
741
|
+
| `$text` | Text search | `{ bio: { $text: 'developer' } }` |
|
|
742
|
+
|
|
743
|
+
### 📊 Aggregation Pipeline Stages
|
|
744
|
+
|
|
745
|
+
| Stage | Description | Example |
|
|
746
|
+
|-------|-------------|---------|
|
|
747
|
+
| `$match` | Filter documents | `{ $match: { age: { $gte: 18 } } }` |
|
|
748
|
+
| `$group` | Group by field | `{ $group: { _id: '$category', count: { $count: {} } } }` |
|
|
749
|
+
| `$sort` | Sort results | `{ $sort: { createdAt: -1 } }` |
|
|
750
|
+
| `$limit` | Limit results | `{ $limit: 10 }` |
|
|
751
|
+
| `$skip` | Skip results | `{ $skip: 20 }` |
|
|
752
|
+
| `$project` | Project fields | `{ $project: { name: 1, email: 1 } }` |
|
|
753
|
+
| `$lookup` | Join collections | `{ $lookup: { from: 'posts', localField: '_id', foreignField: 'userId', as: 'userPosts' } }` |
|
|
754
|
+
|
|
755
|
+
#### Aggregation Accumulator Operators
|
|
756
|
+
|
|
757
|
+
| Operator | Description | Example |
|
|
758
|
+
|----------|-------------|---------|
|
|
759
|
+
| `$sum` | Sum values | `{ total: { $sum: '$amount' } }` |
|
|
760
|
+
| `$avg` | Average values | `{ avgAge: { $avg: '$age' } }` |
|
|
761
|
+
| `$min` | Minimum value | `{ minPrice: { $min: '$price' } }` |
|
|
762
|
+
| `$max` | Maximum value | `{ maxScore: { $max: '$score' } }` |
|
|
763
|
+
| `$count` | Count documents | `{ total: { $count: {} } }` |
|
|
764
|
+
|
|
765
|
+
### 💾 Cache Strategies
|
|
766
|
+
|
|
767
|
+
| Strategy | Description | Best For | Configuration |
|
|
768
|
+
|----------|-------------|----------|---------------|
|
|
769
|
+
| `lru` | Least Recently Used | General purpose | `maxSize`, `ttl` |
|
|
770
|
+
| `lfu` | Least Frequently Used | Repeated queries | `maxSize`, `ttl` |
|
|
771
|
+
| `ttl` | Time To Live | Time-sensitive data | `ttl` |
|
|
772
|
+
| `none` | No caching | Real-time data | - |
|
|
773
|
+
|
|
774
|
+
<details>
|
|
775
|
+
<summary><strong>Examples</strong></summary>
|
|
696
776
|
|
|
697
777
|
```javascript
|
|
698
|
-
//
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
//
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
language: 'en',
|
|
705
|
-
notifications: true
|
|
778
|
+
// Configure LRU cache with TTL
|
|
779
|
+
users.configureCacheStrategy({
|
|
780
|
+
type: 'lru',
|
|
781
|
+
maxSize: 200, // Max 200 cached queries
|
|
782
|
+
ttl: 60000, // Expire after 1 minute
|
|
783
|
+
enabled: true
|
|
706
784
|
});
|
|
707
785
|
|
|
708
|
-
//
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
// Get all stored items
|
|
715
|
-
const allItems = quickStore.getAll();
|
|
716
|
-
|
|
717
|
-
// Clear QuickStore
|
|
718
|
-
quickStore.clear();
|
|
719
|
-
```
|
|
720
|
-
|
|
721
|
-
## 📊 Storage Management
|
|
722
|
-
|
|
723
|
-
### Size Limits and Auto-Cleanup
|
|
724
|
-
|
|
725
|
-
```javascript
|
|
726
|
-
// Configure storage limits
|
|
727
|
-
db.updateSettings({
|
|
728
|
-
sizeLimitKB: 100000, // 100MB total limit
|
|
729
|
-
bufferLimitKB: 80000, // Start cleanup at 80MB
|
|
730
|
-
freeSpaceEvery: 30000 // Check every 30 seconds
|
|
786
|
+
// Configure LFU for frequently accessed data
|
|
787
|
+
products.configureCacheStrategy({
|
|
788
|
+
type: 'lfu',
|
|
789
|
+
maxSize: 500,
|
|
790
|
+
ttl: 300000 // 5 minutes
|
|
731
791
|
});
|
|
732
792
|
|
|
733
|
-
//
|
|
734
|
-
|
|
793
|
+
// TTL-only for session data
|
|
794
|
+
sessions.configureCacheStrategy({
|
|
795
|
+
type: 'ttl',
|
|
796
|
+
ttl: 900000 // 15 minutes
|
|
797
|
+
});
|
|
735
798
|
|
|
736
|
-
//
|
|
737
|
-
|
|
738
|
-
|
|
799
|
+
// Disable caching for real-time data
|
|
800
|
+
liveData.configureCacheStrategy({
|
|
801
|
+
type: 'none'
|
|
739
802
|
});
|
|
740
803
|
|
|
741
|
-
//
|
|
742
|
-
|
|
743
|
-
console.log(`Collection size: ${stats.sizeKB} KB`);
|
|
744
|
-
console.log(`Document count: ${stats.length}`);
|
|
804
|
+
// Manual cache control
|
|
805
|
+
users.clearCache(); // Clear all cached queries
|
|
745
806
|
```
|
|
746
807
|
|
|
747
|
-
|
|
808
|
+
</details>
|
|
748
809
|
|
|
749
|
-
|
|
750
|
-
// Database statistics
|
|
751
|
-
const dbStats = db.getStats();
|
|
752
|
-
console.log(dbStats);
|
|
753
|
-
/* Output:
|
|
754
|
-
{
|
|
755
|
-
name: 'myDatabase',
|
|
756
|
-
totalSizeKB: 2457.3,
|
|
757
|
-
totalDocuments: 1250,
|
|
758
|
-
collections: [
|
|
759
|
-
{
|
|
760
|
-
name: 'users',
|
|
761
|
-
sizeKB: 1024.5,
|
|
762
|
-
documents: 500,
|
|
763
|
-
createdAt: '2024-01-01T00:00:00.000Z',
|
|
764
|
-
modifiedAt: '2024-01-15T10:30:00.000Z'
|
|
765
|
-
}
|
|
766
|
-
]
|
|
767
|
-
}
|
|
768
|
-
*/
|
|
810
|
+
---
|
|
769
811
|
|
|
770
|
-
|
|
771
|
-
const allDatabases = lacerta.listDatabases();
|
|
772
|
-
console.log('Available databases:', allDatabases);
|
|
773
|
-
```
|
|
812
|
+
## Real-World Examples
|
|
774
813
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
### LacertaDB Class
|
|
778
|
-
|
|
779
|
-
| Method | Description | Returns |
|
|
780
|
-
|--------|-------------|---------|
|
|
781
|
-
| `getDatabase(name)` | Get or create a database | `Promise<Database>` |
|
|
782
|
-
| `dropDatabase(name)` | Delete a database | `Promise<void>` |
|
|
783
|
-
| `listDatabases()` | List all database names | `Array<string>` |
|
|
784
|
-
| `createBackup(password?)` | Create full system backup | `Promise<string>` |
|
|
785
|
-
| `restoreBackup(data, password?)` | Restore from backup | `Promise<Object>` |
|
|
786
|
-
|
|
787
|
-
### Database Class
|
|
788
|
-
|
|
789
|
-
| Method | Description | Returns |
|
|
790
|
-
|--------|-------------|---------|
|
|
791
|
-
| `createCollection(name, options?)` | Create new collection | `Promise<Collection>` |
|
|
792
|
-
| `getCollection(name)` | Get existing collection | `Promise<Collection>` |
|
|
793
|
-
| `dropCollection(name)` | Delete collection | `Promise<void>` |
|
|
794
|
-
| `listCollections()` | Get collection names | `Array<string>` |
|
|
795
|
-
| `getStats()` | Get database statistics | `Object` |
|
|
796
|
-
| `updateSettings(settings)` | Update configuration | `void` |
|
|
797
|
-
| `export(format, password?)` | Export database | `Promise<string>` |
|
|
798
|
-
| `import(data, format, password?)` | Import data | `Promise<Object>` |
|
|
799
|
-
| `clearAll()` | Delete all collections | `Promise<void>` |
|
|
800
|
-
|
|
801
|
-
### Collection Class
|
|
802
|
-
|
|
803
|
-
| Method | Description | Returns |
|
|
804
|
-
|--------|-------------|---------|
|
|
805
|
-
| `add(data, options?)` | Add document | `Promise<string>` |
|
|
806
|
-
| `get(id, options?)` | Get document by ID | `Promise<Object>` |
|
|
807
|
-
| `getAll(options?)` | Get all documents | `Promise<Array>` |
|
|
808
|
-
| `update(id, updates, options?)` | Update document | `Promise<string>` |
|
|
809
|
-
| `delete(id)` | Delete document | `Promise<void>` |
|
|
810
|
-
| `query(filter, options?)` | Query documents | `Promise<Array>` |
|
|
811
|
-
| `aggregate(pipeline)` | Run aggregation | `Promise<Array>` |
|
|
812
|
-
| `batchAdd(documents, options?)` | Add multiple documents | `Promise<Array>` |
|
|
813
|
-
| `batchUpdate(updates, options?)` | Update multiple documents | `Promise<Array>` |
|
|
814
|
-
| `batchDelete(ids)` | Delete multiple documents | `Promise<Array>` |
|
|
815
|
-
| `clear()` | Remove all documents | `Promise<void>` |
|
|
816
|
-
| `on(event, callback)` | Add event listener | `void` |
|
|
817
|
-
| `off(event, callback)` | Remove event listener | `void` |
|
|
818
|
-
| `clearCache()` | Clear query cache | `void` |
|
|
819
|
-
|
|
820
|
-
## 💡 Examples
|
|
821
|
-
|
|
822
|
-
### Todo Application
|
|
814
|
+
### 💬 Chat Application
|
|
823
815
|
|
|
824
816
|
```javascript
|
|
825
|
-
// Initialize
|
|
817
|
+
// Initialize encrypted database for chat
|
|
826
818
|
const lacerta = new LacertaDB();
|
|
827
|
-
const db = await lacerta.
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
await
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
819
|
+
const db = await lacerta.getSecureDatabase('chat-app', userPin);
|
|
820
|
+
|
|
821
|
+
// Create collections
|
|
822
|
+
const messages = await db.createCollection('messages');
|
|
823
|
+
const conversations = await db.createCollection('conversations');
|
|
824
|
+
|
|
825
|
+
// Create indexes for performance
|
|
826
|
+
await messages.createIndex('conversationId', { type: 'hash' });
|
|
827
|
+
await messages.createIndex('timestamp', { type: 'btree' });
|
|
828
|
+
await messages.createIndex('content', { type: 'text' });
|
|
829
|
+
|
|
830
|
+
// Store message with attachment
|
|
831
|
+
const messageId = await messages.add({
|
|
832
|
+
conversationId: 'conv_123',
|
|
833
|
+
userId: 'user_456',
|
|
834
|
+
content: 'Check out this photo!',
|
|
835
|
+
timestamp: Date.now(),
|
|
836
|
+
read: false
|
|
843
837
|
}, {
|
|
844
|
-
|
|
838
|
+
attachments: [imageFile],
|
|
839
|
+
permanent: false
|
|
845
840
|
});
|
|
846
841
|
|
|
847
|
-
//
|
|
848
|
-
await
|
|
849
|
-
|
|
850
|
-
|
|
842
|
+
// Get recent messages with pagination
|
|
843
|
+
const recentMessages = await messages.query(
|
|
844
|
+
{ conversationId: 'conv_123' },
|
|
845
|
+
{
|
|
846
|
+
sort: { timestamp: -1 },
|
|
847
|
+
limit: 20,
|
|
848
|
+
skip: 0
|
|
849
|
+
}
|
|
850
|
+
);
|
|
851
|
+
|
|
852
|
+
// Search messages
|
|
853
|
+
const searchResults = await messages.query({
|
|
854
|
+
content: { $text: 'photo' }
|
|
851
855
|
});
|
|
852
856
|
|
|
853
|
-
//
|
|
854
|
-
|
|
855
|
-
{
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
$sum: { $cond: ['$completed', 1, 0] }
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
]);
|
|
857
|
+
// Mark messages as read
|
|
858
|
+
await messages.batchUpdate(
|
|
859
|
+
unreadMessages.map(msg => ({
|
|
860
|
+
id: msg._id,
|
|
861
|
+
data: { read: true }
|
|
862
|
+
}))
|
|
863
|
+
);
|
|
865
864
|
```
|
|
866
865
|
|
|
867
|
-
### E-Commerce
|
|
866
|
+
### 🛒 E-Commerce Cart
|
|
868
867
|
|
|
869
868
|
```javascript
|
|
870
|
-
//
|
|
869
|
+
// Shopping cart with product management
|
|
871
870
|
const db = await lacerta.getDatabase('shop');
|
|
871
|
+
const cart = await db.createCollection('cart');
|
|
872
872
|
const products = await db.createCollection('products');
|
|
873
|
-
const orders = await db.createCollection('orders');
|
|
874
|
-
|
|
875
|
-
// Product with images
|
|
876
|
-
const productId = await products.add(
|
|
877
|
-
{
|
|
878
|
-
name: 'Wireless Mouse',
|
|
879
|
-
price: 29.99,
|
|
880
|
-
stock: 150,
|
|
881
|
-
category: 'electronics'
|
|
882
|
-
},
|
|
883
|
-
{
|
|
884
|
-
attachments: [productImage1, productImage2]
|
|
885
|
-
}
|
|
886
|
-
);
|
|
887
873
|
|
|
888
|
-
//
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
}, {
|
|
895
|
-
projection: { name: 1, stock: 1, price: 1 }
|
|
874
|
+
// Add product to cart
|
|
875
|
+
await cart.add({
|
|
876
|
+
productId: 'prod_123',
|
|
877
|
+
quantity: 2,
|
|
878
|
+
price: 29.99,
|
|
879
|
+
addedAt: Date.now()
|
|
896
880
|
});
|
|
897
881
|
|
|
898
|
-
//
|
|
899
|
-
const
|
|
900
|
-
{ $
|
|
901
|
-
{
|
|
902
|
-
$lookup: {
|
|
882
|
+
// Calculate cart total with aggregation
|
|
883
|
+
const cartTotal = await cart.aggregate([
|
|
884
|
+
{ $lookup: {
|
|
903
885
|
from: 'products',
|
|
904
886
|
localField: 'productId',
|
|
905
887
|
foreignField: '_id',
|
|
906
888
|
as: 'product'
|
|
907
889
|
}
|
|
908
890
|
},
|
|
909
|
-
{
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
orderCount: { $count: {} }
|
|
891
|
+
{ $project: {
|
|
892
|
+
quantity: 1,
|
|
893
|
+
price: 1,
|
|
894
|
+
total: { $multiply: ['$quantity', '$price'] }
|
|
914
895
|
}
|
|
915
896
|
},
|
|
916
|
-
{ $
|
|
897
|
+
{ $group: {
|
|
898
|
+
_id: null,
|
|
899
|
+
subtotal: { $sum: '$total' },
|
|
900
|
+
items: { $sum: '$quantity' }
|
|
901
|
+
}
|
|
902
|
+
}
|
|
917
903
|
]);
|
|
904
|
+
|
|
905
|
+
// Clear old cart items
|
|
906
|
+
const oneWeekAgo = Date.now() - (7 * 24 * 60 * 60 * 1000);
|
|
907
|
+
await cart.query(
|
|
908
|
+
{ addedAt: { $lt: oneWeekAgo } }
|
|
909
|
+
).then(oldItems =>
|
|
910
|
+
cart.batchDelete(oldItems.map(item => item._id))
|
|
911
|
+
);
|
|
918
912
|
```
|
|
919
913
|
|
|
920
|
-
###
|
|
914
|
+
### 📝 Note-Taking App
|
|
921
915
|
|
|
922
916
|
```javascript
|
|
923
|
-
//
|
|
924
|
-
const
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
917
|
+
// Note app with full-text search
|
|
918
|
+
const db = await lacerta.getDatabase('notes');
|
|
919
|
+
const notes = await db.createCollection('notes');
|
|
920
|
+
|
|
921
|
+
// Create search index
|
|
922
|
+
await notes.createIndex('content', { type: 'text' });
|
|
923
|
+
await notes.createIndex('tags', { type: 'hash' });
|
|
924
|
+
|
|
925
|
+
// Save note with auto-save
|
|
926
|
+
let noteId;
|
|
927
|
+
const autoSave = async (content) => {
|
|
928
|
+
if (noteId) {
|
|
929
|
+
await notes.update(noteId, {
|
|
930
|
+
content,
|
|
931
|
+
lastModified: Date.now()
|
|
932
|
+
});
|
|
933
|
+
} else {
|
|
934
|
+
noteId = await notes.add({
|
|
935
|
+
title: 'New Note',
|
|
936
|
+
content,
|
|
937
|
+
tags: [],
|
|
938
|
+
createdAt: Date.now()
|
|
939
|
+
}, { permanent: true });
|
|
939
940
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
// Cleanup expired sessions
|
|
943
|
-
const now = Date.now();
|
|
944
|
-
const expired = await sessions.query({
|
|
945
|
-
expiresAt: { $lt: now }
|
|
946
|
-
});
|
|
941
|
+
};
|
|
947
942
|
|
|
948
|
-
|
|
943
|
+
// Search notes
|
|
944
|
+
const searchNotes = async (query) => {
|
|
945
|
+
return await notes.query({
|
|
946
|
+
$or: [
|
|
947
|
+
{ content: { $text: query } },
|
|
948
|
+
{ title: { $regex: query } },
|
|
949
|
+
{ tags: { $in: [query] } }
|
|
950
|
+
]
|
|
951
|
+
});
|
|
952
|
+
};
|
|
949
953
|
```
|
|
950
954
|
|
|
951
|
-
|
|
955
|
+
---
|
|
956
|
+
|
|
957
|
+
## Performance Optimization
|
|
952
958
|
|
|
953
|
-
|
|
959
|
+
### 📈 Performance Monitoring
|
|
954
960
|
|
|
955
961
|
```javascript
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
switch(error.code) {
|
|
966
|
-
case 'DOCUMENT_NOT_FOUND':
|
|
967
|
-
// Handle missing document
|
|
968
|
-
break;
|
|
969
|
-
case 'ENCRYPTION_FAILED':
|
|
970
|
-
// Handle encryption error
|
|
971
|
-
break;
|
|
972
|
-
case 'QUOTA_EXCEEDED':
|
|
973
|
-
// Handle storage limit
|
|
974
|
-
break;
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
```
|
|
962
|
+
// Enable performance monitoring
|
|
963
|
+
lacerta.performanceMonitor.startMonitoring();
|
|
964
|
+
|
|
965
|
+
// Get real-time statistics
|
|
966
|
+
const stats = lacerta.performanceMonitor.getStats();
|
|
967
|
+
console.log(`Operations/sec: ${stats.opsPerSec}`);
|
|
968
|
+
console.log(`Avg latency: ${stats.avgLatency}ms`);
|
|
969
|
+
console.log(`Cache hit rate: ${stats.cacheHitRate}%`);
|
|
970
|
+
console.log(`Memory usage: ${stats.memoryUsageMB}MB`);
|
|
979
971
|
|
|
980
|
-
|
|
972
|
+
// Get optimization recommendations
|
|
973
|
+
const tips = lacerta.performanceMonitor.getOptimizationTips();
|
|
974
|
+
tips.forEach(tip => console.log('💡', tip));
|
|
981
975
|
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
| `ENCRYPTION_FAILED` | Encryption/decryption error | Check password |
|
|
987
|
-
| `COMPRESSION_FAILED` | Compression error | Check data format |
|
|
988
|
-
| `QUOTA_EXCEEDED` | Storage limit reached | Increase limit or cleanup |
|
|
989
|
-
| `INVALID_OPERATION` | Operation not allowed | Check document flags |
|
|
990
|
-
| `TRANSACTION_FAILED` | Database transaction error | Retry operation |
|
|
976
|
+
// Monitor specific operations
|
|
977
|
+
const startTime = performance.now();
|
|
978
|
+
await users.query({ age: { $gte: 18 } });
|
|
979
|
+
console.log(`Query took: ${performance.now() - startTime}ms`);
|
|
991
980
|
|
|
992
|
-
|
|
981
|
+
// Stop monitoring
|
|
982
|
+
lacerta.performanceMonitor.stopMonitoring();
|
|
983
|
+
```
|
|
993
984
|
|
|
994
|
-
###
|
|
985
|
+
### ⚡ Best Practices
|
|
995
986
|
|
|
987
|
+
#### 1. **Indexing Strategy**
|
|
996
988
|
```javascript
|
|
997
|
-
//
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
byAgeRange: { age: { $gte: 25, $lte: 35 } }, // B-Tree index
|
|
1001
|
-
byDescription: { $text: 'search terms' }, // Text index
|
|
1002
|
-
byLocation: { $near: { lat: 0, lng: 0 } } // Geo index
|
|
1003
|
-
};
|
|
989
|
+
// Index frequently queried fields
|
|
990
|
+
await users.createIndex('email', { unique: true });
|
|
991
|
+
await users.createIndex('createdAt', { type: 'btree' });
|
|
1004
992
|
|
|
1005
|
-
//
|
|
1006
|
-
await
|
|
1007
|
-
await
|
|
1008
|
-
await users.createIndex('bio', { type: 'text' });
|
|
1009
|
-
await users.createIndex('location', { type: 'geo' });
|
|
993
|
+
// Use compound indexes for complex queries
|
|
994
|
+
await orders.createIndex('userId', { type: 'hash' });
|
|
995
|
+
await orders.createIndex('status', { type: 'hash' });
|
|
1010
996
|
```
|
|
1011
997
|
|
|
1012
|
-
|
|
998
|
+
#### 2. **Compression Settings**
|
|
999
|
+
```javascript
|
|
1000
|
+
// Enable compression for large documents
|
|
1001
|
+
await logs.add(largeLogData, { compressed: true });
|
|
1002
|
+
|
|
1003
|
+
// Disable for frequently accessed small docs
|
|
1004
|
+
await config.add(settings, { compressed: false });
|
|
1005
|
+
```
|
|
1013
1006
|
|
|
1007
|
+
#### 3. **Batch Operations**
|
|
1014
1008
|
```javascript
|
|
1015
|
-
//
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
//
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
// Time-sensitive - TTL only
|
|
1024
|
-
tempTokens: { type: 'ttl', ttl: 300000 },
|
|
1025
|
-
|
|
1026
|
-
// Sensitive data - No cache
|
|
1027
|
-
privateKeys: { type: 'none', enabled: false }
|
|
1028
|
-
};
|
|
1009
|
+
// Good: Single batch operation
|
|
1010
|
+
await users.batchAdd(thousandUsers);
|
|
1011
|
+
|
|
1012
|
+
// Bad: Individual adds in loop
|
|
1013
|
+
for (const user of thousandUsers) {
|
|
1014
|
+
await users.add(user); // Slow!
|
|
1015
|
+
}
|
|
1029
1016
|
```
|
|
1030
1017
|
|
|
1031
|
-
|
|
1018
|
+
#### 4. **Memory Management**
|
|
1019
|
+
```javascript
|
|
1020
|
+
// Configure size limits
|
|
1021
|
+
db.updateSettings({
|
|
1022
|
+
sizeLimitKB: 50000, // 50MB limit
|
|
1023
|
+
bufferLimitKB: 40000, // Start cleanup at 40MB
|
|
1024
|
+
freeSpaceEvery: 10000 // Check every 10 seconds
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
// Mark important docs as permanent
|
|
1028
|
+
await users.add(adminUser, { permanent: true });
|
|
1029
|
+
```
|
|
1032
1030
|
|
|
1031
|
+
#### 5. **Query Optimization**
|
|
1033
1032
|
```javascript
|
|
1034
|
-
//
|
|
1035
|
-
const
|
|
1036
|
-
|
|
1037
|
-
|
|
1033
|
+
// Use projection to reduce data transfer
|
|
1034
|
+
const names = await users.query(
|
|
1035
|
+
{ active: true },
|
|
1036
|
+
{ projection: { name: 1, email: 1 } }
|
|
1038
1037
|
);
|
|
1039
1038
|
|
|
1040
|
-
//
|
|
1041
|
-
await
|
|
1042
|
-
|
|
1043
|
-
// Regular PIN rotation
|
|
1044
|
-
const rotatePin = async () => {
|
|
1045
|
-
const newPin = SecureDatabaseEncryption.generateSecurePIN(12);
|
|
1046
|
-
await secureDb.changePin(currentPin, newPin);
|
|
1047
|
-
// Securely store newPin
|
|
1048
|
-
};
|
|
1039
|
+
// Use indexes for sorting
|
|
1040
|
+
await messages.createIndex('timestamp', { type: 'btree' });
|
|
1041
|
+
const recent = await messages.query({}, { sort: { timestamp: -1 } });
|
|
1049
1042
|
```
|
|
1050
1043
|
|
|
1051
|
-
|
|
1044
|
+
---
|
|
1052
1045
|
|
|
1053
|
-
|
|
1054
|
-
// Enable monitoring during development
|
|
1055
|
-
lacerta.performanceMonitor.startMonitoring();
|
|
1046
|
+
## Migration Guide
|
|
1056
1047
|
|
|
1057
|
-
|
|
1058
|
-
const testPerformance = async () => {
|
|
1059
|
-
// Test without index
|
|
1060
|
-
const start1 = Date.now();
|
|
1061
|
-
await collection.query({ field: 'value' });
|
|
1062
|
-
console.log('Without index:', Date.now() - start1);
|
|
1063
|
-
|
|
1064
|
-
// Create index
|
|
1065
|
-
await collection.createIndex('field', { type: 'hash' });
|
|
1066
|
-
|
|
1067
|
-
// Test with index
|
|
1068
|
-
const start2 = Date.now();
|
|
1069
|
-
await collection.query({ field: 'value' });
|
|
1070
|
-
console.log('With index:', Date.now() - start2);
|
|
1071
|
-
};
|
|
1048
|
+
### Schema Migrations
|
|
1072
1049
|
|
|
1073
|
-
|
|
1074
|
-
const
|
|
1075
|
-
```
|
|
1050
|
+
```javascript
|
|
1051
|
+
const migrationManager = new MigrationManager(db);
|
|
1076
1052
|
|
|
1077
|
-
|
|
1053
|
+
// Add a migration
|
|
1054
|
+
migrationManager.addMigration({
|
|
1055
|
+
version: '2.0.0',
|
|
1056
|
+
name: 'Add user roles',
|
|
1057
|
+
up: async (doc) => {
|
|
1058
|
+
if (doc.type === 'user' && !doc.role) {
|
|
1059
|
+
return { ...doc, role: 'member' };
|
|
1060
|
+
}
|
|
1061
|
+
return null;
|
|
1062
|
+
},
|
|
1063
|
+
down: async (doc) => {
|
|
1064
|
+
if (doc.type === 'user') {
|
|
1065
|
+
const { role, ...rest } = doc;
|
|
1066
|
+
return rest;
|
|
1067
|
+
}
|
|
1068
|
+
return null;
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
1078
1071
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1072
|
+
// Add another migration
|
|
1073
|
+
migrationManager.addMigration({
|
|
1074
|
+
version: '2.1.0',
|
|
1075
|
+
name: 'Add timestamps',
|
|
1076
|
+
up: async (doc) => {
|
|
1077
|
+
if (!doc.updatedAt) {
|
|
1078
|
+
return { ...doc, updatedAt: doc._modified };
|
|
1079
|
+
}
|
|
1080
|
+
return null;
|
|
1081
|
+
}
|
|
1085
1082
|
});
|
|
1086
1083
|
|
|
1087
|
-
//
|
|
1088
|
-
await
|
|
1084
|
+
// Run all migrations up to version
|
|
1085
|
+
await migrationManager.runMigrations('2.1.0');
|
|
1089
1086
|
|
|
1090
|
-
//
|
|
1091
|
-
|
|
1092
|
-
const stats = db.getStats();
|
|
1093
|
-
if (stats.totalSizeKB > 90000) {
|
|
1094
|
-
// Cleanup old non-permanent documents
|
|
1095
|
-
await collection.freeSpace();
|
|
1096
|
-
}
|
|
1097
|
-
};
|
|
1087
|
+
// Rollback if needed
|
|
1088
|
+
await migrationManager.rollback('1.0.0');
|
|
1098
1089
|
```
|
|
1099
1090
|
|
|
1100
|
-
|
|
1091
|
+
### Upgrading from v0.5.x to v0.6.x
|
|
1092
|
+
|
|
1093
|
+
```javascript
|
|
1094
|
+
// Export data from old version
|
|
1095
|
+
const oldDb = await getOldDatabase();
|
|
1096
|
+
const backup = await oldDb.export('json');
|
|
1097
|
+
|
|
1098
|
+
// Import to new version
|
|
1099
|
+
const newLacerta = new LacertaDB();
|
|
1100
|
+
const newDb = await newLacerta.getDatabase('upgraded-db');
|
|
1101
|
+
await newDb.import(backup, 'json');
|
|
1102
|
+
|
|
1103
|
+
// Update indexes for better performance
|
|
1104
|
+
const collections = newDb.listCollections();
|
|
1105
|
+
for (const collName of collections) {
|
|
1106
|
+
const coll = await newDb.getCollection(collName);
|
|
1107
|
+
await coll.verifyIndexes();
|
|
1108
|
+
}
|
|
1109
|
+
```
|
|
1101
1110
|
|
|
1102
|
-
|
|
1111
|
+
---
|
|
1103
1112
|
|
|
1104
|
-
|
|
1113
|
+
## Troubleshooting
|
|
1105
1114
|
|
|
1106
|
-
###
|
|
1115
|
+
### Common Issues and Solutions
|
|
1107
1116
|
|
|
1108
|
-
|
|
1109
|
-
// v4.x code (still works)
|
|
1110
|
-
const collection = await db.createCollection('data');
|
|
1111
|
-
const results = await collection.query({ status: 'active' });
|
|
1117
|
+
#### 🔴 **QuotaExceededError**
|
|
1112
1118
|
|
|
1113
|
-
|
|
1114
|
-
const collection = await db.createCollection('data');
|
|
1119
|
+
**Problem:** Browser storage limit reached
|
|
1115
1120
|
|
|
1116
|
-
|
|
1117
|
-
|
|
1121
|
+
**Solutions:**
|
|
1122
|
+
```javascript
|
|
1123
|
+
// 1. Check current usage
|
|
1124
|
+
const stats = db.getStats();
|
|
1125
|
+
console.log(`Using ${stats.totalSizeKB}KB`);
|
|
1118
1126
|
|
|
1119
|
-
//
|
|
1120
|
-
await
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1127
|
+
// 2. Clear old data
|
|
1128
|
+
await logs.clear();
|
|
1129
|
+
|
|
1130
|
+
// 3. Enable automatic cleanup
|
|
1131
|
+
db.updateSettings({
|
|
1132
|
+
sizeLimitKB: 45000,
|
|
1133
|
+
bufferLimitKB: 40000
|
|
1124
1134
|
});
|
|
1125
1135
|
|
|
1126
|
-
//
|
|
1127
|
-
|
|
1136
|
+
// 4. Use compression
|
|
1137
|
+
await collection.add(data, { compressed: true });
|
|
1128
1138
|
```
|
|
1129
1139
|
|
|
1130
|
-
|
|
1140
|
+
#### 🔴 **Slow Query Performance**
|
|
1131
1141
|
|
|
1132
|
-
|
|
1133
|
-
2. **Configure Cache Strategies** based on access patterns
|
|
1134
|
-
3. **Enable PIN encryption** for databases with sensitive data
|
|
1135
|
-
4. **Update force delete** calls for permanent document management
|
|
1136
|
-
5. **Monitor performance** to identify optimization opportunities
|
|
1142
|
+
**Problem:** Queries taking too long
|
|
1137
1143
|
|
|
1144
|
+
**Solutions:**
|
|
1138
1145
|
```javascript
|
|
1139
|
-
//
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
// 4. For sensitive data, create new secure database
|
|
1157
|
-
const secureDb = await lacerta.getSecureDatabase('secure', pin);
|
|
1158
|
-
// Migrate sensitive data to secure database
|
|
1159
|
-
|
|
1160
|
-
console.log('Migration complete!');
|
|
1161
|
-
};
|
|
1146
|
+
// 1. Add appropriate indexes
|
|
1147
|
+
await collection.createIndex('fieldName', { type: 'btree' });
|
|
1148
|
+
|
|
1149
|
+
// 2. Use query projection
|
|
1150
|
+
const results = await collection.query(
|
|
1151
|
+
filter,
|
|
1152
|
+
{ projection: { needed: 1, fields: 1 } }
|
|
1153
|
+
);
|
|
1154
|
+
|
|
1155
|
+
// 3. Enable caching
|
|
1156
|
+
collection.configureCacheStrategy({
|
|
1157
|
+
type: 'lru',
|
|
1158
|
+
maxSize: 100
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
// 4. Check index health
|
|
1162
|
+
const report = await collection.verifyIndexes();
|
|
1162
1163
|
```
|
|
1163
1164
|
|
|
1164
|
-
|
|
1165
|
+
#### 🔴 **Memory Leaks in SPAs**
|
|
1165
1166
|
|
|
1166
|
-
|
|
1167
|
+
**Problem:** Memory usage growing over time
|
|
1167
1168
|
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1169
|
+
**Solutions:**
|
|
1170
|
+
```javascript
|
|
1171
|
+
// 1. Clean up on component unmount
|
|
1172
|
+
componentWillUnmount() {
|
|
1173
|
+
this.collection.off('update', this.handleUpdate);
|
|
1174
|
+
this.db.destroy();
|
|
1175
|
+
}
|
|
1172
1176
|
|
|
1173
|
-
|
|
1177
|
+
// 2. Use destroy method
|
|
1178
|
+
window.addEventListener('beforeunload', () => {
|
|
1179
|
+
lacerta.destroy();
|
|
1180
|
+
});
|
|
1174
1181
|
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1182
|
+
// 3. Clear caches periodically
|
|
1183
|
+
setInterval(() => {
|
|
1184
|
+
collection.clearCache();
|
|
1185
|
+
}, 300000); // Every 5 minutes
|
|
1186
|
+
```
|
|
1187
|
+
|
|
1188
|
+
#### 🔴 **Encryption Issues**
|
|
1189
|
+
|
|
1190
|
+
**Problem:** Cannot access encrypted database
|
|
1179
1191
|
|
|
1180
|
-
|
|
1181
|
-
|
|
1192
|
+
**Solutions:**
|
|
1193
|
+
```javascript
|
|
1194
|
+
// 1. Verify PIN is correct
|
|
1195
|
+
try {
|
|
1196
|
+
const db = await lacerta.getSecureDatabase('app', pin);
|
|
1197
|
+
} catch (error) {
|
|
1198
|
+
if (error.code === 'ENCRYPTION_FAILED') {
|
|
1199
|
+
console.error('Invalid PIN');
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1182
1202
|
|
|
1183
|
-
|
|
1184
|
-
|
|
1203
|
+
// 2. Check if salt is preserved
|
|
1204
|
+
const metadata = db.encryption.exportMetadata();
|
|
1205
|
+
localStorage.setItem('app-salt', metadata.salt);
|
|
1185
1206
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1207
|
+
// 3. Use same encryption config
|
|
1208
|
+
const db = await lacerta.getSecureDatabase('app', pin, salt, {
|
|
1209
|
+
iterations: 100000, // Must match original
|
|
1210
|
+
keyLength: 256
|
|
1211
|
+
});
|
|
1188
1212
|
```
|
|
1189
1213
|
|
|
1190
|
-
###
|
|
1214
|
+
### Debug Mode
|
|
1191
1215
|
|
|
1192
1216
|
```javascript
|
|
1193
|
-
//
|
|
1194
|
-
|
|
1217
|
+
// Enable debug logging
|
|
1218
|
+
window.LACERTA_DEBUG = true;
|
|
1219
|
+
|
|
1220
|
+
// Log all operations
|
|
1221
|
+
const originalAdd = collection.add;
|
|
1222
|
+
collection.add = async function(...args) {
|
|
1223
|
+
console.log('Adding document:', args);
|
|
1224
|
+
const result = await originalAdd.apply(this, args);
|
|
1225
|
+
console.log('Document added:', result);
|
|
1226
|
+
return result;
|
|
1227
|
+
};
|
|
1228
|
+
```
|
|
1195
1229
|
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1230
|
+
---
|
|
1231
|
+
|
|
1232
|
+
## Error Handling
|
|
1233
|
+
|
|
1234
|
+
### Error Types
|
|
1235
|
+
|
|
1236
|
+
| Error Code | Description | Resolution |
|
|
1237
|
+
|------------|-------------|------------|
|
|
1238
|
+
| `DATABASE_OPEN_FAILED` | Cannot open database | Check browser support, clear browser data |
|
|
1239
|
+
| `DOCUMENT_NOT_FOUND` | Document doesn't exist | Verify document ID exists |
|
|
1240
|
+
| `COLLECTION_NOT_FOUND` | Collection doesn't exist | Create collection first |
|
|
1241
|
+
| `COLLECTION_EXISTS` | Collection already exists | Use getCollection instead |
|
|
1242
|
+
| `UNIQUE_CONSTRAINT` | Unique index violation | Use different value or update existing |
|
|
1243
|
+
| `QUOTA_EXCEEDED` | Storage limit reached | Clear old data or increase limit |
|
|
1244
|
+
| `ENCRYPTION_NOT_INITIALIZED` | Encryption required | Use getSecureDatabase |
|
|
1245
|
+
| `ENCRYPTION_FAILED` | Decryption failed | Check PIN and salt |
|
|
1246
|
+
| `PERMANENT_DOCUMENT_PROTECTION` | Cannot delete permanent | Use force: true option |
|
|
1247
|
+
| `TRANSACTION_FAILED` | Transaction aborted | Retry operation |
|
|
1248
|
+
| `INVALID_FORMAT` | Invalid data format | Check data structure |
|
|
1249
|
+
|
|
1250
|
+
<details>
|
|
1251
|
+
<summary><strong>Example Error Handling</strong></summary>
|
|
1252
|
+
|
|
1253
|
+
```javascript
|
|
1254
|
+
// Comprehensive error handling
|
|
1255
|
+
class DatabaseService {
|
|
1256
|
+
async safeOperation(operation) {
|
|
1257
|
+
try {
|
|
1258
|
+
return await operation();
|
|
1259
|
+
} catch (error) {
|
|
1260
|
+
switch (error.code) {
|
|
1261
|
+
case 'UNIQUE_CONSTRAINT':
|
|
1262
|
+
console.error('Duplicate entry:', error.message);
|
|
1263
|
+
// Handle duplicate
|
|
1264
|
+
break;
|
|
1265
|
+
|
|
1266
|
+
case 'QUOTA_EXCEEDED':
|
|
1267
|
+
console.warn('Storage full, cleaning up...');
|
|
1268
|
+
await this.cleanup();
|
|
1269
|
+
// Retry operation
|
|
1270
|
+
return await operation();
|
|
1271
|
+
|
|
1272
|
+
case 'DOCUMENT_NOT_FOUND':
|
|
1273
|
+
console.error('Document not found');
|
|
1274
|
+
return null;
|
|
1275
|
+
|
|
1276
|
+
case 'ENCRYPTION_FAILED':
|
|
1277
|
+
console.error('Invalid credentials');
|
|
1278
|
+
throw new Error('Authentication failed');
|
|
1279
|
+
|
|
1280
|
+
default:
|
|
1281
|
+
console.error('Database error:', error);
|
|
1282
|
+
throw error;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1206
1286
|
|
|
1207
|
-
|
|
1208
|
-
|
|
1287
|
+
async cleanup() {
|
|
1288
|
+
const logs = await db.getCollection('logs');
|
|
1289
|
+
const oldLogs = await logs.query({
|
|
1290
|
+
timestamp: { $lt: Date.now() - 86400000 }
|
|
1291
|
+
});
|
|
1292
|
+
await logs.batchDelete(oldLogs.map(log => log._id));
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
// Usage
|
|
1297
|
+
const service = new DatabaseService();
|
|
1298
|
+
const result = await service.safeOperation(async () => {
|
|
1299
|
+
return await users.add({ email: 'test@example.com' });
|
|
1300
|
+
});
|
|
1209
1301
|
```
|
|
1210
1302
|
|
|
1211
|
-
|
|
1303
|
+
</details>
|
|
1212
1304
|
|
|
1213
|
-
|
|
1305
|
+
---
|
|
1214
1306
|
|
|
1215
|
-
##
|
|
1307
|
+
## Comparison
|
|
1308
|
+
|
|
1309
|
+
### LacertaDB vs Alternatives
|
|
1310
|
+
|
|
1311
|
+
| Feature | LacertaDB | LocalStorage | IndexedDB (Raw) | PouchDB | Dexie.js |
|
|
1312
|
+
|---------|-----------|--------------|-----------------|---------|----------|
|
|
1313
|
+
| **Size Limit** | ~2GB* | 5-10MB | ~2GB* | ~2GB* | ~2GB* |
|
|
1314
|
+
| **Encryption** | ✅ Built-in | ❌ | ❌ | ⚠️ Plugin | ❌ |
|
|
1315
|
+
| **Compression** | ✅ Automatic | ❌ | ❌ | ❌ | ❌ |
|
|
1316
|
+
| **Query Language** | ✅ MongoDB-like | ❌ | ❌ | ✅ Map/Reduce | ⚠️ Limited |
|
|
1317
|
+
| **Indexes** | ✅ Multiple types | ❌ | ⚠️ Basic | ✅ | ✅ |
|
|
1318
|
+
| **Aggregation** | ✅ Full pipeline | ❌ | ❌ | ⚠️ Views | ❌ |
|
|
1319
|
+
| **File Attachments** | ✅ OPFS | ❌ | ❌ | ✅ | ❌ |
|
|
1320
|
+
| **Performance Monitor** | ✅ Built-in | ❌ | ❌ | ❌ | ❌ |
|
|
1321
|
+
| **Bundle Size** | ~45KB | 0KB | 0KB | ~140KB | ~90KB |
|
|
1322
|
+
| **TypeScript** | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
1323
|
+
| **Learning Curve** | Medium | Low | High | Medium | Low |
|
|
1324
|
+
|
|
1325
|
+
*Actual limit varies by browser and available disk space
|
|
1326
|
+
|
|
1327
|
+
### When to Use LacertaDB
|
|
1328
|
+
|
|
1329
|
+
✅ **Use LacertaDB when you need:**
|
|
1330
|
+
- Client-side encryption for sensitive data
|
|
1331
|
+
- Complex queries and aggregations
|
|
1332
|
+
- Automatic compression to save space
|
|
1333
|
+
- MongoDB-like query syntax
|
|
1334
|
+
- Built-in performance monitoring
|
|
1335
|
+
- File attachment support
|
|
1336
|
+
|
|
1337
|
+
❌ **Consider alternatives when:**
|
|
1338
|
+
- You need < 1MB storage (use localStorage)
|
|
1339
|
+
- You need server synchronization (use PouchDB)
|
|
1340
|
+
- You need minimal bundle size (use raw IndexedDB)
|
|
1341
|
+
- You only need key-value storage (use localStorage)
|
|
1342
|
+
|
|
1343
|
+
---
|
|
1344
|
+
|
|
1345
|
+
## 📝 License
|
|
1216
1346
|
|
|
1217
|
-
|
|
1218
|
-
- Uses TurboSerial and TurboBase64 for high performance
|
|
1219
|
-
- Inspired by MongoDB's query language
|
|
1220
|
-
- Powered by modern browser APIs (IndexedDB, OPFS, Web Crypto)
|
|
1347
|
+
MIT © 2024 LacertaDB Contributors
|
|
1221
1348
|
|
|
1222
|
-
|
|
1349
|
+
---
|
|
1223
1350
|
|
|
1224
|
-
|
|
1225
|
-
- 🧠 **Smart Caching**: LRU, LFU, and TTL strategies with per-collection configuration
|
|
1226
|
-
- 🔐 **Database Encryption**: PIN-based security with 1M PBKDF2 iterations for private keys
|
|
1227
|
-
- 💪 **Force Delete**: Administrative control over permanent documents
|
|
1228
|
-
- 📊 **Enhanced Monitoring**: Detailed performance metrics and optimization tips
|
|
1351
|
+
## 🙏 Acknowledgments
|
|
1229
1352
|
|
|
1353
|
+
Special thanks to all contributors and the open-source community.
|
|
1354
|
+
|
|
1355
|
+
Built with ❤️ using:
|
|
1356
|
+
- [TurboSerial](https://github.com/pixagram/turboserial) for serialization
|
|
1357
|
+
- [TurboBase64](https://github.com/pixagram/turbobase64) for encoding
|
|
1358
|
+
|
|
1359
|
+
---
|
|
1230
1360
|
---
|
|
1231
1361
|
|
|
1232
|
-
<
|
|
1233
|
-
<strong
|
|
1234
|
-
|
|
1235
|
-
<i>50x faster queries • Military-grade encryption • Smart caching</i>
|
|
1236
|
-
<br><br>
|
|
1237
|
-
Made with ❤️ by Pixagram Blockchain
|
|
1238
|
-
</div>
|
|
1362
|
+
<p align="center">
|
|
1363
|
+
<strong>Star ⭐ this repository if you find it helpful!</strong>
|
|
1364
|
+
</p>
|