or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cursor-operations.mddatabase-management.mddocument-operations.mdindex-management.mdindex.mdquery-system.md

cursor-operations.mddocs/

0

# Cursor Operations

1

2

Chainable query interface for sorting, pagination, projection, and result processing with lazy evaluation and MongoDB-style query building.

3

4

## Capabilities

5

6

### Cursor Creation

7

8

Cursors are automatically created when calling find methods without a callback parameter, enabling method chaining.

9

10

```javascript { .api }

11

/**

12

* Cursor creation through find methods without callback

13

* @returns {Cursor} Chainable cursor instance

14

*/

15

interface CursorCreation {

16

find(query: object, projection?: object): Cursor;

17

findOne(query: object, projection?: object): Cursor;

18

count(query: object): Cursor;

19

}

20

21

/**

22

* Cursor interface for chaining operations

23

*/

24

interface Cursor {

25

limit(limit: number): Cursor;

26

skip(skip: number): Cursor;

27

sort(sortQuery: object): Cursor;

28

projection(projection: object): Cursor;

29

exec(callback: function): void;

30

}

31

```

32

33

**Usage Examples:**

34

35

```javascript

36

const db = new Datastore({ filename: './products.db', autoload: true });

37

38

// Create cursor from find (no callback)

39

const cursor = db.find({ category: 'electronics' });

40

41

// Create cursor from findOne

42

const singleCursor = db.findOne({ sku: 'ABC123' });

43

44

// Create cursor from count

45

const countCursor = db.count({ status: 'active' });

46

47

// Cursors are chainable

48

const chainedCursor = db.find({ price: { $gte: 100 } })

49

.sort({ price: 1 })

50

.skip(10)

51

.limit(5);

52

```

53

54

### Result Limiting

55

56

Control the maximum number of documents returned by the query.

57

58

```javascript { .api }

59

/**

60

* Limit the number of results returned

61

* @param {number} limit - Maximum number of documents to return

62

* @returns {Cursor} Cursor instance for chaining

63

*/

64

limit(limit);

65

```

66

67

**Usage Examples:**

68

69

```javascript

70

const db = new Datastore({ filename: './products.db', autoload: true });

71

72

// Get top 10 products

73

db.find({})

74

.limit(10)

75

.exec((err, products) => {

76

console.log('Top 10 products:', products);

77

});

78

79

// Get 5 most expensive products

80

db.find({})

81

.sort({ price: -1 })

82

.limit(5)

83

.exec((err, products) => {

84

console.log('5 most expensive products:', products);

85

});

86

87

// Pagination - first page (5 items)

88

db.find({ category: 'books' })

89

.sort({ title: 1 })

90

.limit(5)

91

.exec((err, books) => {

92

console.log('First 5 books:', books);

93

});

94

95

// Single result using limit

96

db.find({ featured: true })

97

.sort({ priority: -1 })

98

.limit(1)

99

.exec((err, products) => {

100

console.log('Top featured product:', products[0]);

101

});

102

```

103

104

### Result Skipping

105

106

Skip a specified number of documents from the beginning of the result set, useful for pagination.

107

108

```javascript { .api }

109

/**

110

* Skip specified number of documents from the beginning

111

* @param {number} skip - Number of documents to skip

112

* @returns {Cursor} Cursor instance for chaining

113

*/

114

skip(skip);

115

```

116

117

**Usage Examples:**

118

119

