@r5v/mongoose-paginate 1.0.13 → 1.0.15
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 +210 -17
- package/dist/{types/aggregationPagingQuery.d.ts → aggregationPagingQuery.d.ts} +6 -7
- package/dist/aggregationPagingQuery.d.ts.map +1 -0
- package/dist/aggregationPagingQuery.js +19 -4
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -1
- package/dist/{types/pagingQuery.d.ts → pagingQuery.d.ts} +6 -7
- package/dist/pagingQuery.d.ts.map +1 -0
- package/dist/pagingQuery.js +8 -3
- package/dist/tests/aggregationPagingQuery.spec.d.ts +2 -0
- package/dist/tests/aggregationPagingQuery.spec.d.ts.map +1 -0
- package/dist/tests/aggregationPagingQuery.spec.js +419 -0
- package/dist/tests/buildPopulateFromString.spec.d.ts +2 -0
- package/dist/tests/buildPopulateFromString.spec.d.ts.map +1 -0
- package/dist/tests/buildPopulateFromString.spec.js +223 -0
- package/dist/tests/dotNotation.spec.d.ts.map +1 -0
- package/dist/tests/findProtectedPaths.spec.d.ts.map +1 -0
- package/dist/tests/findProtectedPaths.spec.js +128 -6
- package/dist/tests/getPathsWithRef.spec.d.ts.map +1 -0
- package/dist/tests/getPathsWithRef.spec.js +92 -18
- package/dist/tests/getPropertyFromDotNotation.spec.d.ts.map +1 -0
- package/dist/tests/insertPopulate.spec.d.ts.map +1 -0
- package/dist/tests/isJsonString.spec.d.ts +2 -0
- package/dist/tests/isJsonString.spec.d.ts.map +1 -0
- package/dist/tests/isJsonString.spec.js +116 -0
- package/dist/tests/isValidDateString.spec.d.ts +2 -0
- package/dist/tests/isValidDateString.spec.d.ts.map +1 -0
- package/dist/tests/isValidDateString.spec.js +116 -0
- package/dist/tests/pagingQuery.spec.d.ts.map +1 -0
- package/dist/tests/pagingQuery.spec.js +238 -13
- package/dist/tests/parseParams.spec.d.ts +2 -0
- package/dist/tests/parseParams.spec.d.ts.map +1 -0
- package/dist/tests/parseParams.spec.js +212 -0
- package/dist/tests/parseSortString.spec.d.ts.map +1 -0
- package/dist/tests/schemaTraversal.spec.d.ts +2 -0
- package/dist/tests/schemaTraversal.spec.d.ts.map +1 -0
- package/dist/tests/schemaTraversal.spec.js +139 -0
- package/dist/types/index.d.ts +72 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/buildPopulateFromString.d.ts +8 -0
- package/dist/utils/buildPopulateFromString.d.ts.map +1 -0
- package/dist/utils/buildPopulateFromString.js +54 -49
- package/dist/utils/dotNotation.d.ts +11 -0
- package/dist/utils/dotNotation.d.ts.map +1 -0
- package/dist/utils/dotNotation.js +2 -2
- package/dist/utils/findKeyWithValue.d.ts.map +1 -0
- package/dist/utils/findProtectedPaths.d.ts.map +1 -0
- package/dist/utils/findProtectedPaths.js +14 -56
- package/dist/utils/getPathsWithRef.d.ts +5 -0
- package/dist/utils/getPathsWithRef.d.ts.map +1 -0
- package/dist/utils/getPathsWithRef.js +59 -96
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +33 -0
- package/dist/utils/isJsonString.d.ts.map +1 -0
- package/dist/utils/isValidDateString.d.ts.map +1 -0
- package/dist/utils/parseParams.d.ts.map +1 -0
- package/dist/utils/parseParams.js +14 -12
- package/dist/utils/parsePopulateQuery.d.ts.map +1 -0
- package/dist/utils/parsePopulateQuery.js +1 -1
- package/dist/{types/utils → utils}/parseSortString.d.ts +1 -1
- package/dist/utils/parseSortString.d.ts.map +1 -0
- package/dist/utils/schemaTraversal.d.ts +9 -0
- package/dist/utils/schemaTraversal.d.ts.map +1 -0
- package/dist/utils/schemaTraversal.js +37 -0
- package/package.json +23 -2
- package/dist/types/aggregationPagingQuery.d.ts.map +0 -1
- package/dist/types/pagingQuery.d.ts.map +0 -1
- package/dist/types/tests/dotNotation.spec.d.ts.map +0 -1
- package/dist/types/tests/findProtectedPaths.spec.d.ts.map +0 -1
- package/dist/types/tests/getPathsWithRef.spec.d.ts.map +0 -1
- package/dist/types/tests/getPropertyFromDotNotation.spec.d.ts.map +0 -1
- package/dist/types/tests/insertPopulate.spec.d.ts.map +0 -1
- package/dist/types/tests/pagingQuery.spec.d.ts.map +0 -1
- package/dist/types/tests/parseSortString.spec.d.ts.map +0 -1
- package/dist/types/types/index.d.ts +0 -61
- package/dist/types/types/index.d.ts.map +0 -1
- package/dist/types/utils/buildPopulateFromString.d.ts +0 -8
- package/dist/types/utils/buildPopulateFromString.d.ts.map +0 -1
- package/dist/types/utils/dotNotation.d.ts +0 -11
- package/dist/types/utils/dotNotation.d.ts.map +0 -1
- package/dist/types/utils/findKeyWithValue.d.ts.map +0 -1
- package/dist/types/utils/findProtectedPaths.d.ts.map +0 -1
- package/dist/types/utils/getPathsWithRef.d.ts +0 -5
- package/dist/types/utils/getPathsWithRef.d.ts.map +0 -1
- package/dist/types/utils/isJsonString.d.ts.map +0 -1
- package/dist/types/utils/isValidDateString.d.ts.map +0 -1
- package/dist/types/utils/parseParams.d.ts.map +0 -1
- package/dist/types/utils/parsePopulateQuery.d.ts.map +0 -1
- package/dist/types/utils/parseSortString.d.ts.map +0 -1
- /package/dist/{types/tests → tests}/dotNotation.spec.d.ts +0 -0
- /package/dist/{types/tests → tests}/findProtectedPaths.spec.d.ts +0 -0
- /package/dist/{types/tests → tests}/getPathsWithRef.spec.d.ts +0 -0
- /package/dist/{types/tests → tests}/getPropertyFromDotNotation.spec.d.ts +0 -0
- /package/dist/{types/tests → tests}/insertPopulate.spec.d.ts +0 -0
- /package/dist/{types/tests → tests}/pagingQuery.spec.d.ts +0 -0
- /package/dist/{types/tests → tests}/parseSortString.spec.d.ts +0 -0
- /package/dist/{types/utils → utils}/findKeyWithValue.d.ts +0 -0
- /package/dist/{types/utils → utils}/findProtectedPaths.d.ts +0 -0
- /package/dist/{types/utils → utils}/isJsonString.d.ts +0 -0
- /package/dist/{types/utils → utils}/isValidDateString.d.ts +0 -0
- /package/dist/{types/utils → utils}/parseParams.d.ts +0 -0
- /package/dist/{types/utils → utils}/parsePopulateQuery.d.ts +0 -0
package/README.md
CHANGED
|
@@ -82,7 +82,49 @@ PagingQuery(Express.Request, mongoose.Model, options )
|
|
|
82
82
|
| $sort | space separated mongoose sort string \n name -value | inserts sort into the query. In aggregation queries this will insert a sort after the existing pipeline | PagingQuery, AggregationPagingQuery |
|
|
83
83
|
| $preSort | comma separated dot notation string \n books,-_id \| -_id,-name,address.-address1 | in aggregate queries this will insert a sort object after the initial match in the pipeline. | AggregationPagingQuery |
|
|
84
84
|
| $count | comma separated dot notation string | this comma separate string will traverse the results and insert a new field with the $size of the selected key/value . this new name field will be appended with `_count` | AggregationPagingQuery |
|
|
85
|
-
| $postFilter | Any Json Object | creates and additional filter and in aggregation queries is appended to the end of the pipeline | AggregationPagingQuery |
|
|
85
|
+
| $postFilter | Any Json Object | creates and additional filter and in aggregation queries is appended to the end of the pipeline | AggregationPagingQuery |
|
|
86
|
+
|
|
87
|
+
#### Example URLs
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Basic pagination
|
|
91
|
+
GET /api/books?$limit=10&$skip=20
|
|
92
|
+
|
|
93
|
+
# Filter by field (JSON format)
|
|
94
|
+
GET /api/books?$filter={"genre":"Horror"}
|
|
95
|
+
|
|
96
|
+
# Filter with operators
|
|
97
|
+
GET /api/books?$filter={"price.amount":{"$gte":20,"$lte":50}}
|
|
98
|
+
|
|
99
|
+
# Sort (space-separated: field direction)
|
|
100
|
+
GET /api/books?$sort=price.amount%20desc
|
|
101
|
+
GET /api/books?$sort=title%20asc,createdAt%20desc
|
|
102
|
+
|
|
103
|
+
# Select specific fields
|
|
104
|
+
GET /api/books?$select=title,isbn,price
|
|
105
|
+
|
|
106
|
+
# Populate references
|
|
107
|
+
GET /api/books?$populate=author,publisher
|
|
108
|
+
|
|
109
|
+
# Deep populate with field selection
|
|
110
|
+
GET /api/books?$populate=author[firstName,lastName],publisher[name]
|
|
111
|
+
|
|
112
|
+
# Aggregation with preSort (sorts before pipeline)
|
|
113
|
+
GET /api/books?$preSort=createdAt%20desc
|
|
114
|
+
|
|
115
|
+
# Aggregation with postFilter (filters on computed fields)
|
|
116
|
+
GET /api/books?$postFilter={"priceCategory":"expensive"}
|
|
117
|
+
|
|
118
|
+
# Count array fields
|
|
119
|
+
GET /api/books?$count=authors,tags
|
|
120
|
+
# Result includes: authors_count, tags_count
|
|
121
|
+
|
|
122
|
+
# Disable paging (returns array instead of paged object)
|
|
123
|
+
GET /api/books?$paging=false
|
|
124
|
+
|
|
125
|
+
# Lean query (returns plain objects)
|
|
126
|
+
GET /api/books?$lean=true
|
|
127
|
+
```
|
|
86
128
|
|
|
87
129
|
#### Options
|
|
88
130
|
|
|
@@ -97,12 +139,143 @@ PagingQuery(Express.Request, mongoose.Model, options )
|
|
|
97
139
|
| staticPostFilter | Mongo Filter Object | create a filter on the pipeline that is added after all the pipeline stages. this cannot be overwritten by params | AggregationPagingQuery | | {} |
|
|
98
140
|
| staticFilter | Mongo Filter Object | create a filter on the pipeline that is added before all the pipeline stages. on find requests, this is added to the filter object. this cannot be overwritten by params | AggregationPagingQuery | | {} |
|
|
99
141
|
| pipeline | MongoPipelineStage[] | pipeline request object. if the first item in pipeline stage is a $match or another required first stage operator. it will be placed before all other modifiers | AggregationPagingQuery | true | [] |
|
|
100
|
-
| removeProtected
|
|
142
|
+
| removeProtected | boolean | **EXPERIMENTAL** - auto remove protected fields (those with `select: false` in schema) from aggregation results. Use with caution and test thoroughly in your environment | AggregationPagingQuery | | false |
|
|
101
143
|
|
|
102
144
|
## Utilities
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
145
|
+
|
|
146
|
+
All utilities can be imported directly from the main package or from the `/utils` subpath:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// Direct import
|
|
150
|
+
import { buildPopulate, parseSortString, getPropertyFromDotNotation } from '@r5v/mongoose-paginate'
|
|
151
|
+
|
|
152
|
+
// Subpath import (all utilities)
|
|
153
|
+
import * as utils from '@r5v/mongoose-paginate/utils'
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Populate Utilities
|
|
157
|
+
|
|
158
|
+
| Name | Signature | Description |
|
|
159
|
+
|:-----|:----------|:------------|
|
|
160
|
+
| `buildPopulate` | `(pathString: string) => PopulateItem[]` | Creates a Mongoose populate object from dot notation string. Supports deep population with field selection using bracket notation: `"author[name].books[title],publisher[name]"` |
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { buildPopulate } from '@r5v/mongoose-paginate'
|
|
164
|
+
|
|
165
|
+
const populate = buildPopulate('author[name,email].books[title],publisher')
|
|
166
|
+
// Result: [
|
|
167
|
+
// { path: 'author', select: 'name email', populate: [{ path: 'books', select: 'title' }] },
|
|
168
|
+
// { path: 'publisher' }
|
|
169
|
+
// ]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Sort Utilities
|
|
173
|
+
|
|
174
|
+
| Name | Signature | Description |
|
|
175
|
+
|:-----|:----------|:------------|
|
|
176
|
+
| `parseSortString` | `(sortString: string) => [string, SortOrder][]` | Parses a sort string into Mongoose sort array format. Example: `"name -createdAt"` → `[["name", 1], ["createdAt", -1]]` |
|
|
177
|
+
| `parseAggregateSortString` | `(sortString: string) => {[key: string]: SortOrder}` | Parses a sort string into aggregation pipeline sort object. Example: `"name -createdAt"` → `{ name: 1, createdAt: -1 }` |
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import { parseSortString, parseAggregateSortString } from '@r5v/mongoose-paginate'
|
|
181
|
+
|
|
182
|
+
const sortArray = parseSortString('name 1, createdAt -1')
|
|
183
|
+
// Result: [["name", 1], ["createdAt", -1]]
|
|
184
|
+
|
|
185
|
+
const sortObj = parseAggregateSortString('name 1, createdAt -1')
|
|
186
|
+
// Result: { name: 1, createdAt: -1 }
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Dot Notation Utilities
|
|
190
|
+
|
|
191
|
+
| Name | Signature | Description |
|
|
192
|
+
|:-----|:----------|:------------|
|
|
193
|
+
| `getPropertyFromDotNotation` | `(obj: any, path: string) => any` | Gets a nested property value using dot notation path |
|
|
194
|
+
| `dotNotationToObject` | `(dotString: string, value?: unknown) => object` | Converts a dot notation string to a nested object |
|
|
195
|
+
| `createObjectFromDotNotation` | `(dotNotationMap: {[key: string]: any}) => object` | Creates a nested object from multiple dot notation key-value pairs |
|
|
196
|
+
| `setPropertyFromDotNotation` | `(obj: Record<string, unknown>, path: string, value: unknown) => object` | Sets a nested property value (mutates original object) |
|
|
197
|
+
| `setPropertiesFromDotNotation` | `(obj: Record<string, unknown>, dotNotationMap: Record<string, unknown>) => object` | Sets multiple nested properties (mutates original object) |
|
|
198
|
+
| `setPropertyFromDotNotationImmutable` | `(obj: Record<string, unknown>, path: string, value: unknown) => object` | Sets a nested property value (returns new object) |
|
|
199
|
+
| `setPropertiesFromDotNotationImmutable` | `(obj: Record<string, unknown>, dotNotationMap: Record<string, unknown>) => object` | Sets multiple nested properties (returns new object) |
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import {
|
|
203
|
+
getPropertyFromDotNotation,
|
|
204
|
+
dotNotationToObject,
|
|
205
|
+
setPropertyFromDotNotation
|
|
206
|
+
} from '@r5v/mongoose-paginate'
|
|
207
|
+
|
|
208
|
+
// Get nested value
|
|
209
|
+
const user = { profile: { name: 'John', address: { city: 'NYC' } } }
|
|
210
|
+
getPropertyFromDotNotation(user, 'profile.address.city') // 'NYC'
|
|
211
|
+
|
|
212
|
+
// Create nested object from dot notation
|
|
213
|
+
dotNotationToObject('user.profile.name', 'John')
|
|
214
|
+
// Result: { user: { profile: { name: 'John' } } }
|
|
215
|
+
|
|
216
|
+
// Set nested property
|
|
217
|
+
const obj = { user: { name: 'John' } }
|
|
218
|
+
setPropertyFromDotNotation(obj, 'user.email', 'john@example.com')
|
|
219
|
+
// Result: { user: { name: 'John', email: 'john@example.com' } }
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Validation Utilities
|
|
223
|
+
|
|
224
|
+
| Name | Signature | Description |
|
|
225
|
+
|:-----|:----------|:------------|
|
|
226
|
+
| `isJsonString` | `(str: string) => boolean` | Checks if a string is valid JSON |
|
|
227
|
+
| `isValidDateString` | `(value: string) => boolean` | Checks if a string is a valid ISO 8601 date format |
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
import { isJsonString, isValidDateString } from '@r5v/mongoose-paginate'
|
|
231
|
+
|
|
232
|
+
isJsonString('{"name": "John"}') // true
|
|
233
|
+
isJsonString('invalid') // false
|
|
234
|
+
|
|
235
|
+
isValidDateString('2024-01-15') // true
|
|
236
|
+
isValidDateString('2024-01-15T10:30:00.000Z') // true
|
|
237
|
+
isValidDateString('invalid-date') // false
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Schema Traversal Utilities
|
|
241
|
+
|
|
242
|
+
| Name | Signature | Description |
|
|
243
|
+
|:-----|:----------|:------------|
|
|
244
|
+
| `buildFullPath` | `(parentPath: string, childPath: string) => string` | Builds a dot-notation path from parent and child segments |
|
|
245
|
+
| `traverseSchemaObject` | `(obj: any, callback: (obj, path) => void, currentPath?: string) => void` | Recursively traverses a Mongoose schema object, calling callback for each node |
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { buildFullPath, traverseSchemaObject } from '@r5v/mongoose-paginate'
|
|
249
|
+
|
|
250
|
+
// Build dot-notation paths
|
|
251
|
+
buildFullPath('user', 'profile') // 'user.profile'
|
|
252
|
+
buildFullPath('', 'name') // 'name'
|
|
253
|
+
buildFullPath('parent', '') // 'parent'
|
|
254
|
+
|
|
255
|
+
// Traverse schema objects
|
|
256
|
+
traverseSchemaObject(schemaType, (obj, currentPath) => {
|
|
257
|
+
if (obj.options?.ref) {
|
|
258
|
+
console.log(`Found ref at ${currentPath}: ${obj.options.ref}`)
|
|
259
|
+
}
|
|
260
|
+
})
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Types
|
|
264
|
+
|
|
265
|
+
All TypeScript types are exported and can be imported:
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
import type {
|
|
269
|
+
PagingQueryOptions,
|
|
270
|
+
AggregateQueryOptions,
|
|
271
|
+
QueryParameters,
|
|
272
|
+
PagingQueryParsedRequestParams,
|
|
273
|
+
AggregateQueryParsedRequestParams
|
|
274
|
+
} from '@r5v/mongoose-paginate'
|
|
275
|
+
|
|
276
|
+
// Or from the types subpath
|
|
277
|
+
import type * from '@r5v/mongoose-paginate/types'
|
|
278
|
+
```
|
|
106
279
|
|
|
107
280
|
## Build
|
|
108
281
|
|
|
@@ -123,18 +296,38 @@ $ yarn run start
|
|
|
123
296
|
|
|
124
297
|
#### Aggregations Order of operations
|
|
125
298
|
|
|
126
|
-
1.
|
|
127
|
-
2.
|
|
128
|
-
3.
|
|
129
|
-
4.
|
|
130
|
-
5.
|
|
131
|
-
6.
|
|
132
|
-
7.
|
|
133
|
-
8.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
1.
|
|
299
|
+
1. First pipeline stage (if `$search`, `$searchMeta`, or `$geoNear`)
|
|
300
|
+
2. `$match` combining: `staticFilter` + `$filter` (if `enableFilter`) + first pipeline `$match` stage
|
|
301
|
+
3. `$preSort` (if `enablePreSort` is true)
|
|
302
|
+
4. Remaining pipeline stages
|
|
303
|
+
5. `$select` / `$project`
|
|
304
|
+
6. `$count` (adds `_count` fields for array sizes)
|
|
305
|
+
7. `$postFilter` combining: `staticPostFilter` + `$postFilter` (if `enablePostFilter`)
|
|
306
|
+
8. `$sort` (final sorting, can sort on computed fields)
|
|
307
|
+
9. Apply pagination (`$skip`, `$limit`) and options
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
### 1.0.15
|
|
311
|
+
- **Re-enabled `removeProtected` option** for `AggregationPagingQuery` (EXPERIMENTAL) - Automatically excludes fields marked with `select: false` in your schema (e.g., passwords, secret keys) from aggregation results. Use with caution and test thoroughly in your environment.
|
|
312
|
+
- Uses refactored `findProtectedPaths` utility which safely traverses schemas without JSON serialization
|
|
313
|
+
|
|
314
|
+
### 1.0.14
|
|
315
|
+
- Added proper TypeScript package exports with subpaths (`/utils`, `/types`)
|
|
316
|
+
- Exported all utility functions (dot notation, sort parsing, validation)
|
|
317
|
+
- Exported all TypeScript types
|
|
318
|
+
- **Fixed Express Request type compatibility** - Added `RequestLike` interface to allow custom request types without index signature issues
|
|
319
|
+
- Fixed JSON.parse crash on malformed `$filter`/`$postFilter` parameters
|
|
320
|
+
- Fixed regex global flag bug in populate parsing
|
|
321
|
+
- Added input validation to constructors
|
|
322
|
+
- Improved type safety (reduced `any` usage)
|
|
323
|
+
- Removed deprecated `$includes` parameter
|
|
324
|
+
- Replaced `JSON.parse(JSON.stringify())` with `structuredClone()`
|
|
325
|
+
- Fixed inconsistent equality operator (`==` to `===`)
|
|
326
|
+
- **Fixed empty pipeline crash** - `AggregationPagingQuery.initQuery` no longer crashes when pipeline is empty
|
|
327
|
+
- **Refactored `buildPopulate`** - Extracted inner functions to module level for better testability
|
|
328
|
+
- **Refactored `findProtectedPaths`** - Removed JSON.stringify that could fail on circular references
|
|
329
|
+
- **Refactored `getPathsWithRef`** - Simplified input handling, removed brittle string parsing
|
|
330
|
+
- **Added `schemaTraversal` utility** - Shared `buildFullPath` and `traverseSchemaObject` functions for schema traversal
|
|
138
331
|
|
|
139
332
|
### 1.0.13
|
|
140
333
|
- Fix issue where disablePaging was not working on aggregation query
|
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import { Model, Aggregate } from "mongoose";
|
|
2
|
-
import type {
|
|
3
|
-
import type { AggregateQueryOptions, QueryParameters, AggregateQueryParsedRequestParams } from './types';
|
|
2
|
+
import type { AggregateQueryOptions, AggregateQueryParsedRequestParams, RequestLike } from './types';
|
|
4
3
|
export declare class AggregationPagingQuery {
|
|
5
4
|
params: AggregateQueryParsedRequestParams;
|
|
6
5
|
options: AggregateQueryOptions;
|
|
7
|
-
query: Aggregate<
|
|
6
|
+
query: Aggregate<unknown[]> | undefined;
|
|
8
7
|
protectedPaths: string[];
|
|
9
8
|
model: Model<any>;
|
|
10
|
-
constructor(req:
|
|
9
|
+
constructor(req: RequestLike, model: Model<any>, options: AggregateQueryOptions);
|
|
11
10
|
findProtectedPaths: (model: Model<any>) => string[];
|
|
12
11
|
parseParams: (defaultParams: import("./types").PagingQueryParsedRequestParams | AggregateQueryParsedRequestParams, params: import("qs").ParsedQs, isAggregate?: boolean) => import("./types").PagingQueryParsedRequestParams | AggregateQueryParsedRequestParams;
|
|
13
12
|
isValidDateString: (value: string) => boolean;
|
|
14
13
|
isJsonString: (str: string) => boolean;
|
|
15
14
|
parseSortString: (sortString: string) => [string, import("mongoose").SortOrder][];
|
|
16
|
-
parseAggregateSortString: (sortString:
|
|
15
|
+
parseAggregateSortString: (sortString: string) => {
|
|
17
16
|
[key: string]: import("mongoose").SortOrder;
|
|
18
17
|
};
|
|
19
18
|
createCounts: () => void;
|
|
20
19
|
initQuery: () => Promise<void>;
|
|
21
|
-
typeCastObject: (strOrObj:
|
|
20
|
+
typeCastObject: <T>(strOrObj: T) => T;
|
|
22
21
|
private removeProtectedFields;
|
|
23
|
-
exec: () => Promise<
|
|
22
|
+
exec: () => Promise<unknown>;
|
|
24
23
|
}
|
|
25
24
|
//# sourceMappingURL=aggregationPagingQuery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregationPagingQuery.d.ts","sourceRoot":"","sources":["../src/aggregationPagingQuery.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,EACL,SAAS,EAKZ,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAC,qBAAqB,EAAE,iCAAiC,EAAE,WAAW,EAAC,MAAM,SAAS,CAAA;AAQlG,qBAAa,sBAAsB;IAC/B,MAAM,EAAG,iCAAiC,CAYzC;IACD,OAAO,EAAE,qBAAqB,CAM7B;IACD,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,SAAS,CAAA;IACvC,cAAc,EAAE,MAAM,EAAE,CAAK;IAC7B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;gBACL,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,qBAAqB;IAoB/E,kBAAkB,kCAAqB;IACvC,WAAW,sPAAc;IACzB,iBAAiB,6BAAoB;IACrC,YAAY,2BAAe;IAC3B,eAAe,mEAAkB;IACjC,wBAAwB;;MAA2B;IACnD,YAAY,aAOX;IACD,SAAS,sBAsDR;IACD,cAAc,GAAI,CAAC,EAAE,UAAU,CAAC,KAAG,CAAC,CAmCnC;IAED,OAAO,CAAC,qBAAqB,CAS5B;IACD,IAAI,yBAgCH;CAEJ"}
|
|
@@ -36,7 +36,6 @@ class AggregationPagingQuery {
|
|
|
36
36
|
$sort: {},
|
|
37
37
|
$paging: true,
|
|
38
38
|
$populate: [],
|
|
39
|
-
$includes: [], // to be removed
|
|
40
39
|
$select: "",
|
|
41
40
|
$count: [],
|
|
42
41
|
$postFilter: {},
|
|
@@ -46,6 +45,7 @@ class AggregationPagingQuery {
|
|
|
46
45
|
enableFilter: false,
|
|
47
46
|
enablePostFilter: false,
|
|
48
47
|
enablePreSort: true,
|
|
48
|
+
removeProtected: false,
|
|
49
49
|
pipeline: []
|
|
50
50
|
};
|
|
51
51
|
this.protectedPaths = [];
|
|
@@ -66,7 +66,7 @@ class AggregationPagingQuery {
|
|
|
66
66
|
};
|
|
67
67
|
this.initQuery = () => __awaiter(this, void 0, void 0, function* () {
|
|
68
68
|
const { $filter, $sort, $preSort, $select, $count, $postFilter } = this.params;
|
|
69
|
-
const _a = this.options, { enableFilter, enablePreSort, enablePostFilter, staticFilter, staticPostFilter, pipeline } = _a, options = __rest(_a, ["enableFilter", "enablePreSort", "enablePostFilter", "staticFilter", "staticPostFilter", "pipeline"]);
|
|
69
|
+
const _a = this.options, { enableFilter, enablePreSort, enablePostFilter, staticFilter, staticPostFilter, removeProtected, pipeline } = _a, options = __rest(_a, ["enableFilter", "enablePreSort", "enablePostFilter", "staticFilter", "staticPostFilter", "removeProtected", "pipeline"]);
|
|
70
70
|
this.query = this.model.aggregate();
|
|
71
71
|
const [p1, ...pipes] = pipeline;
|
|
72
72
|
const filterObj = { $and: [Object.assign({}, staticFilter)] };
|
|
@@ -84,7 +84,7 @@ class AggregationPagingQuery {
|
|
|
84
84
|
}
|
|
85
85
|
const typeCastFilter = this.typeCastObject(filterObj);
|
|
86
86
|
const typeCastPostFilter = this.typeCastObject(postFilterObj);
|
|
87
|
-
if (Object.keys(p1).some(k => ["$search", "$searchMeta", "$geoNear"].includes(k))) {
|
|
87
|
+
if (p1 && Object.keys(p1).some(k => ["$search", "$searchMeta", "$geoNear"].includes(k))) {
|
|
88
88
|
this.query.append(p1);
|
|
89
89
|
firstObj = true;
|
|
90
90
|
}
|
|
@@ -94,13 +94,16 @@ class AggregationPagingQuery {
|
|
|
94
94
|
if (this.options.enablePreSort && Object.keys($preSort).length) {
|
|
95
95
|
this.query.sort($preSort);
|
|
96
96
|
}
|
|
97
|
-
if (!firstObj) {
|
|
97
|
+
if (!firstObj && p1) {
|
|
98
98
|
this.query.append(p1);
|
|
99
99
|
}
|
|
100
100
|
pipes.forEach(item => { var _a; return (_a = this.query) === null || _a === void 0 ? void 0 : _a.append(item); });
|
|
101
101
|
if ($select) {
|
|
102
102
|
this.query.project($select);
|
|
103
103
|
}
|
|
104
|
+
if (removeProtected) {
|
|
105
|
+
this.removeProtectedFields();
|
|
106
|
+
}
|
|
104
107
|
if ($count.length) {
|
|
105
108
|
this.createCounts();
|
|
106
109
|
}
|
|
@@ -180,9 +183,21 @@ class AggregationPagingQuery {
|
|
|
180
183
|
const req = yield this.query.exec();
|
|
181
184
|
return req[0];
|
|
182
185
|
});
|
|
186
|
+
if (!req || typeof req.query !== 'object') {
|
|
187
|
+
throw new Error('Invalid request object: must have a query property');
|
|
188
|
+
}
|
|
189
|
+
if (!model || typeof model.aggregate !== 'function') {
|
|
190
|
+
throw new Error('Invalid model: must be a Mongoose model');
|
|
191
|
+
}
|
|
192
|
+
if (!options || !Array.isArray(options.pipeline)) {
|
|
193
|
+
throw new Error('Invalid options: pipeline must be an array');
|
|
194
|
+
}
|
|
183
195
|
this.options = Object.assign(Object.assign({}, this.options), options);
|
|
184
196
|
this.model = model;
|
|
185
197
|
this.params = this.parseParams(this.params, req.query, true);
|
|
198
|
+
if (this.options.removeProtected) {
|
|
199
|
+
this.protectedPaths = this.findProtectedPaths(model);
|
|
200
|
+
}
|
|
186
201
|
this.initQuery();
|
|
187
202
|
}
|
|
188
203
|
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { PagingQuery } from './pagingQuery';
|
|
2
|
+
export { AggregationPagingQuery } from './aggregationPagingQuery';
|
|
3
|
+
export type { RequestLike, QueryParameters, StandardParsedRequestParams, PagingQueryParsedRequestParams, AggregateQueryParsedRequestParams, StandardQueryOptions, PagingQueryOptions, AggregateQueryOptions } from './types';
|
|
4
|
+
export { buildPopulate } from './utils/buildPopulateFromString';
|
|
5
|
+
export { parseSortString, parseAggregateSortString } from './utils/parseSortString';
|
|
6
|
+
export { getPropertyFromDotNotation, dotNotationToObject, createObjectFromDotNotation, setPropertyFromDotNotation, setPropertiesFromDotNotation, setPropertyFromDotNotationImmutable, setPropertiesFromDotNotationImmutable } from './utils/dotNotation';
|
|
7
|
+
export { isJsonString } from './utils/isJsonString';
|
|
8
|
+
export { isValidDateString } from './utils/isValidDateString';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AAGjE,YAAY,EACR,WAAW,EACX,eAAe,EACf,2BAA2B,EAC3B,8BAA8B,EAC9B,iCAAiC,EACjC,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EACxB,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAC/D,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAA;AACnF,OAAO,EACH,0BAA0B,EAC1B,mBAAmB,EACnB,2BAA2B,EAC3B,0BAA0B,EAC1B,4BAA4B,EAC5B,mCAAmC,EACnC,qCAAqC,EACxC,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.buildPopulate = exports.AggregationPagingQuery = exports.PagingQuery = void 0;
|
|
3
|
+
exports.isValidDateString = exports.isJsonString = exports.setPropertiesFromDotNotationImmutable = exports.setPropertyFromDotNotationImmutable = exports.setPropertiesFromDotNotation = exports.setPropertyFromDotNotation = exports.createObjectFromDotNotation = exports.dotNotationToObject = exports.getPropertyFromDotNotation = exports.parseAggregateSortString = exports.parseSortString = exports.buildPopulate = exports.AggregationPagingQuery = exports.PagingQuery = void 0;
|
|
4
|
+
// Main classes
|
|
4
5
|
var pagingQuery_1 = require("./pagingQuery");
|
|
5
6
|
Object.defineProperty(exports, "PagingQuery", { enumerable: true, get: function () { return pagingQuery_1.PagingQuery; } });
|
|
6
7
|
var aggregationPagingQuery_1 = require("./aggregationPagingQuery");
|
|
7
8
|
Object.defineProperty(exports, "AggregationPagingQuery", { enumerable: true, get: function () { return aggregationPagingQuery_1.AggregationPagingQuery; } });
|
|
9
|
+
// Utilities - re-export for convenience
|
|
8
10
|
var buildPopulateFromString_1 = require("./utils/buildPopulateFromString");
|
|
9
11
|
Object.defineProperty(exports, "buildPopulate", { enumerable: true, get: function () { return buildPopulateFromString_1.buildPopulate; } });
|
|
12
|
+
var parseSortString_1 = require("./utils/parseSortString");
|
|
13
|
+
Object.defineProperty(exports, "parseSortString", { enumerable: true, get: function () { return parseSortString_1.parseSortString; } });
|
|
14
|
+
Object.defineProperty(exports, "parseAggregateSortString", { enumerable: true, get: function () { return parseSortString_1.parseAggregateSortString; } });
|
|
15
|
+
var dotNotation_1 = require("./utils/dotNotation");
|
|
16
|
+
Object.defineProperty(exports, "getPropertyFromDotNotation", { enumerable: true, get: function () { return dotNotation_1.getPropertyFromDotNotation; } });
|
|
17
|
+
Object.defineProperty(exports, "dotNotationToObject", { enumerable: true, get: function () { return dotNotation_1.dotNotationToObject; } });
|
|
18
|
+
Object.defineProperty(exports, "createObjectFromDotNotation", { enumerable: true, get: function () { return dotNotation_1.createObjectFromDotNotation; } });
|
|
19
|
+
Object.defineProperty(exports, "setPropertyFromDotNotation", { enumerable: true, get: function () { return dotNotation_1.setPropertyFromDotNotation; } });
|
|
20
|
+
Object.defineProperty(exports, "setPropertiesFromDotNotation", { enumerable: true, get: function () { return dotNotation_1.setPropertiesFromDotNotation; } });
|
|
21
|
+
Object.defineProperty(exports, "setPropertyFromDotNotationImmutable", { enumerable: true, get: function () { return dotNotation_1.setPropertyFromDotNotationImmutable; } });
|
|
22
|
+
Object.defineProperty(exports, "setPropertiesFromDotNotationImmutable", { enumerable: true, get: function () { return dotNotation_1.setPropertiesFromDotNotationImmutable; } });
|
|
23
|
+
var isJsonString_1 = require("./utils/isJsonString");
|
|
24
|
+
Object.defineProperty(exports, "isJsonString", { enumerable: true, get: function () { return isJsonString_1.isJsonString; } });
|
|
25
|
+
var isValidDateString_1 = require("./utils/isValidDateString");
|
|
26
|
+
Object.defineProperty(exports, "isValidDateString", { enumerable: true, get: function () { return isValidDateString_1.isValidDateString; } });
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import type { PagingQueryParsedRequestParams, PagingQueryOptions } from './types';
|
|
2
|
-
import { Model, QueryWithHelpers } from "mongoose";
|
|
3
|
-
import type { Request } from 'express';
|
|
1
|
+
import type { PagingQueryParsedRequestParams, PagingQueryOptions, RequestLike } from './types';
|
|
2
|
+
import { Model, QueryWithHelpers, SortOrder } from "mongoose";
|
|
4
3
|
export declare class PagingQuery {
|
|
5
4
|
params: PagingQueryParsedRequestParams;
|
|
6
5
|
options: PagingQueryOptions;
|
|
7
|
-
query: QueryWithHelpers<
|
|
6
|
+
query: QueryWithHelpers<unknown, unknown> | null;
|
|
8
7
|
model: Model<any>;
|
|
9
|
-
constructor(req:
|
|
8
|
+
constructor(req: RequestLike, model: Model<any>, options?: Partial<PagingQueryOptions>);
|
|
10
9
|
private isJsonString;
|
|
11
10
|
private initQuery;
|
|
12
|
-
parseSortString: (sortString: string) => [string,
|
|
11
|
+
parseSortString: (sortString: string) => [string, SortOrder][];
|
|
13
12
|
parseParams: (defaultParams: PagingQueryParsedRequestParams | import("./types").AggregateQueryParsedRequestParams, params: import("qs").ParsedQs, isAggregate?: boolean) => PagingQueryParsedRequestParams | import("./types").AggregateQueryParsedRequestParams;
|
|
14
|
-
exec: () => Promise<
|
|
13
|
+
exec: () => Promise<unknown>;
|
|
15
14
|
}
|
|
16
15
|
//# sourceMappingURL=pagingQuery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pagingQuery.d.ts","sourceRoot":"","sources":["../src/pagingQuery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,8BAA8B,EAAE,kBAAkB,EAAE,WAAW,EAAC,MAAM,SAAS,CAAA;AAC5F,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAC,MAAM,UAAU,CAAC;AAS7D,qBAAa,WAAW;IACpB,MAAM,EAAE,8BAA8B,CASrC;IACD,OAAO,EAAE,kBAAkB,CAAK;IAChC,KAAK,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,IAAI,CAAO;IACvD,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;gBAEL,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,GAAE,OAAO,CAAC,kBAAkB,CAAM;IAY1F,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,SAAS,CAqChB;IACD,eAAe,gDAAkB;IACjC,WAAW,sPAAc;IACzB,IAAI,yBAuBH;CACJ"}
|
package/dist/pagingQuery.js
CHANGED
|
@@ -34,7 +34,6 @@ class PagingQuery {
|
|
|
34
34
|
$sort: [],
|
|
35
35
|
$paging: true,
|
|
36
36
|
$populate: [],
|
|
37
|
-
$includes: [], // to be removed
|
|
38
37
|
$select: "",
|
|
39
38
|
$lean: false
|
|
40
39
|
};
|
|
@@ -45,7 +44,7 @@ class PagingQuery {
|
|
|
45
44
|
const { $filter, $sort, $select, $skip, $limit, $populate, $lean } = this.params;
|
|
46
45
|
const _a = this.options, { single, staticFilter, disablePaging, disableFilter } = _a, options = __rest(_a, ["single", "staticFilter", "disablePaging", "disableFilter"]);
|
|
47
46
|
const filter = disableFilter ? Object.assign({}, staticFilter) : Object.assign(Object.assign({}, $filter), staticFilter);
|
|
48
|
-
this.query = single ? this.model.findOne(filter) : this.model.find(filter);
|
|
47
|
+
this.query = (single ? this.model.findOne(filter) : this.model.find(filter));
|
|
49
48
|
this.query.setOptions(options);
|
|
50
49
|
if (disablePaging || single) {
|
|
51
50
|
this.params.$paging = false;
|
|
@@ -89,11 +88,17 @@ class PagingQuery {
|
|
|
89
88
|
skip: $skip,
|
|
90
89
|
items: []
|
|
91
90
|
};
|
|
92
|
-
resObj.items = yield this.query.exec();
|
|
91
|
+
resObj.items = (yield this.query.exec());
|
|
93
92
|
resObj.totalRows = yield this.model.countDocuments(Object.assign(Object.assign({}, $filter), staticFilter));
|
|
94
93
|
resObj.rows = resObj.items.length;
|
|
95
94
|
return resObj;
|
|
96
95
|
});
|
|
96
|
+
if (!req || typeof req.query !== 'object') {
|
|
97
|
+
throw new Error('Invalid request object: must have a query property');
|
|
98
|
+
}
|
|
99
|
+
if (!model || typeof model.find !== 'function') {
|
|
100
|
+
throw new Error('Invalid model: must be a Mongoose model');
|
|
101
|
+
}
|
|
97
102
|
this.options = options;
|
|
98
103
|
this.model = model;
|
|
99
104
|
this.params = this.parseParams(this.params, req.query);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregationPagingQuery.spec.d.ts","sourceRoot":"","sources":["../../src/tests/aggregationPagingQuery.spec.ts"],"names":[],"mappings":""}
|