0
# Advanced Features
1
2
Advanced functionality for custom operations, fine-grained control, and extending Sift's capabilities.
3
4
## Capabilities
5
6
### Custom Query Operations
7
8
Create query testers with custom operation sets, allowing for fine-grained control over which operations are available.
9
10
#### Create Query Tester
11
12
Creates a query tester without built-in operations, enabling custom operation sets.
13
14
```typescript { .api }
15
/**
16
* Creates a query tester without built-in operations for custom operation sets
17
* @param query - Query object using available operations
18
* @param options - Configuration including custom operations and comparison function
19
* @returns Filter function that can be used with Array.filter() or for testing values
20
*/
21
function createQueryTester<TItem, TSchema = TItem>(
22
query: Query<TSchema>,
23
options?: Partial<Options>
24
): (item: TItem) => boolean;
25
```
26
27
**Usage Examples:**
28
29
```typescript
30
import { createQueryTester, $eq, $gt, $in } from "sift";
31
32
// Create tester with only specific operations
33
const customFilter = createQueryTester(
34
{ age: { $gt: 18 }, status: { $in: ["active", "pending"] } },
35
{
36
operations: { $eq, $gt, $in },
37
compare: (a, b) => a === b // Custom comparison
38
}
39
);
40
41
const users = [
42
{ age: 25, status: "active" },
43
{ age: 16, status: "pending" },
44
{ age: 30, status: "inactive" }
45
];
46
47
const filtered = users.filter(customFilter);
48
// [{ age: 25, status: "active" }]
49
```
50
51
#### Create Default Query Operation
52
53
Creates a query operation with all default MongoDB operations.
54
55
```typescript { .api }
56
/**
57
* Creates a query operation with all default MongoDB operations
58
* @param query - Query object
59
* @param ownerQuery - Parent query object
60
* @param options - Partial options (operations will be merged with defaults)
61
* @returns QueryOperation instance
62
*/
63
function createDefaultQueryOperation<TItem, TSchema extends TItem = TItem>(
64
query: Query<TSchema>,
65
ownerQuery: any,
66
options?: Partial<Options>
67
): QueryOperation<TItem>;
68
```
69
70
#### Create Query Operation
71
72
Creates a query operation from a query object with custom options.
73
74
```typescript { .api }
75
/**
76
* Creates a query operation from a query object
77
* @param query - Query object
78
* @param ownerQuery - Parent query object (optional)
79
* @param options - Partial options including operations and comparison function
80
* @returns QueryOperation instance
81
*/
82
function createQueryOperation<TItem, TSchema = TItem>(
83
query: Query<TSchema>,
84
ownerQuery?: any,
85
options?: Partial<Options>
86
): QueryOperation<TItem>;
87
```
88
89
### Operation Infrastructure
90
91
Low-level operation creation and testing functionality for building custom operations.
92
93
#### Create Operation Tester
94
95
Creates a tester function from an operation instance.
96
97
```typescript { .api }
98
/**
99
* Creates a tester function from an operation instance
100
* @param operation - Operation instance to create tester for
101
* @returns Function that tests items against the operation
102
*/
103
function createOperationTester<TItem>(
104
operation: Operation<TItem>
105
): (item: TItem, key?: Key, owner?: any) => boolean;
106
```
107
108
#### Create Equals Operation
109
110
Creates an equals operation for custom operation development.
111
112
```typescript { .api }
113
/**
114
* Creates an equals operation for custom operation development
115
* @param params - Parameters for the operation (value to match or test function)
116
* @param ownerQuery - Parent query object
117
* @param options - Operation options including comparison function
118
* @returns EqualsOperation instance
119
*/
120
function createEqualsOperation(
121
params: any,
122
ownerQuery: any,
123
options: Options
124
): EqualsOperation;
125
```
126
127
**Usage Examples:**
128
129
```typescript
130
import {
131
createQueryOperation,
132
createOperationTester,
133
createEqualsOperation,
134
$eq
135
} from "sift";
136
137
// Create custom operation
138
const customEquals = createEqualsOperation(
139
(value) => value > 10,
140
null,
141
{
142
operations: { $eq },
143
compare: (a, b) => a === b
144
}
145
);
146
147
// Test the operation
148
const tester = createOperationTester(customEquals);
149
console.log(tester(15)); // true
150
console.log(tester(5)); // false
151
152
// Build complex query operation
153
const queryOp = createQueryOperation(
154
{ age: { $eq: 25 } },
155
null,
156
{
157
operations: { $eq },
158
compare: (a, b) => a === b
159
}
160
);
161
162
const queryTester = createOperationTester(queryOp);
163
console.log(queryTester({ age: 25 })); // true
164
```
165
166
### Utility Functions
167
168
Helper functions for comparison, type checking, and operation creation.
169
170
#### Create Tester
171
172
Creates a tester function from a value, function, or regular expression.
173
174
```typescript { .api }
175
/**
176
* Creates a tester function from a value, function, or regular expression
177
* @param a - Value, function, or RegExp to create tester from
178
* @param compare - Comparison function for value testing
179
* @returns Tester function
180
*/
181
function createTester(a: any, compare: Comparator): Tester;
182
183
type Tester = (
184
item: any,
185
key?: Key,
186
owner?: any,
187
root?: boolean,
188
leaf?: boolean
189
) => boolean;
190
```
191
192
#### Contains Operation
193
194
Checks if a query object contains operation keys.
195
196
```typescript { .api }
197
/**
198
* Checks if a query object contains operation keys
199
* @param query - Query object to inspect
200
* @param options - Options containing available operations
201
* @returns True if query contains operations, false otherwise
202
*/
203
function containsOperation(query: any, options: Options): boolean;
204
```
205
206
#### Numerical Operation Creator
207
208
Higher-order function for creating numerical operations with type coercion.
209
210
```typescript { .api }
211
/**
212
* Creates numerical operations with automatic type coercion
213
* @param createTester - Function that creates the test logic
214
* @returns Operation creator function
215
*/
216
function numericalOperation(
217
createTester: (value: any) => Tester
218
): OperationCreator<any>;
219
220
/**
221
* Higher-order function for numerical operation creators
222
* @param createNumericalOperation - Operation creator for numerical operations
223
* @returns Wrapped operation creator
224
*/
225
function numericalOperationCreator(
226
createNumericalOperation: OperationCreator<any>
227
): OperationCreator<any>;
228
```
229
230
**Usage Examples:**
231
232
```typescript
233
import {
234
createTester,
235
containsOperation,
236
numericalOperation,
237
$eq
238
} from "sift";
239
240
// Create custom tester from function
241
const customTester = createTester(
242
(value) => typeof value === "string" && value.length > 5,
243
(a, b) => a === b
244
);
245
246
console.log(customTester("hello world")); // true
247
console.log(customTester("hi")); // false
248
249
// Check if query contains operations
250
const hasOps = containsOperation(
251
{ age: { $eq: 25 } },
252
{ operations: { $eq }, compare: (a, b) => a === b }
253
); // true
254
255
const noOps = containsOperation(
256
{ name: "Alice" },
257
{ operations: { $eq }, compare: (a, b) => a === b }
258
); // false
259
260
// Create custom numerical operation
261
const $between = numericalOperation((range) => (value) => {
262
const [min, max] = range;
263
return value >= min && value <= max;
264
});
265
266
// Use custom operation
267
const customQuery = createQueryTester(
268
{ score: [70, 90] }, // between 70 and 90
269
{ operations: { $between } }
270
);
271
```
272
273
### Type Checking Utilities
274
275
Utility functions for type detection and value comparison.
276
277
#### Type Checker
278
279
Creates type checking functions for runtime type validation.
280
281
```typescript { .api }
282
/**
283
* Creates type checking functions for runtime validation
284
* @param type - Type name to check for
285
* @returns Type guard function
286
*/
287
function typeChecker<TType>(type: string): (value: any) => value is TType;
288
```
289
290
#### Comparison and Utility Functions
291
292
```typescript { .api }
293
/**
294
* Converts values to comparable form (handles Dates, arrays, toJSON)
295
* @param value - Value to make comparable
296
* @returns Comparable representation of value
297
*/
298
function comparable(value: any): any;
299
300
/**
301
* Coerces potentially null values to null
302
* @param value - Value to coerce
303
* @returns Coerced value
304
*/
305
function coercePotentiallyNull(value: any): any;
306
307
/**
308
* Deep equality comparison function
309
* @param a - First value
310
* @param b - Second value
311
* @returns True if values are deeply equal
312
*/
313
function equals(a: any, b: any): boolean;
314
315
/**
316
* Type guard for arrays
317
* @param value - Value to check
318
* @returns True if value is an array
319
*/
320
function isArray(value: any): value is Array<any>;
321
322
/**
323
* Type guard for objects
324
* @param value - Value to check
325
* @returns True if value is an object
326
*/
327
function isObject(value: any): value is Object;
328
329
/**
330
* Type guard for functions
331
* @param value - Value to check
332
* @returns True if value is a function
333
*/
334
function isFunction(value: any): value is Function;
335
336
/**
337
* Checks if key is a property (not a function) of an object
338
* @param item - Object to check
339
* @param key - Key to test
340
* @returns True if key is a non-function property
341
*/
342
function isProperty(item: any, key: any): boolean;
343
344
/**
345
* Checks if value is a plain object (not instance of custom class)
346
* @param value - Value to check
347
* @returns True if value is a vanilla object
348
*/
349
function isVanillaObject(value: any): boolean;
350
```
351
352
**Usage Examples:**
353
354
```typescript
355
import {
356
typeChecker,
357
comparable,
358
equals,
359
isArray,
360
isVanillaObject
361
} from "sift";
362
363
// Create type checkers
364
const isString = typeChecker<string>("String");
365
const isNumber = typeChecker<number>("Number");
366
367
console.log(isString("hello")); // true
368
console.log(isNumber(42)); // true
369
console.log(isString(42)); // false
370
371
// Value comparison
372
const date1 = new Date("2023-01-01");
373
const date2 = new Date("2023-01-01");
374
375
console.log(comparable(date1)); // timestamp number
376
console.log(equals(date1, date2)); // true (deep equality)
377
378
// Type checking
379
console.log(isArray([1, 2, 3])); // true
380
console.log(isVanillaObject({ a: 1 })); // true
381
console.log(isVanillaObject(new Date())); // false
382
383
// Custom comparison in operations
384
const customSift = createQueryTester(
385
{ created: new Date("2023-01-01") },
386
{
387
operations: { $eq },
388
compare: (a, b) => {
389
// Custom date comparison ignoring time
390
if (a instanceof Date && b instanceof Date) {
391
return a.toDateString() === b.toDateString();
392
}
393
return equals(a, b);
394
}
395
}
396
);
397
```
398
399
### Custom Operation Development
400
401
Framework for developing custom query operations.
402
403
#### Base Operation Classes
404
405
```typescript { .api }
406
/**
407
* Base abstract class for all operations
408
*/
409
abstract class BaseOperation<TParams, TItem = any> implements Operation<TItem> {
410
readonly keep: boolean;
411
readonly done: boolean;
412
abstract readonly propop: boolean;
413
414
constructor(
415
readonly params: TParams,
416
readonly ownerQuery: any,
417
readonly options: Options,
418
readonly name?: string
419
);
420
421
protected init(): void;
422
reset(): void;
423
abstract next(
424
item: any,
425
key: Key,
426
parent: any,
427
root: boolean,
428
leaf?: boolean
429
): void;
430
}
431
432
/**
433
* Equals operation class for value matching
434
*/
435
class EqualsOperation<TParam> extends BaseOperation<TParam> {
436
readonly propop = true;
437
438
constructor(params: TParam, ownerQuery: any, options: Options);
439
next(item: any, key: Key, parent: any): void;
440
}
441
442
/**
443
* Query operation class for complex queries
444
*/
445
class QueryOperation<TItem> implements Operation<TItem> {
446
readonly propop = true;
447
readonly keep: boolean;
448
readonly done: boolean;
449
450
next(item: TItem, key: Key, parent: any, root: boolean): void;
451
reset(): void;
452
}
453
```
454
455
**Usage Examples:**
456
457
```typescript
458
import { BaseOperation, createQueryTester, Options, Query } from "sift";
459
460
// Custom operation example
461
class $startsWith extends BaseOperation<string> {
462
readonly propop = true;
463
private testValue: string;
464
465
init() {
466
this.testValue = this.params.toLowerCase();
467
}
468
469
next(item: any) {
470
if (typeof item === "string") {
471
if (item.toLowerCase().startsWith(this.testValue)) {
472
this.done = true;
473
this.keep = true;
474
}
475
}
476
}
477
}
478
479
// Operation creator function
480
const createStartsWithOp = (
481
params: string,
482
ownerQuery: Query<any>,
483
options: Options,
484
name: string
485
) => new $startsWith(params, ownerQuery, options, name);
486
487
// Use custom operation
488
const customFilter = createQueryTester(
489
{ name: { $startsWith: "Al" } },
490
{ operations: { $startsWith: createStartsWithOp } }
491
);
492
493
const users = [
494
{ name: "Alice" },
495
{ name: "Bob" },
496
{ name: "Alexander" }
497
];
498
499
const filtered = users.filter(customFilter);
500
// [{ name: "Alice" }, { name: "Alexander" }]
501
```
502
503
## Error Handling
504
505
Sift operations can throw errors in specific circumstances:
506
507
- **$elemMatch**: Throws `Error` if params is not an object
508
- **$and/$or/$nor**: Throws `Error` if params is an empty array
509
- **$in operations**: Throws `Error` if nested operations are used in $in/$nin values
510
- **$type**: Throws `Error` if string type alias doesn't exist
511
- **$where**: Throws `Error` in CSP mode when using string expressions
512
- **Unsupported operations**: Throws `Error` for unknown $ operators
513
514
**Error Handling Examples:**
515
516
```typescript
517
import sift from "sift";
518
519
try {
520
// This will throw - empty array not allowed
521
const filter = sift({ $and: [] });
522
} catch (error) {
523
console.log(error.message); // "$and/$or/$nor must be a nonempty array"
524
}
525
526
try {
527
// This will throw - malformed $elemMatch
528
const filter = sift({ items: { $elemMatch: "invalid" } });
529
} catch (error) {
530
console.log(error.message); // "Malformed query. $elemMatch must by an object."
531
}
532
533
try {
534
// This will throw - unknown type alias
535
const filter = sift({ field: { $type: "invalidtype" } });
536
} catch (error) {
537
console.log(error.message); // "Type alias does not exist"
538
}
539
```