```javascript

120

const db = new Datastore({ filename: './users.db', autoload: true });

121

122

// Pagination - skip first 10 results

123

db.find({})

124

.sort({ createdAt: -1 })

125

.skip(10)

126

.limit(10)

127

.exec((err, users) => {

128

console.log('Users 11-20 (page 2):', users);

129

});

130

131

// Skip first page for second page

132

const pageSize = 20;

133

const pageNumber = 2; // 0-indexed: page 2 = third page

134

const skipCount = pageNumber * pageSize;

135

136

db.find({ status: 'active' })

137

.sort({ name: 1 })

138

.skip(skipCount)

139

.limit(pageSize)

140

.exec((err, users) => {

141

console.log(`Page ${pageNumber + 1} users:`, users);

142

});

143

144

// Skip recent entries to get older data

145

db.find({ type: 'log' })

146

.sort({ timestamp: -1 })

147

.skip(100) // Skip 100 most recent

148

.limit(50) // Get next 50

149

.exec((err, logs) => {

150

console.log('Older log entries:', logs);

151

});

152

153

// Advanced pagination with total count

154

function paginateUsers(page, pageSize, callback) {

155

const skip = page * pageSize;

156

157

// Get total count

158

db.count({}, (err, total) => {

159

if (err) return callback(err);

160

161

// Get page data

162

db.find({})

163

.sort({ name: 1 })

164

.skip(skip)

165

.limit(pageSize)

166

.exec((err, users) => {

167

if (err) return callback(err);

168

169

callback(null, {

170

users: users,

171

totalCount: total,

172

currentPage: page,

173

totalPages: Math.ceil(total / pageSize),

174

hasNextPage: (page + 1) * pageSize < total,

175

hasPrevPage: page > 0

176

});

177

});

178

});

179

}

180

181

// Usage

182

paginateUsers(0, 10, (err, result) => {

183

if (!err) {

184

console.log('Pagination result:', result);

185

}

186

});

187

```

188

189

### Result Sorting

190

191

Sort query results by one or multiple fields in ascending or descending order.

192

193

```javascript { .api }

194

/**

195

* Sort results by specified fields

196

* @param {object} sortQuery - Sort specification object

197

* @returns {Cursor} Cursor instance for chaining

198

*/

199

sort(sortQuery);

200

201

/**

202

* Sort specification format

203

*/

204

interface SortQuery {

205

[fieldName: string]: 1 | -1; // 1 for ascending, -1 for descending

206

}

207

```

208

209

**Usage Examples:**

210

211

```javascript

212

const db = new Datastore({ filename: './products.db', autoload: true });

213

214

// Sort by single field - ascending

215

db.find({})

216

.sort({ name: 1 })

217

.exec((err, products) => {

218

console.log('Products sorted by name (A-Z):', products);

219

});

220

221

// Sort by single field - descending

222

db.find({})

223

.sort({ price: -1 })

224

.exec((err, products) => {

225

console.log('Products sorted by price (high to low):', products);

226

});

227

228

// Sort by multiple fields

229

db.find({})

230

.sort({ category: 1, price: -1 })

231

.exec((err, products) => {

232

console.log('Products sorted by category (A-Z) then price (high to low):', products);

233

});

234

235

// Sort by nested fields using dot notation

236

db.find({})

237

.sort({ 'details.weight': 1 })

238

.exec((err, products) => {

239

console.log('Products sorted by weight:', products);

240

});

241

242

// Sort by date fields

243

db.find({})

244

.sort({ createdAt: -1 }) // Most recent first

245

.exec((err, products) => {

246

console.log('Products sorted by creation date (newest first):', products);

247

});

248

249

// Combining sort with other operations

250

db.find({ category: 'electronics' })

251

.sort({ rating: -1, price: 1 }) // Best rated first, then cheapest

252

.limit(10)

253

.exec((err, products) => {

254

console.log('Top 10 electronics by rating and price:', products);

255

});

256

257

// Custom string comparison (if compareStrings option was set)

258

const customDb = new Datastore({

259

filename: './data.db',

260

compareStrings: (a, b) => a.localeCompare(b, 'en', { sensitivity: 'base' })

261

});

262

263

customDb.find({})

264

.sort({ name: 1 })

265

.exec((err, docs) => {

266

console.log('Documents sorted with custom string comparison:', docs);

267

});

268

```

269

270

### Field Projection

271

272

Select specific fields to include or exclude in the query results, reducing data transfer and memory usage.

273

274

```javascript { .api }

275

/**

276

* Set field projection for results

277

* @param {object} projection - Field projection specification

278

* @returns {Cursor} Cursor instance for chaining

279

*/

280

projection(projection);

281

282

/**

283

* Projection specification format

284

*/

285

interface ProjectionQuery {

286

[fieldName: string]: 1 | 0; // 1 to include, 0 to exclude (cannot mix except with _id)

287

}

288

```

289

290

**Usage Examples:**

291

292

