0
# Query Building
1
2
Fluent query builder with method chaining, population, lean queries, and advanced MongoDB query operations for flexible data retrieval.
3
4
## Capabilities
5
6
### Query Construction
7
8
Create and build queries with fluent method chaining and MongoDB operators.
9
10
```javascript { .api }
11
interface Query<ResultType, DocType> {
12
/**
13
* Add where conditions to query
14
* @param path - Field path or condition object
15
* @param val - Value for equality match
16
* @returns this query
17
*/
18
where(path: string | object, val?: any): this;
19
20
/**
21
* Specify equality condition
22
* @param value - Value to match
23
* @returns this query
24
*/
25
equals(value: any): this;
26
27
/**
28
* Greater than condition
29
* @param value - Comparison value
30
* @returns this query
31
*/
32
gt(value: any): this;
33
34
/**
35
* Greater than or equal condition
36
* @param value - Comparison value
37
* @returns this query
38
*/
39
gte(value: any): this;
40
41
/**
42
* Less than condition
43
* @param value - Comparison value
44
* @returns this query
45
*/
46
lt(value: any): this;
47
48
/**
49
* Less than or equal condition
50
* @param value - Comparison value
51
* @returns this query
52
*/
53
lte(value: any): this;
54
55
/**
56
* In array condition
57
* @param value - Array of values to match
58
* @returns this query
59
*/
60
in(value: any[]): this;
61
62
/**
63
* Not in array condition
64
* @param value - Array of values to exclude
65
* @returns this query
66
*/
67
nin(value: any[]): this;
68
69
/**
70
* Field exists condition
71
* @param val - True to check existence, false for non-existence
72
* @returns this query
73
*/
74
exists(val?: boolean): this;
75
76
/**
77
* Regular expression match
78
* @param val - Regular expression or string pattern
79
* @returns this query
80
*/
81
regex(val: RegExp | string): this;
82
83
/**
84
* Array size condition
85
* @param val - Required array length
86
* @returns this query
87
*/
88
size(val: number): this;
89
90
/**
91
* All array elements match
92
* @param val - Array of values that must all be present
93
* @returns this query
94
*/
95
all(val: any[]): this;
96
97
/**
98
* Element match condition for arrays
99
* @param val - Condition object for array elements
100
* @returns this query
101
*/
102
elemMatch(val: any): this;
103
104
/**
105
* Logical OR conditions
106
* @param conditions - Array of condition objects
107
* @returns this query
108
*/
109
or(conditions: any[]): this;
110
111
/**
112
* Logical AND conditions
113
* @param conditions - Array of condition objects
114
* @returns this query
115
*/
116
and(conditions: any[]): this;
117
118
/**
119
* Logical NOR conditions
120
* @param conditions - Array of condition objects
121
* @returns this query
122
*/
123
nor(conditions: any[]): this;
124
125
/**
126
* Return lean objects instead of Mongoose documents
127
* @param val - Enable/disable lean mode
128
* @returns this query
129
*/
130
lean<T = any>(val?: boolean): Query<T>;
131
132
/**
133
* Set cursor batch size for streaming
134
* @param val - Batch size
135
* @returns this query
136
*/
137
batchSize(val: number): this;
138
139
/**
140
* Allow disk usage for large operations
141
* @param v - Allow disk usage
142
* @returns this query
143
*/
144
allowDiskUse(v: boolean): this;
145
146
/**
147
* Add comment to query for profiling
148
* @param val - Comment string
149
* @returns this query
150
*/
151
comment(val: string): this;
152
153
/**
154
* Return cursor for streaming results
155
* @param options - Cursor options
156
* @returns Query cursor
157
*/
158
cursor(options?: any): QueryCursor<DocType>;
159
160
/**
161
* Set query session for transactions
162
* @param session - MongoDB session
163
* @returns this query
164
*/
165
session(session: ClientSession): this;
166
167
/**
168
* Transform query results before returning
169
* @param fn - Transform function
170
* @returns this query
171
*/
172
transform(fn: Function): this;
173
174
/**
175
* Set maximum time for query execution
176
* @param ms - Maximum time in milliseconds
177
* @returns this query
178
*/
179
maxTimeMS(ms: number): this;
180
181
/**
182
* Get query options object
183
* @returns Current query options
184
*/
185
getOptions(): QueryOptions;
186
187
/**
188
* Check if query has projection
189
* @returns True if query has field selection
190
*/
191
selected(): boolean;
192
193
/**
194
* Promise then handler
195
* @param resolve - Success handler
196
* @param reject - Error handler
197
* @returns Promise
198
*/
199
then(resolve?: Function, reject?: Function): Promise<ResultType>;
200
201
/**
202
* Promise catch handler
203
* @param reject - Error handler
204
* @returns Promise
205
*/
206
catch(reject?: (reason: any) => any): Promise<ResultType>;
207
}
208
```
209
210
**Usage Examples:**
211
212
```javascript
213
const User = mongoose.model('User', userSchema);
214
215
// Basic conditions
216
const adults = await User.find().where('age').gte(18);
217
const youngAdults = await User.find().where('age').gte(18).lte(30);
218
219
// Multiple conditions
220
const activeUsers = await User.find()
221
.where('status').equals('active')
222
.where('lastLogin').gte(new Date(Date.now() - 30 * 24 * 60 * 60 * 1000));
223
224
// Array conditions
225
const usersWithTags = await User.find()
226
.where('tags').in(['premium', 'verified'])
227
.where('skills').size(3)
228
.where('certifications').exists(true);
229
230
// Text search
231
const searchResults = await User.find()
232
.where('name').regex(/john/i)
233
.where('bio').regex('developer');
234
235
// Complex conditions
236
const complexQuery = await User.find()
237
.or([
238
{ age: { $lt: 25 } },
239
{ status: 'premium' }
240
])
241
.and([
242
{ active: true },
243
{ verified: true }
244
]);
245
```
246
247
### Field Selection and Projection
248
249
Control which fields are returned in query results.
250
251
```javascript { .api }
252
interface Query<ResultType, DocType> {
253
/**
254
* Select fields to include or exclude
255
* @param fields - Field selection string or object
256
* @returns this query
257
*/
258
select(fields: string | object): this;
259
260
/**
261
* Exclude specific fields
262
* @param fields - Fields to exclude
263
* @returns this query
264
*/
265
deselect(fields: string): this;
266
267
/**
268
* Return distinct values for a field
269
* @param field - Field to get distinct values for
270
* @returns Query for distinct values
271
*/
272
distinct(field: string): Query<any[], DocType>;
273
}
274
```
275
276
**Usage Examples:**
277
278
```javascript
279
// Select specific fields
280
const users = await User.find().select('name email age');
281
const users = await User.find().select({ name: 1, email: 1, age: 1 });
282
283
// Exclude fields
284
const users = await User.find().select('-password -secret');
285
const users = await User.find().select({ password: 0, secret: 0 });
286
287
// Mixed selection (include some, exclude others)
288
const users = await User.find().select('name email -_id');
289
290
// Get distinct values
291
const ages = await User.find().distinct('age');
292
const statuses = await User.find({ active: true }).distinct('status');
293
```
294
295
### Sorting and Pagination
296
297
Sort results and implement pagination with limit and skip.
298
299
```javascript { .api }
300
interface Query<ResultType, DocType> {
301
/**
302
* Sort query results
303
* @param sort - Sort specification
304
* @returns this query
305
*/
306
sort(sort: string | object): this;
307
308
/**
309
* Limit number of results
310
* @param val - Maximum number of documents to return
311
* @returns this query
312
*/
313
limit(val: number): this;
314
315
/**
316
* Skip number of results
317
* @param val - Number of documents to skip
318
* @returns this query
319
*/
320
skip(val: number): this;
321
322
/**
323
* Set query slice for arrays
324
* @param path - Array field path
325
* @param val - Number of elements or [skip, limit] array
326
* @returns this query
327
*/
328
slice(path: string, val: number | [number, number]): this;
329
}
330
```
331
332
**Usage Examples:**
333
334
```javascript
335
// Sort by single field
336
const users = await User.find().sort('name'); // Ascending
337
const users = await User.find().sort('-createdAt'); // Descending
338
339
// Sort by multiple fields
340
const users = await User.find().sort({ age: -1, name: 1 });
341
const users = await User.find().sort('age -name');
342
343
// Pagination
344
const page = 2;
345
const limit = 10;
346
const users = await User.find()
347
.sort('-createdAt')
348
.skip((page - 1) * limit)
349
.limit(limit);
350
351
// Array slicing
352
const posts = await Post.find()
353
.select('title comments')
354
.slice('comments', 5) // First 5 comments
355
.slice('tags', [2, 5]); // Skip 2, take 5 tags
356
```
357
358
### Population
359
360
Populate referenced documents with flexible options and nested population.
361
362
```javascript { .api }
363
interface Query<ResultType, DocType> {
364
/**
365
* Populate referenced fields
366
* @param path - Path to populate or populate options
367
* @returns this query
368
*/
369
populate(path: string | PopulateOptions | PopulateOptions[]): this;
370
371
/**
372
* Populate virtual fields
373
* @param path - Virtual path to populate
374
* @returns this query
375
*/
376
populateVirtuals(path: string): this;
377
}
378
379
interface PopulateOptions {
380
/** Path to populate */
381
path: string;
382
383
/** Fields to select from populated documents */
384
select?: string | object;
385
386
/** Model to populate from */
387
model?: string | Model<any>;
388
389
/** Additional query conditions for populated documents */
390
match?: any;
391
392
/** Sort populated documents */
393
sort?: any;
394
395
/** Limit populated documents */
396
limit?: number;
397
398
/** Skip populated documents */
399
skip?: number;
400
401
/** Populate nested fields */
402
populate?: PopulateOptions | PopulateOptions[];
403
404
/** Transform populated results */
405
transform?: Function;
406
407
/** Preserve null and undefined values */
408
strictPopulate?: boolean;
409
410
/** Count instead of populating documents */
411
count?: boolean;
412
}
413
```
414
415
**Usage Examples:**
416
417
```javascript
418
// Basic population
419
const posts = await Post.find().populate('author');
420
421
// Population with field selection
422
const posts = await Post.find().populate('author', 'name email');
423
424
// Population with conditions
425
const posts = await Post.find().populate({
426
path: 'comments',
427
match: { approved: true },
428
select: 'text author createdAt',
429
sort: { createdAt: -1 },
430
limit: 10
431
});
432
433
// Multiple populations
434
const posts = await Post.find().populate([
435
{ path: 'author', select: 'name' },
436
{ path: 'category', select: 'name slug' },
437
{
438
path: 'comments',
439
populate: { path: 'author', select: 'name avatar' }
440
}
441
]);
442
443
// Nested population
444
const users = await User.find().populate({
445
path: 'posts',
446
populate: {
447
path: 'comments',
448
populate: {
449
path: 'author',
450
select: 'name'
451
}
452
}
453
});
454
455
// Virtual population
456
userSchema.virtual('posts', {
457
ref: 'Post',
458
localField: '_id',
459
foreignField: 'author'
460
});
461
462
const users = await User.find().populate('posts');
463
```
464
465
### Query Execution
466
467
Execute queries and control result format and behavior.
468
469
```javascript { .api }
470
interface Query<ResultType, DocType> {
471
/**
472
* Execute the query
473
* @returns Promise resolving to query results
474
*/
475
exec(): Promise<ResultType>;
476
477
/**
478
* Execute query and return a cursor
479
* @param options - Cursor options
480
* @returns Query cursor for streaming results
481
*/
482
cursor(options?: CursorOptions): QueryCursor<DocType>;
483
484
/**
485
* Execute query and return readable stream
486
* @param options - Stream options
487
* @returns Readable stream of documents
488
*/
489
stream(options?: any): NodeJS.ReadableStream;
490
491
/**
492
* Get query execution plan
493
* @param verbosity - Explanation verbosity level
494
* @returns Promise resolving to execution plan
495
*/
496
explain(verbosity?: string): Promise<any>;
497
498
/**
499
* Return lean documents (plain objects)
500
* @param val - Enable/disable lean mode
501
* @returns this query
502
*/
503
lean(val?: boolean | object): this;
504
505
/**
506
* Set read preference
507
* @param pref - Read preference
508
* @param tags - Read preference tags
509
* @returns this query
510
*/
511
read(pref: string, tags?: any[]): this;
512
513
/**
514
* Set query comment for profiling
515
* @param val - Comment string
516
* @returns this query
517
*/
518
comment(val: string): this;
519
520
/**
521
* Force index usage
522
* @param val - Index specification
523
* @returns this query
524
*/
525
hint(val: object): this;
526
527
/**
528
* Set maximum execution time
529
* @param ms - Maximum time in milliseconds
530
* @returns this query
531
*/
532
maxTimeMS(ms: number): this;
533
}
534
535
interface CursorOptions {
536
/** Transform function for each document */
537
transform?: Function;
538
539
/** Batch size for cursor */
540
batchSize?: number;
541
542
/** Use lean mode */
543
lean?: boolean;
544
}
545
```
546
547
**Usage Examples:**
548
549
```javascript
550
// Basic execution
551
const users = await User.find({ active: true }).exec();
552
553
// Lean queries (return plain objects)
554
const users = await User.find().lean();
555
const fastUsers = await User.find().lean({ virtuals: true });
556
557
// Cursor for large datasets
558
const cursor = User.find().cursor();
559
for (let doc = await cursor.next(); doc != null; doc = await cursor.next()) {
560
console.log(doc.name);
561
}
562
563
// Streaming
564
const stream = User.find().stream();
565
stream.on('data', (doc) => {
566
console.log('Received:', doc.name);
567
});
568
569
// Query optimization
570
const users = await User.find()
571
.hint({ email: 1 }) // Force index usage
572
.maxTimeMS(5000) // 5 second timeout
573
.comment('User lookup query')
574
.read('secondary'); // Read from secondary
575
576
// Execution plan
577
const plan = await User.find({ age: { $gte: 18 } }).explain('executionStats');
578
console.log('Query execution plan:', plan);
579
```
580
581
### Advanced Query Options
582
583
Configure query behavior, collation, and database-specific options.
584
585
```javascript { .api }
586
interface Query<ResultType, DocType> {
587
/**
588
* Use database session for transactions
589
* @param session - MongoDB session
590
* @returns this query
591
*/
592
session(session: ClientSession): this;
593
594
/**
595
* Set collation for string comparisons
596
* @param collation - Collation specification
597
* @returns this query
598
*/
599
collation(collation: CollationOptions): this;
600
601
/**
602
* Set batch size for cursor operations
603
* @param val - Batch size
604
* @returns this query
605
*/
606
batchSize(val: number): this;
607
608
/**
609
* Allow disk usage for large operations
610
* @param val - Enable disk usage
611
* @returns this query
612
*/
613
allowDiskUse(val: boolean): this;
614
615
/**
616
* Set maximum number of documents to scan
617
* @param val - Maximum scan count
618
* @returns this query
619
*/
620
maxscan(val: number): this;
621
622
/**
623
* Enable/disable query timeout
624
* @param val - Enable timeout
625
* @returns this query
626
*/
627
timeout(val: boolean): this;
628
629
/**
630
* Cast query conditions using schema
631
* @param model - Model to use for casting
632
* @param obj - Object to cast (optional)
633
* @returns Casted conditions
634
*/
635
cast(model?: Model<DocType>, obj?: any): any;
636
}
637
638
interface CollationOptions {
639
locale: string;
640
caseLevel?: boolean;
641
caseFirst?: string;
642
strength?: number;
643
numericOrdering?: boolean;
644
alternate?: string;
645
maxVariable?: string;
646
backwards?: boolean;
647
}
648
```
649
650
**Usage Examples:**
651
652
```javascript
653
// Transaction session
654
const session = await mongoose.startSession();
655
session.startTransaction();
656
657
try {
658
const users = await User.find({ status: 'pending' })
659
.session(session);
660
661
await User.updateMany(
662
{ _id: { $in: users.map(u => u._id) } },
663
{ status: 'approved' }
664
).session(session);
665
666
await session.commitTransaction();
667
} catch (error) {
668
await session.abortTransaction();
669
throw error;
670
} finally {
671
session.endSession();
672
}
673
674
// Collation for case-insensitive sorting
675
const users = await User.find()
676
.sort({ name: 1 })
677
.collation({ locale: 'en', strength: 2 });
678
679
// Performance tuning
680
const results = await User.find({ status: 'active' })
681
.batchSize(100)
682
.maxTimeMS(10000)
683
.allowDiskUse(true)
684
.maxscan(1000);
685
```
686
687
## Types
688
689
```javascript { .api }
690
interface QueryCursor<T> extends NodeJS.ReadableStream {
691
/** Get next document from cursor */
692
next(): Promise<T | null>;
693
694
/** Close the cursor */
695
close(): Promise<void>;
696
697
/** Check if cursor is closed */
698
closed: boolean;
699
700
/** Transform each document */
701
map<U>(fn: (doc: T) => U): QueryCursor<U>;
702
703
/** Add cursor event listeners */
704
on(event: 'data' | 'error' | 'end', listener: Function): this;
705
}
706
707
type ProjectionType<T> =
708
| string
709
| { [K in keyof T]?: 1 | 0 | boolean }
710
| { [key: string]: 1 | 0 | boolean };
711
712
interface QueryOptions {
713
/** Use lean mode */
714
lean?: boolean;
715
716
/** Population options */
717
populate?: string | PopulateOptions | PopulateOptions[];
718
719
/** Field selection */
720
select?: string | object;
721
722
/** Sort specification */
723
sort?: string | object;
724
725
/** Limit results */
726
limit?: number;
727
728
/** Skip results */
729
skip?: number;
730
731
/** Maximum time for query execution */
732
maxTimeMS?: number;
733
734
/** Query comment */
735
comment?: string;
736
737
/** Force index usage */
738
hint?: object;
739
740
/** Read preference */
741
read?: any;
742
743
/** Database session */
744
session?: ClientSession;
745
746
/** Collation options */
747
collation?: CollationOptions;
748
749
/** Batch size */
750
batchSize?: number;
751
752
/** Allow disk usage */
753
allowDiskUse?: boolean;
754
}
755
```