or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdindex.mdquery-operators.md

advanced-features.mddocs/

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

```