aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/shared/searchQueryParser.test.ts36
-rw-r--r--packages/shared/searchQueryParser.ts24
-rw-r--r--packages/shared/types/search.ts10
-rw-r--r--packages/trpc/lib/search.ts16
4 files changed, 83 insertions, 3 deletions
diff --git a/packages/shared/searchQueryParser.test.ts b/packages/shared/searchQueryParser.test.ts
index 3954e871..37275284 100644
--- a/packages/shared/searchQueryParser.test.ts
+++ b/packages/shared/searchQueryParser.test.ts
@@ -332,6 +332,42 @@ describe("Search Query Parser", () => {
inverse: true,
},
});
+ expect(parseSearchQuery("source:rss")).toEqual({
+ result: "full",
+ text: "",
+ matcher: {
+ type: "source",
+ source: "rss",
+ inverse: false,
+ },
+ });
+ expect(parseSearchQuery("-source:rss")).toEqual({
+ result: "full",
+ text: "",
+ matcher: {
+ type: "source",
+ source: "rss",
+ inverse: true,
+ },
+ });
+ expect(parseSearchQuery("source:web")).toEqual({
+ result: "full",
+ text: "",
+ matcher: {
+ type: "source",
+ source: "web",
+ inverse: false,
+ },
+ });
+ expect(parseSearchQuery("-source:web")).toEqual({
+ result: "full",
+ text: "",
+ matcher: {
+ type: "source",
+ source: "web",
+ inverse: true,
+ },
+ });
});
test("! negation alias for -", () => {
// ! should work exactly like - for negation
diff --git a/packages/shared/searchQueryParser.ts b/packages/shared/searchQueryParser.ts
index 027a662f..7eb3b185 100644
--- a/packages/shared/searchQueryParser.ts
+++ b/packages/shared/searchQueryParser.ts
@@ -16,7 +16,7 @@ import {
} from "typescript-parsec";
import { z } from "zod";
-import { BookmarkTypes } from "./types/bookmarks";
+import { BookmarkTypes, zBookmarkSourceSchema } from "./types/bookmarks";
import { Matcher } from "./types/search";
import { parseRelativeDate } from "./utils/relativeDateUtils";
@@ -42,7 +42,10 @@ const lexerRules: [RegExp, TokenType][] = [
[/^\s+or/i, TokenType.Or],
[/^#/, TokenType.Hash],
- [/^(is|url|list|after|before|age|feed|title|tag):/, TokenType.Qualifier],
+ [
+ /^(is|url|list|after|before|age|feed|title|tag|source):/,
+ TokenType.Qualifier,
+ ],
[/^"([^"]+)"/, TokenType.StringLiteral],
@@ -230,6 +233,23 @@ MATCHER.setPattern(
inverse: !!minus,
},
};
+ case "source:": {
+ const parsed = zBookmarkSourceSchema.safeParse(ident);
+ if (!parsed.success) {
+ return {
+ text: (minus?.text ?? "") + qualifier.text + ident,
+ matcher: undefined,
+ };
+ }
+ return {
+ text: "",
+ matcher: {
+ type: "source",
+ source: parsed.data,
+ inverse: !!minus,
+ },
+ };
+ }
case "after:":
try {
return {
diff --git a/packages/shared/types/search.ts b/packages/shared/types/search.ts
index c29270b8..b653d883 100644
--- a/packages/shared/types/search.ts
+++ b/packages/shared/types/search.ts
@@ -1,6 +1,6 @@
import { z } from "zod";
-import { BookmarkTypes } from "./bookmarks";
+import { BookmarkTypes, zBookmarkSourceSchema } from "./bookmarks";
const zTagNameMatcher = z.object({
type: z.literal("tagName"),
@@ -88,6 +88,12 @@ const zBrokenLinksMatcher = z.object({
brokenLinks: z.boolean(),
});
+const zSourceMatcher = z.object({
+ type: z.literal("source"),
+ source: zBookmarkSourceSchema,
+ inverse: z.boolean(),
+});
+
const zNonRecursiveMatcher = z.union([
zTagNameMatcher,
zListNameMatcher,
@@ -103,6 +109,7 @@ const zNonRecursiveMatcher = z.union([
zTypeMatcher,
zRssFeedNameMatcher,
zBrokenLinksMatcher,
+ zSourceMatcher,
]);
type NonRecursiveMatcher = z.infer<typeof zNonRecursiveMatcher>;
@@ -127,6 +134,7 @@ export const zMatcherSchema: z.ZodType<Matcher> = z.lazy(() => {
zTypeMatcher,
zRssFeedNameMatcher,
zBrokenLinksMatcher,
+ zSourceMatcher,
z.object({
type: z.literal("and"),
matchers: z.array(zMatcherSchema),
diff --git a/packages/trpc/lib/search.ts b/packages/trpc/lib/search.ts
index 88f10f22..51e51d1c 100644
--- a/packages/trpc/lib/search.ts
+++ b/packages/trpc/lib/search.ts
@@ -373,6 +373,22 @@ async function getIds(
),
);
}
+ case "source": {
+ return db
+ .select({ id: bookmarks.id })
+ .from(bookmarks)
+ .where(
+ and(
+ eq(bookmarks.userId, userId),
+ matcher.inverse
+ ? or(
+ ne(bookmarks.source, matcher.source),
+ isNull(bookmarks.source),
+ )
+ : eq(bookmarks.source, matcher.source),
+ ),
+ );
+ }
case "and": {
const vals = await Promise.all(
matcher.matchers.map((m) => getIds(db, userId, m)),