aboutsummaryrefslogtreecommitdiffstats
path: root/packages/shared
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2024-03-01 21:01:00 +0000
committerMohamedBassem <me@mbassem.com>2024-03-01 22:11:49 +0000
commita5434730ede1272f195d6a4b13207b840a5ac2cf (patch)
tree14c8a22fbf573b36f16a434349fd3516b38ea539 /packages/shared
parent75d315dda4232ee3b89abf054f0b6ee10105ffe3 (diff)
downloadkarakeep-a5434730ede1272f195d6a4b13207b840a5ac2cf.tar.zst
feature: Add full text search support
Diffstat (limited to 'packages/shared')
-rw-r--r--packages/shared/config.ts6
-rw-r--r--packages/shared/package.json1
-rw-r--r--packages/shared/queues.ts22
-rw-r--r--packages/shared/search.ts50
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;
+}