Renamed and reorganized some objets & methods

This commit is contained in:
Simon Cambier
2024-05-26 17:39:31 +02:00
parent 0e9f7d9c20
commit 85b7810d07
7 changed files with 165 additions and 182 deletions

View File

@@ -1,5 +1,5 @@
import { Notice, TFile } from 'obsidian' import { TFile } from 'obsidian'
import type { DocumentRef, IndexedDocument } from './globals' import type { IndexedDocument } from './globals'
import { import {
extractHeadingsFromCache, extractHeadingsFromCache,
getAliasesFromMetadata, getAliasesFromMetadata,
@@ -10,14 +10,12 @@ import {
isFileOffice, isFileOffice,
isFilePDF, isFilePDF,
logDebug, logDebug,
makeMD5,
removeDiacritics, removeDiacritics,
stripMarkdownCharacters, stripMarkdownCharacters,
} from './tools/utils' } from './tools/utils'
import type { CanvasData } from 'obsidian/canvas' import type { CanvasData } from 'obsidian/canvas'
import type MiniSearch from 'minisearch'
import type { AsPlainObject } from 'minisearch'
import type OmnisearchPlugin from './main' import type OmnisearchPlugin from './main'
import { getNonExistingNotes } from './tools/notes'
export class CacheManager { export class CacheManager {
/** /**
@@ -73,8 +71,7 @@ export class CacheManager {
return return
} }
this.nextQueryIsEmpty = false this.nextQueryIsEmpty = false
// TODO: rename const database = this.plugin.database
const database = this.plugin.cache
let history = await database.searchHistory.toArray() let history = await database.searchHistory.toArray()
history = history.filter(s => s.query !== query).reverse() history = history.filter(s => s.query !== query).reverse()
history.unshift({ query }) history.unshift({ query })
@@ -87,7 +84,7 @@ export class CacheManager {
* @returns The search history, in reverse chronological order * @returns The search history, in reverse chronological order
*/ */
public async getSearchHistory(): Promise<ReadonlyArray<string>> { public async getSearchHistory(): Promise<ReadonlyArray<string>> {
const data = (await this.plugin.cache.searchHistory.toArray()) const data = (await this.plugin.database.searchHistory.toArray())
.reverse() .reverse()
.map(o => o.query) .map(o => o.query)
if (this.nextQueryIsEmpty) { if (this.nextQueryIsEmpty) {
@@ -96,101 +93,6 @@ export class CacheManager {
return data return data
} }
public getDocumentsChecksum(documents: IndexedDocument[]): string {
return makeMD5(
JSON.stringify(
documents.sort((a, b) => {
if (a.path < b.path) {
return -1
} else if (a.path > b.path) {
return 1
}
return 0
})
)
)
}
//#region Minisearch
public async getMinisearchCache(): Promise<{
paths: DocumentRef[]
data: AsPlainObject
} | null> {
try {
const cachedIndex = (
await this.plugin.cache.minisearch.toArray()
)[0]
return cachedIndex
} catch (e) {
new Notice(
'Omnisearch - Cache missing or invalid. Some freezes may occur while Omnisearch indexes your vault.'
)
console.error('Omnisearch - Error while loading Minisearch cache')
console.error(e)
return null
}
}
public async writeMinisearchCache(
minisearch: MiniSearch,
indexed: Map<string, number>
): Promise<void> {
const paths = Array.from(indexed).map(([k, v]) => ({ path: k, mtime: v }))
// TODO: rename
const database = this.plugin.cache
await database.minisearch.clear()
await database.minisearch.add({
date: new Date().toISOString(),
paths,
data: minisearch.toJSON(),
})
console.log('Omnisearch - Search cache written')
}
public isFileIndexable(path: string): boolean {
return this.isFilenameIndexable(path) || this.isContentIndexable(path)
}
//#endregion Minisearch
public isContentIndexable(path: string): boolean {
const settings = this.plugin.settings
const hasTextExtractor = !!this.plugin.getTextExtractor()
const canIndexPDF = hasTextExtractor && settings.PDFIndexing
const canIndexImages = hasTextExtractor && settings.imagesIndexing
return (
this.isFilePlaintext(path) ||
isFileCanvas(path) ||
isFileFromDataloomPlugin(path) ||
(canIndexPDF && isFilePDF(path)) ||
(canIndexImages && isFileImage(path))
)
}
public isFilenameIndexable(path: string): boolean {
return (
this.canIndexUnsupportedFiles() ||
this.isFilePlaintext(path) ||
isFileCanvas(path) ||
isFileFromDataloomPlugin(path)
)
}
public canIndexUnsupportedFiles(): boolean {
return (
this.plugin.settings.unsupportedFilesIndexing === 'yes' ||
(this.plugin.settings.unsupportedFilesIndexing === 'default' &&
!!this.plugin.app.vault.getConfig('showUnsupportedFiles'))
)
}
private isFilePlaintext(path: string): boolean {
return [...this.plugin.settings.indexedFileTypes, 'md'].some(t =>
path.endsWith(`.${t}`)
)
}
/** /**
* This function is responsible for extracting the text from a file and * This function is responsible for extracting the text from a file and
* returning it as an `IndexedDocument` object. * returning it as an `IndexedDocument` object.
@@ -209,7 +111,7 @@ export class CacheManager {
// ** Plain text ** // ** Plain text **
// Just read the file content // Just read the file content
if (this.isFilePlaintext(path)) { if (this.plugin.notesIndexer.isFilePlaintext(path)) {
content = await app.vault.cachedRead(file) content = await app.vault.cachedRead(file)
} }
@@ -283,7 +185,7 @@ export class CacheManager {
} }
// ** Unsupported files ** // ** Unsupported files **
else if (this.isFilenameIndexable(path)) { else if (this.plugin.notesIndexer.isFilenameIndexable(path)) {
content = file.path content = file.path
} }
@@ -297,10 +199,9 @@ export class CacheManager {
// Look for links that lead to non-existing files, // Look for links that lead to non-existing files,
// and add them to the index. // and add them to the index.
if (metadata) { if (metadata) {
// // FIXME: https://github.com/scambier/obsidian-omnisearch/issues/129 // const nonExisting = getNonExistingNotes(this.plugin.app, file, metadata)
// const nonExisting = getNonExistingNotes(file, metadata)
// for (const name of nonExisting.filter( // for (const name of nonExisting.filter(
// o => !cacheManager.getLiveDocument(o) // o => !this.getLiveDocument(o)
// )) { // )) {
// NotesIndex.addNonExistingToIndex(name, file.path) // NotesIndex.addNonExistingToIndex(name, file.path)
// } // }

View File

@@ -1,10 +1,11 @@
import Dexie from 'dexie' import Dexie from 'dexie'
import type MiniSearch from 'minisearch'
import type { AsPlainObject } from 'minisearch' import type { AsPlainObject } from 'minisearch'
import type { DocumentRef } from './globals' import type { DocumentRef } from './globals'
import { Notice } from 'obsidian' import { Notice } from 'obsidian'
import type OmnisearchPlugin from './main' import type OmnisearchPlugin from './main'
export class OmnisearchCache extends Dexie { export class Database extends Dexie {
public static readonly dbVersion = 8 public static readonly dbVersion = 8
searchHistory!: Dexie.Table<{ id?: number; query: string }, number> searchHistory!: Dexie.Table<{ id?: number; query: string }, number>
minisearch!: Dexie.Table< minisearch!: Dexie.Table<
@@ -17,29 +18,61 @@ export class OmnisearchCache extends Dexie {
> >
constructor(private plugin: OmnisearchPlugin) { constructor(private plugin: OmnisearchPlugin) {
super(OmnisearchCache.getDbName(plugin.app.appId)) super(Database.getDbName(plugin.app.appId))
// Database structure // Database structure
this.version(OmnisearchCache.dbVersion).stores({ this.version(Database.dbVersion).stores({
searchHistory: '++id', searchHistory: '++id',
minisearch: 'date', minisearch: 'date',
}) })
} }
public static getDbName(appId: string) { private static getDbName(appId: string) {
return 'omnisearch/cache/' + appId return 'omnisearch/cache/' + appId
} }
//#endregion Table declarations //#endregion Table declarations
public async getMinisearchCache(): Promise<{
paths: DocumentRef[]
data: AsPlainObject
} | null> {
try {
const cachedIndex = (await this.plugin.database.minisearch.toArray())[0]
return cachedIndex
} catch (e) {
new Notice(
'Omnisearch - Cache missing or invalid. Some freezes may occur while Omnisearch indexes your vault.'
)
console.error('Omnisearch - Error while loading Minisearch cache')
console.error(e)
return null
}
}
public async writeMinisearchCache(
minisearch: MiniSearch,
indexed: Map<string, number>
): Promise<void> {
const paths = Array.from(indexed).map(([k, v]) => ({ path: k, mtime: v }))
const database = this.plugin.database
await database.minisearch.clear()
await database.minisearch.add({
date: new Date().toISOString(),
paths,
data: minisearch.toJSON(),
})
console.log('Omnisearch - Search cache written')
}
/** /**
* Deletes Omnisearch databases that have an older version than the current one * Deletes Omnisearch databases that have an older version than the current one
*/ */
public async clearOldDatabases(): Promise<void> { public async clearOldDatabases(): Promise<void> {
const toDelete = (await indexedDB.databases()).filter( const toDelete = (await indexedDB.databases()).filter(
db => db =>
db.name === OmnisearchCache.getDbName(this.plugin.app.appId) && db.name === Database.getDbName(this.plugin.app.appId) &&
// version multiplied by 10 https://github.com/dexie/Dexie.js/issues/59 // version multiplied by 10 https://github.com/dexie/Dexie.js/issues/59
db.version !== OmnisearchCache.dbVersion * 10 db.version !== Database.dbVersion * 10
) )
if (toDelete.length) { if (toDelete.length) {
console.log('Omnisearch - Those IndexedDb databases will be deleted:') console.log('Omnisearch - Those IndexedDb databases will be deleted:')

View File

@@ -15,11 +15,11 @@ import {
} from './settings' } from './settings'
import { eventBus, EventNames, indexingStep, IndexingStepType, type TextExtractorApi } from './globals' import { eventBus, EventNames, indexingStep, IndexingStepType, type TextExtractorApi } from './globals'
import { notifyOnIndexed, registerAPI } from './tools/api' import { notifyOnIndexed, registerAPI } from './tools/api'
import { OmnisearchCache } from './database' import { Database } from './database'
import { SearchEngine } from './search/search-engine' import { SearchEngine } from './search/search-engine'
import { CacheManager } from './cache-manager' import { CacheManager } from './cache-manager'
import { logDebug } from './tools/utils' import { logDebug } from './tools/utils'
import { NotesIndexer } from './notes-index' import { NotesIndexer } from './notes-indexer'
import { TextProcessor } from "./tools/text-processing"; import { TextProcessor } from "./tools/text-processing";
export default class OmnisearchPlugin extends Plugin { export default class OmnisearchPlugin extends Plugin {
@@ -29,7 +29,7 @@ export default class OmnisearchPlugin extends Plugin {
// FIXME: merge cache and cacheManager, or find other names // FIXME: merge cache and cacheManager, or find other names
public readonly cacheManager: CacheManager public readonly cacheManager: CacheManager
public readonly cache = new OmnisearchCache(this) public readonly database = new Database(this)
public readonly notesIndexer = new NotesIndexer(this) public readonly notesIndexer = new NotesIndexer(this)
public readonly textProcessor = new TextProcessor(this) public readonly textProcessor = new TextProcessor(this)
@@ -58,7 +58,7 @@ export default class OmnisearchPlugin extends Plugin {
} }
await cleanOldCacheFiles(this.app) await cleanOldCacheFiles(this.app)
await this.cache.clearOldDatabases() await this.database.clearOldDatabases()
registerAPI(this) registerAPI(this)
@@ -98,7 +98,7 @@ export default class OmnisearchPlugin extends Plugin {
// Listeners to keep the search index up-to-date // Listeners to keep the search index up-to-date
this.registerEvent( this.registerEvent(
this.app.vault.on('create', file => { this.app.vault.on('create', file => {
if (this.cacheManager.isFileIndexable(file.path)) { if (this.notesIndexer.isFileIndexable(file.path)) {
logDebug('Indexing new file', file.path) logDebug('Indexing new file', file.path)
// await cacheManager.addToLiveCache(file.path) // await cacheManager.addToLiveCache(file.path)
searchEngine.addFromPaths([file.path]) searchEngine.addFromPaths([file.path])
@@ -114,7 +114,7 @@ export default class OmnisearchPlugin extends Plugin {
) )
this.registerEvent( this.registerEvent(
this.app.vault.on('modify', async file => { this.app.vault.on('modify', async file => {
if (this.cacheManager.isFileIndexable(file.path)) { if (this.notesIndexer.isFileIndexable(file.path)) {
logDebug('Updating file', file.path) logDebug('Updating file', file.path)
await this.cacheManager.addToLiveCache(file.path) await this.cacheManager.addToLiveCache(file.path)
this.notesIndexer.markNoteForReindex(file) this.notesIndexer.markNoteForReindex(file)
@@ -123,7 +123,7 @@ export default class OmnisearchPlugin extends Plugin {
) )
this.registerEvent( this.registerEvent(
this.app.vault.on('rename', async (file, oldPath) => { this.app.vault.on('rename', async (file, oldPath) => {
if (this.cacheManager.isFileIndexable(file.path)) { if (this.notesIndexer.isFileIndexable(file.path)) {
logDebug('Renaming file', file.path) logDebug('Renaming file', file.path)
this.cacheManager.removeFromLiveCache(oldPath) this.cacheManager.removeFromLiveCache(oldPath)
await this.cacheManager.addToLiveCache(file.path) await this.cacheManager.addToLiveCache(file.path)
@@ -161,7 +161,7 @@ export default class OmnisearchPlugin extends Plugin {
// Clear cache when disabling Omnisearch // Clear cache when disabling Omnisearch
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
await this.cache.clearCache() await this.database.clearCache()
} }
this.apiHttpServer.close() this.apiHttpServer.close()
} }
@@ -199,7 +199,7 @@ export default class OmnisearchPlugin extends Plugin {
indexingStep.set(IndexingStepType.ReadingFiles) indexingStep.set(IndexingStepType.ReadingFiles)
const files = this.app.vault const files = this.app.vault
.getFiles() .getFiles()
.filter(f => this.cacheManager.isFileIndexable(f.path)) .filter(f => this.notesIndexer.isFileIndexable(f.path))
console.log(`Omnisearch - ${files.length} files total`) console.log(`Omnisearch - ${files.length} files total`)
console.log( console.log(
`Omnisearch - Cache is ${isCacheEnabled() ? 'enabled' : 'disabled'}` `Omnisearch - Cache is ${isCacheEnabled() ? 'enabled' : 'disabled'}`

View File

@@ -1,55 +0,0 @@
import type { TAbstractFile } from 'obsidian'
import type OmnisearchPlugin from './main'
export class NotesIndexer {
private notesToReindex = new Set<TAbstractFile>()
constructor(private plugin: OmnisearchPlugin) {}
/**
* Updated notes are not reindexed immediately for performance reasons.
* They're added to a list, and reindex is done the next time we open Omnisearch.
*/
public markNoteForReindex(note: TAbstractFile): void {
this.notesToReindex.add(note)
}
public async refreshIndex(): Promise<void> {
const paths = [...this.notesToReindex].map(n => n.path)
if (paths.length) {
const searchEngine = this.plugin.searchEngine
searchEngine.removeFromPaths(paths)
await searchEngine.addFromPaths(paths)
this.notesToReindex.clear()
// console.log(`Omnisearch - Reindexed ${paths.length} file(s)`)
}
}
}
// /**
// * Index a non-existing note.
// * Useful to find internal links that lead (yet) to nowhere
// * @param name
// * @param parent The note referencing the
// */
// export function addNonExistingToIndex(name: string, parent: string): void {
// name = removeAnchors(name)
// const filename = name + (name.endsWith('.md') ? '' : '.md')
//
// const note: IndexedDocument = {
// path: filename,
// basename: name,
// mtime: 0,
//
// content: '',
// tags: [],
// aliases: '',
// headings1: '',
// headings2: '',
// headings3: '',
//
// doesNotExist: true,
// parent,
// }
// // searchEngine.addDocuments([note])
// }

105
src/notes-indexer.ts Normal file
View File

@@ -0,0 +1,105 @@
import type { TAbstractFile } from 'obsidian'
import type OmnisearchPlugin from './main'
import { removeAnchors } from './tools/notes'
import type { IndexedDocument } from './globals'
import {
isFileCanvas,
isFileFromDataloomPlugin,
isFileImage,
isFilePDF,
} from './tools/utils'
export class NotesIndexer {
private notesToReindex = new Set<TAbstractFile>()
constructor(private plugin: OmnisearchPlugin) {}
/**
* Updated notes are not reindexed immediately for performance reasons.
* They're added to a list, and reindex is done the next time we open Omnisearch.
*/
public markNoteForReindex(note: TAbstractFile): void {
this.notesToReindex.add(note)
}
public async refreshIndex(): Promise<void> {
const paths = [...this.notesToReindex].map(n => n.path)
if (paths.length) {
const searchEngine = this.plugin.searchEngine
searchEngine.removeFromPaths(paths)
await searchEngine.addFromPaths(paths)
this.notesToReindex.clear()
}
}
public isFileIndexable(path: string): boolean {
return this.isFilenameIndexable(path) || this.isContentIndexable(path)
}
public isContentIndexable(path: string): boolean {
const settings = this.plugin.settings
const hasTextExtractor = !!this.plugin.getTextExtractor()
const canIndexPDF = hasTextExtractor && settings.PDFIndexing
const canIndexImages = hasTextExtractor && settings.imagesIndexing
return (
this.isFilePlaintext(path) ||
isFileCanvas(path) ||
isFileFromDataloomPlugin(path) ||
(canIndexPDF && isFilePDF(path)) ||
(canIndexImages && isFileImage(path))
)
}
public isFilenameIndexable(path: string): boolean {
return (
this.canIndexUnsupportedFiles() ||
this.isFilePlaintext(path) ||
isFileCanvas(path) ||
isFileFromDataloomPlugin(path)
)
}
public canIndexUnsupportedFiles(): boolean {
return (
this.plugin.settings.unsupportedFilesIndexing === 'yes' ||
(this.plugin.settings.unsupportedFilesIndexing === 'default' &&
!!this.plugin.app.vault.getConfig('showUnsupportedFiles'))
)
}
/**
* Index a non-existing note.
* Useful to find internal links that lead (yet) to nowhere
* @param name
* @param parent The note referencing the
*/
public addNonExistingToIndex(name: string, parent: string): void {
name = removeAnchors(name)
const filename = name + (name.endsWith('.md') ? '' : '.md')
const note: IndexedDocument = {
path: filename,
basename: name,
mtime: 0,
content: '',
cleanedContent: '',
tags: [],
unmarkedTags: [],
aliases: '',
headings1: '',
headings2: '',
headings3: '',
doesNotExist: true,
parent,
}
// searchEngine.addDocuments([note])
}
public isFilePlaintext(path: string): boolean {
return [...this.plugin.settings.indexedFileTypes, 'md'].some(t =>
path.endsWith(`.${t}`)
)
}
}

View File

@@ -25,7 +25,7 @@ export class SearchEngine {
* Return true if the cache is valid * Return true if the cache is valid
*/ */
async loadCache(): Promise<boolean> { async loadCache(): Promise<boolean> {
const cache = await this.plugin.cacheManager.getMinisearchCache() const cache = await this.plugin.database.getMinisearchCache()
if (cache) { if (cache) {
this.minisearch = await MiniSearch.loadJSAsync( this.minisearch = await MiniSearch.loadJSAsync(
cache.data, cache.data,
@@ -414,7 +414,7 @@ export class SearchEngine {
} }
public async writeToCache(): Promise<void> { public async writeToCache(): Promise<void> {
await this.plugin.cacheManager.writeMinisearchCache( await this.plugin.database.writeMinisearchCache(
this.minisearch, this.minisearch,
this.indexedDocuments this.indexedDocuments
) )

View File

@@ -93,8 +93,7 @@ export class SettingsTab extends PluginSettingTab {
display(): void { display(): void {
const { containerEl } = this const { containerEl } = this
// TODO: rename const database = this.plugin.database
const database = this.plugin.cache
const textExtractor = this.plugin.getTextExtractor() const textExtractor = this.plugin.getTextExtractor()
containerEl.empty() containerEl.empty()