```javascript

293

const db = new Datastore({ filename: './users.db', autoload: true });

294

295

// Include specific fields only

296

db.find({})

297

.projection({ name: 1, email: 1 })

298

.exec((err, users) => {

299

// Results contain only name, email, and _id fields

300

console.log('User names and emails:', users);

301

});

302

303

// Exclude specific fields

304

db.find({})

305

.projection({ password: 0, secretKey: 0 })

306

.exec((err, users) => {

307

// Results contain all fields except password and secretKey

308

console.log('Users without sensitive data:', users);

309

});

310

311

// Exclude _id field

312

db.find({})

313

.projection({ _id: 0, name: 1, email: 1 })

314

.exec((err, users) => {

315

// Results contain only name and email (no _id)

316

console.log('Users without IDs:', users);

317

});

318

319

// Nested field projection

320

db.find({})

321

.projection({ 'profile.name': 1, 'profile.email': 1 })

322

.exec((err, users) => {

323

console.log('User profile names and emails:', users);

324

});

325

326

// Projection with other cursor operations

327

db.find({ status: 'active' })

328

.sort({ lastLogin: -1 })

329

.limit(50)

330

.projection({ name: 1, lastLogin: 1, status: 1 })

331

.exec((err, users) => {

332

console.log('Recent active users (limited fields):', users);

333

});

334

335

// Projection for performance optimization

336

db.find({ category: 'electronics' })

337

.projection({ name: 1, price: 1, rating: 1 }) // Only essential fields

338

.sort({ rating: -1 })

339

.limit(100)

340

.exec((err, products) => {

341

console.log('Electronics summary data:', products);

342

});

343

344

// Complex projection example

345

db.find({

346

$and: [

347

{ role: { $in: ['admin', 'moderator'] } },

348

{ active: true }

349

]

350

})

351

.projection({

352

name: 1,

353

role: 1,

354

'permissions.read': 1,

355

'permissions.write': 1,

356

lastLogin: 1,

357

password: 0, // Explicitly exclude sensitive data

358

apiKeys: 0 // Explicitly exclude sensitive data

359

})

360

.sort({ lastLogin: -1 })

361

.exec((err, staff) => {

362

console.log('Staff members with permissions:', staff);

363

});

364

```

365

366

### Cursor Execution

367

368

Execute the constructed cursor query and retrieve results through a callback function.

369

370

```javascript { .api }

371

/**

372

* Execute the cursor query and return results

373

* @param {function} callback - Callback function (err, results) => {}

374

*/

375

exec(callback);

376

377

/**

378

* Execution callback signatures vary by operation type

379

*/

380

interface ExecutionCallbacks {

381

find: (err: Error, docs: object[]) => void; // Array of documents

382

findOne: (err: Error, doc: object) => void; // Single document or null

383

count: (err: Error, count: number) => void; // Number of matching documents

384

}

385

```

386

387

**Usage Examples:**

388

389

```javascript

390

const db = new Datastore({ filename: './orders.db', autoload: true });

391

392

// Execute find cursor

393

db.find({ status: 'pending' })

394

.sort({ createdAt: -1 })

395

.limit(20)

396

.exec((err, orders) => {

397

if (err) {

398

console.error('Query failed:', err);

399

return;

400

}

401

console.log('Found', orders.length, 'pending orders');

402

orders.forEach(order => {

403

console.log('- Order', order._id, 'for', order.customerName);

404

});

405

});

406

407

// Execute findOne cursor

408

db.findOne({ orderId: 'ORD_12345' })

409

.projection({ customerName: 1, total: 1, status: 1 })

410

.exec((err, order) => {

411

if (err) {

412

console.error('Query failed:', err);

413

return;

414

}

415

if (order) {

416

console.log('Order found:', order);

417

} else {

418

console.log('Order not found');

419

}

420

});

421

422

// Execute count cursor

423

db.count({ status: 'completed', total: { $gte: 100 } })

424

.exec((err, count) => {

425

if (err) {

426

console.error('Count failed:', err);

427

return;

428

}

429

console.log('High-value completed orders:', count);

430

});

431

432

// Error handling patterns

433

db.find({ invalidField: { $invalidOperator: 'value' } })

434

.exec((err, results) => {

435

if (err) {

436

console.error('Query error:', err.message);

437

console.error('Error type:', err.name);

438

return;

439

}

440

console.log('Results:', results);

441

});

442

443

// Promise-style wrapper for modern async/await usage

444

function promisifyQuery(cursor) {

445

return new Promise((resolve, reject) => {

446

cursor.exec((err, result) => {

447

if (err) reject(err);

448

else resolve(result);

449

});

450

});

451

}

452

453

// Usage with async/await

454

async function getTopProducts() {

455

try {

456

const products = await promisifyQuery(

457

db.find({ featured: true })

458

.sort({ rating: -1 })

459

.limit(10)

460

.projection({ name: 1, rating: 1, price: 1 })

461

);

462

console.log('Top featured products:', products);

463

return products;

464

} catch (err) {

465

console.error('Failed to get top products:', err);

466

throw err;

467

}

468

}

469

```

