Skip to content
This repository was archived by the owner on Jun 22, 2021. It is now read-only.

Commit 1a8d753

Browse files
authored
fix(createPaginationFilter): Fixes cursors for sorts with multiple keys. (#15)
1 parent 1939701 commit 1a8d753

File tree

2 files changed

+57
-26
lines changed

2 files changed

+57
-26
lines changed

src/utils/createPaginationFilter/index.test.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import createCursorFromEntity from '../createCursorFromEntity';
1111
import createPaginationFilter from './index';
1212

1313
describe('createCursorFromEntity', () => {
14-
const sort: Sort<TestEntity> = { id: asc, numberProp: desc };
14+
const sort: Sort<TestEntity> = { numberProp: desc, id: asc };
1515

1616
it('should return empty filter when the cursor is start', () => {
1717
const pagination: Pagination = { cursor: start, direction: forward, limit: 1 };
@@ -25,8 +25,12 @@ describe('createCursorFromEntity', () => {
2525
const pagination: Pagination = { cursor, direction: forward, limit: 1 };
2626
const actualResult = createPaginationFilter<TestEntity>(pagination, sort);
2727
const expectedResult: Filter<TestEntity> = {
28-
id: { $gt: testEntity.id },
29-
numberProp: { $lte: testEntity.numberProp },
28+
$or: [{
29+
numberProp: { $lte: testEntity.numberProp },
30+
}, {
31+
id: { $gt: testEntity.id },
32+
numberProp: testEntity.numberProp,
33+
}],
3034
};
3135
assert.deepEqual(actualResult, expectedResult);
3236
});
@@ -36,8 +40,12 @@ describe('createCursorFromEntity', () => {
3640
const pagination: Pagination = { cursor, direction: backward, limit: 1 };
3741
const actualResult = createPaginationFilter<TestEntity>(pagination, sort);
3842
const expectedResult: Filter<TestEntity> = {
39-
id: { $lt: testEntity.id },
40-
numberProp: { $gte: testEntity.numberProp },
43+
$or: [{
44+
numberProp: { $gte: testEntity.numberProp },
45+
}, {
46+
id: { $lt: testEntity.id },
47+
numberProp: testEntity.numberProp,
48+
}],
4149
};
4250
assert.deepEqual(actualResult, expectedResult);
4351
});
+44-21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as atob from 'atob';
2-
import { get, mapValues } from 'lodash';
2+
import { get } from 'lodash';
33
import { start } from '../../types/Cursor';
44
import Entity from '../../types/Entity';
55
// tslint:disable-next-line:no-unused
@@ -13,30 +13,53 @@ const xor = (conditionA: boolean, conditionB: boolean) => {
1313
return (conditionA && !conditionB) || (!conditionA && conditionB);
1414
};
1515

16+
const getCursorKeyFilter = <E extends Entity>(
17+
sortKey: string,
18+
sort: Sort<E>,
19+
pagination: Pagination,
20+
cursorObj: any,
21+
) => {
22+
const sortOrder = get(sort, sortKey);
23+
const ascendingPagination = !xor(
24+
sortOrder === asc,
25+
pagination.direction === forward,
26+
);
27+
const cursorValue = get(cursorObj, sortKey);
28+
if (ascendingPagination) {
29+
if (sortKey === 'id') {
30+
return { $gt: cursorValue };
31+
} else {
32+
return { $gte: cursorValue };
33+
}
34+
} else {
35+
if (sortKey === 'id') {
36+
return { $lt: cursorValue };
37+
} else {
38+
return { $lte: cursorValue };
39+
}
40+
}
41+
};
42+
1643
export default <E extends Entity>(pagination: Pagination, sort: Sort<E>): Filter<E> => {
17-
if (pagination.cursor === start) {
44+
const cursor = pagination.cursor;
45+
if (cursor === start) {
1846
return {};
1947
}
20-
const cursor = pagination.cursor;
48+
2149
const cursorObj = JSON.parse(atob(cursor));
22-
const filter = mapValues(cursorObj, (cursorValue, sortKey) => {
23-
const ascendingPagination = !xor(
24-
get(sort, sortKey) === asc,
25-
pagination.direction === forward,
26-
);
27-
if (ascendingPagination) {
28-
if (sortKey === 'id') {
29-
return { $gt: cursorValue };
30-
} else {
31-
return { $gte: cursorValue };
32-
}
33-
} else {
34-
if (sortKey === 'id') {
35-
return { $lt: cursorValue };
36-
} else {
37-
return { $lte: cursorValue };
38-
}
39-
}
50+
const sortKeys = Object.keys(sort);
51+
const sortKeyFilters = sortKeys.map((sortKey, keyIndex) => {
52+
const sortKeysToMatch = sortKeys.slice(0, keyIndex);
53+
const matchFilter = sortKeysToMatch.reduce((result: any, sortKeyToMatch) => {
54+
result[sortKeyToMatch] = cursorObj[sortKeyToMatch];
55+
return result;
56+
}, {});
57+
58+
const cursorKeyFilter = getCursorKeyFilter(sortKey, sort, pagination, cursorObj);
59+
matchFilter[sortKey] = cursorKeyFilter;
60+
return matchFilter;
4061
});
62+
63+
const filter = { $or: sortKeyFilters };
4164
return filter as any as Filter<E>;
4265
};

0 commit comments

Comments
 (0)