From 0d931225bb832c6548cd543d29196420cc822cf9 Mon Sep 17 00:00:00 2001 From: naskya Date: Wed, 10 Jan 2024 20:15:39 +0900 Subject: [PATCH] feat: post search filters --- README.md | 13 +- locales/en-US.yml | 7 + locales/ja-JP.yml | 7 + neko/pnpm-lock.yaml | 10 +- .../backend/src/server/api/endpoints/meta.ts | 2 +- .../src/server/api/endpoints/notes/search.ts | 16 ++ packages/backend/src/server/nodeinfo.ts | 2 +- packages/client/package.json | 1 + packages/client/src/components/MkDialog.vue | 224 ++++++++--------- .../client/src/components/MkSearchBox.vue | 236 ++++++++++++++++++ .../src/components/MkSimpleTextWindow.vue | 43 ++++ packages/client/src/pages/search.vue | 42 +++- packages/client/src/router.ts | 5 + packages/client/src/scripts/search.ts | 117 +++++---- packages/client/src/ui/visitor/a.vue | 2 +- packages/client/src/ui/visitor/b.vue | 6 +- 16 files changed, 557 insertions(+), 176 deletions(-) create mode 100644 packages/client/src/components/MkSearchBox.vue create mode 100644 packages/client/src/components/MkSimpleTextWindow.vue diff --git a/README.md b/README.md index 3746a77f..bf91e729 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,15 @@ - 非ログインユーザーにもローカルタイムラインとグローバルタイムラインを公開できるように変更 - コントロールパネルから設定すると `https://server.example.com/timeline` で公開されます -- 検索フィルターを強化中 - - `from:me` を検索クエリの末尾につけると自分の投稿のみを検索できるように変更 - - 検索クエリの例: `予定 from:me` - - ホーム・フォロワー限定・ダイレクト・秘密の投稿を含む自分の全ての投稿から検索します +- 検索フィルターを強化 + - 以下の機能があります + - AND 検索 + - OR 検索 + - 自分の(未収載・フォロワー限定・ダイレクト・秘密を含む)全ての投稿からの検索 + - 特定のユーザーの投稿の検索 + - 特定のサーバーの投稿の検索 + - 特定の期間の投稿の検索 + - 添付ファイル付きの投稿の検索 - 全文検索のエンジンを [PGroonga](https://pgroonga.github.io/) に変更 - PGroonga のインストールが必要になります!詳しくは[この投稿](https://post.naskya.net/notes/9ldi29amfanomef5)をご覧ください - Meilisearch, Elasticsearch, Sonic は使えません diff --git a/locales/en-US.yml b/locales/en-US.yml index ae58bc3d..8bec50af 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -2205,3 +2205,10 @@ enablePullToRefresh: "Enable \"Pull down to refresh\"" pullToRefreshThreshold: "Pull distance for reloading" publishTimelines: "Publish timelines for visitors" publishTimelinesDescription: "If enabled, the Local and Global timeline will be shown on {url} even when signed out." +searchWords: "Words to search / ID or URL to lookup" +searchWordsDescription: "To search for posts, enter the search term. Separate words with a space for an AND search, or 'OR' (without quotes) between words for an OR search.\nFor example, 'morning night' will find posts that contain both 'morning' and 'night', and 'morning OR night' will find posts that contain either 'morning' or 'night' (or both).\n\nIf you want to go to a specific user page or post page, enter the ID or URL in this field and click the 'Lookup' button. Clicking 'Search' will search for posts that literally contain the ID/URL." +searchUsers: "Posted from (optional)" +searchUsersDescription: "To search for posts by a specific user/server, enter the ID (@user@example.com, or @user for a local user) or domain name (example.com).\n\nIf you enter 'me' (without quotes), all of your posts (including unlisted, followers-only, direct, and secret posts) will be searched.\n\nIf you enter 'local' (without quotes), the results will be filtered to include only posts from this server." +searchRange: "Posted within (optional)" +searchRangeDescription: "If you want to filter the time period, enter it in this format: 20220615-20231031\n\nIf you leave out the year (like 0105-0106 or 20231105-0110), it's interpreted as the current year.\n\nYou can also omit either the start or end date. For example, -0102 will filter the search results to show only posts made before 2 January this year, and 20231026- will filter the results to show only posts made after 26 October 2023." +searchPostsWithFiles: "Only posts with files" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 3138986a..51d1c689 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2047,3 +2047,10 @@ enablePullToRefresh: "「下に引っ張って再読み込み」を有効にす pullToRefreshThreshold: "再読み込みするために引っ張る距離" publishTimelines: "非ログインユーザーにもタイムラインを公開する" publishTimelinesDescription: "有効にすると、{url} でローカルタイムラインとグローバルタイムラインが公開されます。" +searchWords: "検索語句・照会するIDやURL" +searchWordsDescription: "投稿を検索するには、ここに検索語句を入力してください。空白区切りでAND検索になり、ORを挟むとOR検索になります。\n例えば「朝 夜」と入力すると「朝」と「夜」が両方含まれた投稿を検索し、「朝 OR 夜」と入力すると「朝」または「夜」(または両方)が含まれた投稿を検索します。\n\n特定のユーザーや投稿のページに飛びたい場合には、この欄にID (@user@example.com) や投稿のURLを入力し「照会」を押してください。「検索」を押すとそのIDやURLが文字通り含まれる投稿を検索します。" +searchUsers: "投稿元(オプション)" +searchUsersDescription: "投稿検索で投稿者を絞りたい場合、@user@example.com(ローカルユーザーなら @user)の形式で投稿者のIDを入力してください。ユーザーIDではなくドメイン名 (example.com) を指定すると、そのサーバーの投稿を検索します。\n\nme とだけ入力すると、自分の投稿を検索します。この検索結果には未収載・フォロワー限定・ダイレクト・秘密を含む全ての投稿が含まれます。\n\nlocal とだけ入力すると、ローカルサーバーの投稿を検索します。" +searchRange: "投稿期間(オプション)" +searchRangeDescription: "投稿検索で投稿期間を絞りたい場合、20220615-20231031 のような形式で投稿期間を入力してください。今年の日付を指定する場合には年の指定を省略できます(0105-0106 や 20231105-0110 のように)。\n\n開始日と終了日のどちらか一方は省略可能です。例えば -0102 とすると今年1月2日までの投稿のみを、20231026- とすると2023年10月26日以降の投稿のみを検索します。" +searchPostsWithFiles: "添付ファイルのある投稿のみ" diff --git a/neko/pnpm-lock.yaml b/neko/pnpm-lock.yaml index 310e8927..2a002388 100644 --- a/neko/pnpm-lock.yaml +++ b/neko/pnpm-lock.yaml @@ -755,6 +755,9 @@ importers: mfm-js: specifier: 0.24.0 version: 0.24.0 + moment: + specifier: ^2.30.1 + version: 2.30.1 photoswipe: specifier: 5.4.3 version: 5.4.3 @@ -12595,9 +12598,8 @@ packages: ufo: 1.3.2 dev: true - /moment@2.29.4: - resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} - dev: false + /moment@2.30.1: + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -15802,7 +15804,7 @@ packages: resolution: {integrity: sha512-7SNMJKtQBJlwBUp1jxFT7bXya71cnINXPCYJ2AVhlQE4MKL7o2QiPdAXbMdWRiLeykQ2rx+7TNrnoGzvzhO+eA==} engines: {node: '>=10.0.0'} dependencies: - moment: 2.29.4 + moment: 2.30.1 dev: false /systeminformation@5.21.22: diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 68e5643e..977d57a3 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -514,7 +514,7 @@ export default define(meta, paramDef, async (ps, me) => { globalTimeLine: !instance.disableGlobalTimeline, gusstTimeline: instance.enableGuestTimeline, emailRequiredForSignup: instance.emailRequiredForSignup, - searchFilters: false, // TODO: implement search filters + searchFilters: true, hcaptcha: instance.enableHcaptcha, recaptcha: instance.enableRecaptcha, objectStorage: instance.useObjectStorage, diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 7f971978..cf721e1e 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -40,6 +40,8 @@ export const paramDef = { query: { type: "string" }, sinceId: { type: "string", format: "misskey:id" }, untilId: { type: "string", format: "misskey:id" }, + sinceDate: { type: "number", nullable: true }, + untilDate: { type: "number", nullable: true }, limit: { type: "integer", minimum: 1, maximum: 100, default: 10 }, offset: { type: "integer", default: 0 }, host: { @@ -53,6 +55,7 @@ export const paramDef = { nullable: true, default: null, }, + withFiles: { type: "boolean", nullable: true }, channelId: { type: "string", format: "misskey:id", @@ -78,6 +81,8 @@ export default define(meta, paramDef, async (ps, me) => { Notes.createQueryBuilder("note"), ps.sinceId, ps.untilId, + ps.sinceDate ?? undefined, + ps.untilDate ?? undefined, ); if (ps.channelId != null) { @@ -102,6 +107,17 @@ export default define(meta, paramDef, async (ps, me) => { query.andWhere("note.userId = :userId", { userId: ps.userId }); } + if (ps.host === null) { + query.andWhere("note.userHost IS NULL"); + } + if (ps.host != null) { + query.andWhere("note.userHost = :userHost", { userHost: ps.host }); + } + + if (ps.withFiles === true) { + query.andWhere("note.fileIds != '{}'"); + } + query .leftJoinAndSelect("user.avatar", "avatar") .leftJoinAndSelect("user.banner", "banner") diff --git a/packages/backend/src/server/nodeinfo.ts b/packages/backend/src/server/nodeinfo.ts index fec4be23..b7be1975 100644 --- a/packages/backend/src/server/nodeinfo.ts +++ b/packages/backend/src/server/nodeinfo.ts @@ -82,7 +82,7 @@ const nodeinfo2 = async () => { disableRecommendedTimeline: meta.disableRecommendedTimeline, disableGlobalTimeline: meta.disableGlobalTimeline, emailRequiredForSignup: meta.emailRequiredForSignup, - searchFilters: false, // TODO: implement search filters + searchFilters: true, postEditing: true, postImports: meta.experimentalFeatures?.postImports || false, enableHcaptcha: meta.enableHcaptcha, diff --git a/packages/client/package.json b/packages/client/package.json index 940528d9..1c2fe7e0 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -64,6 +64,7 @@ "libopenmpt-wasm": "github:TheEssem/libopenmpt-packaging#build", "matter-js": "0.19.0", "mfm-js": "0.24.0", + "moment": "^2.30.1", "photoswipe": "5.4.3", "prettier": "3.1.1", "prismjs": "1.29.0", diff --git a/packages/client/src/components/MkDialog.vue b/packages/client/src/components/MkDialog.vue index 40332e3d..ed920f89 100644 --- a/packages/client/src/components/MkDialog.vue +++ b/packages/client/src/components/MkDialog.vue @@ -103,7 +103,7 @@ " /> -