}
return []
}
+
+interface KeywordDBQueryResult extends DBQueryResult {
+ keyword_score?: number
+}
+
+const keywordSearch = async (query: string, limit: number, filter?: string): Promise<KeywordDBQueryResult[]> => {
+ try {
+ const keywords = query
+ .toLowerCase()
+ .split(/\s+/)
+ .filter((word: string) => word.length > 2 && !['the', 'and', 'for', 'with', 'this', 'that'].includes(word))
+
+ if (keywords.length === 0) {
+ return []
+ }
+
+ const vectorResults = await window.database.search(query, limit, filter)
+
+ const resultsWithKeywordScores = vectorResults
+ .map((result) => {
+ let score = 0
+ keywords.forEach((keyword) => {
+ const regex = new RegExp(keyword, 'gi')
+ const matches = result.content.match(regex)
+ if (matches) {
+ score += matches.length
+ }
+ })
+
+ return {
+ ...result,
+ keyword_score: score,
+ }
+ })
+ .sort((a, b) => (b.keyword_score || 0) - (a.keyword_score || 0))
+
+ return resultsWithKeywordScores
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error('Error performing keyword search:', error)
+ return []
+ }
+}
+
+const combineAndRankResults = (
+ vectorResults: DBQueryResult[],
+ keywordResults: KeywordDBQueryResult[],
+ limit: number,
+ vectorWeight = 0.7,
+): DBQueryResult[] => {
+ const keywordWeight = 1 - vectorWeight
+ const resultsMap = new Map<string, DBQueryResult & { combinedScore: number; keywordScore?: number }>()
+
+ const maxKeywordScore = keywordResults.length > 0 ? Math.max(...keywordResults.map((r) => r.keyword_score || 0)) : 1