aboutsummaryrefslogtreecommitdiffstats
path: root/packages/trpc/lib
diff options
context:
space:
mode:
Diffstat (limited to 'packages/trpc/lib')
-rw-r--r--packages/trpc/lib/__tests__/search.test.ts91
-rw-r--r--packages/trpc/lib/search.ts72
2 files changed, 142 insertions, 21 deletions
diff --git a/packages/trpc/lib/__tests__/search.test.ts b/packages/trpc/lib/__tests__/search.test.ts
index aa57527b..31f87dfd 100644
--- a/packages/trpc/lib/__tests__/search.test.ts
+++ b/packages/trpc/lib/__tests__/search.test.ts
@@ -160,53 +160,130 @@ beforeEach(async () => {
describe("getBookmarkIdsFromMatcher", () => {
it("should handle tagName matcher", async () => {
- const matcher: Matcher = { type: "tagName", tagName: "tag1" };
+ const matcher: Matcher = {
+ type: "tagName",
+ tagName: "tag1",
+ inverse: false,
+ };
const result = await getBookmarkIdsFromMatcher(mockCtx, matcher);
expect(result).toEqual(["b1"]);
});
+ it("should handle tagName matcher with inverse=true", async () => {
+ const matcher: Matcher = {
+ type: "tagName",
+ tagName: "tag1",
+ inverse: true,
+ };
+ const result = await getBookmarkIdsFromMatcher(mockCtx, matcher);
+ expect(result.sort()).toEqual(["b2", "b3", "b4", "b5", "b6"]);
+ });
+
it("should handle listName matcher", async () => {
- const matcher: Matcher = { type: "listName", listName: "list1" };
+ const matcher: Matcher = {
+ type: "listName",
+ listName: "list1",
+ inverse: false,
+ };
const result = await getBookmarkIdsFromMatcher(mockCtx, matcher);
expect(result).toEqual(["b1", "b6"]);
});
+ it("should handle listName matcher with inverse=true", async () => {
+ const matcher: Matcher = {
+ type: "listName",
+ listName: "list1",
+ inverse: true,
+ };
+ const result = await getBookmarkIdsFromMatcher(mockCtx, matcher);
+ expect(result.sort()).toEqual(["b2", "b3", "b4", "b5"]);
+ });
+
it("should handle archived matcher", async () => {
const matcher: Matcher = { type: "archived", archived: true };
const result = await getBookmarkIdsFromMatcher(mockCtx, matcher);
expect(result).toEqual(["b2", "b3", "b6"]);
});
+ it("should handle archived matcher archived=false", async () => {
+ const matcher: Matcher = { type: "archived", archived: false };
+ const result = await getBookmarkIdsFromMatcher(mockCtx, matcher);
+ expect(result).toEqual(["b1", "b4", "b5"]);
+ });
+
it("should handle favourited matcher", async () => {
const matcher: Matcher = { type: "favourited", favourited: true };
const result = await getBookmarkIdsFromMatcher(mockCtx, matcher);
expect(result).toEqual(["b2", "b4"]);
});
+ it("should handle favourited matcher favourited=false", async () => {
+ const matcher: Matcher = { type: "favourited", favourited: false };
+ const result = await getBookmarkIdsFromMatcher(mockCtx, matcher);
+ expect(result).toEqual(["b1", "b3", "b5", "b6"]);
+ });
+
it("should handle url matcher", async () => {
- const matcher: Matcher = { type: "url", url: "example.com" };
+ const matcher: Matcher = {
+ type: "url",
+ url: "example.com",
+ inverse: false,
+ };
const result = await getBookmarkIdsFromMatcher(mockCtx, matcher);
expect(result).toEqual(["b1", "b4"]);
});
+ it("should handle url matcher with inverse=true", async () => {
+ const matcher: Matcher = {
+ type: "url",
+ url: "example.com",
+ inverse: true,
+ };
+ const result = await getBookmarkIdsFromMatcher(mockCtx, matcher);
+ // Not that only bookmarks of type link are returned
+ expect(result.sort()).toEqual(["b2"]);
+ });
+
it("should handle dateAfter matcher", async () => {
const matcher: Matcher = {
type: "dateAfter",
dateAfter: new Date("2024-01-02"),
+ inverse: false,
};
const result = await getBookmarkIdsFromMatcher(mockCtx, matcher);
expect(result).toEqual(["b2", "b3", "b4", "b5", "b6"]);
});
+ it("should handle dateAfter matcher with inverse=true", async () => {
+ const matcher: Matcher = {
+ type: "dateAfter",
+ dateAfter: new Date("2024-01-02"),
+ inverse: true,
+ };
+ const result = await getBookmarkIdsFromMatcher(mockCtx, matcher);
+ expect(result).toEqual(["b1"]);
+ });
+
it("should handle dateBefore matcher", async () => {
const matcher: Matcher = {
type: "dateBefore",
dateBefore: new Date("2024-01-02"),
+ inverse: false,
};
const result = await getBookmarkIdsFromMatcher(mockCtx, matcher);
expect(result).toEqual(["b1", "b2"]);
});
+ it("should handle dateBefore matcher with inverse=true", async () => {
+ const matcher: Matcher = {
+ type: "dateBefore",
+ dateBefore: new Date("2024-01-02"),
+ inverse: true,
+ };
+ const result = await getBookmarkIdsFromMatcher(mockCtx, matcher);
+ expect(result.sort()).toEqual(["b3", "b4", "b5", "b6"]);
+ });
+
it("should handle AND matcher", async () => {
const matcher: Matcher = {
type: "and",
@@ -235,8 +312,8 @@ describe("getBookmarkIdsFromMatcher", () => {
const matcher: Matcher = {
type: "or",
matchers: [
- { type: "listName", listName: "favorites" },
- { type: "tagName", tagName: "work" },
+ { type: "listName", listName: "favorites", inverse: false },
+ { type: "tagName", tagName: "work", inverse: false },
],
};
const result = await getBookmarkIdsFromMatcher(mockCtx, matcher);
@@ -250,8 +327,8 @@ describe("getBookmarkIdsFromMatcher", () => {
{
type: "or",
matchers: [
- { type: "listName", listName: "favorites" },
- { type: "tagName", tagName: "work" },
+ { type: "listName", listName: "favorites", inverse: false },
+ { type: "tagName", tagName: "work", inverse: false },
],
},
{
diff --git a/packages/trpc/lib/search.ts b/packages/trpc/lib/search.ts
index 0ee9c76e..fcc5abda 100644
--- a/packages/trpc/lib/search.ts
+++ b/packages/trpc/lib/search.ts
@@ -1,4 +1,15 @@
-import { and, eq, gte, like, lte, sql } from "drizzle-orm";
+import {
+ and,
+ eq,
+ exists,
+ gt,
+ gte,
+ like,
+ lt,
+ lte,
+ notExists,
+ notLike,
+} from "drizzle-orm";
import {
bookmarkLinks,
@@ -76,26 +87,56 @@ async function getIds(
): Promise<BookmarkQueryReturnType[]> {
switch (matcher.type) {
case "tagName": {
+ const comp = matcher.inverse ? notExists : exists;
return db
- .select({ id: sql<string>`${tagsOnBookmarks.bookmarkId}`.as("id") })
- .from(tagsOnBookmarks)
- .innerJoin(bookmarkTags, eq(tagsOnBookmarks.tagId, bookmarkTags.id))
+ .selectDistinct({ id: bookmarks.id })
+ .from(bookmarks)
.where(
and(
- eq(bookmarkTags.userId, userId),
- eq(bookmarkTags.name, matcher.tagName),
+ eq(bookmarks.userId, userId),
+ comp(
+ db
+ .select()
+ .from(tagsOnBookmarks)
+ .innerJoin(
+ bookmarkTags,
+ eq(tagsOnBookmarks.tagId, bookmarkTags.id),
+ )
+ .where(
+ and(
+ eq(tagsOnBookmarks.bookmarkId, bookmarks.id),
+ eq(bookmarkTags.userId, userId),
+ eq(bookmarkTags.name, matcher.tagName),
+ ),
+ ),
+ ),
),
);
}
case "listName": {
+ const comp = matcher.inverse ? notExists : exists;
return db
- .select({ id: sql<string>`${bookmarksInLists.bookmarkId}`.as("id") })
- .from(bookmarksInLists)
- .innerJoin(bookmarkLists, eq(bookmarksInLists.listId, bookmarkLists.id))
+ .selectDistinct({ id: bookmarks.id })
+ .from(bookmarks)
.where(
and(
- eq(bookmarkLists.userId, userId),
- eq(bookmarkLists.name, matcher.listName),
+ 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),
+ ),
+ ),
+ ),
),
);
}
@@ -111,6 +152,7 @@ async function getIds(
);
}
case "url": {
+ const comp = matcher.inverse ? notLike : like;
return db
.select({ id: bookmarkLinks.id })
.from(bookmarkLinks)
@@ -118,7 +160,7 @@ async function getIds(
.where(
and(
eq(bookmarks.userId, userId),
- like(bookmarkLinks.url, `%${matcher.url}%`),
+ comp(bookmarkLinks.url, `%${matcher.url}%`),
),
);
}
@@ -134,24 +176,26 @@ async function getIds(
);
}
case "dateAfter": {
+ const comp = matcher.inverse ? lt : gte;
return db
.select({ id: bookmarks.id })
.from(bookmarks)
.where(
and(
eq(bookmarks.userId, userId),
- gte(bookmarks.createdAt, matcher.dateAfter),
+ comp(bookmarks.createdAt, matcher.dateAfter),
),
);
}
case "dateBefore": {
+ const comp = matcher.inverse ? gt : lte;
return db
.select({ id: bookmarks.id })
.from(bookmarks)
.where(
and(
eq(bookmarks.userId, userId),
- lte(bookmarks.createdAt, matcher.dateBefore),
+ comp(bookmarks.createdAt, matcher.dateBefore),
),
);
}