From 0d0576faabc0e2ce65daa8a21a98c75b3b2c9ff8 Mon Sep 17 00:00:00 2001 From: Johnpaul Chiwetelu <49923152+Myestery@users.noreply.github.com> Date: Tue, 20 Jan 2026 00:03:17 +0100 Subject: [PATCH] feat: optimize empty search to use cached /nodes endpoint (#8159) ## Summary Optimizes the Manager dialog to use the cached `GET /nodes` endpoint instead of `GET /nodes/search` for empty search queries (when the dialog first opens). This significantly reduces Algolia usage since empty searches account for the majority of search requests. ## Changes - **registrySearchProvider.ts**: Modified `searchPacks()` to detect empty queries and route them to `listAllPacks()` instead of `search()` - **registrySearchProvider.test.ts**: Added 5 new test cases covering empty query behavior - Cache clearing now clears both `search` and `listAllPacks` caches ## Technical Details **Empty Query Flow (NEW):** - Query: `""` or whitespace - Endpoint: `GET /nodes?limit=X&page=Y` - Cache: Server-side cached (via omitting `latest` parameter) - Result: Fast, cached node pack list **Non-Empty Query Flow (UNCHANGED):** - Query: Any non-empty string - Endpoint: `GET /nodes/search?search=X` or `comfy_node_search=X` - Result: Search results as before ## Testing ```bash pnpm test:unit -- src/services/providers/registrySearchProvider.test.ts pnpm typecheck ``` --- .../providers/registrySearchProvider.test.ts | 79 ++++++++++++++++++- .../providers/registrySearchProvider.ts | 32 +++++--- 2 files changed, 100 insertions(+), 11 deletions(-) diff --git a/src/services/providers/registrySearchProvider.test.ts b/src/services/providers/registrySearchProvider.test.ts index fe25018ad..b564bf7a7 100644 --- a/src/services/providers/registrySearchProvider.test.ts +++ b/src/services/providers/registrySearchProvider.test.ts @@ -11,6 +11,8 @@ vi.mock('@/stores/comfyRegistryStore', () => ({ describe('useComfyRegistrySearchProvider', () => { const mockSearchCall = vi.fn() const mockSearchClear = vi.fn() + const mockListAllPacksCall = vi.fn() + const mockListAllPacksClear = vi.fn() beforeEach(() => { vi.clearAllMocks() @@ -20,6 +22,10 @@ describe('useComfyRegistrySearchProvider', () => { search: { call: mockSearchCall, clear: mockSearchClear + }, + listAllPacks: { + call: mockListAllPacksCall, + clear: mockListAllPacksClear } } as any) }) @@ -111,14 +117,85 @@ describe('useComfyRegistrySearchProvider', () => { expect(result.nodePacks).toEqual([]) expect(result.querySuggestions).toEqual([]) }) + + it('should use listAllPacks for empty query', async () => { + const mockResults = { + nodes: [ + { id: '1', name: 'Pack 1' }, + { id: '2', name: 'Pack 2' } + ] + } + mockListAllPacksCall.mockResolvedValue(mockResults) + + const provider = useComfyRegistrySearchProvider() + const result = await provider.searchPacks('', { + pageSize: 20, + pageNumber: 0 + }) + + expect(mockListAllPacksCall).toHaveBeenCalledWith({ + limit: 20, + page: 1 + }) + expect(mockSearchCall).not.toHaveBeenCalled() + expect(result.nodePacks).toEqual(mockResults.nodes) + expect(result.querySuggestions).toEqual([]) + }) + + it('should use listAllPacks for whitespace-only query', async () => { + const mockResults = { + nodes: [{ id: '1', name: 'Pack 1' }] + } + mockListAllPacksCall.mockResolvedValue(mockResults) + + const provider = useComfyRegistrySearchProvider() + const result = await provider.searchPacks(' ', { + pageSize: 10, + pageNumber: 0 + }) + + expect(mockListAllPacksCall).toHaveBeenCalledWith({ + limit: 10, + page: 1 + }) + expect(mockSearchCall).not.toHaveBeenCalled() + expect(result.nodePacks).toEqual(mockResults.nodes) + }) + + it('should handle empty results from listAllPacks', async () => { + mockListAllPacksCall.mockResolvedValue({ nodes: [] }) + + const provider = useComfyRegistrySearchProvider() + const result = await provider.searchPacks('', { + pageSize: 10, + pageNumber: 0 + }) + + expect(result.nodePacks).toEqual([]) + expect(result.querySuggestions).toEqual([]) + }) + + it('should handle null results from listAllPacks', async () => { + mockListAllPacksCall.mockResolvedValue(null) + + const provider = useComfyRegistrySearchProvider() + const result = await provider.searchPacks('', { + pageSize: 10, + pageNumber: 0 + }) + + expect(result.nodePacks).toEqual([]) + expect(result.querySuggestions).toEqual([]) + }) }) describe('clearSearchCache', () => { - it('should delegate to store search.clear', () => { + it('should clear both search and listAllPacks caches', () => { const provider = useComfyRegistrySearchProvider() provider.clearSearchCache() expect(mockSearchClear).toHaveBeenCalled() + expect(mockListAllPacksClear).toHaveBeenCalled() }) }) diff --git a/src/services/providers/registrySearchProvider.ts b/src/services/providers/registrySearchProvider.ts index a2cede14a..d9f334205 100644 --- a/src/services/providers/registrySearchProvider.ts +++ b/src/services/providers/registrySearchProvider.ts @@ -25,33 +25,45 @@ export const useComfyRegistrySearchProvider = (): NodePackSearchProvider => { ): Promise => { const { pageSize, pageNumber, restrictSearchableAttributes } = params - // Determine search mode based on searchable attributes + // For empty queries, use the cached listAllPacks endpoint instead of search + if (!query || query.trim() === '') { + const listParams = { + limit: pageSize, + page: pageNumber + 1 // Registry API uses 1-based pagination + // Note: omitting 'latest' parameter defaults to cached result + } + + const listResult = await registryStore.listAllPacks.call(listParams) + const nodePacks = listResult?.nodes ?? [] + + return { + nodePacks, + querySuggestions: [] + } + } + + // For non-empty queries, use the search endpoint const isNodeSearch = restrictSearchableAttributes?.includes('comfy_nodes') const searchParams = { search: isNodeSearch ? undefined : query, comfy_node_search: isNodeSearch ? query : undefined, limit: pageSize, - page: pageNumber + 1 // Registry API uses 1-based pagination + page: pageNumber + 1 } const searchResult = await registryStore.search.call(searchParams) - - if (!searchResult || !searchResult.nodes) { - return { - nodePacks: [], - querySuggestions: [] - } - } + const nodePacks = searchResult?.nodes ?? [] return { - nodePacks: searchResult.nodes, + nodePacks, querySuggestions: [] // Registry doesn't support query suggestions } } const clearSearchCache = () => { registryStore.search.clear() + registryStore.listAllPacks.clear() } const getSortValue = (