sort.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import { MongoInvalidArgumentError } from './error';
  2. /** @public */
  3. export type SortDirection =
  4. | 1
  5. | -1
  6. | 'asc'
  7. | 'desc'
  8. | 'ascending'
  9. | 'descending'
  10. | { $meta: string };
  11. /** @public */
  12. export type Sort =
  13. | string
  14. | Exclude<SortDirection, { $meta: string }>
  15. | string[]
  16. | { [key: string]: SortDirection }
  17. | Map<string, SortDirection>
  18. | [string, SortDirection][]
  19. | [string, SortDirection];
  20. /** Below stricter types were created for sort that correspond with type that the cmd takes */
  21. /** @internal */
  22. export type SortDirectionForCmd = 1 | -1 | { $meta: string };
  23. /** @internal */
  24. export type SortForCmd = Map<string, SortDirectionForCmd>;
  25. /** @internal */
  26. type SortPairForCmd = [string, SortDirectionForCmd];
  27. /** @internal */
  28. function prepareDirection(direction: any = 1): SortDirectionForCmd {
  29. const value = `${direction}`.toLowerCase();
  30. if (isMeta(direction)) return direction;
  31. switch (value) {
  32. case 'ascending':
  33. case 'asc':
  34. case '1':
  35. return 1;
  36. case 'descending':
  37. case 'desc':
  38. case '-1':
  39. return -1;
  40. default:
  41. throw new MongoInvalidArgumentError(`Invalid sort direction: ${JSON.stringify(direction)}`);
  42. }
  43. }
  44. /** @internal */
  45. function isMeta(t: SortDirection): t is { $meta: string } {
  46. return typeof t === 'object' && t != null && '$meta' in t && typeof t.$meta === 'string';
  47. }
  48. /** @internal */
  49. function isPair(t: Sort): t is [string, SortDirection] {
  50. if (Array.isArray(t) && t.length === 2) {
  51. try {
  52. prepareDirection(t[1]);
  53. return true;
  54. } catch (e) {
  55. return false;
  56. }
  57. }
  58. return false;
  59. }
  60. function isDeep(t: Sort): t is [string, SortDirection][] {
  61. return Array.isArray(t) && Array.isArray(t[0]);
  62. }
  63. function isMap(t: Sort): t is Map<string, SortDirection> {
  64. return t instanceof Map && t.size > 0;
  65. }
  66. /** @internal */
  67. function pairToMap(v: [string, SortDirection]): SortForCmd {
  68. return new Map([[`${v[0]}`, prepareDirection([v[1]])]]);
  69. }
  70. /** @internal */
  71. function deepToMap(t: [string, SortDirection][]): SortForCmd {
  72. const sortEntries: SortPairForCmd[] = t.map(([k, v]) => [`${k}`, prepareDirection(v)]);
  73. return new Map(sortEntries);
  74. }
  75. /** @internal */
  76. function stringsToMap(t: string[]): SortForCmd {
  77. const sortEntries: SortPairForCmd[] = t.map(key => [`${key}`, 1]);
  78. return new Map(sortEntries);
  79. }
  80. /** @internal */
  81. function objectToMap(t: { [key: string]: SortDirection }): SortForCmd {
  82. const sortEntries: SortPairForCmd[] = Object.entries(t).map(([k, v]) => [
  83. `${k}`,
  84. prepareDirection(v)
  85. ]);
  86. return new Map(sortEntries);
  87. }
  88. /** @internal */
  89. function mapToMap(t: Map<string, SortDirection>): SortForCmd {
  90. const sortEntries: SortPairForCmd[] = Array.from(t).map(([k, v]) => [
  91. `${k}`,
  92. prepareDirection(v)
  93. ]);
  94. return new Map(sortEntries);
  95. }
  96. /** converts a Sort type into a type that is valid for the server (SortForCmd) */
  97. export function formatSort(
  98. sort: Sort | undefined,
  99. direction?: SortDirection
  100. ): SortForCmd | undefined {
  101. if (sort == null) return undefined;
  102. if (typeof sort === 'string') return new Map([[sort, prepareDirection(direction)]]);
  103. if (typeof sort !== 'object') {
  104. throw new MongoInvalidArgumentError(
  105. `Invalid sort format: ${JSON.stringify(sort)} Sort must be a valid object`
  106. );
  107. }
  108. if (!Array.isArray(sort)) {
  109. return isMap(sort) ? mapToMap(sort) : Object.keys(sort).length ? objectToMap(sort) : undefined;
  110. }
  111. if (!sort.length) return undefined;
  112. if (isDeep(sort)) return deepToMap(sort);
  113. if (isPair(sort)) return pairToMap(sort);
  114. return stringsToMap(sort);
  115. }