Feature/172 recency (#439)
* Fixed ts errors * #172 - First draft at recency boost
This commit is contained in:
@@ -98,9 +98,9 @@
|
||||
},
|
||||
}
|
||||
}
|
||||
let elFolderPathIcon: HTMLElement
|
||||
let elFilePathIcon: HTMLElement
|
||||
let elEmbedIcon: HTMLElement
|
||||
let elFolderPathIcon: HTMLElement | null = null
|
||||
let elFilePathIcon: HTMLElement | null = null
|
||||
let elEmbedIcon: HTMLElement | null = null
|
||||
|
||||
$: {
|
||||
imagePath = null
|
||||
|
||||
@@ -41,6 +41,13 @@ export const enum Action {
|
||||
OpenInNewLeaf = 'open-in-new-leaf',
|
||||
}
|
||||
|
||||
export const enum RecencyCutoff {
|
||||
Disabled = '0',
|
||||
Day = '1',
|
||||
Week = '2',
|
||||
Month = '3',
|
||||
}
|
||||
|
||||
export type DocumentRef = { path: string; mtime: number }
|
||||
|
||||
export type IndexedDocument = {
|
||||
@@ -100,8 +107,8 @@ export type TextExtractorApi = {
|
||||
}
|
||||
|
||||
export type AIImageAnalyzerAPI = {
|
||||
analyzeImage: (file: TFile) => Promise<string>;
|
||||
canBeAnalyzed: (file: TFile) => boolean;
|
||||
analyzeImage: (file: TFile) => Promise<string>
|
||||
canBeAnalyzed: (file: TFile) => boolean
|
||||
}
|
||||
|
||||
export const SEPARATORS =
|
||||
|
||||
@@ -3,7 +3,12 @@ import MiniSearch, {
|
||||
type Options,
|
||||
type SearchResult,
|
||||
} from 'minisearch'
|
||||
import type { DocumentRef, IndexedDocument, ResultNote } from '../globals'
|
||||
import {
|
||||
RecencyCutoff,
|
||||
type DocumentRef,
|
||||
type IndexedDocument,
|
||||
type ResultNote,
|
||||
} from '../globals'
|
||||
|
||||
import { chunkArray, logVerbose, removeDiacritics } from '../tools/utils'
|
||||
import { Notice } from 'obsidian'
|
||||
@@ -171,6 +176,25 @@ export class SearchEngine {
|
||||
},
|
||||
// The query is already tokenized, don't tokenize again
|
||||
tokenize: text => [text],
|
||||
boostDocument(_id, _term, storedFields) {
|
||||
if (
|
||||
!storedFields?.mtime ||
|
||||
settings.recencyBoost === RecencyCutoff.Disabled
|
||||
) {
|
||||
return 1
|
||||
}
|
||||
const mtime = storedFields?.mtime as number
|
||||
const now = new Date().valueOf()
|
||||
const daysElapsed = (now - mtime) / (24 * 3600)
|
||||
|
||||
// Documents boost
|
||||
const cutoff = {
|
||||
[RecencyCutoff.Day]: -3,
|
||||
[RecencyCutoff.Week]: -0.3,
|
||||
[RecencyCutoff.Month]: -0.1,
|
||||
} as const
|
||||
return 1 + Math.exp(cutoff[settings.recencyBoost] * daysElapsed)
|
||||
},
|
||||
})
|
||||
|
||||
logVerbose(`Found ${results.length} results`, results)
|
||||
@@ -300,7 +324,8 @@ export class SearchEngine {
|
||||
|
||||
const documents = await Promise.all(
|
||||
results.map(
|
||||
async result => await this.plugin.documentsRepository.getDocument(result.id)
|
||||
async result =>
|
||||
await this.plugin.documentsRepository.getDocument(result.id)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -376,7 +401,8 @@ export class SearchEngine {
|
||||
|
||||
const documents = await Promise.all(
|
||||
results.map(
|
||||
async result => await this.plugin.documentsRepository.getDocument(result.id)
|
||||
async result =>
|
||||
await this.plugin.documentsRepository.getDocument(result.id)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -503,7 +529,7 @@ export class SearchEngine {
|
||||
'headings2',
|
||||
'headings3',
|
||||
],
|
||||
storeFields: ['tags'],
|
||||
storeFields: ['tags', 'mtime'],
|
||||
logger(_level, _message, code) {
|
||||
if (code === 'version_conflict') {
|
||||
new Notice(
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
SliderComponent,
|
||||
} from 'obsidian'
|
||||
import { writable } from 'svelte/store'
|
||||
import { K_DISABLE_OMNISEARCH } from './globals'
|
||||
import { K_DISABLE_OMNISEARCH, RecencyCutoff } from './globals'
|
||||
import type OmnisearchPlugin from './main'
|
||||
import { enableVerboseLogging, getCtrlKeyLabel } from './tools/utils'
|
||||
import { debounce } from 'lodash-es'
|
||||
@@ -29,6 +29,8 @@ export interface OmnisearchSettings extends WeightingSettings {
|
||||
useCache: boolean
|
||||
/** Respect the "excluded files" Obsidian setting by downranking results ignored files */
|
||||
hideExcluded: boolean
|
||||
/** Boost more recent files */
|
||||
recencyBoost: RecencyCutoff
|
||||
/** downrank files in the given folders */
|
||||
downrankedFoldersFilters: string[]
|
||||
/** Ignore diacritics when indexing files */
|
||||
@@ -317,6 +319,24 @@ export class SettingsTab extends PluginSettingTab {
|
||||
})
|
||||
)
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Recency boost (experimental)')
|
||||
.setDesc('Files that have been modified more recently than [selected cutoff] are given a higher rank.')
|
||||
.addDropdown(dropdown =>
|
||||
dropdown
|
||||
.addOptions({
|
||||
[RecencyCutoff.Disabled]: 'Disabled',
|
||||
[RecencyCutoff.Day]: '24 hours',
|
||||
[RecencyCutoff.Week]: '7 days',
|
||||
[RecencyCutoff.Month]: '30 days',
|
||||
})
|
||||
.setValue(settings.recencyBoost)
|
||||
.onChange(async v => {
|
||||
settings.recencyBoost = v as RecencyCutoff
|
||||
await saveSettings(this.plugin)
|
||||
})
|
||||
)
|
||||
|
||||
// Downranked files
|
||||
new Setting(containerEl)
|
||||
.setName('Folders to downrank in search results')
|
||||
@@ -398,7 +418,9 @@ export class SettingsTab extends PluginSettingTab {
|
||||
// Set Vim like navigation keys
|
||||
new Setting(containerEl)
|
||||
.setName('Set Vim like navigation keys')
|
||||
.setDesc(`Navigate down the results with ${getCtrlKeyLabel()} + J/N, or navigate up with ${getCtrlKeyLabel()} + K/P.`)
|
||||
.setDesc(
|
||||
`Navigate down the results with ${getCtrlKeyLabel()} + J/N, or navigate up with ${getCtrlKeyLabel()} + K/P.`
|
||||
)
|
||||
.addToggle(toggle =>
|
||||
toggle
|
||||
.setValue(settings.vimLikeNavigationShortcut)
|
||||
@@ -793,6 +815,7 @@ export function getDefaultSettings(app: App): OmnisearchSettings {
|
||||
return {
|
||||
useCache: true,
|
||||
hideExcluded: false,
|
||||
recencyBoost: RecencyCutoff.Disabled,
|
||||
downrankedFoldersFilters: [] as string[],
|
||||
ignoreDiacritics: true,
|
||||
ignoreArabicDiacritics: false,
|
||||
|
||||
Reference in New Issue
Block a user