[Manager] Add suggestions to search (#3041)

This commit is contained in:
Christian Byrne
2025-03-14 10:24:28 -07:00
committed by GitHub
parent c6b3e2a0ed
commit 8be25883cd
4 changed files with 96 additions and 27 deletions

View File

@@ -32,6 +32,7 @@
v-model:searchQuery="searchQuery"
v-model:searchMode="searchMode"
:searchResults="searchResults"
:suggestions="suggestions"
/>
<div class="flex-1 overflow-auto">
<div
@@ -141,8 +142,14 @@ const tabs = ref<TabItem[]>([
])
const selectedTab = ref<TabItem>(tabs.value[0])
const { searchQuery, pageNumber, isLoading, searchResults, searchMode } =
useRegistrySearch()
const {
searchQuery,
pageNumber,
isLoading,
searchResults,
searchMode,
suggestions
} = useRegistrySearch()
pageNumber.value = 0
const isInitialLoad = computed(

View File

@@ -1,15 +1,29 @@
<template>
<div class="relative w-full p-6">
<div class="flex items-center w-full">
<IconField class="w-5/12">
<InputIcon class="pi pi-search" />
<InputText
v-model="searchQuery"
:placeholder="$t('manager.searchPlaceholder')"
class="w-full rounded-2xl"
autofocus
/>
</IconField>
<AutoComplete
v-model.lazy="searchQuery"
:suggestions="suggestions || []"
:placeholder="$t('manager.searchPlaceholder')"
:complete-on-focus="false"
:delay="8"
optionLabel="query"
class="w-full"
@complete="stubTrue"
@option-select="onOptionSelect"
:pt="{
pcInputText: {
root: {
autofocus: true,
class: 'w-5/12 rounded-2xl'
}
},
loader: {
style: 'display: none'
}
}"
>
</AutoComplete>
</div>
<div class="flex mt-3 text-sm">
<div class="flex gap-6 ml-1">
@@ -34,18 +48,21 @@
</template>
<script setup lang="ts">
import IconField from 'primevue/iconfield'
import InputIcon from 'primevue/inputicon'
import InputText from 'primevue/inputtext'
import { stubTrue } from 'lodash'
import AutoComplete, {
AutoCompleteOptionSelectEvent
} from 'primevue/autocomplete'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import SearchFilterDropdown from '@/components/dialog/content/manager/registrySearchBar/SearchFilterDropdown.vue'
import type { NodesIndexSuggestion } from '@/services/algoliaSearchService'
import type { PackField, SearchOption } from '@/types/comfyManagerTypes'
import { components } from '@/types/comfyRegistryTypes'
const props = defineProps<{
const { searchResults } = defineProps<{
searchResults?: components['schemas']['Node'][]
suggestions?: NodesIndexSuggestion[]
}>()
const searchQuery = defineModel<string>('searchQuery')
@@ -55,7 +72,7 @@ const sortField = defineModel<PackField>('sortField', { default: 'downloads' })
const { t } = useI18n()
const hasResults = computed(
() => searchQuery.value?.trim() && props.searchResults?.length
() => searchQuery.value?.trim() && searchResults?.length
)
const sortOptions: SearchOption<PackField>[] = [
@@ -68,4 +85,8 @@ const filterOptions: SearchOption<string>[] = [
{ id: 'packs', label: t('manager.filter.nodePack') },
{ id: 'nodes', label: t('g.nodes') }
]
const onOptionSelect = (event: AutoCompleteOptionSelectEvent) => {
searchQuery.value = event.value.query
}
</script>

View File

@@ -7,6 +7,7 @@ import {
SearchAttribute,
useAlgoliaSearchService
} from '@/services/algoliaSearchService'
import type { NodesIndexSuggestion } from '@/services/algoliaSearchService'
import { PackField } from '@/types/comfyManagerTypes'
const SEARCH_DEBOUNCE_TIME = 16
@@ -23,6 +24,7 @@ export function useRegistrySearch() {
const pageNumber = ref(0)
const searchQuery = ref('')
const results = ref<AlgoliaNodePack[]>([])
const suggestions = ref<NodesIndexSuggestion[]>([])
const searchAttributes = computed<SearchAttribute[]>(() =>
searchMode.value === 'nodes' ? ['comfy_nodes'] : ['name', 'description']
@@ -49,11 +51,16 @@ export function useRegistrySearch() {
const onQueryChange = async () => {
isLoading.value = true
results.value = await searchPacks(searchQuery.value, {
pageSize: pageSize.value,
pageNumber: pageNumber.value,
restrictSearchableAttributes: searchAttributes.value
})
const { nodePacks, querySuggestions } = await searchPacks(
searchQuery.value,
{
pageSize: pageSize.value,
pageNumber: pageNumber.value,
restrictSearchableAttributes: searchAttributes.value
}
)
results.value = nodePacks
suggestions.value = querySuggestions
isLoading.value = false
}
@@ -71,6 +78,7 @@ export function useRegistrySearch() {
sortField,
searchMode,
searchQuery,
suggestions,
searchResults: resultsAsRegistryPacks,
nodeSearchResults: resultsAsNodes
}

View File

@@ -1,6 +1,7 @@
import type {
BaseSearchParamsWithoutQuery,
Hit
Hit,
SearchResponse
} from 'algoliasearch/dist/lite/browser'
import { liteClient as algoliasearch } from 'algoliasearch/dist/lite/builds/browser'
import { omit } from 'lodash'
@@ -50,6 +51,8 @@ export interface AlgoliaNodePack {
'latest_version',
'comfy_node_extract_status'
>
/** `total_install` index only */
icon_url: RegistryNodePack['icon']
}
export type SearchAttribute = keyof AlgoliaNodePack
@@ -70,6 +73,20 @@ const RETRIEVE_ATTRIBUTES: SearchAttribute[] = [
'id'
]
export interface NodesIndexSuggestion {
nb_words: number
nodes_index: {
exact_nb_hits: number
facets: {
exact_matches: Record<string, number>
analytics: Record<string, any>
}
}
objectID: RegistryNodePack['id']
popularity: number
query: string
}
type SearchNodePacksParams = BaseSearchParamsWithoutQuery & {
pageSize: number
pageNumber: number
@@ -112,6 +129,7 @@ export const useAlgoliaSearchService = () => {
license: algoliaNode.license,
downloads: algoliaNode.total_install,
status: algoliaNode.status,
icon: algoliaNode.icon_url,
latest_version: toRegistryLatestVersion(algoliaNode),
publisher: toRegistryPublisher(algoliaNode)
}
@@ -123,11 +141,16 @@ export const useAlgoliaSearchService = () => {
const searchPacks = async (
query: string,
params: SearchNodePacksParams
): Promise<Hit<AlgoliaNodePack>[]> => {
): Promise<{
nodePacks: Hit<AlgoliaNodePack>[]
querySuggestions: Hit<NodesIndexSuggestion>[]
}> => {
const { pageSize, pageNumber } = params
const rest = omit(params, ['pageSize', 'pageNumber'])
const { results } = await searchClient.search<AlgoliaNodePack>({
const { results } = await searchClient.search<
AlgoliaNodePack | NodesIndexSuggestion
>({
requests: [
{
query,
@@ -135,15 +158,25 @@ export const useAlgoliaSearchService = () => {
attributesToRetrieve: RETRIEVE_ATTRIBUTES,
...rest,
hitsPerPage: pageSize,
length: pageSize,
page: pageNumber
},
{
indexName: 'nodes_index_query_suggestions',
query
}
],
strategy: 'none'
})
// Narrow from `SearchResponse<T> | SearchForFacetValuesResponse` to `SearchResponse<T>`
return 'hits' in results[0] ? results[0].hits : [] // Only querying a single index for now
const [nodePacks, querySuggestions] = results as [
SearchResponse<AlgoliaNodePack>,
SearchResponse<NodesIndexSuggestion>
]
return {
nodePacks: nodePacks.hits,
querySuggestions: querySuggestions.hits
}
}
return {