aboutsummaryrefslogtreecommitdiffstats
path: root/packages/trpc/lib/search.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/trpc/lib/search.ts')
-rw-r--r--packages/trpc/lib/search.ts110
1 files changed, 63 insertions, 47 deletions
diff --git a/packages/trpc/lib/search.ts b/packages/trpc/lib/search.ts
index 7bb78a01..d4130798 100644
--- a/packages/trpc/lib/search.ts
+++ b/packages/trpc/lib/search.ts
@@ -4,21 +4,21 @@ import {
exists,
gt,
gte,
- inArray,
isNotNull,
like,
lt,
lte,
ne,
notExists,
- notInArray,
notLike,
} from "drizzle-orm";
import {
bookmarkAssets,
bookmarkLinks,
+ bookmarkLists,
bookmarks,
+ bookmarksInLists,
bookmarkTags,
rssFeedImportsTable,
rssFeedsTable,
@@ -28,7 +28,6 @@ import { Matcher } from "@karakeep/shared/types/search";
import { toAbsoluteDate } from "@karakeep/shared/utils/relativeDateUtils";
import { AuthedContext } from "..";
-import { List } from "../models/lists";
interface BookmarkQueryReturnType {
id: string;
@@ -88,20 +87,21 @@ function union(vals: BookmarkQueryReturnType[][]): BookmarkQueryReturnType[] {
}
async function getIds(
- ctx: AuthedContext,
+ db: AuthedContext["db"],
+ userId: string,
matcher: Matcher,
): Promise<BookmarkQueryReturnType[]> {
switch (matcher.type) {
case "tagName": {
const comp = matcher.inverse ? notExists : exists;
- return ctx.db
+ return db
.selectDistinct({ id: bookmarks.id })
.from(bookmarks)
.where(
and(
- eq(bookmarks.userId, ctx.user.id),
+ eq(bookmarks.userId, userId),
comp(
- ctx.db
+ db
.select()
.from(tagsOnBookmarks)
.innerJoin(
@@ -111,7 +111,7 @@ async function getIds(
.where(
and(
eq(tagsOnBookmarks.bookmarkId, bookmarks.id),
- eq(bookmarkTags.userId, ctx.user.id),
+ eq(bookmarkTags.userId, userId),
eq(bookmarkTags.name, matcher.tagName),
),
),
@@ -121,14 +121,14 @@ async function getIds(
}
case "tagged": {
const comp = matcher.tagged ? exists : notExists;
- return ctx.db
+ return db
.select({ id: bookmarks.id })
.from(bookmarks)
.where(
and(
- eq(bookmarks.userId, ctx.user.id),
+ eq(bookmarks.userId, userId),
comp(
- ctx.db
+ db
.select()
.from(tagsOnBookmarks)
.where(and(eq(tagsOnBookmarks.bookmarkId, bookmarks.id))),
@@ -137,43 +137,59 @@ async function getIds(
);
}
case "listName": {
- const lists = await List.fromName(ctx, matcher.listName);
- const ids = await Promise.all(lists.map((l) => l.getBookmarkIds()));
- const comp = matcher.inverse ? notInArray : inArray;
- return ctx.db
- .select({ id: bookmarks.id })
+ const comp = matcher.inverse ? notExists : exists;
+ return db
+ .selectDistinct({ id: bookmarks.id })
.from(bookmarks)
.where(
and(
- eq(bookmarks.userId, ctx.user.id),
- comp(bookmarks.id, ids.flat()),
+ eq(bookmarks.userId, userId),
+ comp(
+ db
+ .select()
+ .from(bookmarksInLists)
+ .innerJoin(
+ bookmarkLists,
+ eq(bookmarksInLists.listId, bookmarkLists.id),
+ )
+ .where(
+ and(
+ eq(bookmarksInLists.bookmarkId, bookmarks.id),
+ eq(bookmarkLists.userId, userId),
+ eq(bookmarkLists.name, matcher.listName),
+ ),
+ ),
+ ),
),
);
}
case "inlist": {
- const lists = await List.getAll(ctx);
- const ids = await Promise.all(lists.map((l) => l.getBookmarkIds()));
- const comp = matcher.inList ? inArray : notInArray;
- return ctx.db
+ const comp = matcher.inList ? exists : notExists;
+ return db
.select({ id: bookmarks.id })
.from(bookmarks)
.where(
and(
- eq(bookmarks.userId, ctx.user.id),
- comp(bookmarks.id, ids.flat()),
+ eq(bookmarks.userId, userId),
+ comp(
+ db
+ .select()
+ .from(bookmarksInLists)
+ .where(and(eq(bookmarksInLists.bookmarkId, bookmarks.id))),
+ ),
),
);
}
case "rssFeedName": {
const comp = matcher.inverse ? notExists : exists;
- return ctx.db
+ return db
.selectDistinct({ id: bookmarks.id })
.from(bookmarks)
.where(
and(
- eq(bookmarks.userId, ctx.user.id),
+ eq(bookmarks.userId, userId),
comp(
- ctx.db
+ db
.select()
.from(rssFeedImportsTable)
.innerJoin(
@@ -183,7 +199,7 @@ async function getIds(
.where(
and(
eq(rssFeedImportsTable.bookmarkId, bookmarks.id),
- eq(rssFeedsTable.userId, ctx.user.id),
+ eq(rssFeedsTable.userId, userId),
eq(rssFeedsTable.name, matcher.feedName),
),
),
@@ -192,36 +208,36 @@ async function getIds(
);
}
case "archived": {
- return ctx.db
+ return db
.select({ id: bookmarks.id })
.from(bookmarks)
.where(
and(
- eq(bookmarks.userId, ctx.user.id),
+ eq(bookmarks.userId, userId),
eq(bookmarks.archived, matcher.archived),
),
);
}
case "url": {
const comp = matcher.inverse ? notLike : like;
- return ctx.db
+ return db
.select({ id: bookmarkLinks.id })
.from(bookmarkLinks)
.leftJoin(bookmarks, eq(bookmarks.id, bookmarkLinks.id))
.where(
and(
- eq(bookmarks.userId, ctx.user.id),
+ eq(bookmarks.userId, userId),
comp(bookmarkLinks.url, `%${matcher.url}%`),
),
)
.union(
- ctx.db
+ db
.select({ id: bookmarkAssets.id })
.from(bookmarkAssets)
.leftJoin(bookmarks, eq(bookmarks.id, bookmarkAssets.id))
.where(
and(
- eq(bookmarks.userId, ctx.user.id),
+ eq(bookmarks.userId, userId),
// When a user is asking for a link, the inverse matcher should match only assets with URLs.
isNotNull(bookmarkAssets.sourceUrl),
comp(bookmarkAssets.sourceUrl, `%${matcher.url}%`),
@@ -230,73 +246,73 @@ async function getIds(
);
}
case "favourited": {
- return ctx.db
+ return db
.select({ id: bookmarks.id })
.from(bookmarks)
.where(
and(
- eq(bookmarks.userId, ctx.user.id),
+ eq(bookmarks.userId, userId),
eq(bookmarks.favourited, matcher.favourited),
),
);
}
case "dateAfter": {
const comp = matcher.inverse ? lt : gte;
- return ctx.db
+ return db
.select({ id: bookmarks.id })
.from(bookmarks)
.where(
and(
- eq(bookmarks.userId, ctx.user.id),
+ eq(bookmarks.userId, userId),
comp(bookmarks.createdAt, matcher.dateAfter),
),
);
}
case "dateBefore": {
const comp = matcher.inverse ? gt : lte;
- return ctx.db
+ return db
.select({ id: bookmarks.id })
.from(bookmarks)
.where(
and(
- eq(bookmarks.userId, ctx.user.id),
+ eq(bookmarks.userId, userId),
comp(bookmarks.createdAt, matcher.dateBefore),
),
);
}
case "age": {
const comp = matcher.relativeDate.direction === "newer" ? gte : lt;
- return ctx.db
+ return db
.select({ id: bookmarks.id })
.from(bookmarks)
.where(
and(
- eq(bookmarks.userId, ctx.user.id),
+ eq(bookmarks.userId, userId),
comp(bookmarks.createdAt, toAbsoluteDate(matcher.relativeDate)),
),
);
}
case "type": {
const comp = matcher.inverse ? ne : eq;
- return ctx.db
+ return db
.select({ id: bookmarks.id })
.from(bookmarks)
.where(
and(
- eq(bookmarks.userId, ctx.user.id),
+ eq(bookmarks.userId, userId),
comp(bookmarks.type, matcher.typeName),
),
);
}
case "and": {
const vals = await Promise.all(
- matcher.matchers.map((m) => getIds(ctx, m)),
+ matcher.matchers.map((m) => getIds(db, userId, m)),
);
return intersect(vals);
}
case "or": {
const vals = await Promise.all(
- matcher.matchers.map((m) => getIds(ctx, m)),
+ matcher.matchers.map((m) => getIds(db, userId, m)),
);
return union(vals);
}
@@ -311,6 +327,6 @@ export async function getBookmarkIdsFromMatcher(
ctx: AuthedContext,
matcher: Matcher,
): Promise<string[]> {
- const results = await getIds(ctx, matcher);
+ const results = await getIds(ctx.db, ctx.user.id, matcher);
return results.map((r) => r.id);
}