@@ -21,7 +21,9 @@ function encodePaginationTokens(params, response) {
2121
2222 if ( response . previous ) {
2323 let previousPaginatedField = objectPath . get ( response . previous , params . paginatedField ) ;
24- if ( params . sortCaseInsensitive ) previousPaginatedField = previousPaginatedField . toLowerCase ( ) ;
24+ if ( params . sortCaseInsensitive ) {
25+ previousPaginatedField = previousPaginatedField ?. toLowerCase ?. ( ) ?? '' ;
26+ }
2527 if ( shouldSecondarySortOnId ) {
2628 response . previous = bsonUrlEncoding . encode ( [ previousPaginatedField , response . previous . _id ] ) ;
2729 } else {
@@ -30,7 +32,9 @@ function encodePaginationTokens(params, response) {
3032 }
3133 if ( response . next ) {
3234 let nextPaginatedField = objectPath . get ( response . next , params . paginatedField ) ;
33- if ( params . sortCaseInsensitive ) nextPaginatedField = nextPaginatedField . toLowerCase ( ) ;
35+ if ( params . sortCaseInsensitive ) {
36+ nextPaginatedField = nextPaginatedField ?. toLowerCase ?. ( ) ?? '' ;
37+ }
3438 if ( shouldSecondarySortOnId ) {
3539 response . next = bsonUrlEncoding . encode ( [ nextPaginatedField , response . next . _id ] ) ;
3640 } else {
@@ -112,36 +116,90 @@ module.exports = {
112116
113117 const sortAsc =
114118 ( ! params . sortAscending && params . previous ) || ( params . sortAscending && ! params . previous ) ;
115- const comparisonOp = sortAsc ? '$gt' : '$lt' ;
116119
117120 // a `next` cursor will have precedence over a `previous` cursor.
118121 const op = params . next || params . previous ;
119122
120123 if ( params . paginatedField == '_id' ) {
121- return {
122- _id : {
123- [ comparisonOp ] : op ,
124- } ,
125- } ;
124+ if ( sortAsc ) {
125+ return { _id : { $gt : op } } ;
126+ } else {
127+ return { _id : { $lt : op } } ;
128+ }
126129 } else {
127130 const field = params . sortCaseInsensitive ? '__lc' : params . paginatedField ;
128- return {
129- $or : [
130- {
131- [ field ] : {
132- [ comparisonOp ] : op [ 0 ] ,
133- } ,
134- } ,
135- {
136- [ field ] : {
137- $eq : op [ 0 ] ,
138- } ,
139- _id : {
140- [ comparisonOp ] : op [ 1 ] ,
141- } ,
142- } ,
143- ] ,
144- } ;
131+
132+ const notUndefined = { [ field ] : { $exists : true } } ;
133+ const onlyUndefs = { [ field ] : { $exists : false } } ;
134+ const notNullNorUndefined = { [ field ] : { $ne : null } } ;
135+ const nullOrUndefined = { [ field ] : null } ;
136+ const onlyNulls = { $and : [ { [ field ] : { $exists : true } } , { [ field ] : null } ] } ;
137+
138+ const [ paginatedFieldValue , idValue ] = op ;
139+ switch ( paginatedFieldValue ) {
140+ case null :
141+ if ( sortAsc ) {
142+ return {
143+ $or : [
144+ notNullNorUndefined ,
145+ {
146+ ...onlyNulls ,
147+ _id : { $gt : idValue } ,
148+ } ,
149+ ] ,
150+ } ;
151+ } else {
152+ return {
153+ $or : [
154+ onlyUndefs ,
155+ {
156+ ...onlyNulls ,
157+ _id : { $lt : idValue } ,
158+ } ,
159+ ] ,
160+ } ;
161+ }
162+ case undefined :
163+ if ( sortAsc ) {
164+ return {
165+ $or : [
166+ notUndefined ,
167+ {
168+ ...onlyUndefs ,
169+ _id : { $gt : idValue } ,
170+ } ,
171+ ] ,
172+ } ;
173+ } else {
174+ return {
175+ ...onlyUndefs ,
176+ _id : { $lt : idValue } ,
177+ } ;
178+ }
179+ default :
180+ if ( sortAsc ) {
181+ return {
182+ $or : [
183+ { [ field ] : { $gt : paginatedFieldValue } } ,
184+ {
185+ [ field ] : { $eq : paginatedFieldValue } ,
186+ _id : { $gt : idValue } ,
187+ } ,
188+ ] ,
189+ } ;
190+ } else {
191+ return {
192+ $or : [
193+ { [ field ] : { $lt : paginatedFieldValue } } ,
194+ nullOrUndefined ,
195+ {
196+ [ field ] : { $eq : paginatedFieldValue } ,
197+ _id : { $lt : idValue } ,
198+ } ,
199+ ] ,
200+ } ;
201+ }
202+ }
145203 }
146204 } ,
147205} ;
0 commit comments