Skip to content

Commit e8f36b8

Browse files
committed
test(lib): add test cases for pagination / sorting / filtering / seaerch / performance
1 parent d061cc8 commit e8f36b8

9 files changed

Lines changed: 996 additions & 26 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ node_modules
1111
temp
1212
playground/.nuxt
1313
docs/.vitepress/dist
14-
docs/.vitepress/cache
14+
docs/.vitepress/cache
15+
.vercel

docs/features/sorting.md

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,138 @@ const result = query(data, {
106106
```
107107

108108
This will sort the data based on the math scores in descending order.
109+
110+
## Advanced Sorting with Value Parsing
111+
112+
The `SortOptions` interface now includes a `parser` option, which allows you to prepare and format values before sorting. This is particularly useful when dealing with mixed data types or when you need to apply custom sorting logic.
113+
114+
```typescript
115+
export interface SortOptions {
116+
key: string
117+
dir?: 'asc' | 'desc'
118+
parser?: 'number' | 'boolean' | 'string' | ((value: any) => string | number | boolean | null | undefined)
119+
}
120+
```
121+
122+
The `parser` option can be one of the following:
123+
124+
- A string value: `'number'`, `'boolean'`, or `'string'` for basic type conversion
125+
- A custom function that takes the value and returns a parsed version
126+
127+
### Examples
128+
129+
#### Using Built-in Parsers
130+
131+
1. Sorting mixed string and number values as numbers:
132+
133+
```ts twoslash
134+
import { query } from '@chronicstone/array-query'
135+
136+
const data = [
137+
{ id: 1, value: '10' },
138+
{ id: 2, value: 5 },
139+
{ id: 3, value: '15' },
140+
{ id: 4, value: '3' }
141+
]
142+
143+
const result = query(data, {
144+
sort: { key: 'value', dir: 'asc', parser: 'number' }
145+
})
146+
```
147+
148+
Result:
149+
```ts twoslash
150+
[
151+
{ id: 4, value: '3' },
152+
{ id: 2, value: 5 },
153+
{ id: 1, value: '10' },
154+
{ id: 3, value: '15' }
155+
]
156+
```
157+
158+
2. Sorting boolean values:
159+
160+
```ts twoslash
161+
import { query } from '@chronicstone/array-query'
162+
163+
const data = [
164+
{ id: 1, isActive: 'true' },
165+
{ id: 2, isActive: false },
166+
{ id: 3, isActive: 'false' },
167+
{ id: 4, isActive: true }
168+
]
169+
170+
const result = query(data, {
171+
sort: { key: 'isActive', dir: 'desc', parser: 'boolean' }
172+
})
173+
```
174+
175+
Result:
176+
```ts twoslash
177+
[
178+
{ id: 1, isActive: 'true' },
179+
{ id: 4, isActive: true },
180+
{ id: 2, isActive: false },
181+
{ id: 3, isActive: 'false' }
182+
]
183+
```
184+
185+
#### Using Custom Parser Functions
186+
187+
3. Sorting by date strings:
188+
189+
```ts twoslash
190+
import { query } from '@chronicstone/array-query'
191+
192+
const data = [
193+
{ id: 1, date: '2023-05-15' },
194+
{ id: 2, date: '2023-01-10' },
195+
{ id: 3, date: '2023-12-01' }
196+
]
197+
198+
const result = query(data, {
199+
sort: {
200+
key: 'date',
201+
dir: 'asc',
202+
parser: value => new Date(value).getTime()
203+
}
204+
})
205+
```
206+
207+
Result:
208+
```ts twoslash
209+
[
210+
{ id: 2, date: '2023-01-10' },
211+
{ id: 1, date: '2023-05-15' },
212+
{ id: 3, date: '2023-12-01' }
213+
]
214+
```
215+
216+
4. Sorting by a computed value:
217+
218+
```ts twoslash
219+
import { query } from '@chronicstone/array-query'
220+
221+
const data = [
222+
{ id: 1, firstName: 'John', lastName: 'Doe' },
223+
{ id: 2, firstName: 'Jane', lastName: 'Smith' },
224+
{ id: 3, firstName: 'Alice', lastName: 'Johnson' }
225+
]
226+
227+
const result = query(data, {
228+
sort: {
229+
key: 'firstName',
230+
dir: 'asc',
231+
parser: (value, item) => `${item.lastName}, ${item.firstName}`
232+
}
233+
})
234+
```
235+
236+
Result:
237+
```ts twoslash
238+
[
239+
{ id: 3, firstName: 'Alice', lastName: 'Johnson' },
240+
{ id: 2, firstName: 'Jane', lastName: 'Smith' },
241+
{ id: 1, firstName: 'John', lastName: 'Doe' }
242+
]
243+
```

src/query.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@ export function query<T extends GenericObject, P extends QueryParams>(
2323
const sortArray = Array.isArray(params.sort) ? params.sort : [params.sort]
2424

2525
result = result.sort((a, b) => {
26-
for (const { key, dir } of sortArray) {
27-
const aValue = getObjectProperty(a, key)
28-
const bValue = getObjectProperty(b, key)
26+
for (const { key, dir, parser } of sortArray) {
27+
const parserHandler = typeof parser === 'function' ? parser : (v: any) => parser === 'number' ? Number(v) : parser === 'boolean' ? Boolean(v) : parser === 'string' ? String(v) : v
28+
const aParsed = parserHandler(getObjectProperty(a, key)) ?? null
29+
const bParsed = parserHandler(getObjectProperty(b, key)) ?? null
2930

30-
if (aValue !== bValue) {
31-
const comparison = (aValue < bValue) ? -1 : 1
31+
if (aParsed !== bParsed) {
32+
const comparison = (aParsed < bParsed) ? -1 : 1
3233
return dir === 'asc' ? comparison : -comparison
3334
}
3435
}
@@ -162,7 +163,7 @@ export function query<T extends GenericObject, P extends QueryParams>(
162163
const unpaginatedRows = [...result]
163164
const totalRows = result.length
164165
const totalPages = Math.ceil(totalRows / params.limit)
165-
const start = params.offset ?? 0
166+
const start = ((params?.page ?? 1) - 1) * params.limit
166167
const end = start + params.limit
167168
result = result.slice(start, end)
168169

src/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,15 @@ export interface SearchOptions {
6666
export interface SortOptions {
6767
key: string
6868
dir?: 'asc' | 'desc'
69+
parser?: 'number' | 'boolean' | 'string' | ((value: any) => string | number | boolean | null | undefined)
6970
}
7071

7172
export interface QueryParams {
7273
sort?: SortOptions | Array<SortOptions>
7374
search?: SearchOptions
7475
filter?: FilterOptions
7576
limit?: number
76-
offset?: number
77+
page?: number
7778
}
7879

7980
export type QueryResult<T extends GenericObject, P extends QueryParams> = P extends { limit: number } ? { totalRows: number, totalPages: number, rows: T[], unpaginatedRows: T[] } : { rows: T[] }

0 commit comments

Comments
 (0)