## 概要
[[🦉Various Complements]]の[[Intelligent suggestion prioritization]]に表題の対応を行う。
## Issue
https://github.com/tadashi-aikawa/obsidian-various-complements-plugin/issues/131
## 事前調査
`suggester.ts`のsortロジックについて
```ts
const candidate = Array.from(words)
.map((x) => judge(x, query, queryStartWithUpper))
.filter((x) => x.value !== undefined)
.sort((a, b) => {
const aWord = a.word as HitWord;
const bWord = b.word as HitWord;
const notSameWordType = aWord.type !== bWord.type;
if (frontMatter && notSameWordType) {
return bWord.type === "frontMatter" ? 1 : -1;
}
if (selectionHistoryStorage) {
const ret = selectionHistoryStorage.compare(
aWord as HitWord,
bWord as HitWord
);
if (ret !== 0) {
return ret;
}
}
if (a.value!.length !== b.value!.length) {
return a.value!.length > b.value!.length ? 1 : -1;
}
if (notSameWordType) {
return WordTypeMeta.of(bWord.type).priority >
WordTypeMeta.of(aWord.type).priority
? 1
: -1;
}
if (a.alias !== b.alias) {
return a.alias ? 1 : -1;
}
return 0;
})
.map((x) => x.word)
.slice(0, max);
```
[[フロントマター]]は優先していいので、手を加えるのは下記の部分。
```ts
if (selectionHistoryStorage) {
const ret = selectionHistoryStorage.compare(
aWord as HitWord,
bWord as HitWord
);
if (ret !== 0) {
return ret;
}
}
```
Historyを取得してScoreを計算する流れ。
```ts
compare(w1: HitWord, w2: HitWord): -1 | 0 | 1 {
const score1 = calcScore(this.getSelectionHistory(w1));
const score2 = calcScore(this.getSelectionHistory(w2));
if (score1 === score2) {
return 0;
}
return score1 > score2 ? -1 : 1;
}
```
wordから
- ヒットした値は何か? (タグやエイリアスになることも)
- 値は何か? (ファイル名)
- typeは何か? (`currentFile`や`internalLink`など)
をもとに履歴を取得する。
```ts
getSelectionHistory(word: HitWord): SelectionHistory | undefined {
return this.data[word.hit]?.[word.value]?.[word.type];
}
```
選択された回数 x 調整値によってスコアが計算される。
```ts
function calcScore(history: SelectionHistory | undefined): number {
if (!history) {
return 0;
}
const behind = Date.now() - history.lastUpdated;
// noinspection IfStatementWithTooManyBranchesJS
if (behind < MIN) {
return 8 * history.count;
} else if (behind < HOUR) {
return 4 * history.count;
} else if (behind < DAY) {
return 2 * history.count;
} else if (behind < WEEK) {
return 0.5 * history.count;
} else {
return 0.25 * history.count;
}
}
```
## 実装方針
パフォーマンスが落ちないことを考慮して対応する。
- `SelectionHistory`に最後に選択したものかどうかの情報を保持し、その場合はint maxを返す
- メリット
- 現在のフローがそのまま使える
- デメリット
- 最後の選択が入れ替わったときに、変更前のフラグを変更するのが面倒そう
- historyを捜査してlastUseをfalseにする必要あり (パフォーマンスが少し落ちる)
- `SelectionHistory`とは別に、最後に選択した情報を記録し、それに一致する場合はint max返す
- メリット
- 現在のフローがそのまま使え、パフォーマンスも落ちない
- デメリット
- 最後の挿入時間を保管し、`calcScore`や`compare`から参照させなければならない
**- 同じ `SelectionHistoryStorage` だからスムーズにいけそう** ✨
- すべての候補を取得したあと、最もlastUpdatedが新しいhistoryを持っていた候補を一番上釣り上げる
- メリット
- 新しいデータを持つ必要がない
- デメリット
- 現在のソートが終わった後に、もう一度一巡する必要がある (パフォーマンスが落ちる)
`version`が使えそう。
```ts
export class SelectionHistoryStorage {
data: SelectionHistoryTree;
version: number;
persistedVersion: number;
```
いや... これだと最後と一致しなければダメだから無理だ... あくまでも
**候補の中で最も新しいもの**
でなければいけない...。となると
- すべての候補を取得したあと、最もlastUpdatedが新しいhistoryを持っていた候補を一番上釣り上げる
でないとダメ。
## パフォーマンス
```ts
const start = performance.now();
const latestUpdated = max(
filteredJudgement.map(
(x) =>
selectionHistoryStorage?.getSelectionHistory(x.word as HitWord)
?.lastUpdated ?? 0
),
0
);
console.log(performance.now() - start);
```
`0.1`だったので0.1msだから大丈夫そうな気がする...。