0
# Advanced Querying
1
2
Chainable query interface with filtering, sorting, transformations, and aggregation operations using the Resultset class for complex data operations.
3
4
## Capabilities
5
6
### Resultset Constructor
7
8
Creates a chainable query interface from a collection for complex operations.
9
10
```javascript { .api }
11
/**
12
* Creates a new resultset for chainable operations
13
* @param collection - Source collection
14
* @param options - Resultset options
15
*/
16
constructor Resultset(collection: Collection, options?: object);
17
```
18
19
**Usage Examples:**
20
21
```javascript
22
// Create resultset from collection
23
const users = db.getCollection('users');
24
const resultset = new users.constructor.Resultset(users);
25
26
// Or create via collection.chain()
27
const resultset = users.chain();
28
```
29
30
### Query Operations
31
32
Filter documents using various query criteria and logical operations.
33
34
```javascript { .api }
35
/**
36
* Apply find query to filter documents
37
* @param query - MongoDB-style query object
38
* @param firstOnly - If true, stop after first match
39
* @returns Resultset for chaining
40
*/
41
find(query: object, firstOnly?: boolean): Resultset;
42
43
/**
44
* Apply OR query across multiple expressions
45
* @param expressionArray - Array of query expressions
46
* @returns Resultset for chaining
47
*/
48
findOr(expressionArray: object[]): Resultset;
49
50
/**
51
* Apply AND query across multiple expressions
52
* @param expressionArray - Array of query expressions
53
* @returns Resultset for chaining
54
*/
55
findAnd(expressionArray: object[]): Resultset;
56
57
/**
58
* Filter using custom function
59
* @param fun - Filter function returning boolean
60
* @returns Resultset for chaining
61
*/
62
where(fun: (obj: object) => boolean): Resultset;
63
```
64
65
**Usage Examples:**
66
67
```javascript
68
// Basic find operations
69
const activeUsers = users.chain()
70
.find({ active: true })
71
.data();
72
73
// OR query
74
const result = users.chain()
75
.findOr([
76
{ department: 'Engineering' },
77
{ department: 'Design' },
78
{ age: { $gt: 50 } }
79
])
80
.data();
81
82
// AND query
83
const result = users.chain()
84
.findAnd([
85
{ active: true },
86
{ age: { $gte: 25 } },
87
{ department: { $ne: 'Intern' } }
88
])
89
.data();
90
91
// Custom filter function
92
const result = users.chain()
93
.where(obj => obj.email.includes('@company.com'))
94
.data();
95
```
96
97
### Sorting Operations
98
99
Sort resultset data using various criteria and custom comparison functions.
100
101
```javascript { .api }
102
/**
103
* Sort using custom comparison function
104
* @param comparefun - Function returning -1, 0, or 1 for comparison
105
* @returns Resultset for chaining
106
*/
107
sort(comparefun: (a: object, b: object) => number): Resultset;
108
109
/**
110
* Simple sort by property name
111
* @param propname - Property name to sort by
112
* @param options - Sort options (desc: boolean)
113
* @returns Resultset for chaining
114
*/
115
simplesort(propname: string, options?: { desc?: boolean }): Resultset;
116
117
/**
118
* Sort by multiple properties with individual sort directions
119
* @param properties - Array of [property, isDescending] pairs
120
* @returns Resultset for chaining
121
*/
122
compoundsort(properties: [string, boolean][]): Resultset;
123
```
124
125
**Usage Examples:**
126
127
```javascript
128
// Simple ascending sort
129
const result = users.chain()
130
.find({ active: true })
131
.simplesort('name')
132
.data();
133
134
// Simple descending sort
135
const result = users.chain()
136
.find({ active: true })
137
.simplesort('age', { desc: true })
138
.data();
139
140
// Compound sort
141
const result = users.chain()
142
.find({ active: true })
143
.compoundsort([
144
['department', false], // ascending
145
['age', true] // descending
146
])
147
.data();
148
149
// Custom sort function
150
const result = users.chain()
151
.find({ active: true })
152
.sort((a, b) => {
153
// Sort by name length, then alphabetically
154
if (a.name.length !== b.name.length) {
155
return a.name.length - b.name.length;
156
}
157
return a.name.localeCompare(b.name);
158
})
159
.data();
160
```
161
162
### Pagination Operations
163
164
Limit and offset results for pagination and data chunking.
165
166
```javascript { .api }
167
/**
168
* Limit number of results returned
169
* @param qty - Maximum number of results
170
* @returns Resultset for chaining
171
*/
172
limit(qty: number): Resultset;
173
174
/**
175
* Skip specified number of results
176
* @param pos - Number of results to skip
177
* @returns Resultset for chaining
178
*/
179
offset(pos: number): Resultset;
180
```
181
182
**Usage Examples:**
183
184
```javascript
185
// Get first 10 users
186
const firstPage = users.chain()
187
.find({ active: true })
188
.limit(10)
189
.data();
190
191
// Get users 11-20 (second page)
192
const secondPage = users.chain()
193
.find({ active: true })
194
.offset(10)
195
.limit(10)
196
.data();
197
198
// Get top 5 highest paid employees
199
const topEarners = users.chain()
200
.find({ active: true })
201
.simplesort('salary', { desc: true })
202
.limit(5)
203
.data();
204
```
205
206
### Data Retrieval
207
208
Retrieve the final results from the query chain.
209
210
```javascript { .api }
211
/**
212
* Execute the query chain and return results
213
* @param options - Data retrieval options
214
* @returns Array of documents matching the query chain
215
*/
216
data(options?: { removeMeta?: boolean, forceClones?: boolean }): object[];
217
218
/**
219
* Get count of documents in resultset without retrieving data
220
* @returns Number of documents that would be returned
221
*/
222
count(): number;
223
```
224
225
**Usage Examples:**
226
227
```javascript
228
// Get results with metadata
229
const results = users.chain()
230
.find({ active: true })
231
.simplesort('name')
232
.data();
233
234
// Get results without metadata
235
const cleanResults = users.chain()
236
.find({ active: true })
237
.data({ removeMeta: true });
238
239
// Just get the count
240
const activeCount = users.chain()
241
.find({ active: true })
242
.count();
243
```
244
245
### Transformation Operations
246
247
Apply transformations and modifications to query results.
248
249
```javascript { .api }
250
/**
251
* Apply a transform to the resultset
252
* @param transform - Transform name or function
253
* @param parameters - Transform parameters
254
* @returns Resultset for chaining
255
*/
256
transform(transform: string | Function, parameters?: object): Resultset;
257
258
/**
259
* Update documents in the resultset
260
* @param updateFunction - Function to update each document
261
* @returns Resultset for chaining
262
*/
263
update(updateFunction: (obj: object) => void): object[];
264
265
/**
266
* Remove documents in the resultset from the collection
267
* @returns Array of removed documents
268
*/
269
remove(): object[];
270
271
/**
272
* Copy the resultset
273
* @returns New resultset with same filters
274
*/
275
copy(): Resultset;
276
277
/**
278
* Reset resultset to include all documents
279
* @returns Resultset for chaining
280
*/
281
reset(): Resultset;
282
283
/**
284
* Create a branch (copy) of the resultset
285
* @returns New resultset with same filters
286
*/
287
branch(): Resultset;
288
289
/**
290
* Serialize resultset to JSON
291
* @returns JSON representation of the resultset
292
*/
293
toJSON(): string;
294
```
295
296
**Usage Examples:**
297
298
```javascript
299
// Apply transform
300
const transformed = users.chain()
301
.find({ active: true })
302
.transform('extractName') // Assuming transform exists
303
.data();
304
305
// Update documents in resultset
306
const updated = users.chain()
307
.find({ department: 'Engineering' })
308
.update(obj => {
309
obj.lastReviewDate = new Date();
310
obj.needsReview = false;
311
});
312
313
// Remove documents matching criteria
314
const removed = users.chain()
315
.find({ active: false, lastLogin: { $lt: new Date('2020-01-01') } })
316
.remove();
317
318
// Copy resultset for different operations
319
const baseQuery = users.chain().find({ active: true });
320
const engineers = baseQuery.copy().find({ department: 'Engineering' }).data();
321
const designers = baseQuery.copy().find({ department: 'Design' }).data();
322
```
323
324
### Aggregation Operations
325
326
Perform aggregation and join operations on query results.
327
328
```javascript { .api }
329
/**
330
* Perform map-reduce operation on resultset
331
* @param mapFunction - Map function to apply to each document
332
* @param reduceFunction - Reduce function to combine results
333
* @returns Reduced result
334
*/
335
mapReduce(mapFunction: (obj: object) => any, reduceFunction: (array: any[]) => any): any;
336
337
/**
338
* Perform equi-join with external data
339
* @param joinData - External data to join with
340
* @param leftJoinKey - Property from resultset documents
341
* @param rightJoinKey - Property from external data
342
* @param mapFun - Optional function to transform join results
343
* @param dataOptions - Additional options
344
* @returns Array of joined results
345
*/
346
eqJoin(joinData: any[], leftJoinKey: string, rightJoinKey: string, mapFun?: Function, dataOptions?: object): object[];
347
348
/**
349
* Apply map operation to transform each document
350
* @param mapFun - Map function to apply
351
* @param dataOptions - Additional options
352
* @returns Array of transformed results
353
*/
354
map(mapFun: (obj: object) => any, dataOptions?: object): any[];
355
```
356
357
**Usage Examples:**
358
359
```javascript
360
// Map-reduce to calculate department statistics
361
const deptStats = users.chain()
362
.find({ active: true })
363
.mapReduce(
364
obj => ({ [obj.department]: { count: 1, totalSalary: obj.salary } }),
365
results => {
366
const stats = {};
367
results.forEach(result => {
368
Object.keys(result).forEach(dept => {
369
if (!stats[dept]) {
370
stats[dept] = { count: 0, totalSalary: 0 };
371
}
372
stats[dept].count += result[dept].count;
373
stats[dept].totalSalary += result[dept].totalSalary;
374
});
375
});
376
// Calculate averages
377
Object.keys(stats).forEach(dept => {
378
stats[dept].avgSalary = stats[dept].totalSalary / stats[dept].count;
379
});
380
return stats;
381
}
382
);
383
384
// Join with external orders data
385
const orders = [
386
{ customerId: 1, total: 150.00, date: '2023-01-15' },
387
{ customerId: 2, total: 75.50, date: '2023-01-16' }
388
];
389
390
const usersWithOrders = users.chain()
391
.find({ active: true })
392
.eqJoin(orders, 'id', 'customerId', (left, right) => ({
393
user: left,
394
orderTotal: right.total,
395
orderDate: right.date
396
}));
397
398
// Map to extract specific fields
399
const userSummaries = users.chain()
400
.find({ active: true })
401
.map(obj => ({
402
name: obj.name,
403
email: obj.email,
404
department: obj.department
405
}));
406
```
407
408
## Query Operators
409
410
LokiJS supports MongoDB-style query operators for complex filtering:
411
412
```javascript { .api }
413
// Comparison operators
414
$eq: any; // Strict equality
415
$aeq: any; // Abstract equality (==)
416
$ne: any; // Not equal
417
$gt: any; // Greater than
418
$gte: any; // Greater than or equal
419
$lt: any; // Less than
420
$lte: any; // Less than or equal
421
422
// Array operators
423
$in: any[]; // Value in array
424
$nin: any[]; // Value not in array
425
$contains: any; // Array contains value
426
$containsAny: any[]; // Array contains any of values
427
$containsNone: any[]; // Array contains none of values
428
429
// Object operators
430
$exists: boolean; // Property exists
431
$type: string; // Type checking ('string', 'number', etc.)
432
$size: number; // Array/object size
433
$regex: RegExp; // Regular expression match
434
435
// Logical operators
436
$and: object[]; // Logical AND
437
$or: object[]; // Logical OR
438
$not: object; // Logical NOT
439
440
// Numeric operators
441
$between: [any, any]; // Between two values
442
$finite: boolean; // Is finite number
443
444
// Advanced operators
445
$dteq: any; // Date/abstract equality
446
$jgt: any; // JavaScript greater than (lightweight)
447
$jgte: any; // JavaScript greater than or equal
448
$jlt: any; // JavaScript less than (lightweight)
449
$jlte: any; // JavaScript less than or equal
450
$jbetween: [any, any]; // JavaScript between (lightweight)
451
$inSet: Set<any>; // Value in Set object
452
$keyin: object; // Property key exists in object
453
$nkeyin: object; // Property key does not exist in object
454
$definedin: object; // Property is defined in object (not undefined)
455
$undefinedin: object; // Property is undefined in object
456
$containsString: string; // String contains substring
457
$elemMatch: object; // Array element matches query
458
459
// Custom operators
460
$where: Function; // Custom function filter
461
```
462
463
**Usage Examples:**
464
465
```javascript
466
// Comparison operators
467
const adults = users.chain().find({ age: { $gte: 18 } }).data();
468
const active = users.chain().find({ status: { $ne: 'inactive' } }).data();
469
470
// Array operators
471
const managers = users.chain().find({
472
roles: { $contains: 'manager' }
473
}).data();
474
475
const techRoles = users.chain().find({
476
skills: { $containsAny: ['JavaScript', 'Python', 'Java'] }
477
}).data();
478
479
// Object operators
480
const hasPhone = users.chain().find({
481
phone: { $exists: true }
482
}).data();
483
484
const numbers = users.chain().find({
485
employeeId: { $type: 'number' }
486
}).data();
487
488
// Logical operators
489
const seniorEngineers = users.chain().find({
490
$and: [
491
{ department: 'Engineering' },
492
{ experience: { $gte: 5 } },
493
{ level: { $in: ['Senior', 'Lead', 'Principal'] } }
494
]
495
}).data();
496
497
// Custom function filter
498
const recentHires = users.chain().find({
499
$where: function(obj) {
500
const hireDate = new Date(obj.hireDate);
501
const sixMonthsAgo = new Date();
502
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
503
return hireDate > sixMonthsAgo;
504
}
505
}).data();
506
507
// Advanced operators
508
const advancedUsers = users.chain().find({
509
// Date equality
510
createdAt: { $dteq: new Date('2023-01-01') },
511
512
// Lightweight JavaScript comparisons
513
score: { $jgt: 85 },
514
515
// Between range (lightweight)
516
age: { $jbetween: [25, 65] },
517
518
// Set membership
519
department: { $inSet: new Set(['Engineering', 'Design']) },
520
521
// Object property checks
522
userId: { $keyin: userMap },
523
preferences: { $definedin: { theme: 'dark' } },
524
525
// String operations
526
email: { $containsString: '@company.com' },
527
528
// Array element matching
529
orders: {
530
$elemMatch: {
531
status: 'completed',
532
total: { $gt: 100 }
533
}
534
}
535
}).data();
536
```
537
538
### Additional Query Examples
539
540
Advanced query patterns using the extended operator set.
541
542
```javascript
543
// Complex nested queries
544
const complexResults = users.chain()
545
.find({
546
$and: [
547
{ status: 'active' },
548
{
549
$or: [
550
{ department: { $inSet: new Set(['Engineering', 'Design']) } },
551
{ skills: { $containsAny: ['leadership', 'management'] } }
552
]
553
},
554
{
555
projects: {
556
$elemMatch: {
557
status: 'completed',
558
budget: { $jbetween: [10000, 100000] },
559
deadline: { $dteq: new Date('2023-12-31') }
560
}
561
}
562
}
563
]
564
})
565
.data();
566
567
// String and object operations
568
const textSearchResults = users.chain()
569
.find({
570
// String contains
571
biography: { $containsString: 'engineer' },
572
573
// Property existence
574
permissions: { $definedin: { admin: true } },
575
576
// Key membership
577
userId: { $keyin: { 1: true, 2: true, 3: true } },
578
579
// Undefined check
580
deletedAt: { $undefinedin: {} }
581
})
582
.data();
583
```
584
585
### Resultset Aliases
586
587
Alternative method names for MongoDB compatibility.
588
589
```javascript { .api }
590
// Logical operator aliases
591
$or(expressionArray: object[]): Resultset; // Alias for findOr
592
$and(expressionArray: object[]): Resultset; // Alias for findAnd
593
```
594
595
**Usage Examples:**
596
597
```javascript
598
// Use MongoDB-style operator aliases
599
const result1 = users.chain()
600
.$or([
601
{ department: 'Engineering' },
602
{ department: 'Design' }
603
])
604
.data();
605
606
const result2 = users.chain()
607
.$and([
608
{ active: true },
609
{ age: { $gte: 25 } }
610
])
611
.data();
612
```
613
614
## Properties
615
616
```javascript { .api }
617
interface Resultset {
618
/** Reference to source collection */
619
collection: Collection;
620
/** Array of filtered row indices */
621
filteredrows: number[];
622
/** Filter initialization flag */
623
filterInitialized: boolean;
624
}
625
```