diff options
| author | MohamedBassem <me@mbassem.com> | 2024-03-01 21:01:00 +0000 |
|---|---|---|
| committer | MohamedBassem <me@mbassem.com> | 2024-03-01 22:11:49 +0000 |
| commit | a5434730ede1272f195d6a4b13207b840a5ac2cf (patch) | |
| tree | 14c8a22fbf573b36f16a434349fd3516b38ea539 /packages/shared | |
| parent | 75d315dda4232ee3b89abf054f0b6ee10105ffe3 (diff) | |
| download | karakeep-a5434730ede1272f195d6a4b13207b840a5ac2cf.tar.zst | |
feature: Add full text search support
Diffstat (limited to 'packages/shared')
| -rw-r--r-- | packages/shared/config.ts | 6 | ||||
| -rw-r--r-- | packages/shared/package.json | 1 | ||||
| -rw-r--r-- | packages/shared/queues.ts | 22 | ||||
| -rw-r--r-- | packages/shared/search.ts | 50 |
4 files changed, 79 insertions, 0 deletions
diff --git a/packages/shared/config.ts b/packages/shared/config.ts index 6ca7b89d..1dee4c4d 100644 --- a/packages/shared/config.ts +++ b/packages/shared/config.ts @@ -29,6 +29,12 @@ const serverConfig = { browserExecutablePath: process.env.BROWSER_EXECUTABLE_PATH, // If not set, the system's browser will be used browserUserDataDir: process.env.BROWSER_USER_DATA_DIR, }, + meilisearch: process.env.MEILI_ADDR + ? { + address: process.env.MEILI_ADDR || "http://127.0.0.1:7700", + key: process.env.MEILI_MASTER_KEY || "", + } + : undefined, logLevel: process.env.LOG_LEVEL || "debug", demoMode: (process.env.DEMO_MODE ?? "false") == "true", }; diff --git a/packages/shared/package.json b/packages/shared/package.json index 9f6b5498..0b3a8078 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -4,6 +4,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "meilisearch": "^0.37.0", "winston": "^3.11.0", "zod": "^3.22.4" }, diff --git a/packages/shared/queues.ts b/packages/shared/queues.ts index 0155b1e7..a2cbeceb 100644 --- a/packages/shared/queues.ts +++ b/packages/shared/queues.ts @@ -27,3 +27,25 @@ export type ZOpenAIRequest = z.infer<typeof zOpenAIRequestSchema>; export const OpenAIQueue = new Queue<ZOpenAIRequest, void>("openai_queue", { connection: queueConnectionDetails, }); + +// Search Indexing Worker +export const zSearchIndexingRequestSchema = z.object({ + bookmarkId: z.string(), + type: z.enum(["index", "delete"]), +}); +export type ZSearchIndexingRequest = z.infer< + typeof zSearchIndexingRequestSchema +>; +export const SearchIndexingQueue = new Queue<ZSearchIndexingRequest, void>( + "searching_indexing", + { + connection: queueConnectionDetails, + defaultJobOptions: { + attempts: 5, + backoff: { + type: "exponential", + delay: 1000, + }, + }, + }, +); diff --git a/packages/shared/search.ts b/packages/shared/search.ts new file mode 100644 index 00000000..3bdf1ad1 --- /dev/null +++ b/packages/shared/search.ts @@ -0,0 +1,50 @@ +import { MeiliSearch, Index } from "meilisearch"; +import serverConfig from "./config"; +import { z } from "zod"; + +export const zBookmarkIdxSchema = z.object({ + id: z.string(), + userId: z.string(), + url: z.string().nullish(), + title: z.string().nullish(), + description: z.string().nullish(), + content: z.string().nullish(), + tags: z.array(z.string()).default([]), +}); + +export type ZBookmarkIdx = z.infer<typeof zBookmarkIdxSchema>; + +let searchClient: MeiliSearch | undefined; + +if (serverConfig.meilisearch) { + searchClient = new MeiliSearch({ + host: serverConfig.meilisearch.address, + apiKey: serverConfig.meilisearch.key, + }); +} + +const BOOKMARKS_IDX_NAME = "bookmarks"; + +let idxClient: Index<ZBookmarkIdx> | undefined; + +export async function getSearchIdxClient(): Promise<Index<ZBookmarkIdx> | null> { + if (idxClient) { + return idxClient; + } + if (!searchClient) { + return null; + } + + const indicies = await searchClient.getIndexes(); + let idxFound = indicies.results.find((i) => i.uid == BOOKMARKS_IDX_NAME); + if (!idxFound) { + const idx = await searchClient.createIndex(BOOKMARKS_IDX_NAME, { + primaryKey: "id", + }); + await searchClient.waitForTask(idx.taskUid); + idxFound = await searchClient.getIndex<ZBookmarkIdx>(BOOKMARKS_IDX_NAME); + const taskId = await idxFound.updateFilterableAttributes(["id", "userId"]); + await searchClient.waitForTask(taskId.taskUid); + } + return idxFound; +} |
