@classytic/mongokit 3.0.6 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -320,10 +320,12 @@ repo.on('error:create', ({ context, error }) => {
320
320
  ### Query Parser
321
321
 
322
322
  ```javascript
323
- import { queryParser } from '@classytic/mongokit/utils';
323
+ import { QueryParser } from '@classytic/mongokit';
324
+
325
+ const queryParser = new QueryParser();
324
326
 
325
327
  app.get('/users', async (req, res) => {
326
- const { filters, limit, page, sort } = queryParser.parseQuery(req.query);
328
+ const { filters, limit, page, sort } = queryParser.parse(req.query);
327
329
  const result = await userRepo.getAll({ filters, limit, page, sort });
328
330
  res.json(result);
329
331
  });
@@ -1,3 +1,3 @@
1
- export { a as aggregate, c as create, _ as deleteActions, r as read, u as update } from '../index-CkwbNdpJ.js';
1
+ export { b as aggregate, c as create, _ as deleteActions, r as read, u as update } from '../index-3Nkm_Brq.js';
2
2
  import 'mongoose';
3
- import '../types-DDDYo18H.js';
3
+ import '../types-CrSoCuWu.js';
@@ -403,20 +403,51 @@ async function countBy(Model, field, query = {}, options = {}) {
403
403
  return aggregate(Model, pipeline, options);
404
404
  }
405
405
  async function lookup(Model, lookupOptions) {
406
- const { from, localField, foreignField, as, pipeline = [], query = {}, options = {} } = lookupOptions;
406
+ const { from, localField, foreignField, as, pipeline = [], let: letVars, query = {}, options = {} } = lookupOptions;
407
407
  const aggPipeline = [];
408
408
  if (Object.keys(query).length > 0) {
409
409
  aggPipeline.push({ $match: query });
410
410
  }
411
- aggPipeline.push({
412
- $lookup: {
413
- from,
414
- localField,
415
- foreignField,
416
- as,
417
- ...pipeline.length > 0 ? { pipeline } : {}
411
+ const usePipelineForm = pipeline.length > 0 || letVars;
412
+ if (usePipelineForm) {
413
+ if (pipeline.length === 0 && localField && foreignField) {
414
+ const autoPipeline = [
415
+ {
416
+ $match: {
417
+ $expr: {
418
+ $eq: [`$${foreignField}`, `$$${localField}`]
419
+ }
420
+ }
421
+ }
422
+ ];
423
+ aggPipeline.push({
424
+ $lookup: {
425
+ from,
426
+ let: { [localField]: `$${localField}`, ...letVars || {} },
427
+ pipeline: autoPipeline,
428
+ as
429
+ }
430
+ });
431
+ } else {
432
+ aggPipeline.push({
433
+ $lookup: {
434
+ from,
435
+ ...letVars && { let: letVars },
436
+ pipeline,
437
+ as
438
+ }
439
+ });
418
440
  }
419
- });
441
+ } else {
442
+ aggPipeline.push({
443
+ $lookup: {
444
+ from,
445
+ localField,
446
+ foreignField,
447
+ as
448
+ }
449
+ });
450
+ }
420
451
  return aggregate(Model, aggPipeline, options);
421
452
  }
422
453
  async function unwind(Model, field, options = {}) {
@@ -1,5 +1,166 @@
1
- import { Model, ClientSession, PipelineStage } from 'mongoose';
2
- import { A as AnyDocument, C as CreateOptions, h as ObjectId, n as OperationOptions, S as SelectSpec, e as PopulateSpec, f as SortSpec, U as UpdateOptions, p as UpdateWithValidationResult, o as UpdateManyResult, D as DeleteResult, X as GroupResult, T as LookupOptions, Y as MinMaxResult } from './types-DDDYo18H.js';
1
+ import { PipelineStage, ClientSession, Model } from 'mongoose';
2
+ import { A as AnyDocument, r as CreateOptions, k as ObjectId, q as OperationOptions, S as SelectSpec, e as PopulateSpec, f as SortSpec, l as UpdateOptions, t as UpdateWithValidationResult, s as UpdateManyResult, D as DeleteResult, Q as GroupResult, T as MinMaxResult } from './types-CrSoCuWu.js';
3
+
4
+ /**
5
+ * LookupBuilder - MongoDB $lookup Utility
6
+ *
7
+ * Standalone builder for efficient custom field joins using MongoDB $lookup aggregation.
8
+ * Optimized for millions of records with proper index usage.
9
+ *
10
+ * Features:
11
+ * - Join on custom fields (slugs, SKUs, codes, etc.)
12
+ * - Pipeline support for complex transformations
13
+ * - Index-aware query building
14
+ * - Single vs Array result handling
15
+ * - Nested lookups
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * // Simple lookup - join employees with departments by slug
20
+ * const lookup = new LookupBuilder('departments')
21
+ * .localField('departmentSlug')
22
+ * .foreignField('slug')
23
+ * .as('department')
24
+ * .single(); // Unwrap array to single object
25
+ *
26
+ * const pipeline = lookup.build();
27
+ * const results = await Employee.aggregate(pipeline);
28
+ *
29
+ * // Advanced lookup with pipeline
30
+ * const lookup = new LookupBuilder('products')
31
+ * .localField('productIds')
32
+ * .foreignField('sku')
33
+ * .pipeline([
34
+ * { $match: { status: 'active' } },
35
+ * { $project: { name: 1, price: 1 } }
36
+ * ])
37
+ * .as('products');
38
+ * ```
39
+ */
40
+
41
+ interface LookupOptions {
42
+ /** Collection to join with */
43
+ from: string;
44
+ /** Field from the input documents */
45
+ localField: string;
46
+ /** Field from the documents of the "from" collection */
47
+ foreignField: string;
48
+ /** Name of the new array field to add to the input documents */
49
+ as?: string;
50
+ /** Whether to unwrap array to single object */
51
+ single?: boolean;
52
+ /** Additional pipeline to run on the joined collection */
53
+ pipeline?: PipelineStage[];
54
+ /** Optional let variables for pipeline */
55
+ let?: Record<string, string>;
56
+ /** Query filter to apply before join (legacy, for aggregate.ts compatibility) */
57
+ query?: Record<string, unknown>;
58
+ /** Query options (legacy, for aggregate.ts compatibility) */
59
+ options?: {
60
+ session?: ClientSession;
61
+ };
62
+ }
63
+ /**
64
+ * Fluent builder for MongoDB $lookup aggregation stage
65
+ * Optimized for custom field joins at scale
66
+ */
67
+ declare class LookupBuilder {
68
+ private options;
69
+ constructor(from?: string);
70
+ /**
71
+ * Set the collection to join with
72
+ */
73
+ from(collection: string): this;
74
+ /**
75
+ * Set the local field (source collection)
76
+ * IMPORTANT: This field should be indexed for optimal performance
77
+ */
78
+ localField(field: string): this;
79
+ /**
80
+ * Set the foreign field (target collection)
81
+ * IMPORTANT: This field should be indexed (preferably unique) for optimal performance
82
+ */
83
+ foreignField(field: string): this;
84
+ /**
85
+ * Set the output field name
86
+ * Defaults to the collection name if not specified
87
+ */
88
+ as(fieldName: string): this;
89
+ /**
90
+ * Mark this lookup as returning a single document
91
+ * Automatically unwraps the array result to a single object or null
92
+ */
93
+ single(isSingle?: boolean): this;
94
+ /**
95
+ * Add a pipeline to filter/transform joined documents
96
+ * Useful for filtering, sorting, or limiting joined results
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * lookup.pipeline([
101
+ * { $match: { status: 'active' } },
102
+ * { $sort: { priority: -1 } },
103
+ * { $limit: 5 }
104
+ * ]);
105
+ * ```
106
+ */
107
+ pipeline(stages: PipelineStage[]): this;
108
+ /**
109
+ * Set let variables for use in pipeline
110
+ * Allows referencing local document fields in the pipeline
111
+ */
112
+ let(variables: Record<string, string>): this;
113
+ /**
114
+ * Build the $lookup aggregation stage(s)
115
+ * Returns an array of pipeline stages including $lookup and optional $unwind
116
+ *
117
+ * IMPORTANT: MongoDB $lookup has two mutually exclusive forms:
118
+ * 1. Simple form: { from, localField, foreignField, as }
119
+ * 2. Pipeline form: { from, let, pipeline, as }
120
+ *
121
+ * When pipeline or let is specified, we use the pipeline form.
122
+ * Otherwise, we use the simpler localField/foreignField form.
123
+ */
124
+ build(): PipelineStage[];
125
+ /**
126
+ * Build and return only the $lookup stage (without $unwind)
127
+ * Useful when you want to handle unwrapping yourself
128
+ */
129
+ buildLookupOnly(): PipelineStage.Lookup;
130
+ /**
131
+ * Static helper: Create a simple lookup in one line
132
+ */
133
+ static simple(from: string, localField: string, foreignField: string, options?: {
134
+ as?: string;
135
+ single?: boolean;
136
+ }): PipelineStage[];
137
+ /**
138
+ * Static helper: Create multiple lookups at once
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * const pipeline = LookupBuilder.multiple([
143
+ * { from: 'departments', localField: 'deptSlug', foreignField: 'slug', single: true },
144
+ * { from: 'managers', localField: 'managerId', foreignField: '_id', single: true }
145
+ * ]);
146
+ * ```
147
+ */
148
+ static multiple(lookups: LookupOptions[]): PipelineStage[];
149
+ /**
150
+ * Static helper: Create a nested lookup (lookup within lookup)
151
+ * Useful for multi-level joins like Order -> Product -> Category
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * // Join orders with products, then products with categories
156
+ * const pipeline = LookupBuilder.nested([
157
+ * { from: 'products', localField: 'productSku', foreignField: 'sku', as: 'product', single: true },
158
+ * { from: 'categories', localField: 'product.categorySlug', foreignField: 'slug', as: 'product.category', single: true }
159
+ * ]);
160
+ * ```
161
+ */
162
+ static nested(lookups: LookupOptions[]): PipelineStage[];
163
+ }
3
164
 
4
165
  /**
5
166
  * Create Actions
@@ -269,6 +430,12 @@ declare function countBy(Model: Model<any>, field: string, query?: Record<string
269
430
  }): Promise<GroupResult[]>;
270
431
  /**
271
432
  * Lookup (join) with another collection
433
+ *
434
+ * MongoDB $lookup has two mutually exclusive forms:
435
+ * 1. Simple form: { from, localField, foreignField, as }
436
+ * 2. Pipeline form: { from, let, pipeline, as }
437
+ *
438
+ * This function automatically selects the appropriate form based on parameters.
272
439
  */
273
440
  declare function lookup<TDoc = AnyDocument>(Model: Model<TDoc>, lookupOptions: LookupOptions): Promise<TDoc[]>;
274
441
  /**
@@ -334,4 +501,4 @@ declare namespace index {
334
501
  export { aggregate$1 as aggregate, create$1 as create, _delete as deleteActions, index_read as read, update$1 as update };
335
502
  }
336
503
 
337
- export { _delete as _, aggregate$1 as a, create$1 as c, index as i, read as r, update$1 as u };
504
+ export { type LookupOptions as L, _delete as _, LookupBuilder as a, aggregate$1 as b, create$1 as c, index as i, read as r, update$1 as u };