470

471

### Cursor Chaining Patterns

472

473

Common patterns for combining cursor operations effectively.

474

475

```javascript

476

const db = new Datastore({ filename: './data.db', autoload: true });

477

478

// Pattern 1: Pagination with sorting

479

function getPaginatedData(page, pageSize, sortField = 'createdAt', sortOrder = -1) {

480

return db.find({})

481

.sort({ [sortField]: sortOrder })

482

.skip(page * pageSize)

483

.limit(pageSize);

484

}

485

486

// Usage

487

getPaginatedData(2, 10, 'name', 1).exec((err, data) => {

488

console.log('Page 3 data:', data);

489

});

490

491

// Pattern 2: Search with relevance sorting

492

function searchProducts(searchTerm, maxResults = 20) {

493

return db.find({

494

$or: [

495

{ name: { $regex: new RegExp(searchTerm, 'i') } },

496

{ description: { $regex: new RegExp(searchTerm, 'i') } }

497

]

498

})

499

.sort({ featured: -1, rating: -1 }) // Featured first, then by rating

500

.limit(maxResults)

501

.projection({ name: 1, description: 1, price: 1, rating: 1, featured: 1 });

502

}

503

504

// Pattern 3: Recent items with exclusions

505

function getRecentItems(excludeIds = [], limit = 50) {

506

const query = excludeIds.length > 0

507

? { _id: { $nin: excludeIds } }

508

: {};

509

510

return db.find(query)

511

.sort({ updatedAt: -1 })

512

.limit(limit)

513

.projection({ title: 1, updatedAt: 1, status: 1 });

514

}

515

516

// Pattern 4: Conditional sorting and projection

517

function getUsers(role = null, includeDetails = false) {

518

const query = role ? { role: role } : {};

519

const projection = includeDetails

520

? { password: 0, apiKey: 0 } // Exclude sensitive fields

521

: { name: 1, email: 1, role: 1 }; // Include only basic fields

522

523

return db.find(query)

524

.sort({ name: 1 })

525

.projection(projection);

526

}

527

528

// Pattern 5: Complex filtering with performance optimization

529

function getFilteredProducts(filters) {

530

let cursor = db.find(filters.query || {});

531

532

if (filters.sort) {

533

cursor = cursor.sort(filters.sort);

534

}

535

536

if (filters.skip) {

537

cursor = cursor.skip(filters.skip);

538

}

539

540

if (filters.limit) {

541

cursor = cursor.limit(filters.limit);

542

}

543

544

if (filters.fields) {

545

cursor = cursor.projection(filters.fields);

546

}

547

548

return cursor;

549

}

550

551

// Usage

552

const filters = {

553

query: { category: 'electronics', price: { $lte: 500 } },

554

sort: { rating: -1, price: 1 },

555

skip: 20,

556

limit: 10,

557

fields: { name: 1, price: 1, rating: 1 }

558

};

559

560

getFilteredProducts(filters).exec((err, products) => {

561

console.log('Filtered products:', products);

562

});

563

```

564

565

## Cursor Performance Considerations

566

567

- Cursors use lazy evaluation - operations are not executed until `exec()` is called

568

- Sorting is performed in memory, so large result sets may impact performance

569

- Use `limit()` to reduce memory usage for large queries

570

- Combine `projection()` with queries to minimize data transfer

571

- Index frequently sorted fields for better performance

572

- Use `skip()` sparingly with large offsets as it still processes skipped documents