Skip to content

Commit 75077fe

Browse files
[Manager] Add registry search fallback with gateway pattern (#4187)
1 parent d5ecfb2 commit 75077fe

File tree

14 files changed

+1687
-260
lines changed

14 files changed

+1687
-260
lines changed

src/components/dialog/content/manager/ManagerDialogContent.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
v-model:sortField="sortField"
3333
:search-results="searchResults"
3434
:suggestions="suggestions"
35+
:sort-options="sortOptions"
3536
/>
3637
<div class="flex-1 overflow-auto">
3738
<div
@@ -179,7 +180,8 @@ const {
179180
searchResults,
180181
searchMode,
181182
sortField,
182-
suggestions
183+
suggestions,
184+
sortOptions
183185
} = useRegistrySearch({
184186
initialSortField: initialState.sortField,
185187
initialSearchMode: initialState.searchMode,

src/components/dialog/content/manager/registrySearchBar/RegistrySearchBar.vue

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
/>
3535
<SearchFilterDropdown
3636
v-model:modelValue="sortField"
37-
:options="sortOptions"
37+
:options="availableSortOptions"
3838
:label="$t('g.sort')"
3939
/>
4040
</div>
@@ -56,21 +56,26 @@ import { computed } from 'vue'
5656
import { useI18n } from 'vue-i18n'
5757
5858
import SearchFilterDropdown from '@/components/dialog/content/manager/registrySearchBar/SearchFilterDropdown.vue'
59-
import type { NodesIndexSuggestion } from '@/types/algoliaTypes'
6059
import {
6160
type SearchOption,
6261
SortableAlgoliaField
6362
} from '@/types/comfyManagerTypes'
6463
import { components } from '@/types/comfyRegistryTypes'
64+
import type {
65+
QuerySuggestion,
66+
SearchMode,
67+
SortableField
68+
} from '@/types/searchServiceTypes'
6569
66-
const { searchResults } = defineProps<{
70+
const { searchResults, sortOptions } = defineProps<{
6771
searchResults?: components['schemas']['Node'][]
68-
suggestions?: NodesIndexSuggestion[]
72+
suggestions?: QuerySuggestion[]
73+
sortOptions?: SortableField[]
6974
}>()
7075
7176
const searchQuery = defineModel<string>('searchQuery')
72-
const searchMode = defineModel<string>('searchMode', { default: 'packs' })
73-
const sortField = defineModel<SortableAlgoliaField>('sortField', {
77+
const searchMode = defineModel<SearchMode>('searchMode', { default: 'packs' })
78+
const sortField = defineModel<string>('sortField', {
7479
default: SortableAlgoliaField.Downloads
7580
})
7681
@@ -80,18 +85,19 @@ const hasResults = computed(
8085
() => searchQuery.value?.trim() && searchResults?.length
8186
)
8287
83-
const sortOptions: SearchOption<SortableAlgoliaField>[] = [
84-
{ id: SortableAlgoliaField.Downloads, label: t('manager.sort.downloads') },
85-
{ id: SortableAlgoliaField.Created, label: t('manager.sort.created') },
86-
{ id: SortableAlgoliaField.Updated, label: t('manager.sort.updated') },
87-
{ id: SortableAlgoliaField.Publisher, label: t('manager.sort.publisher') },
88-
{ id: SortableAlgoliaField.Name, label: t('g.name') }
89-
]
90-
const filterOptions: SearchOption<string>[] = [
88+
const availableSortOptions = computed<SearchOption<string>[]>(() => {
89+
if (!sortOptions) return []
90+
return sortOptions.map((field) => ({
91+
id: field.id,
92+
label: field.label
93+
}))
94+
})
95+
const filterOptions: SearchOption<SearchMode>[] = [
9196
{ id: 'packs', label: t('manager.filter.nodePack') },
9297
{ id: 'nodes', label: t('g.nodes') }
9398
]
9499
100+
// When a dropdown query suggestion is selected, update the search query
95101
const onOptionSelect = (event: AutoCompleteOptionSelectEvent) => {
96102
searchQuery.value = event.value.query
97103
}
Lines changed: 42 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,61 @@
11
import { watchDebounced } from '@vueuse/core'
2-
import type { Hit } from 'algoliasearch/dist/lite/browser'
3-
import { memoize, orderBy } from 'lodash'
2+
import { orderBy } from 'lodash'
43
import { computed, ref, watch } from 'vue'
54

6-
import { useAlgoliaSearchService } from '@/services/algoliaSearchService'
7-
import type {
8-
AlgoliaNodePack,
9-
NodesIndexSuggestion,
10-
SearchAttribute
11-
} from '@/types/algoliaTypes'
5+
import { DEFAULT_PAGE_SIZE } from '@/constants/searchConstants'
6+
import { useRegistrySearchGateway } from '@/services/gateway/registrySearchGateway'
7+
import type { SearchAttribute } from '@/types/algoliaTypes'
128
import { SortableAlgoliaField } from '@/types/comfyManagerTypes'
9+
import type { components } from '@/types/comfyRegistryTypes'
10+
import type { QuerySuggestion, SearchMode } from '@/types/searchServiceTypes'
11+
12+
type RegistryNodePack = components['schemas']['Node']
1313

1414
const SEARCH_DEBOUNCE_TIME = 320
15-
const DEFAULT_PAGE_SIZE = 64
1615
const DEFAULT_SORT_FIELD = SortableAlgoliaField.Downloads // Set in the index configuration
17-
const SORT_DIRECTIONS: Record<SortableAlgoliaField, 'asc' | 'desc'> = {
18-
[SortableAlgoliaField.Downloads]: 'desc',
19-
[SortableAlgoliaField.Created]: 'desc',
20-
[SortableAlgoliaField.Updated]: 'desc',
21-
[SortableAlgoliaField.Publisher]: 'asc',
22-
[SortableAlgoliaField.Name]: 'asc'
23-
}
24-
25-
const isDateField = (field: SortableAlgoliaField): boolean =>
26-
field === SortableAlgoliaField.Created ||
27-
field === SortableAlgoliaField.Updated
2816

2917
/**
3018
* Composable for managing UI state of Comfy Node Registry search.
3119
*/
32-
export function useRegistrySearch(options: {
33-
initialSortField?: SortableAlgoliaField
34-
initialSearchMode?: 'nodes' | 'packs'
35-
initialSearchQuery?: string
36-
initialPageNumber?: number
37-
}) {
20+
export function useRegistrySearch(
21+
options: {
22+
initialSortField?: string
23+
initialSearchMode?: SearchMode
24+
initialSearchQuery?: string
25+
initialPageNumber?: number
26+
} = {}
27+
) {
3828
const {
39-
initialSortField = SortableAlgoliaField.Downloads,
29+
initialSortField = DEFAULT_SORT_FIELD,
4030
initialSearchMode = 'packs',
4131
initialSearchQuery = '',
4232
initialPageNumber = 0
4333
} = options
4434

4535
const isLoading = ref(false)
46-
const sortField = ref<SortableAlgoliaField>(initialSortField)
47-
const searchMode = ref<'nodes' | 'packs'>(initialSearchMode)
36+
const sortField = ref<string>(initialSortField)
37+
const searchMode = ref<SearchMode>(initialSearchMode)
4838
const pageSize = ref(DEFAULT_PAGE_SIZE)
4939
const pageNumber = ref(initialPageNumber)
5040
const searchQuery = ref(initialSearchQuery)
51-
const results = ref<AlgoliaNodePack[]>([])
52-
const suggestions = ref<NodesIndexSuggestion[]>([])
41+
const searchResults = ref<RegistryNodePack[]>([])
42+
const suggestions = ref<QuerySuggestion[]>([])
5343

5444
const searchAttributes = computed<SearchAttribute[]>(() =>
5545
searchMode.value === 'nodes' ? ['comfy_nodes'] : ['name', 'description']
5646
)
5747

58-
const resultsAsRegistryPacks = computed(() =>
59-
results.value ? results.value.map(algoliaToRegistry) : []
60-
)
61-
const resultsAsNodes = computed(() =>
62-
results.value
63-
? results.value.reduce(
64-
(acc, hit) => acc.concat(hit.comfy_nodes),
65-
[] as string[]
66-
)
67-
: []
68-
)
48+
const searchGateway = useRegistrySearchGateway()
6949

70-
const { searchPacksCached, toRegistryPack, clearSearchPacksCache } =
71-
useAlgoliaSearchService()
72-
73-
const algoliaToRegistry = memoize(
74-
toRegistryPack,
75-
(algoliaNode: AlgoliaNodePack) => algoliaNode.id
76-
)
77-
const getSortValue = (pack: Hit<AlgoliaNodePack>) => {
78-
if (isDateField(sortField.value)) {
79-
const value = pack[sortField.value]
80-
return value ? new Date(value).getTime() : 0
81-
} else {
82-
const value = pack[sortField.value]
83-
return value ?? 0
84-
}
85-
}
50+
const { searchPacks, clearSearchCache, getSortValue, getSortableFields } =
51+
searchGateway
8652

8753
const updateSearchResults = async (options: { append?: boolean }) => {
8854
isLoading.value = true
8955
if (!options.append) {
9056
pageNumber.value = 0
9157
}
92-
const { nodePacks, querySuggestions } = await searchPacksCached(
58+
const { nodePacks, querySuggestions } = await searchPacks(
9359
searchQuery.value,
9460
{
9561
pageSize: pageSize.value,
@@ -102,17 +68,22 @@ export function useRegistrySearch(options: {
10268

10369
// Results are sorted by the default field to begin with -- so don't manually sort again
10470
if (sortField.value && sortField.value !== DEFAULT_SORT_FIELD) {
71+
// Get the sort direction from the provider's sortable fields
72+
const sortableFields = getSortableFields()
73+
const fieldConfig = sortableFields.find((f) => f.id === sortField.value)
74+
const direction = fieldConfig?.direction || 'desc'
75+
10576
sortedPacks = orderBy(
10677
nodePacks,
107-
[getSortValue],
108-
[SORT_DIRECTIONS[sortField.value]]
78+
[(pack) => getSortValue(pack, sortField.value)],
79+
[direction]
10980
)
11081
}
11182

112-
if (options.append && results.value?.length) {
113-
results.value = results.value.concat(sortedPacks)
83+
if (options.append && searchResults.value?.length) {
84+
searchResults.value = searchResults.value.concat(sortedPacks)
11485
} else {
115-
results.value = sortedPacks
86+
searchResults.value = sortedPacks
11687
}
11788
suggestions.value = querySuggestions
11889
isLoading.value = false
@@ -128,6 +99,10 @@ export function useRegistrySearch(options: {
12899
immediate: true
129100
})
130101

102+
const sortOptions = computed(() => {
103+
return getSortableFields()
104+
})
105+
131106
return {
132107
isLoading,
133108
pageNumber,
@@ -136,8 +111,8 @@ export function useRegistrySearch(options: {
136111
searchMode,
137112
searchQuery,
138113
suggestions,
139-
searchResults: resultsAsRegistryPacks,
140-
nodeSearchResults: resultsAsNodes,
141-
clearCache: clearSearchPacksCache
114+
searchResults,
115+
sortOptions,
116+
clearCache: clearSearchCache
142117
}
143118
}

src/constants/searchConstants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const SEARCH_CACHE_MAX_SIZE = 64
2+
export const DEFAULT_PAGE_SIZE = 64
3+
export const MIN_CHARS_FOR_SUGGESTIONS_ALGOLIA = 2

0 commit comments

Comments
 (0)