Encapsulate logic into filters instead of spreading around
| @@ -1,113 +0,0 @@ | |||
| 1 | ## Input Sources to printCharacters | ||
| 2 | |||
| 3 | `printCharacters` is the main function that triggers the fuzzy search process if fuzzy search is enabled. | ||
| 4 | |||
| 5 | ```mermaid | ||
| 6 | %%{init: {'flowchart': {'nodeSpacing': 50, 'rankSpacing': 50}}}%% | ||
| 7 | flowchart TD | ||
| 8 | subgraph User Actions | ||
| 9 | UA1[Character Search Input] | ||
| 10 | UA2[Tag Filter Click] | ||
| 11 | UA3[Folder Navigation] | ||
| 12 | UA4[Character Delete] | ||
| 13 | UA5[Character Create] | ||
| 14 | UA6[Character Import] | ||
| 15 | UA7[Clear All Filters] | ||
| 16 | UA8[Bulk Edit Operations] | ||
| 17 | UA9[Persona Changes] | ||
| 18 | end | ||
| 19 | |||
| 20 | subgraph API Events | ||
| 21 | API1[Character List Update] | ||
| 22 | API2[Group Update] | ||
| 23 | API3[Tag Update] | ||
| 24 | end | ||
| 25 | |||
| 26 | subgraph System Events | ||
| 27 | SE1[Page Load] | ||
| 28 | SE2[Content Manager Update] | ||
| 29 | SE3[Extension Events] | ||
| 30 | end | ||
| 31 | |||
| 32 | UA1 -->|triggers| PCD[printCharactersDebounced] | ||
| 33 | UA2 -->|triggers| PCD | ||
| 34 | UA7 -->|triggers| PCD | ||
| 35 | UA8 -->|triggers| PCD | ||
| 36 | UA9 -->|triggers| PCD | ||
| 37 | |||
| 38 | UA3 -->|triggers| PC[printCharacters] | ||
| 39 | UA4 -->|triggers| PC | ||
| 40 | UA5 -->|triggers| PC | ||
| 41 | UA6 -->|triggers| PC | ||
| 42 | |||
| 43 | API1 -->|triggers| PC | ||
| 44 | API2 -->|triggers| PC | ||
| 45 | API3 -->|triggers| PC | ||
| 46 | |||
| 47 | SE1 -->|triggers| PC | ||
| 48 | SE2 -->|triggers| PC | ||
| 49 | SE3 -->|triggers| PC | ||
| 50 | |||
| 51 | PCD -->|debounced call| PC | ||
| 52 | |||
| 53 | style PC fill:#f96,stroke:#333 | ||
| 54 | style PCD fill:#f96,stroke:#333 | ||
| 55 | ``` | ||
| 56 | |||
| 57 | This diagram shows how `printCharacters` is called throughout the application: | ||
| 58 | |||
| 59 | 1. User Actions that trigger character list updates: | ||
| 60 | - Search input (debounced) | ||
| 61 | - Tag filter clicks (debounced) | ||
| 62 | - Folder navigation (direct) | ||
| 63 | - Character management operations (direct) | ||
| 64 | |||
| 65 | 2. API Events that require list refresh: | ||
| 66 | - Character list updates | ||
| 67 | - Group updates | ||
| 68 | - Tag system updates | ||
| 69 | |||
| 70 | 3. System Events: | ||
| 71 | - Initial page load | ||
| 72 | - Content manager updates | ||
| 73 | - Extension-triggered refreshes | ||
| 74 | |||
| 75 | |||
| 76 | |||
| 77 | ## Fuzzy Search Flow | ||
| 78 | |||
| 79 | |||
| 80 | This diagram shows the flow of fuzzy search operations: | ||
| 81 | ```mermaid | ||
| 82 | sequenceDiagram | ||
| 83 | participant Data as Data Sources | ||
| 84 | participant PC as printCharacters | ||
| 85 | participant GEL as getEntitiesList | ||
| 86 | participant FH as FilterHelper | ||
| 87 | participant AF as applyFilters | ||
| 88 | participant FS as FuzzySearch Functions | ||
| 89 | participant Cache as FuzzySearchCaches | ||
| 90 | |||
| 91 | Note over Data: Changes from:<br/>- Tags<br/>- Personas<br/>- World Info<br/>- Groups | ||
| 92 | |||
| 93 | Data->>PC: All changes trigger printCharacters<br/>(direct or debounced) | ||
| 94 | |||
| 95 | PC->>GEL: Call with {doFilter: true} | ||
| 96 | GEL->>FH: filterByTagState(entities) | ||
| 97 | GEL->>AF: entitiesFilter.applyFilters(entities) | ||
| 98 | |||
| 99 | AF->>FH: Check scoreCache for existing results | ||
| 100 | FH-->>AF: Return cached scores if exist | ||
| 101 | |||
| 102 | Note over FS: Filter functions include:<br/>SEARCH, <br/>FAV, <br/>GROUP, <br/>FOLDER, <br/>TAG, <br/>WORLD_INFO_SEARCH, <br/>PERSONA_SEARCH | ||
| 103 | AF->>FS: fuzzySearchCharacters/Groups/Tags | ||
| 104 | FS->>Cache: Check/Store results | ||
| 105 | |||
| 106 | FS-->>AF: Return search results | ||
| 107 | AF->>FH: Cache new scores | ||
| 108 | AF-->>GEL: Return filtered entities | ||
| 109 | GEL-->>PC: Return final entities list | ||
| 110 | |||
| 111 | PC->>Cache: clearFuzzySearchCaches() | ||
| 112 | Note over Cache: Cache is cleared at the end of<br/>each printCharacters call,<br/>ensuring fresh results for next search | ||
| 113 | ``` | ||
| @@ -268,7 +268,6 @@ import { initServerHistory } from './scripts/server-history.js'; | |||
| 268 | import { initSettingsSearch } from './scripts/setting-search.js'; | 268 | import { initSettingsSearch } from './scripts/setting-search.js'; |
| 269 | import { initBulkEdit } from './scripts/bulk-edit.js'; | 269 | import { initBulkEdit } from './scripts/bulk-edit.js'; |
| 270 | import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js'; | 270 | import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js'; |
| 271 | import { clearFuzzySearchCaches } from './scripts/power-user.js'; | ||
| 272 | 271 | ||
| 273 | //exporting functions and vars for mods | 272 | //exporting functions and vars for mods |
| 274 | export { | 273 | export { |
| @@ -1516,7 +1515,6 @@ export async function printCharacters(fullRefresh = false) { | |||
| 1516 | }); | 1515 | }); |
| 1517 | 1516 | ||
| 1518 | favsToHotswap(); | 1517 | favsToHotswap(); |
| 1519 | clearFuzzySearchCaches(); | ||
| 1520 | } | 1518 | } |
| 1521 | 1519 | ||
| 1522 | /** Checks the state of the current search, and adds/removes the search sorting option accordingly */ | 1520 | /** Checks the state of the current search, and adds/removes the search sorting option accordingly */ |
| @@ -1623,7 +1621,7 @@ export function getEntitiesList({ doFilter = false, doSort = true } = {}) { | |||
| 1623 | subEntities = filterByTagState(entities, { subForEntity: entity }); | 1621 | subEntities = filterByTagState(entities, { subForEntity: entity }); |
| 1624 | if (doFilter) { | 1622 | if (doFilter) { |
| 1625 | // sub entities filter "hacked" because folder filter should not be applied there, so even in "only folders" mode characters show up | 1623 | // sub entities filter "hacked" because folder filter should not be applied there, so even in "only folders" mode characters show up |
| 1626 | subEntities = entitiesFilter.applyFilters(subEntities, { clearScoreCache: false, tempOverrides: { [FILTER_TYPES.FOLDER]: FILTER_STATES.UNDEFINED } }); | 1624 | subEntities = entitiesFilter.applyFilters(subEntities, { clearScoreCache: false, tempOverrides: { [FILTER_TYPES.FOLDER]: FILTER_STATES.UNDEFINED }, clearFuzzySearchCaches: false }); |
| 1627 | } | 1625 | } |
| 1628 | if (doSort) { | 1626 | if (doSort) { |
| 1629 | sortEntitiesList(subEntities); | 1627 | sortEntitiesList(subEntities); |
| @@ -1636,11 +1634,11 @@ export function getEntitiesList({ doFilter = false, doSort = true } = {}) { | |||
| 1636 | // Second run filters, hiding whatever should be filtered later | 1634 | // Second run filters, hiding whatever should be filtered later |
| 1637 | if (doFilter) { | 1635 | if (doFilter) { |
| 1638 | const beforeFinalEntities = filterByTagState(entities, { globalDisplayFilters: true }); | 1636 | const beforeFinalEntities = filterByTagState(entities, { globalDisplayFilters: true }); |
| 1639 | entities = entitiesFilter.applyFilters(beforeFinalEntities); | 1637 | entities = entitiesFilter.applyFilters(beforeFinalEntities, { clearFuzzySearchCaches: false }); |
| 1640 | 1638 | ||
| 1641 | // Magic for folder filter. If that one is enabled, and no folders are display anymore, we remove that filter to actually show the characters. | 1639 | // Magic for folder filter. If that one is enabled, and no folders are display anymore, we remove that filter to actually show the characters. |
| 1642 | if (isFilterState(entitiesFilter.getFilterData(FILTER_TYPES.FOLDER), FILTER_STATES.SELECTED) && entities.filter(x => x.type == 'tag').length == 0) { | 1640 | if (isFilterState(entitiesFilter.getFilterData(FILTER_TYPES.FOLDER), FILTER_STATES.SELECTED) && entities.filter(x => x.type == 'tag').length == 0) { |
| 1643 | entities = entitiesFilter.applyFilters(beforeFinalEntities, { tempOverrides: { [FILTER_TYPES.FOLDER]: FILTER_STATES.UNDEFINED } }); | 1641 | entities = entitiesFilter.applyFilters(beforeFinalEntities, { tempOverrides: { [FILTER_TYPES.FOLDER]: FILTER_STATES.UNDEFINED }, clearFuzzySearchCaches: false }); |
| 1644 | } | 1642 | } |
| 1645 | } | 1643 | } |
| 1646 | 1644 | ||
| @@ -1656,6 +1654,7 @@ export function getEntitiesList({ doFilter = false, doSort = true } = {}) { | |||
| 1656 | if (doSort) { | 1654 | if (doSort) { |
| 1657 | sortEntitiesList(entities); | 1655 | sortEntitiesList(entities); |
| 1658 | } | 1656 | } |
| 1657 | entitiesFilter.clearFuzzySearchCaches(); | ||
| 1659 | return entities; | 1658 | return entities; |
| 1660 | } | 1659 | } |
| 1661 | 1660 | ||
| @@ -1751,6 +1750,7 @@ export async function getCharacters() { | |||
| 1751 | } | 1750 | } |
| 1752 | 1751 | ||
| 1753 | await getGroups(); | 1752 | await getGroups(); |
| 1753 | // clearFuzzySearchCaches(); | ||
| 1754 | await printCharacters(true); | 1754 | await printCharacters(true); |
| 1755 | } | 1755 | } |
| 1756 | } | 1756 | } |
| @@ -56,6 +56,19 @@ export function isFilterState(a, b) { | |||
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | /** | 58 | /** |
| 59 | * The fuzzy search categories | ||
| 60 | * @type {{ characters: string, worldInfo: string, personas: string, tags: string, groups: string }} | ||
| 61 | */ | ||
| 62 | export const fuzzySearchCategories = Object.freeze({ | ||
| 63 | characters: 'characters', | ||
| 64 | worldInfo: 'worldInfo', | ||
| 65 | personas: 'personas', | ||
| 66 | tags: 'tags', | ||
| 67 | groups: 'groups', | ||
| 68 | }); | ||
| 69 | |||
| 70 | |||
| 71 | /** | ||
| 59 | * Helper class for filtering data. | 72 | * Helper class for filtering data. |
| 60 | * @example | 73 | * @example |
| 61 | * const filterHelper = new FilterHelper(() => console.log('data changed')); | 74 | * const filterHelper = new FilterHelper(() => console.log('data changed')); |
| @@ -73,12 +86,25 @@ export class FilterHelper { | |||
| 73 | scoreCache; | 86 | scoreCache; |
| 74 | 87 | ||
| 75 | /** | 88 | /** |
| 89 | * Cache for fuzzy search results per category. | ||
| 90 | * @type {Object.<string, { resultMap: Map<string, any> }>} | ||
| 91 | */ | ||
| 92 | fuzzySearchCaches; | ||
| 93 | |||
| 94 | /** | ||
| 76 | * Creates a new FilterHelper | 95 | * Creates a new FilterHelper |
| 77 | * @param {Function} onDataChanged Callback to trigger when the filter data changes | 96 | * @param {Function} onDataChanged Callback to trigger when the filter data changes |
| 78 | */ | 97 | */ |
| 79 | constructor(onDataChanged) { | 98 | constructor(onDataChanged) { |
| 80 | this.onDataChanged = onDataChanged; | 99 | this.onDataChanged = onDataChanged; |
| 81 | this.scoreCache = new Map(); | 100 | this.scoreCache = new Map(); |
| 101 | this.fuzzySearchCaches = { | ||
| 102 | [fuzzySearchCategories.characters]: { resultMap: new Map() }, | ||
| 103 | [fuzzySearchCategories.worldInfo]: { resultMap: new Map() }, | ||
| 104 | [fuzzySearchCategories.personas]: { resultMap: new Map() }, | ||
| 105 | [fuzzySearchCategories.tags]: { resultMap: new Map() }, | ||
| 106 | [fuzzySearchCategories.groups]: { resultMap: new Map() }, | ||
| 107 | }; | ||
| 82 | } | 108 | } |
| 83 | 109 | ||
| 84 | /** | 110 | /** |
| @@ -151,7 +177,7 @@ export class FilterHelper { | |||
| 151 | return data; | 177 | return data; |
| 152 | } | 178 | } |
| 153 | 179 | ||
| 154 | const fuzzySearchResults = fuzzySearchWorldInfo(data, term); | 180 | const fuzzySearchResults = fuzzySearchWorldInfo(data, term, this.fuzzySearchCaches); |
| 155 | this.cacheScores(FILTER_TYPES.WORLD_INFO_SEARCH, new Map(fuzzySearchResults.map(i => [i.item?.uid, i.score]))); | 181 | this.cacheScores(FILTER_TYPES.WORLD_INFO_SEARCH, new Map(fuzzySearchResults.map(i => [i.item?.uid, i.score]))); |
| 156 | 182 | ||
| 157 | const filteredData = data.filter(entity => fuzzySearchResults.find(x => x.item === entity)); | 183 | const filteredData = data.filter(entity => fuzzySearchResults.find(x => x.item === entity)); |
| @@ -170,7 +196,7 @@ export class FilterHelper { | |||
| 170 | return data; | 196 | return data; |
| 171 | } | 197 | } |
| 172 | 198 | ||
| 173 | const fuzzySearchResults = fuzzySearchPersonas(data, term); | 199 | const fuzzySearchResults = fuzzySearchPersonas(data, term, this.fuzzySearchCaches); |
| 174 | this.cacheScores(FILTER_TYPES.PERSONA_SEARCH, new Map(fuzzySearchResults.map(i => [i.item.key, i.score]))); | 200 | this.cacheScores(FILTER_TYPES.PERSONA_SEARCH, new Map(fuzzySearchResults.map(i => [i.item.key, i.score]))); |
| 175 | 201 | ||
| 176 | const filteredData = data.filter(name => fuzzySearchResults.find(x => x.item.key === name)); | 202 | const filteredData = data.filter(name => fuzzySearchResults.find(x => x.item.key === name)); |
| @@ -289,9 +315,9 @@ export class FilterHelper { | |||
| 289 | 315 | ||
| 290 | // Save fuzzy search results and scores if enabled | 316 | // Save fuzzy search results and scores if enabled |
| 291 | if (power_user.fuzzy_search) { | 317 | if (power_user.fuzzy_search) { |
| 292 | const fuzzySearchCharactersResults = fuzzySearchCharacters(searchValue); | 318 | const fuzzySearchCharactersResults = fuzzySearchCharacters(searchValue, this.fuzzySearchCaches); |
| 293 | const fuzzySearchGroupsResults = fuzzySearchGroups(searchValue); | 319 | const fuzzySearchGroupsResults = fuzzySearchGroups(searchValue, this.fuzzySearchCaches); |
| 294 | const fuzzySearchTagsResult = fuzzySearchTags(searchValue); | 320 | const fuzzySearchTagsResult = fuzzySearchTags(searchValue, this.fuzzySearchCaches); |
| 295 | this.cacheScores(FILTER_TYPES.SEARCH, new Map(fuzzySearchCharactersResults.map(i => [`character.${i.refIndex}`, i.score]))); | 321 | this.cacheScores(FILTER_TYPES.SEARCH, new Map(fuzzySearchCharactersResults.map(i => [`character.${i.refIndex}`, i.score]))); |
| 296 | this.cacheScores(FILTER_TYPES.SEARCH, new Map(fuzzySearchGroupsResults.map(i => [`group.${i.item.id}`, i.score]))); | 322 | this.cacheScores(FILTER_TYPES.SEARCH, new Map(fuzzySearchGroupsResults.map(i => [`group.${i.item.id}`, i.score]))); |
| 297 | this.cacheScores(FILTER_TYPES.SEARCH, new Map(fuzzySearchTagsResult.map(i => [`tag.${i.item.id}`, i.score]))); | 323 | this.cacheScores(FILTER_TYPES.SEARCH, new Map(fuzzySearchTagsResult.map(i => [`tag.${i.item.id}`, i.score]))); |
| @@ -343,11 +369,14 @@ export class FilterHelper { | |||
| 343 | * @param {object} options - Optional call parameters | 369 | * @param {object} options - Optional call parameters |
| 344 | * @param {boolean} [options.clearScoreCache=true] - Whether the score cache should be cleared. | 370 | * @param {boolean} [options.clearScoreCache=true] - Whether the score cache should be cleared. |
| 345 | * @param {Object.<FilterType, any>} [options.tempOverrides={}] - Temporarily override specific filters for this filter application | 371 | * @param {Object.<FilterType, any>} [options.tempOverrides={}] - Temporarily override specific filters for this filter application |
| 372 | * @param {boolean} [options.clearFuzzySearchCaches=true] - Whether the fuzzy search caches should be cleared. | ||
| 346 | * @returns {any[]} The filtered data. | 373 | * @returns {any[]} The filtered data. |
| 347 | */ | 374 | */ |
| 348 | applyFilters(data, { clearScoreCache = true, tempOverrides = {} } = {}) { | 375 | applyFilters(data, { clearScoreCache = true, tempOverrides = {}, clearFuzzySearchCaches = true } = {}) { |
| 349 | if (clearScoreCache) this.clearScoreCache(); | 376 | if (clearScoreCache) this.clearScoreCache(); |
| 350 | 377 | ||
| 378 | if (clearFuzzySearchCaches) this.clearFuzzySearchCaches(); | ||
| 379 | |||
| 351 | // Save original filter states | 380 | // Save original filter states |
| 352 | const originalStates = {}; | 381 | const originalStates = {}; |
| 353 | for (const key in tempOverrides) { | 382 | for (const key in tempOverrides) { |
| @@ -411,4 +440,20 @@ export class FilterHelper { | |||
| 411 | this.scoreCache = new Map(); | 440 | this.scoreCache = new Map(); |
| 412 | } | 441 | } |
| 413 | } | 442 | } |
| 443 | |||
| 444 | /** | ||
| 445 | * Clears fuzzy search caches | ||
| 446 | * @param {keyof typeof fuzzySearchCategories} [type] Optional cache type to clear. If not provided, clears all caches | ||
| 447 | */ | ||
| 448 | clearFuzzySearchCaches(type = null) { | ||
| 449 | if (type && this.fuzzySearchCaches[type]) { | ||
| 450 | this.fuzzySearchCaches[type].resultMap.clear(); | ||
| 451 | console.log(`Fuzzy search cache cleared for: ${type}`); | ||
| 452 | } else { | ||
| 453 | for (const cache of Object.values(this.fuzzySearchCaches)) { | ||
| 454 | cache.resultMap.clear(); | ||
| 455 | } | ||
| 456 | console.log('All fuzzy search caches cleared'); | ||
| 457 | } | ||
| 458 | } | ||
| 414 | } | 459 | } |
| @@ -53,6 +53,7 @@ import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandE | |||
| 53 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; | 53 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 54 | import { POPUP_TYPE, callGenericPopup } from './popup.js'; | 54 | import { POPUP_TYPE, callGenericPopup } from './popup.js'; |
| 55 | import { loadSystemPrompts } from './sysprompt.js'; | 55 | import { loadSystemPrompts } from './sysprompt.js'; |
| 56 | import { fuzzySearchCategories } from './filters.js'; | ||
| 56 | 57 | ||
| 57 | export { | 58 | export { |
| 58 | loadPowerUserSettings, | 59 | loadPowerUserSettings, |
| @@ -328,22 +329,6 @@ const contextControls = [ | |||
| 328 | let browser_has_focus = true; | 329 | let browser_has_focus = true; |
| 329 | const debug_functions = []; | 330 | const debug_functions = []; |
| 330 | 331 | ||
| 331 | const fuzzySearchCaches = { | ||
| 332 | characters: { resultMap: new Map() }, | ||
| 333 | worldInfo: { resultMap: new Map() }, | ||
| 334 | personas: { resultMap: new Map() }, | ||
| 335 | tags: { resultMap: new Map() }, | ||
| 336 | groups: { resultMap: new Map() }, | ||
| 337 | }; | ||
| 338 | |||
| 339 | const fuzzySearchCategories = { | ||
| 340 | characters: 'characters', | ||
| 341 | worldInfo: 'worldInfo', | ||
| 342 | personas: 'personas', | ||
| 343 | tags: 'tags', | ||
| 344 | groups: 'groups', | ||
| 345 | }; | ||
| 346 | |||
| 347 | const setHotswapsDebounced = debounce(favsToHotswap); | 332 | const setHotswapsDebounced = debounce(favsToHotswap); |
| 348 | 333 | ||
| 349 | function playMessageSound() { | 334 | function playMessageSound() { |
| @@ -1845,19 +1830,26 @@ async function loadContextSettings() { | |||
| 1845 | }); | 1830 | }); |
| 1846 | } | 1831 | } |
| 1847 | 1832 | ||
| 1833 | |||
| 1848 | /** | 1834 | /** |
| 1849 | * Common function to perform fuzzy search with caching | 1835 | * Common function to perform fuzzy search with caching |
| 1850 | * @param {string} type - Type of search from fuzzySearchCategories | 1836 | * @param {string} type - Type of search from fuzzySearchCategories |
| 1851 | * @param {any[]} data - Data array to search in | 1837 | * @param {any[]} data - Data array to search in |
| 1852 | * @param {Array<{name: string, weight: number, getFn?: Function}>} keys - Fuse.js keys configuration | 1838 | * @param {Array<{name: string, weight: number, getFn?: Function}>} keys - Fuse.js keys configuration |
| 1853 | * @param {string} searchValue - The search term | 1839 | * @param {string} searchValue - The search term |
| 1840 | * @param {Object.<string, { resultMap: Map<string, any> }>} fuzzySearchCaches - Fuzzy search caches | ||
| 1854 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score | 1841 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score |
| 1855 | */ | 1842 | */ |
| 1856 | function performFuzzySearch(type, data, keys, searchValue) { | 1843 | function performFuzzySearch(type, data, keys, searchValue, fuzzySearchCaches) { |
| 1844 | let startTime = performance.now(); | ||
| 1857 | const cache = fuzzySearchCaches[type]; | 1845 | const cache = fuzzySearchCaches[type]; |
| 1858 | 1846 | ||
| 1859 | // Check cache for existing results | 1847 | // Check cache for existing results |
| 1860 | if (cache.resultMap.has(searchValue)) { | 1848 | if (cache.resultMap.has(searchValue)) { |
| 1849 | let endTime = performance.now(); | ||
| 1850 | if (endTime - startTime > 1.0) { | ||
| 1851 | console.log(`Fuzzy search for ${type} took ${endTime - startTime}ms (cached)`); | ||
| 1852 | } | ||
| 1861 | return cache.resultMap.get(searchValue); | 1853 | return cache.resultMap.get(searchValue); |
| 1862 | } | 1854 | } |
| 1863 | 1855 | ||
| @@ -1872,26 +1864,20 @@ function performFuzzySearch(type, data, keys, searchValue) { | |||
| 1872 | 1864 | ||
| 1873 | const results = fuse.search(searchValue); | 1865 | const results = fuse.search(searchValue); |
| 1874 | cache.resultMap.set(searchValue, results); | 1866 | cache.resultMap.set(searchValue, results); |
| 1875 | return results; | 1867 | let endTime = performance.now(); |
| 1876 | } | 1868 | if (endTime - startTime > 1.0) { |
| 1877 | 1869 | console.log(`Fuzzy search for ${type} took ${endTime - startTime}ms`); | |
| 1878 | |||
| 1879 | /** | ||
| 1880 | * Clears all fuzzy search caches | ||
| 1881 | */ | ||
| 1882 | export function clearFuzzySearchCaches() { | ||
| 1883 | for (const cache of Object.values(fuzzySearchCaches)) { | ||
| 1884 | cache.resultMap.clear(); | ||
| 1885 | } | 1870 | } |
| 1886 | console.log('Fuzzy search caches cleared'); | 1871 | return results; |
| 1887 | } | 1872 | } |
| 1888 | 1873 | ||
| 1889 | /** | 1874 | /** |
| 1890 | * Fuzzy search characters by a search term | 1875 | * Fuzzy search characters by a search term |
| 1891 | * @param {string} searchValue - The search term | 1876 | * @param {string} searchValue - The search term |
| 1877 | * @param {Object.<string, { resultMap: Map<string, any> }>} fuzzySearchCaches - Fuzzy search caches | ||
| 1892 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score | 1878 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score |
| 1893 | */ | 1879 | */ |
| 1894 | export function fuzzySearchCharacters(searchValue) { | 1880 | export function fuzzySearchCharacters(searchValue, fuzzySearchCaches) { |
| 1895 | const keys = [ | 1881 | const keys = [ |
| 1896 | { name: 'data.name', weight: 20 }, | 1882 | { name: 'data.name', weight: 20 }, |
| 1897 | { name: '#tags', weight: 10, getFn: (character) => getTagsList(character.avatar).map(x => x.name).join('||') }, | 1883 | { name: '#tags', weight: 10, getFn: (character) => getTagsList(character.avatar).map(x => x.name).join('||') }, |
| @@ -1906,16 +1892,17 @@ export function fuzzySearchCharacters(searchValue) { | |||
| 1906 | { name: 'data.alternate_greetings', weight: 1 }, | 1892 | { name: 'data.alternate_greetings', weight: 1 }, |
| 1907 | ]; | 1893 | ]; |
| 1908 | 1894 | ||
| 1909 | return performFuzzySearch(fuzzySearchCategories.characters, characters, keys, searchValue); | 1895 | return performFuzzySearch(fuzzySearchCategories.characters, characters, keys, searchValue, fuzzySearchCaches); |
| 1910 | } | 1896 | } |
| 1911 | 1897 | ||
| 1912 | /** | 1898 | /** |
| 1913 | * Fuzzy search world info entries by a search term | 1899 | * Fuzzy search world info entries by a search term |
| 1914 | * @param {*[]} data - WI items data array | 1900 | * @param {*[]} data - WI items data array |
| 1915 | * @param {string} searchValue - The search term | 1901 | * @param {string} searchValue - The search term |
| 1902 | * @param {Object.<string, { resultMap: Map<string, any> }>} fuzzySearchCaches - Fuzzy search caches | ||
| 1916 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score | 1903 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score |
| 1917 | */ | 1904 | */ |
| 1918 | export function fuzzySearchWorldInfo(data, searchValue) { | 1905 | export function fuzzySearchWorldInfo(data, searchValue, fuzzySearchCaches) { |
| 1919 | const keys = [ | 1906 | const keys = [ |
| 1920 | { name: 'key', weight: 20 }, | 1907 | { name: 'key', weight: 20 }, |
| 1921 | { name: 'group', weight: 15 }, | 1908 | { name: 'group', weight: 15 }, |
| @@ -1926,16 +1913,17 @@ export function fuzzySearchWorldInfo(data, searchValue) { | |||
| 1926 | { name: 'automationId', weight: 1 }, | 1913 | { name: 'automationId', weight: 1 }, |
| 1927 | ]; | 1914 | ]; |
| 1928 | 1915 | ||
| 1929 | return performFuzzySearch(fuzzySearchCategories.worldInfo, data, keys, searchValue); | 1916 | return performFuzzySearch(fuzzySearchCategories.worldInfo, data, keys, searchValue, fuzzySearchCaches); |
| 1930 | } | 1917 | } |
| 1931 | 1918 | ||
| 1932 | /** | 1919 | /** |
| 1933 | * Fuzzy search persona entries by a search term | 1920 | * Fuzzy search persona entries by a search term |
| 1934 | * @param {*[]} data - persona data array | 1921 | * @param {*[]} data - persona data array |
| 1935 | * @param {string} searchValue - The search term | 1922 | * @param {string} searchValue - The search term |
| 1923 | * @param {Object.<string, { resultMap: Map<string, any> }>} fuzzySearchCaches - Fuzzy search caches | ||
| 1936 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score | 1924 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score |
| 1937 | */ | 1925 | */ |
| 1938 | export function fuzzySearchPersonas(data, searchValue) { | 1926 | export function fuzzySearchPersonas(data, searchValue, fuzzySearchCaches) { |
| 1939 | const mappedData = data.map(x => ({ | 1927 | const mappedData = data.map(x => ({ |
| 1940 | key: x, | 1928 | key: x, |
| 1941 | name: power_user.personas[x] ?? '', | 1929 | name: power_user.personas[x] ?? '', |
| @@ -1947,28 +1935,30 @@ export function fuzzySearchPersonas(data, searchValue) { | |||
| 1947 | { name: 'description', weight: 3 }, | 1935 | { name: 'description', weight: 3 }, |
| 1948 | ]; | 1936 | ]; |
| 1949 | 1937 | ||
| 1950 | return performFuzzySearch(fuzzySearchCategories.personas, mappedData, keys, searchValue); | 1938 | return performFuzzySearch(fuzzySearchCategories.personas, mappedData, keys, searchValue, fuzzySearchCaches); |
| 1951 | } | 1939 | } |
| 1952 | 1940 | ||
| 1953 | /** | 1941 | /** |
| 1954 | * Fuzzy search tags by a search term | 1942 | * Fuzzy search tags by a search term |
| 1955 | * @param {string} searchValue - The search term | 1943 | * @param {string} searchValue - The search term |
| 1944 | * @param {Object.<string, { resultMap: Map<string, any> }>} fuzzySearchCaches - Fuzzy search caches | ||
| 1956 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score | 1945 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score |
| 1957 | */ | 1946 | */ |
| 1958 | export function fuzzySearchTags(searchValue) { | 1947 | export function fuzzySearchTags(searchValue, fuzzySearchCaches) { |
| 1959 | const keys = [ | 1948 | const keys = [ |
| 1960 | { name: 'name', weight: 1 }, | 1949 | { name: 'name', weight: 1 }, |
| 1961 | ]; | 1950 | ]; |
| 1962 | 1951 | ||
| 1963 | return performFuzzySearch(fuzzySearchCategories.tags, tags, keys, searchValue); | 1952 | return performFuzzySearch(fuzzySearchCategories.tags, tags, keys, searchValue, fuzzySearchCaches); |
| 1964 | } | 1953 | } |
| 1965 | 1954 | ||
| 1966 | /** | 1955 | /** |
| 1967 | * Fuzzy search groups by a search term | 1956 | * Fuzzy search groups by a search term |
| 1968 | * @param {string} searchValue - The search term | 1957 | * @param {string} searchValue - The search term |
| 1958 | * @param {Object.<string, { resultMap: Map<string, any> }>} fuzzySearchCaches - Fuzzy search caches | ||
| 1969 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score | 1959 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score |
| 1970 | */ | 1960 | */ |
| 1971 | export function fuzzySearchGroups(searchValue) { | 1961 | export function fuzzySearchGroups(searchValue, fuzzySearchCaches) { |
| 1972 | const keys = [ | 1962 | const keys = [ |
| 1973 | { name: 'name', weight: 20 }, | 1963 | { name: 'name', weight: 20 }, |
| 1974 | { name: 'members', weight: 15 }, | 1964 | { name: 'members', weight: 15 }, |
| @@ -1976,7 +1966,7 @@ export function fuzzySearchGroups(searchValue) { | |||
| 1976 | { name: 'id', weight: 1 }, | 1966 | { name: 'id', weight: 1 }, |
| 1977 | ]; | 1967 | ]; |
| 1978 | 1968 | ||
| 1979 | return performFuzzySearch(fuzzySearchCategories.groups, groups, keys, searchValue); | 1969 | return performFuzzySearch(fuzzySearchCategories.groups, groups, keys, searchValue, fuzzySearchCaches); |
| 1980 | } | 1970 | } |
| 1981 | 1971 | ||
| 1982 | /** | 1972 | /** |