You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This module is a fork of (mongo-cursor-pagination)[https://github.com/mixmaxhq/mongo-cursor-pagination] by MixMax. The fork was needed to keep supporting
4
+
Node.js v6 as the upstream version started using async..await.
This module aids in implementing "cursor-based" pagination using Mongo range queries or relevancy-based search results. __This module is currently used in production for the [Mixmax API](https://developer.mixmax.com) to return millions of results a day__.
16
+
This module aids in implementing "cursor-based" pagination using Mongo range queries or relevancy-based search results. **This module is currently used in
17
+
production for the [Mixmax API](https://developer.mixmax.com) to return millions of results a day**.
4
18
5
19
## Background
6
20
7
21
See this [blog post](https://mixmax.com/blog/api-paging-built-the-right-way) for background on why this library was built.
8
22
9
23
API Pagination is typically implemented one of two different ways:
10
24
11
-
1. Offset-based paging. This is traditional paging where `skip` and `limit` parameters are passed on the url (or some variation such as `page_num` and `count`). The API would return the results and some indication of whether there is a next page, such as `has_more` on the response. An issue with this approach is that it assumes a static data set; if collection changes while querying, then results in pages will shift and the response will be wrong.
25
+
1. Offset-based paging. This is traditional paging where `skip` and `limit` parameters are passed on the url (or some variation such as `page_num` and `count`).
26
+
The API would return the results and some indication of whether there is a next page, such as `has_more` on the response. An issue with this approach is that
27
+
it assumes a static data set; if collection changes while querying, then results in pages will shift and the response will be wrong.
12
28
13
-
2. Cursor-based paging. An improved way of paging where an API passes back a "cursor" (an opaque string) to tell the caller where to query the next or previous pages. The cursor is usually passed using query parameters `next` and `previous`. It's implementation is typically more performant that skip/limit because it can jump to any page without traversing all the records. It also handles records being added or removed because it doesn't use fixed offsets.
29
+
2. Cursor-based paging. An improved way of paging where an API passes back a "cursor" (an opaque string) to tell the caller where to query the next or previous
30
+
pages. The cursor is usually passed using query parameters `next` and `previous`. It's implementation is typically more performant that skip/limit because it
31
+
can jump to any page without traversing all the records. It also handles records being added or removed because it doesn't use fixed offsets.
14
32
15
-
This module helps in implementing #2 - cursor based paging - by providing a method that make it easy to query within a Mongo collection. It also helps by returning a url-safe string that you can return with your HTTP response (see example below).
33
+
This module helps in implementing #2 - cursor based paging - by providing a method that make it easy to query within a Mongo collection. It also helps by
34
+
returning a url-safe string that you can return with your HTTP response (see example below).
16
35
17
36
Here are some examples of cursor-based APIs:
18
37
@@ -64,32 +83,46 @@ var MongoClient = require('mongodb').MongoClient;
64
83
var MongoPaging =require('mongo-cursor-pagination');
Search uses Mongo's [text search](https://docs.mongodb.com/v3.2/text-search/) feature and will return paged results ordered by search relevancy. As such, and unlike `find()`, it does not take a `paginatedField` parameter.
147
+
Search uses Mongo's [text search](https://docs.mongodb.com/v3.2/text-search/) feature and will return paged results ordered by search relevancy. As such, and
148
+
unlike `find()`, it does not take a `paginatedField` parameter.
115
149
116
150
```
117
151
Performs a search query on a Mongo collection and pages the results. This is different from
@@ -140,38 +174,55 @@ var MongoClient = require('mongodb').MongoClient;
140
174
var MongoPaging = require('mongo-cursor-pagination');
A popular use of this module is with Express to implement a basic API. As a convenience for this use-case, this library exposes a `findWithReq`functionthat takes the request object from your Express middleware and returns results:
242
+
A popular use of this module is with Express to implement a basic API. As a convenience for this use-case, this library exposes a `findWithReq`functionthat
243
+
takes the request object from your Express middleware and returns results:
192
244
193
245
So this code using `find()`:
194
246
195
247
```js
196
248
router.get('/myobjects', (req, res, next) => {
197
-
MongoPaging.find(db.collection('myobjects'), {
198
-
query: {
199
-
userId: req.user._id
200
-
},
201
-
paginatedField: 'created',
202
-
fields: { // Also need to read req.query.fields to use to filter these fields
203
-
_id: 1,
204
-
created: 1
205
-
},
206
-
limit: req.query.limit, // Also need to cap this to 25
207
-
next: req.query.next,
208
-
previous: req.query.previous,
209
-
}, function(err, result) {
210
-
if (err) {
211
-
next(err);
212
-
} else {
213
-
res.json(result);
214
-
}
215
-
});
249
+
MongoPaging.find(
250
+
db.collection('myobjects'),
251
+
{
252
+
query: {
253
+
userId: req.user._id
254
+
},
255
+
paginatedField: 'created',
256
+
fields: {
257
+
// Also need to read req.query.fields to use to filter these fields
258
+
_id: 1,
259
+
created: 1
260
+
},
261
+
limit: req.query.limit, // Also need to cap this to 25
`findWithReq()` also handles basic security such as making sure the `limit` and `fields` requested on the URL are within the allowed values you specify in`params`.
305
+
`findWithReq()` also handles basic security such as making sure the `limit` and `fields` requested on the URL are within the allowed values you specify in
306
+
`params`.
244
307
245
308
### Number of results
246
309
247
-
If the `limit` parameter isn't passed, then this library will default to returning 50 results. This can be overridden by setting `mongoPaging.config.DEFAULT_LIMIT = <new default limit>;`. Regardless of the `limit` passed in, a maximum of 300 documents will be returned. This can be overridden by setting `mongoPaging.config.MAX_LIMIT = <new max limit>;`.
310
+
If the `limit` parameter isn't passed, then this library will default to returning 50 results. This can be overridden by setting
311
+
`mongoPaging.config.DEFAULT_LIMIT = <new default limit>;`. Regardless of the `limit` passed in, a maximum of 300 documents will be returned. This can be
312
+
overridden by setting `mongoPaging.config.MAX_LIMIT = <new max limit>;`.
248
313
249
314
### Indexes for sorting
250
315
251
-
`mongo-cursor-pagination` uses `_id` as a secondary sorting field when providing a `paginatedField` property. It is recommended that you have an index for optimal performance. Example:
316
+
`mongo-cursor-pagination` uses `_id` as a secondary sorting field when providing a `paginatedField` property. It is recommended that you have an index for
317
+
optimal performance. Example:
252
318
253
319
```
254
320
MongoPaging.find(db.people, {
@@ -276,23 +342,29 @@ To run tests, you first must [start a Mongo server on port 27017](https://mongod
276
342
277
343
## Changelog
278
344
279
-
* 5.0.0 Now `50` results are returned by default, and up to `300` results can be returned if the `limit` parameter is used. These can be overridden by setting `mongoPaging.config.DEFAULT_LIMIT` and `mongoPaging.config.MAX_LIMIT` respectively.
345
+
* 5.0.0 Now `50` results are returned by default, and up to `300` results can be returned if the `limit` parameter is used. These can be overridden by setting
346
+
`mongoPaging.config.DEFAULT_LIMIT` and `mongoPaging.config.MAX_LIMIT` respectively.
280
347
281
348
* 4.1.1 Fixed bug that would overwrite `$or` in queries passed in.
282
349
283
350
* 4.1.0 Adds `sortAscending` option to sort by the `paginatedField` ascending. Defaults to false (existing behavior).
284
351
285
-
* 4.0.0 Breaking API change: `next` and `previous` attributes are now always returned with every response (in case the client wants to poll for new changes). New attributes `hasPrevious` and `hasNext` should now be used know if there are more results in the previous or next page. Before the change, `next` and `previously` could not be replied upon to know if there were more pages.
352
+
* 4.0.0 Breaking API change: `next` and `previous` attributes are now always returned with every response (in case the client wants to poll for new changes).
353
+
New attributes `hasPrevious` and `hasNext` should now be used know if there are more results in the previous or next page. Before the change, `next` and
354
+
`previously` could not be replied upon to know if there were more pages.
286
355
287
356
* 3.1.1 Don't use `let`for backwards compatibility.
288
357
289
-
* 3.1.0 `findInReq()` now accepts dot notation forfields. So you can pass `?fields=users.userId` to only turn the `userId` property for `users`in the response.
358
+
* 3.1.0 `findInReq()` now accepts dot notation forfields. So you can pass `?fields=users.userId` to only turn the `userId` property for `users`in the
359
+
response.
290
360
291
361
* 3.0.1 Fixed bug where the _id field was always returned when a paginatedField was used.
292
362
293
363
* 3.0.0 Breaking API change: `find()` no longer accepts a string for`limit`. Added `findWithReq`.
294
364
295
-
* 2.0.0 Changed API to so you now set global config on the config object instead of the root export itself (e.g. `require('mongo-cursor-pagination').config.MAX_LIMIT = 100`). The default `MAX_LIMIT` is now a more reasonable 25 instead of 100. Added `search()`. Fixed edge case where pages will be incorrect if paginatedField has duplicate values.
365
+
* 2.0.0 Changed API to so you now set global config on the config object instead of the root export itself (e.g.
366
+
`require('mongo-cursor-pagination').config.MAX_LIMIT = 100`). The default `MAX_LIMIT` is now a more reasonable 25 instead of 100. Added `search()`. Fixed edge
367
+
case where pages will be incorrect if paginatedField has duplicate values.
0 commit comments