sehawq.db 2.3.0 → 2.4.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/package.json +1 -1
- package/readme.md +260 -0
- package/src/index.js +323 -1
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -16,6 +16,28 @@ Minimal, dependency-free, and easy-to-use. Perfect for small projects, bots, CLI
|
|
|
16
16
|
- **Dot-notation namespace** — Access nested data with `user.123.balance`.
|
|
17
17
|
- **Sync & Async API** — Choose blocking or non-blocking file operations.
|
|
18
18
|
- **Auto-save** — Writes changes to disk at regular intervals.
|
|
19
|
+
- **🔥 NEW: Advanced Query System** — Filter, sort, and paginate your data.
|
|
20
|
+
- **🔥 NEW: Aggregation Functions** — Calculate sum, average, min, max, and more.
|
|
21
|
+
- **🔥 NEW: Method Chaining** — Chain operations for complex queries.
|
|
22
|
+
|
|
23
|
+
### 🔍 Query System
|
|
24
|
+
- `find(filter)` — Find all entries matching a filter function.
|
|
25
|
+
- `findOne(filter)` — Find the first entry matching a filter.
|
|
26
|
+
- `where(field, operator, value)` — Filter by field with operators (`>`, `<`, `>=`, `<=`, `=`, `!=`, `in`, `contains`, `startsWith`, `endsWith`).
|
|
27
|
+
|
|
28
|
+
### 📊 Aggregation Functions
|
|
29
|
+
- `count(filter)` — Count entries (with optional filter).
|
|
30
|
+
- `sum(field)` — Sum numeric values by field.
|
|
31
|
+
- `avg(field)` — Calculate average of numeric values.
|
|
32
|
+
- `min(field)` / `max(field)` — Find minimum/maximum values.
|
|
33
|
+
- `groupBy(field)` — Group entries by field value.
|
|
34
|
+
|
|
35
|
+
### ⛓️ Method Chaining & Pagination
|
|
36
|
+
- `sort(field, direction)` — Sort results by field (`'asc'` or `'desc'`).
|
|
37
|
+
- `limit(count)` — Limit number of results.
|
|
38
|
+
- `skip(count)` — Skip number of results for pagination.
|
|
39
|
+
- `first()` / `last()` — Get first or last result.
|
|
40
|
+
- `values()` / `keys()` — Extract values or keys only.
|
|
19
41
|
|
|
20
42
|
### 🔧 Array Helpers
|
|
21
43
|
- `push(key, value)` — Add an element to an array.
|
|
@@ -44,3 +66,241 @@ Hooks into database operations:
|
|
|
44
66
|
|
|
45
67
|
```bash
|
|
46
68
|
npm install sehawq.db
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## ⚡ Quick Start (30 seconds)
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
const db = require('sehawq.db')();
|
|
77
|
+
|
|
78
|
+
// Store data
|
|
79
|
+
db.set('user', 'John Doe');
|
|
80
|
+
db.set('score', 100);
|
|
81
|
+
|
|
82
|
+
// Get data
|
|
83
|
+
console.log(db.get('user')); // John Doe
|
|
84
|
+
console.log(db.get('score')); // 100
|
|
85
|
+
|
|
86
|
+
// That's it! 🎉
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 🔧 Detailed Usage
|
|
92
|
+
|
|
93
|
+
### Basic Operations
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
const SehawqDB = require('sehawq.db');
|
|
97
|
+
const db = new SehawqDB({
|
|
98
|
+
path: './mydata.json',
|
|
99
|
+
autoSaveInterval: 5000 // Auto-save every 5 seconds
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Set and get data
|
|
103
|
+
db.set('user.123.name', 'John Doe');
|
|
104
|
+
db.set('user.123.balance', 1000);
|
|
105
|
+
console.log(db.get('user.123')); // { name: 'John Doe', balance: 1000 }
|
|
106
|
+
|
|
107
|
+
// Check if key exists
|
|
108
|
+
if (db.has('user.123')) {
|
|
109
|
+
console.log('User exists!');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Delete data
|
|
113
|
+
db.delete('user.123.balance');
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Array Operations
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
// Initialize an array
|
|
120
|
+
db.set('users', []);
|
|
121
|
+
|
|
122
|
+
// Add items
|
|
123
|
+
db.push('users', { id: 1, name: 'Alice' });
|
|
124
|
+
db.push('users', { id: 2, name: 'Bob' });
|
|
125
|
+
|
|
126
|
+
// Remove items
|
|
127
|
+
db.pull('users', { id: 1, name: 'Alice' });
|
|
128
|
+
|
|
129
|
+
console.log(db.get('users')); // [{ id: 2, name: 'Bob' }]
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Math Operations
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
db.set('score', 100);
|
|
136
|
+
db.add('score', 50); // score = 150
|
|
137
|
+
db.subtract('score', 20); // score = 130
|
|
138
|
+
console.log(db.get('score')); // 130
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Advanced Queries
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
// Sample data
|
|
145
|
+
db.set('user1', { name: 'Alice', age: 25, active: true, score: 95 });
|
|
146
|
+
db.set('user2', { name: 'Bob', age: 30, active: false, score: 87 });
|
|
147
|
+
db.set('user3', { name: 'Charlie', age: 22, active: true, score: 92 });
|
|
148
|
+
|
|
149
|
+
// Find all active users
|
|
150
|
+
const activeUsers = db.find(user => user.active).values();
|
|
151
|
+
console.log(activeUsers);
|
|
152
|
+
|
|
153
|
+
// Find users older than 24
|
|
154
|
+
const olderUsers = db.where('age', '>', 24).values();
|
|
155
|
+
|
|
156
|
+
// Complex query with chaining
|
|
157
|
+
const topActiveUsers = db
|
|
158
|
+
.find(user => user.active)
|
|
159
|
+
.sort('score', 'desc')
|
|
160
|
+
.limit(2)
|
|
161
|
+
.values();
|
|
162
|
+
|
|
163
|
+
console.log(topActiveUsers); // Top 2 active users by score
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Aggregation
|
|
167
|
+
|
|
168
|
+
```javascript
|
|
169
|
+
// Count total users
|
|
170
|
+
const totalUsers = db.count();
|
|
171
|
+
|
|
172
|
+
// Count active users
|
|
173
|
+
const activeCount = db.count(user => user.active);
|
|
174
|
+
|
|
175
|
+
// Calculate average age
|
|
176
|
+
const avgAge = db.avg('age');
|
|
177
|
+
|
|
178
|
+
// Find highest score
|
|
179
|
+
const highestScore = db.max('score');
|
|
180
|
+
|
|
181
|
+
// Group users by active status
|
|
182
|
+
const grouped = db.groupBy('active');
|
|
183
|
+
console.log(grouped);
|
|
184
|
+
// {
|
|
185
|
+
// 'true': [{ name: 'Alice', ... }, { name: 'Charlie', ... }],
|
|
186
|
+
// 'false': [{ name: 'Bob', ... }]
|
|
187
|
+
// }
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Pagination
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
// Get users with pagination (page 2, 10 items per page)
|
|
194
|
+
const page2Users = db
|
|
195
|
+
.find()
|
|
196
|
+
.skip(10)
|
|
197
|
+
.limit(10)
|
|
198
|
+
.values();
|
|
199
|
+
|
|
200
|
+
// Sort and paginate
|
|
201
|
+
const sortedPage = db
|
|
202
|
+
.find()
|
|
203
|
+
.sort('name', 'asc')
|
|
204
|
+
.skip(20)
|
|
205
|
+
.limit(5)
|
|
206
|
+
.values();
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Event Handling
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
// Listen for database events
|
|
213
|
+
db.on('set', (data) => {
|
|
214
|
+
console.log(`Set: ${data.key} = ${data.value}`);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
db.on('delete', (data) => {
|
|
218
|
+
console.log(`Deleted: ${data.key}`);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
db.on('backup', (data) => {
|
|
222
|
+
console.log(`Backup created: ${data.backupPath}`);
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Backup & Restore
|
|
227
|
+
|
|
228
|
+
```javascript
|
|
229
|
+
// Create backup
|
|
230
|
+
await db.backup('./backup.json');
|
|
231
|
+
|
|
232
|
+
// Restore from backup
|
|
233
|
+
await db.restore('./backup.json');
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## 📝 Changelog
|
|
239
|
+
|
|
240
|
+
### Changes in 2.4.2 🔥
|
|
241
|
+
|
|
242
|
+
- ✨ **Added Query System**
|
|
243
|
+
- `find(filter)` — Filter entries with custom functions
|
|
244
|
+
- `findOne(filter)` — Find first matching entry
|
|
245
|
+
- `where(field, operator, value)` — Field-based filtering with operators
|
|
246
|
+
- **Operators**: `>`, `<`, `>=`, `<=`, `=`, `!=`, `in`, `contains`, `startsWith`, `endsWith`
|
|
247
|
+
|
|
248
|
+
- ✨ **Added Aggregation Functions**
|
|
249
|
+
- `count(filter)` — Count entries with optional filtering
|
|
250
|
+
- `sum(field)` — Sum numeric values by field
|
|
251
|
+
- `avg(field)` — Calculate average of numeric values
|
|
252
|
+
- `min(field)` / `max(field)` — Find minimum/maximum values
|
|
253
|
+
- `groupBy(field)` — Group entries by field value
|
|
254
|
+
|
|
255
|
+
- ✨ **Added Method Chaining Support**
|
|
256
|
+
- New `QueryResult` class enables chaining operations
|
|
257
|
+
- `sort(field, direction)` — Sort results ascending or descending
|
|
258
|
+
- `limit(count)` / `skip(count)` — Pagination support
|
|
259
|
+
- `first()` / `last()` — Get first or last result
|
|
260
|
+
- `values()` / `keys()` — Extract values or keys only
|
|
261
|
+
- `filter()` — Apply additional filtering
|
|
262
|
+
- `map()` — Transform results
|
|
263
|
+
|
|
264
|
+
- 🔧 **Enhanced Dot Notation**
|
|
265
|
+
- Full support for nested queries and filtering
|
|
266
|
+
- Deep object traversal for all query operations
|
|
267
|
+
|
|
268
|
+
- 📊 **Advanced Query Examples**
|
|
269
|
+
```javascript
|
|
270
|
+
// Complex chained queries
|
|
271
|
+
db.find(user => user.active)
|
|
272
|
+
.sort('score', 'desc')
|
|
273
|
+
.limit(10)
|
|
274
|
+
.values();
|
|
275
|
+
|
|
276
|
+
// Field-based filtering
|
|
277
|
+
db.where('age', '>=', 18)
|
|
278
|
+
.where('status', 'in', ['premium', 'gold'])
|
|
279
|
+
.count();
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Changes in 2.4.2x
|
|
283
|
+
|
|
284
|
+
- ✨ Initial release with core features
|
|
285
|
+
- 🔧 Basic CRUD operations (`set`, `get`, `delete`, `has`)
|
|
286
|
+
- 🔧 Dot notation support for nested data
|
|
287
|
+
- 🔧 Array helpers (`push`, `pull`)
|
|
288
|
+
- 🔧 Math helpers (`add`, `subtract`)
|
|
289
|
+
- 🔧 Auto-save functionality
|
|
290
|
+
- 🔧 Event emitter system
|
|
291
|
+
- 🔧 Backup & restore functionality
|
|
292
|
+
- 🔧 Atomic file operations with temporary files
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## 📄 License
|
|
297
|
+
|
|
298
|
+
MIT License - see [LICENSE](https://github.com/sehawq/sehawq.db/blob/main/LICENSE) file for details.
|
|
299
|
+
|
|
300
|
+
## 🤝 Contributing
|
|
301
|
+
|
|
302
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
303
|
+
|
|
304
|
+
## 🐛 Issues
|
|
305
|
+
|
|
306
|
+
Found a bug? Please report it on [GitHub Issues](https://github.com/sehawq/sehawq.db/issues).
|
package/src/index.js
CHANGED
|
@@ -113,6 +113,188 @@ class SehawqDB extends EventEmitter {
|
|
|
113
113
|
return this.add(key, -number);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// ---------------- NEW: Query System ----------------
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Find all entries that match the filter function
|
|
120
|
+
* @param {Function} filter - Filter function (item, key) => boolean
|
|
121
|
+
* @returns {QueryResult} Chainable query result
|
|
122
|
+
*/
|
|
123
|
+
find(filter) {
|
|
124
|
+
const results = [];
|
|
125
|
+
|
|
126
|
+
if (typeof filter === 'function') {
|
|
127
|
+
for (const [key, value] of Object.entries(this.data)) {
|
|
128
|
+
if (filter(value, key)) {
|
|
129
|
+
results.push({ key, value });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
// Eğer filter verilmezse tüm dataları döndür
|
|
134
|
+
for (const [key, value] of Object.entries(this.data)) {
|
|
135
|
+
results.push({ key, value });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return new QueryResult(results);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Find first entry that matches the filter
|
|
144
|
+
* @param {Function} filter - Filter function
|
|
145
|
+
* @returns {Object|undefined} First matching item
|
|
146
|
+
*/
|
|
147
|
+
findOne(filter) {
|
|
148
|
+
if (typeof filter === 'function') {
|
|
149
|
+
for (const [key, value] of Object.entries(this.data)) {
|
|
150
|
+
if (filter(value, key)) {
|
|
151
|
+
return { key, value };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Filter by field value with operators
|
|
160
|
+
* @param {string} field - Field name (supports dot notation)
|
|
161
|
+
* @param {string} operator - Comparison operator (=, !=, >, <, >=, <=, in, contains)
|
|
162
|
+
* @param {*} value - Value to compare
|
|
163
|
+
* @returns {QueryResult} Chainable query result
|
|
164
|
+
*/
|
|
165
|
+
where(field, operator, value) {
|
|
166
|
+
return this.find((item, key) => {
|
|
167
|
+
const fieldValue = this._getValueByPath(item, field);
|
|
168
|
+
|
|
169
|
+
switch (operator) {
|
|
170
|
+
case '=':
|
|
171
|
+
case '==':
|
|
172
|
+
return fieldValue === value;
|
|
173
|
+
case '!=':
|
|
174
|
+
return fieldValue !== value;
|
|
175
|
+
case '>':
|
|
176
|
+
return fieldValue > value;
|
|
177
|
+
case '<':
|
|
178
|
+
return fieldValue < value;
|
|
179
|
+
case '>=':
|
|
180
|
+
return fieldValue >= value;
|
|
181
|
+
case '<=':
|
|
182
|
+
return fieldValue <= value;
|
|
183
|
+
case 'in':
|
|
184
|
+
return Array.isArray(value) && value.includes(fieldValue);
|
|
185
|
+
case 'contains':
|
|
186
|
+
return typeof fieldValue === 'string' && fieldValue.includes(value);
|
|
187
|
+
case 'startsWith':
|
|
188
|
+
return typeof fieldValue === 'string' && fieldValue.startsWith(value);
|
|
189
|
+
case 'endsWith':
|
|
190
|
+
return typeof fieldValue === 'string' && fieldValue.endsWith(value);
|
|
191
|
+
default:
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ---------------- NEW: Aggregation System ----------------
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Count total entries
|
|
201
|
+
* @param {Function} [filter] - Optional filter function
|
|
202
|
+
* @returns {number} Count of entries
|
|
203
|
+
*/
|
|
204
|
+
count(filter) {
|
|
205
|
+
if (filter) {
|
|
206
|
+
return this.find(filter).count();
|
|
207
|
+
}
|
|
208
|
+
return Object.keys(this.data).length;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Sum numeric values by field
|
|
213
|
+
* @param {string} field - Field name to sum
|
|
214
|
+
* @param {Function} [filter] - Optional filter function
|
|
215
|
+
* @returns {number} Sum of values
|
|
216
|
+
*/
|
|
217
|
+
sum(field, filter) {
|
|
218
|
+
const items = filter ? this.find(filter).toArray() : this.find().toArray();
|
|
219
|
+
return items.reduce((sum, item) => {
|
|
220
|
+
const val = this._getValueByPath(item.value, field);
|
|
221
|
+
return sum + (typeof val === 'number' ? val : 0);
|
|
222
|
+
}, 0);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Average of numeric values by field
|
|
227
|
+
* @param {string} field - Field name to average
|
|
228
|
+
* @param {Function} [filter] - Optional filter function
|
|
229
|
+
* @returns {number} Average of values
|
|
230
|
+
*/
|
|
231
|
+
avg(field, filter) {
|
|
232
|
+
const items = filter ? this.find(filter).toArray() : this.find().toArray();
|
|
233
|
+
if (items.length === 0) return 0;
|
|
234
|
+
|
|
235
|
+
const sum = items.reduce((total, item) => {
|
|
236
|
+
const val = this._getValueByPath(item.value, field);
|
|
237
|
+
return total + (typeof val === 'number' ? val : 0);
|
|
238
|
+
}, 0);
|
|
239
|
+
|
|
240
|
+
return sum / items.length;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Minimum value by field
|
|
245
|
+
* @param {string} field - Field name
|
|
246
|
+
* @param {Function} [filter] - Optional filter function
|
|
247
|
+
* @returns {*} Minimum value
|
|
248
|
+
*/
|
|
249
|
+
min(field, filter) {
|
|
250
|
+
const items = filter ? this.find(filter).toArray() : this.find().toArray();
|
|
251
|
+
if (items.length === 0) return undefined;
|
|
252
|
+
|
|
253
|
+
return Math.min(...items.map(item => {
|
|
254
|
+
const val = this._getValueByPath(item.value, field);
|
|
255
|
+
return typeof val === 'number' ? val : Infinity;
|
|
256
|
+
}).filter(val => val !== Infinity));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Maximum value by field
|
|
261
|
+
* @param {string} field - Field name
|
|
262
|
+
* @param {Function} [filter] - Optional filter function
|
|
263
|
+
* @returns {*} Maximum value
|
|
264
|
+
*/
|
|
265
|
+
max(field, filter) {
|
|
266
|
+
const items = filter ? this.find(filter).toArray() : this.find().toArray();
|
|
267
|
+
if (items.length === 0) return undefined;
|
|
268
|
+
|
|
269
|
+
return Math.max(...items.map(item => {
|
|
270
|
+
const val = this._getValueByPath(item.value, field);
|
|
271
|
+
return typeof val === 'number' ? val : -Infinity;
|
|
272
|
+
}).filter(val => val !== -Infinity));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Group entries by field value
|
|
277
|
+
* @param {string} field - Field name to group by
|
|
278
|
+
* @param {Function} [filter] - Optional filter function
|
|
279
|
+
* @returns {Object} Grouped results
|
|
280
|
+
*/
|
|
281
|
+
groupBy(field, filter) {
|
|
282
|
+
const items = filter ? this.find(filter).toArray() : this.find().toArray();
|
|
283
|
+
const groups = {};
|
|
284
|
+
|
|
285
|
+
items.forEach(item => {
|
|
286
|
+
const groupKey = this._getValueByPath(item.value, field);
|
|
287
|
+
const key = groupKey !== undefined ? String(groupKey) : 'undefined';
|
|
288
|
+
|
|
289
|
+
if (!groups[key]) {
|
|
290
|
+
groups[key] = [];
|
|
291
|
+
}
|
|
292
|
+
groups[key].push(item);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
return groups;
|
|
296
|
+
}
|
|
297
|
+
|
|
116
298
|
// ---------------- Backup & Restore ----------------
|
|
117
299
|
async backup(backupPath) {
|
|
118
300
|
await fs.writeFile(backupPath, JSON.stringify(this.data, null, 2), "utf8");
|
|
@@ -168,6 +350,146 @@ class SehawqDB extends EventEmitter {
|
|
|
168
350
|
}
|
|
169
351
|
delete obj[keys[0]];
|
|
170
352
|
}
|
|
353
|
+
|
|
354
|
+
_getValueByPath(obj, pathStr) {
|
|
355
|
+
const keys = pathStr.split(".");
|
|
356
|
+
let result = obj;
|
|
357
|
+
for (const key of keys) {
|
|
358
|
+
if (result && Object.prototype.hasOwnProperty.call(result, key)) {
|
|
359
|
+
result = result[key];
|
|
360
|
+
} else {
|
|
361
|
+
return undefined;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ---------------- QueryResult Class (for method chaining) ----------------
|
|
369
|
+
class QueryResult {
|
|
370
|
+
constructor(results) {
|
|
371
|
+
this.results = results || [];
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Sort results by field
|
|
376
|
+
* @param {string} field - Field name to sort by
|
|
377
|
+
* @param {string} direction - 'asc' or 'desc'
|
|
378
|
+
* @returns {QueryResult} Chainable
|
|
379
|
+
*/
|
|
380
|
+
sort(field, direction = 'asc') {
|
|
381
|
+
this.results.sort((a, b) => {
|
|
382
|
+
const aVal = this._getValueByPath(a.value, field);
|
|
383
|
+
const bVal = this._getValueByPath(b.value, field);
|
|
384
|
+
|
|
385
|
+
if (aVal === bVal) return 0;
|
|
386
|
+
|
|
387
|
+
const comparison = aVal > bVal ? 1 : -1;
|
|
388
|
+
return direction === 'desc' ? -comparison : comparison;
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
return this;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Limit number of results
|
|
396
|
+
* @param {number} count - Maximum number of results
|
|
397
|
+
* @returns {QueryResult} Chainable
|
|
398
|
+
*/
|
|
399
|
+
limit(count) {
|
|
400
|
+
this.results = this.results.slice(0, count);
|
|
401
|
+
return this;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Skip number of results
|
|
406
|
+
* @param {number} count - Number of results to skip
|
|
407
|
+
* @returns {QueryResult} Chainable
|
|
408
|
+
*/
|
|
409
|
+
skip(count) {
|
|
410
|
+
this.results = this.results.slice(count);
|
|
411
|
+
return this;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Get count of results
|
|
416
|
+
* @returns {number} Count
|
|
417
|
+
*/
|
|
418
|
+
count() {
|
|
419
|
+
return this.results.length;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Get first result
|
|
424
|
+
* @returns {Object|undefined} First result
|
|
425
|
+
*/
|
|
426
|
+
first() {
|
|
427
|
+
return this.results[0];
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Get last result
|
|
432
|
+
* @returns {Object|undefined} Last result
|
|
433
|
+
*/
|
|
434
|
+
last() {
|
|
435
|
+
return this.results[this.results.length - 1];
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Convert to array
|
|
440
|
+
* @returns {Array} Results array
|
|
441
|
+
*/
|
|
442
|
+
toArray() {
|
|
443
|
+
return this.results;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Get only values (without keys)
|
|
448
|
+
* @returns {Array} Values array
|
|
449
|
+
*/
|
|
450
|
+
values() {
|
|
451
|
+
return this.results.map(item => item.value);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Get only keys
|
|
456
|
+
* @returns {Array} Keys array
|
|
457
|
+
*/
|
|
458
|
+
keys() {
|
|
459
|
+
return this.results.map(item => item.key);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Apply additional filter
|
|
464
|
+
* @param {Function} filter - Filter function
|
|
465
|
+
* @returns {QueryResult} Chainable
|
|
466
|
+
*/
|
|
467
|
+
filter(filter) {
|
|
468
|
+
this.results = this.results.filter(item => filter(item.value, item.key));
|
|
469
|
+
return this;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Map over results
|
|
474
|
+
* @param {Function} mapper - Map function
|
|
475
|
+
* @returns {Array} Mapped results
|
|
476
|
+
*/
|
|
477
|
+
map(mapper) {
|
|
478
|
+
return this.results.map(item => mapper(item.value, item.key));
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
_getValueByPath(obj, pathStr) {
|
|
482
|
+
const keys = pathStr.split(".");
|
|
483
|
+
let result = obj;
|
|
484
|
+
for (const key of keys) {
|
|
485
|
+
if (result && Object.prototype.hasOwnProperty.call(result, key)) {
|
|
486
|
+
result = result[key];
|
|
487
|
+
} else {
|
|
488
|
+
return undefined;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return result;
|
|
492
|
+
}
|
|
171
493
|
}
|
|
172
494
|
|
|
173
|
-
module.exports = SehawqDB;
|
|
495
|
+
module.exports = SehawqDB;
|