diff --git a/src/@types/cozy-client.d.ts b/src/@types/cozy-client.d.ts index a684e0a..81e5f81 100644 --- a/src/@types/cozy-client.d.ts +++ b/src/@types/cozy-client.d.ts @@ -176,10 +176,14 @@ declare module 'cozy-client' { queryDefinition: QueryDefinition, options?: QueryOptions ) => Promise - queryAll: (queryDefinition: QueryDefinition) => Promise + queryAll: ( + queryDefinition: QueryDefinition, + options?: QueryOptions + ) => Promise links: CozyLink[] capabilities: ClientCapabilities registerPlugin: (Plugin: Function, options: unknown) => void + getCollectionFromState: (doctype: string) => unknown } export const createMockClient = (options?: ClientOptions): CozyClient => diff --git a/src/dataproxy/common/DataProxyInterface.ts b/src/dataproxy/common/DataProxyInterface.ts index b674a25..7b6309d 100644 --- a/src/dataproxy/common/DataProxyInterface.ts +++ b/src/dataproxy/common/DataProxyInterface.ts @@ -4,7 +4,7 @@ import type { ClientCapabilities } from 'cozy-client/types/types' export type { SearchIndexes } from '@/search/types' export interface DataProxyWorker { - search: (query: string) => Promise + search: (query: string) => unknown setClient: (clientData: ClientData) => Promise } diff --git a/src/dataproxy/worker/shared-worker.ts b/src/dataproxy/worker/shared-worker.ts index 9bf3460..67b669b 100644 --- a/src/dataproxy/worker/shared-worker.ts +++ b/src/dataproxy/worker/shared-worker.ts @@ -66,7 +66,7 @@ const dataProxy: DataProxyWorker = { updateState() }, - search: async (query: string) => { + search: (query: string) => { if (!client) { throw new Error( 'Client is required to execute a search, please initialize CozyClient' @@ -75,8 +75,11 @@ const dataProxy: DataProxyWorker = { if (!searchEngine) { throw new Error('SearchEngine is not initialized') } - - return searchEngine.search(query) + const startSearchTime = performance.now() + const results = searchEngine.search(query) + const endSearchTime = performance.now() + log.debug(`Search took ${endSearchTime - startSearchTime} ms`) + return results } } diff --git a/src/search/SearchEngine.ts b/src/search/SearchEngine.ts index 2c38e1c..3a802f3 100644 --- a/src/search/SearchEngine.ts +++ b/src/search/SearchEngine.ts @@ -35,7 +35,7 @@ import { isSearchedDoctype } from '@/search/types' -import { shouldKeepFile } from './helpers/normalizeFile' +import { addFilePaths, shouldKeepFile } from './helpers/normalizeFile' const log = Minilog('🗂️ [Indexing]') @@ -254,9 +254,7 @@ class SearchEngine { return this.searchIndexes } - async search(query: string): Promise { - log.debug('[SEARCH] indexes : ', this.searchIndexes) - + search(query: string): SearchResult[] { if (!this.searchIndexes) { // TODO: What if the indexing is running but not finished yet? log.warn('[SEARCH] No search index available') @@ -269,8 +267,9 @@ class SearchEngine { const results = this.limitSearchResults(sortedResults) const normResults: SearchResult[] = [] - for (const res of results) { - const normalizedRes = await normalizeSearchResult(this.client, res, query) + const completedResults = addFilePaths(this.client, results) + for (const res of completedResults) { + const normalizedRes = normalizeSearchResult(this.client, res, query) normResults.push(normalizedRes) } return normResults.filter(res => res.title) @@ -301,12 +300,14 @@ class SearchEngine { limit: FLEXSEARCH_LIMIT, enrich: true }) + const newResults = indexResults.map(res => ({ ...res, doctype: doctype })) searchResults = searchResults.concat(newResults) } + return searchResults } diff --git a/src/search/consts.ts b/src/search/consts.ts index c081d9d..51ac525 100644 --- a/src/search/consts.ts +++ b/src/search/consts.ts @@ -29,6 +29,7 @@ export const CONTACTS_DOCTYPE = 'io.cozy.contacts' export const APPS_DOCTYPE = 'io.cozy.apps' export const TYPE_DIRECTORY = 'directory' +export const TYPE_FILE = 'file' export const ROOT_DIR_ID = 'io.cozy.files.root-dir' export const TRASH_DIR_ID = 'io.cozy.files.trash-dir' export const SHARED_DRIVES_DIR_ID = 'io.cozy.files.shared-drives-dir' diff --git a/src/search/helpers/normalizeFile.ts b/src/search/helpers/normalizeFile.ts index e58abbb..950556a 100644 --- a/src/search/helpers/normalizeFile.ts +++ b/src/search/helpers/normalizeFile.ts @@ -1,17 +1,14 @@ -import CozyClient, { Q } from 'cozy-client' +import CozyClient from 'cozy-client' import { IOCozyFile } from 'cozy-client/types/types' import { FILES_DOCTYPE, TYPE_DIRECTORY, + TYPE_FILE, ROOT_DIR_ID, SHARED_DRIVES_DIR_ID } from '@/search/consts' -import { CozyDoc } from '@/search/types' - -interface FileQueryResult { - data: IOCozyFile -} +import { CozyDoc, isIOCozyFile, RawSearchResult } from '@/search/types' /** * Normalize file for Front usage in component inside @@ -39,25 +36,39 @@ export const normalizeFileWithFolders = ( return { ...file, _type: 'io.cozy.files', path } } -export const normalizeFileWithStore = async ( +export const addFilePaths = ( client: CozyClient, - file: IOCozyFile -): Promise => { - const isDir = file.type === TYPE_DIRECTORY - let path = '' - if (isDir) { - path = file.path ?? '' - } else { - const query = Q(FILES_DOCTYPE).getById(file.dir_id).limitBy(1) - // XXX - Take advantage of cozy-client store to avoid querying database - const { data: parentDir } = (await client.query(query, { - executeFromStore: true, - singleDocData: true - })) as FileQueryResult - const parentPath = parentDir?.path ?? '' - path = `${parentPath}/${file.name}` + results: RawSearchResult[] +): RawSearchResult[] => { + const normResults = [...results] + const filesResults = normResults + .map(res => res.doc) + .filter(doc => isIOCozyFile(doc)) + const files = filesResults.filter(file => file.type === TYPE_FILE) + + if (files.length > 0) { + const dirIds = files.map(file => file.dir_id) + const parentDirs = getDirsFromStore(client, dirIds) + for (const file of files) { + const dir = parentDirs.find(dir => dir._id === file.dir_id) + if (dir) { + const idx = normResults.findIndex(res => res.doc._id === file._id) + normResults[idx].doc.path = dir.path + } + } } - return { ...file, _type: 'io.cozy.files', path } + return normResults +} + +const getDirsFromStore = ( + client: CozyClient, + dirIds: string[] +): IOCozyFile[] => { + // XXX querying from store is surprisingly slow: 100+ ms for 50 docs, while + // this approach takes 2-3ms... It should be investigated in cozy-client + const allFiles = client.getCollectionFromState(FILES_DOCTYPE) as IOCozyFile[] + const dirs = allFiles.filter(file => file.type === TYPE_DIRECTORY) + return dirs.filter(dir => dirIds.includes(dir._id)) } export const shouldKeepFile = (file: IOCozyFile): boolean => { diff --git a/src/search/helpers/normalizeSearchResult.ts b/src/search/helpers/normalizeSearchResult.ts index 094378e..cc2be9f 100644 --- a/src/search/helpers/normalizeSearchResult.ts +++ b/src/search/helpers/normalizeSearchResult.ts @@ -11,14 +11,12 @@ import { SearchResult } from '@/search/types' -import { normalizeFileWithStore } from './normalizeFile' - -export const normalizeSearchResult = async ( +export const normalizeSearchResult = ( client: CozyClient, searchResults: RawSearchResult, query: string -): Promise => { - const doc = await normalizeDoc(client, searchResults.doc) +): SearchResult => { + const doc = searchResults.doc const url = buildOpenURL(client, doc) const type = getSearchResultSlug(doc) const title = getSearchResultTitle(doc) @@ -32,16 +30,6 @@ export const normalizeSearchResult = async ( return normalizedRes } -const normalizeDoc = async ( - client: CozyClient, - doc: CozyDoc -): Promise => { - if (isIOCozyFile(doc)) { - return normalizeFileWithStore(client, doc) - } - return doc -} - const getSearchResultTitle = (doc: CozyDoc): string | null => { if (isIOCozyFile(doc)) { return doc.name