Skip to content

Commit 01fd072

Browse files
committed
feat: add sorting for npm as well
1 parent 357c3e4 commit 01fd072

File tree

2 files changed

+60
-15
lines changed

2 files changed

+60
-15
lines changed

app/pages/search.vue

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang="ts">
22
import type { FilterChip, SortKey } from '#shared/types/preferences'
3-
import { parseSortOption } from '#shared/types/preferences'
3+
import { parseSortOption, PROVIDER_SORT_KEYS } from '#shared/types/preferences'
44
import { onKeyDown } from '@vueuse/core'
55
import { debounce } from 'perfect-debounce'
66
import { isValidNewPackageName, checkPackageExists } from '~/utils/package-name'
@@ -111,8 +111,8 @@ const visibleResults = computed(() => {
111111
// Use structured filters for client-side refinement of search results
112112
const resultsArray = computed(() => visibleResults.value?.objects ?? [])
113113
114-
// Sort keys that the npm registry path doesn't support (only relevance works server-side)
115-
const NON_RELEVANCE_SORT_KEYS: SortKey[] = [
114+
// All possible non-relevance sort keys
115+
const ALL_SORT_KEYS: SortKey[] = [
116116
'downloads-week',
117117
'downloads-day',
118118
'downloads-month',
@@ -125,8 +125,11 @@ const NON_RELEVANCE_SORT_KEYS: SortKey[] = [
125125
'score',
126126
]
127127
128-
// Disable non-relevance sorts when using npm provider (results are relevance-only from server)
129-
const disabledSortKeys = computed<SortKey[]>(() => (isAlgolia.value ? [] : NON_RELEVANCE_SORT_KEYS))
128+
// Disable sort keys the current provider can't meaningfully sort by
129+
const disabledSortKeys = computed<SortKey[]>(() => {
130+
const supported = PROVIDER_SORT_KEYS[isAlgolia.value ? 'algolia' : 'npm']
131+
return ALL_SORT_KEYS.filter(k => !supported.has(k))
132+
})
130133
131134
// Minimal structured filters usage for search context (no client-side filtering)
132135
const {
@@ -154,21 +157,29 @@ const isRelevanceSort = computed(
154157
() => sortOption.value === 'relevance-desc' || sortOption.value === 'relevance-asc',
155158
)
156159
160+
// Maximum eager-load sizes per provider for client-side sorting.
161+
// Algolia supports up to 1000 with offset/length pagination.
162+
// npm supports pagination via `from` parameter (no hard cap, but diminishing relevance).
163+
const EAGER_LOAD_SIZE = { algolia: 500, npm: 500 } as const
164+
157165
// Calculate how many results we need based on current page and preferred page size
158166
const requestedSize = computed(() => {
159167
const numericPrefSize = preferredPageSize.value === 'all' ? 250 : preferredPageSize.value
160168
const base = Math.max(pageSize, currentPage.value * numericPrefSize)
161-
// When sorting by something other than relevance, fetch a large batch from Algolia
169+
// When sorting by something other than relevance, fetch a large batch
162170
// so client-side sorting operates on a meaningful pool of matching results
163171
if (!isRelevanceSort.value) {
164-
return Math.max(base, 250)
172+
const cap = isAlgolia.value ? EAGER_LOAD_SIZE.algolia : EAGER_LOAD_SIZE.npm
173+
return Math.max(base, cap)
165174
}
166175
return base
167176
})
168177
169-
// Reset to relevance sort when switching to npm (which only supports relevance)
178+
// Reset to relevance sort when switching to a provider that doesn't support the current sort key
170179
watch(isAlgolia, algolia => {
171-
if (!algolia && !isRelevanceSort.value) {
180+
const { key } = parseSortOption(sortOption.value)
181+
const supported = PROVIDER_SORT_KEYS[algolia ? 'algolia' : 'npm']
182+
if (!supported.has(key)) {
172183
sortOption.value = 'relevance-desc'
173184
}
174185
})
@@ -192,8 +203,8 @@ const displayResults = computed(() => {
192203
return resultsArray.value
193204
}
194205
195-
// Sort the fetched results client-side (Algolia doesn't support arbitrary
196-
// sort orders without replica indices, so we fetch a large batch and sort here)
206+
// Sort the fetched results client-side — neither Algolia nor npm support
207+
// arbitrary sort orders server-side, so we fetch a large batch and sort here
197208
const { key, direction } = parseSortOption(sortOption.value)
198209
const multiplier = direction === 'asc' ? 1 : -1
199210
@@ -212,6 +223,18 @@ const displayResults = computed(() => {
212223
case 'name':
213224
diff = a.package.name.localeCompare(b.package.name)
214225
break
226+
case 'quality':
227+
diff = (a.score?.detail?.quality ?? 0) - (b.score?.detail?.quality ?? 0)
228+
break
229+
case 'popularity':
230+
diff = (a.score?.detail?.popularity ?? 0) - (b.score?.detail?.popularity ?? 0)
231+
break
232+
case 'maintenance':
233+
diff = (a.score?.detail?.maintenance ?? 0) - (b.score?.detail?.maintenance ?? 0)
234+
break
235+
case 'score':
236+
diff = (a.score?.final ?? 0) - (b.score?.final ?? 0)
237+
break
215238
default:
216239
diff = 0
217240
}

shared/types/preferences.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,34 @@ export const SORT_KEYS: SortKeyConfig[] = [
140140
{ key: 'downloads-year', defaultDirection: 'desc', disabled: true },
141141
{ key: 'updated', defaultDirection: 'desc' },
142142
{ key: 'name', defaultDirection: 'asc' },
143-
{ key: 'quality', defaultDirection: 'desc', disabled: true },
144-
{ key: 'popularity', defaultDirection: 'desc', disabled: true },
145-
{ key: 'maintenance', defaultDirection: 'desc', disabled: true },
146-
{ key: 'score', defaultDirection: 'desc', disabled: true },
143+
{ key: 'quality', defaultDirection: 'desc' },
144+
{ key: 'popularity', defaultDirection: 'desc' },
145+
{ key: 'maintenance', defaultDirection: 'desc' },
146+
{ key: 'score', defaultDirection: 'desc' },
147147
]
148148

149+
/**
150+
* Sort keys each search provider can meaningfully sort by.
151+
*
152+
* Algolia: has download counts and dates but synthetic/zeroed score values
153+
* - quality is 0 or 1 (boolean `popular` flag), maintenance is always 0, score is always 0
154+
* - popularity is `downloadsRatio` (a meaningful float)
155+
*
156+
* npm: has real score values and dates but no download counts in search results
157+
*/
158+
export const PROVIDER_SORT_KEYS: Record<'algolia' | 'npm', Set<SortKey>> = {
159+
algolia: new Set<SortKey>(['relevance', 'downloads-week', 'updated', 'name', 'popularity']),
160+
npm: new Set<SortKey>([
161+
'relevance',
162+
'updated',
163+
'name',
164+
'quality',
165+
'popularity',
166+
'maintenance',
167+
'score',
168+
]),
169+
}
170+
149171
/** All valid sort keys for validation */
150172
const VALID_SORT_KEYS = new Set<SortKey>([
151173
'relevance',

0 commit comments

Comments
 (0)