---@class QuestieSearch local QuestieSearch = QuestieLoader:CreateModule("QuestieSearch"); ---@type QuestieDB local QuestieDB = QuestieLoader:ImportModule("QuestieDB") QuestieSearch.types = {"npc", "object", "item", "quest"} -- Save search results, so the next search has a smaller set to search QuestieSearch.LastResult = { query = '', queryType = '', quest = {}, npc = {}, object = {}, item = {}, } local function _ResetResults() QuestieSearch.LastResult = { query = '', queryType = '', quest = {}, npc = {}, object = {}, item = {}, } end -- Execute a search by name for all types function QuestieSearch:ByName(query) _ResetResults() for _, type in pairs(QuestieSearch.types) do QuestieSearch:Search(query, type, "chars") end return QuestieSearch.LastResult end -- Execute a search by ID for all types function QuestieSearch:ByID(query) _ResetResults() for _,type in pairs(QuestieSearch.types) do QuestieSearch:Search(query, type, "int") end return QuestieSearch.LastResult end --[[ QuestieSearch:Search This function searches a value from the database, including partial matches. Adds the values to QuestieSearch.LastResult. Returns table of found IDs for the selected search type. Parameters: query The search string/int searchType Which database to search, possible values: "npc" "object" "item" "quest" queryType Which type of search to run, possible values: "chars" "int" Optional. Default: "chars" --]] function QuestieSearch:Search(rawQuery, searchType, queryType) queryType = queryType or "chars" local databaseQueryHandle local databaseKeys local overrideKeys local ascensionKeys if searchType == "npc" then databaseQueryHandle = QuestieDB.QueryNPCSingle databaseKeys = QuestieDB.NPCPointers overrideKeys = QuestieDB.npcDataOverrides ascensionKeys = QuestieDB.ascensionNpcIds elseif searchType == "object" then databaseQueryHandle = QuestieDB.QueryObjectSingle databaseKeys = QuestieDB.ObjectPointers overrideKeys = QuestieDB.objectDataOverrides ascensionKeys = QuestieDB.ascensionObjectIds elseif searchType == "item" then databaseQueryHandle = QuestieDB.QueryItemSingle databaseKeys = QuestieDB.ItemPointers overrideKeys = QuestieDB.itemDataOverrides ascensionKeys = QuestieDB.ascensionItemIds elseif searchType == "quest" then databaseQueryHandle = QuestieDB.QueryQuestSingle databaseKeys = QuestieDB.QuestPointers overrideKeys = QuestieDB.questDataOverrides ascensionKeys = QuestieDB.ascensionQuestIds else return end local sanitizedQuery local strictSearch = false if type(rawQuery) ~= "number" then local stringFirst, stringLast = rawQuery:sub(1, 1), rawQuery:sub(-1) if (stringFirst == '"' or stringFirst == "'") and (stringLast == stringFirst) then strictSearch = true sanitizedQuery = rawQuery:sub(2, -2) else sanitizedQuery = rawQuery end else sanitizedQuery = rawQuery end local searchCount = 0; local isTextSearch = queryType == "chars" local isIdSearch = queryType == "int" and tonumber(sanitizedQuery) ~= nil local lastResults = QuestieSearch.LastResult[searchType] -- Fast-path for ID search: allow direct lookups even when the ID isn't in the compiled pointer tables -- (e.g. Ascension custom entries injected via *Overrides*). if isIdSearch then local directId = tonumber(sanitizedQuery) if directId then local directName = databaseQueryHandle(directId, "name") if directName then lastResults[directId] = true QuestieSearch.LastResult.query = rawQuery return lastResults end end end if isTextSearch then local queryToFind = string.lower(sanitizedQuery) local visited = {} local function _RunNameMatch(id) local name = databaseQueryHandle(id, "name") -- Some entries don't have a 'name' because of the way we load corrections if strictSearch then if name and (string.lower(name) == queryToFind) then -- strict search searchCount = searchCount + 1; QuestieSearch.LastResult[searchType][id] = true; else QuestieSearch.LastResult[searchType][id] = nil; end else if name and string.find(string.lower(name), queryToFind) then -- fuzzy search searchCount = searchCount + 1; QuestieSearch.LastResult[searchType][id] = true; else QuestieSearch.LastResult[searchType][id] = nil; end end end if type(databaseKeys) == "table" then for id, _ in pairs(databaseKeys) do visited[id] = true _RunNameMatch(id) end end -- Include custom data injected via overrides, since these IDs may not exist in *Pointers. if type(overrideKeys) == "table" then for id, _ in pairs(overrideKeys) do if not visited[id] then _RunNameMatch(id) end end end -- Also include Ascension custom IDs if type(ascensionKeys) == "table" then for id, _ in pairs(ascensionKeys) do if not visited[id] then visited[id] = true _RunNameMatch(id) end end end elseif isIdSearch then -- Fast-path for custom data injected via overrides (may not exist in *Pointers) local qid = tonumber(sanitizedQuery) if qid and databaseQueryHandle(qid, "name") then lastResults[qid] = true searchCount = searchCount + 1 end for id, _ in pairs(databaseKeys) do if strictSearch then if tostring(id) == sanitizedQuery then -- strict search -- We have a search result or a favourite to display searchCount = searchCount + 1; lastResults[id] = true; else -- This entry doesn't meet the search criteria, removed from the last results lastResults[id] = nil; end else if string.find(tostring(id), sanitizedQuery) then -- fuzzy search -- We have a search result or a favourite to display searchCount = searchCount + 1; lastResults[id] = true; else -- This entry doesn't meet the search criteria, removed from the last results lastResults[id] = nil; end end end end QuestieSearch.LastResult.query = rawQuery return QuestieSearch.LastResult[searchType] end