From 52047459bbd820eede0af3401159c6c581bd1a07 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 27 Jan 2025 22:58:19 -0500 Subject: [PATCH] Search combined (#5271) * Renaming person_mention to person_comment_mention. * Finishing up post body mentions. * Combined tables try 2 * Finishing up combined report table. * Fix ts optionals. * Adding tests, triggers, and history updates for report_combined. * Adding profile. * Add cursor pagination to report_combined view (#5244) * add pagination cursor * store timestamp instead of id in cursor (partial) * Revert "store timestamp instead of id in cursor (partial)" This reverts commit 89359dde4bc5fee39fdd2840828330f398444a36. * use paginated query builder * Fixing migration and paged API. * Using dullbananas trigger procedure * Removing pointless list routes, reorganizing tests. * Fixing column XOR check. * Forgot to remove list report actions. * Cleanup. * Use internal tagging. * Fixing api tests. * Adding a few indexes. * Fixing migration name. * Fixing unique constraints. * Addressing PR comments. * Start working on profile combined * Adding views and replaceable schema. * A few changes to profile view. - Separating the profile fetch from its combined content fetch. - Starting to separate saved_only into its own combined view. * Finishing up combined person_saved and person_content. * Fixing api tests. * Moving to api-v4 routes. * Fixing imports. * Update crates/db_views/src/report_combined_view.rs Co-authored-by: dullbananas * Update crates/db_views/src/report_combined_view.rs Co-authored-by: dullbananas * Update crates/db_views/src/report_combined_view.rs Co-authored-by: dullbananas * Update migrations/2024-12-02-181601_add_report_combined_table/up.sql Co-authored-by: dullbananas * Update migrations/2024-12-02-181601_add_report_combined_table/up.sql Co-authored-by: dullbananas * Fixing import and fmt. * Fixing null types in postgres. * Comment out err. * Fixing TS issues. * Adding types, fixing allow and blocklist crud. * Starting to work on combined views. * Using dullbananas trigger procedure * Adding the full combined view queries. * Adding tests. * taplo fmt. * Upgrading package.json deps. * Updating pnpm * Most of the bulk work done, need to add tests yet. * Finishing up inbox. * Using assert_length * Fixing sql_format. * Running fmt. * Fixing cargo shear. * Fixing clippy. * Addressing PR comments. * Starting to work on search combined. * Fix * Removing serialization * Removing serialization * Moving db_views_actor and _moderator into db_views. - This is necessary because the combined views use both, and that separation was arbitrary to begin with. db_schema has no such crate separation. * Adding search combined view, need to write tests yet. * Filters done, working on tests. * Adding tests for person, post, and community. * Finishing up tests. * Fixing duped trigger. * Remove saved_only test. * Remove pointless post_tags types. * Remove pointless index. * Changing published to saved for person_saved_combined. * Removing comment. * Renaming modlog when_ columns to published. - Fixes #5312 * Adding strum and simplifying imports. * Avoiding clone in map_to_enum * Changing modded_person to other_person. * Update crates/db_views_moderator/src/modlog_combined_view.rs Co-authored-by: dullbananas * Update crates/db_views_moderator/src/modlog_combined_view.rs Co-authored-by: dullbananas * Update crates/db_views_moderator/src/modlog_combined_view.rs Co-authored-by: dullbananas * Addressing PR comments. * Fixing split. * Revert "Adding strum and simplifying imports." This reverts commit 15f167110721429dd6e465f522250c8beb3d4dd7. * Running fmt. * Using assert + matches instead of filter_map. * Adding listPersonContent check. * Updating lemmy-js-client * Fixing mark all as read route, changing mark read to SuccessResponse. * Adding post body mention api test, fixing api tests. * Fixing route locations, and api tests. * Formatting sql. * Formatting sql 2. * Fixing search result, running clippy. * Fixing ts_option. * Adding search_combined.score column, and DB triggers. * Fixing API tests. * Adding an index for score. * Update crates/db_schema/src/newtypes.rs Co-authored-by: dullbananas * Avoiding inner joins for up.sql * Adding person_aggregates.published column. --------- Co-authored-by: dullbananas --- Cargo.lock | 47 +- Cargo.toml | 4 - api_tests/package.json | 12 +- api_tests/pnpm-lock.yaml | 200 +-- api_tests/prepare-drone-federation-test.sh | 2 +- api_tests/src/community.spec.ts | 2 +- api_tests/src/shared.ts | 5 +- crates/api/Cargo.toml | 2 - crates/api/src/community/add_mod.rs | 3 +- crates/api/src/community/ban.rs | 3 +- crates/api/src/community/block.rs | 3 +- crates/api/src/community/follow.rs | 3 +- .../src/community/pending_follows/count.rs | 3 +- .../api/src/community/pending_follows/list.rs | 3 +- crates/api/src/community/random.rs | 3 +- crates/api/src/community/transfer.rs | 3 +- crates/api/src/local_user/add_admin.rs | 3 +- crates/api/src/local_user/ban_person.rs | 3 +- crates/api/src/local_user/block.rs | 3 +- crates/api/src/local_user/list_banned.rs | 3 +- crates/api/src/local_user/list_saved.rs | 2 +- .../local_user/notifications/list_inbox.rs | 3 +- .../local_user/notifications/unread_count.rs | 3 +- .../api/src/reports/report_combined/list.rs | 2 +- crates/api/src/site/leave_admin.rs | 3 +- crates/api/src/site/mod_log.rs | 3 +- crates/api/src/site/purge/community.rs | 3 +- .../site/registration_applications/list.rs | 2 +- crates/api_common/Cargo.toml | 4 - crates/api_common/src/build_response.rs | 3 +- crates/api_common/src/community.rs | 2 +- crates/api_common/src/lib.rs | 2 - crates/api_common/src/person.rs | 8 +- crates/api_common/src/post.rs | 9 +- crates/api_common/src/private_message.rs | 2 +- crates/api_common/src/send_activity.rs | 2 +- crates/api_common/src/site.rs | 38 +- crates/api_common/src/utils.rs | 22 +- crates/api_crud/Cargo.toml | 1 - crates/api_crud/src/community/delete.rs | 3 +- crates/api_crud/src/community/list.rs | 6 +- crates/api_crud/src/post/create.rs | 3 +- crates/api_crud/src/post/read.rs | 5 +- crates/api_crud/src/private_message/create.rs | 3 +- crates/api_crud/src/private_message/delete.rs | 3 +- crates/api_crud/src/private_message/update.rs | 3 +- crates/api_crud/src/site/read.rs | 3 +- crates/api_crud/src/user/my_user.rs | 3 +- crates/apub/Cargo.toml | 1 - .../apub/src/activities/community/report.rs | 2 +- .../create_or_update/private_message.rs | 2 +- crates/apub/src/activities/mod.rs | 2 +- crates/apub/src/api/list_comments.rs | 2 +- crates/apub/src/api/list_person_content.rs | 2 +- crates/apub/src/api/list_posts.rs | 2 +- crates/apub/src/api/read_community.rs | 3 +- crates/apub/src/api/read_person.rs | 3 +- crates/apub/src/api/resolve_object.rs | 3 +- crates/apub/src/api/search.rs | 177 +-- crates/apub/src/api/user_settings_backup.rs | 3 +- .../src/collections/community_follower.rs | 2 +- .../src/collections/community_moderators.rs | 2 +- .../apub/src/collections/community_outbox.rs | 2 +- crates/apub/src/http/community.rs | 2 +- crates/apub/src/http/mod.rs | 2 +- crates/apub/src/objects/community.rs | 2 +- crates/apub/src/objects/post.rs | 2 +- crates/db_perf/src/main.rs | 4 +- .../db_schema/replaceable_schema/triggers.sql | 123 ++ crates/db_schema/src/impls/images.rs | 2 +- crates/db_schema/src/lib.rs | 18 +- crates/db_schema/src/newtypes.rs | 5 + crates/db_schema/src/schema.rs | 17 + crates/db_schema/src/source/combined/mod.rs | 1 + .../db_schema/src/source/combined/search.rs | 28 + crates/db_views/Cargo.toml | 1 + .../src/combined}/inbox_combined_view.rs | 15 +- crates/db_views/src/combined/mod.rs | 12 + .../src/combined}/modlog_combined_view.rs | 2 +- .../person_content_combined_view.rs | 10 +- .../person_saved_combined_view.rs | 8 +- .../{ => combined}/report_combined_view.rs | 2 +- .../src/combined/search_combined_view.rs | 1194 +++++++++++++++++ .../src/{ => comment}/comment_view.rs | 2 +- crates/db_views/src/comment/mod.rs | 2 + .../src/community}/community_follower_view.rs | 0 .../community}/community_moderator_view.rs | 0 .../community}/community_person_ban_view.rs | 0 .../src/community}/community_view.rs | 2 +- .../lib.rs => db_views/src/community/mod.rs} | 7 - crates/db_views/src/lib.rs | 30 +- .../src/{ => local_user}/local_user_view.rs | 0 crates/db_views/src/local_user/mod.rs | 2 + crates/db_views/src/person/mod.rs | 2 + .../src/person}/person_view.rs | 0 crates/db_views/src/post/mod.rs | 4 + .../db_views/src/{ => post}/post_tags_view.rs | 0 crates/db_views/src/{ => post}/post_view.rs | 4 +- crates/db_views/src/private_message/mod.rs | 2 + .../private_message}/private_message_view.rs | 0 .../src/registration_applications/mod.rs | 2 + .../registration_application_view.rs | 2 +- .../src/{ => reports}/comment_report_view.rs | 0 crates/db_views/src/reports/mod.rs | 6 + .../src/{ => reports}/post_report_view.rs | 0 .../private_message_report_view.rs | 0 .../src/{ => site}/custom_emoji_view.rs | 0 .../src/{ => site}/local_image_view.rs | 0 crates/db_views/src/site/mod.rs | 8 + crates/db_views/src/{ => site}/site_view.rs | 0 crates/db_views/src/{ => site}/vote_view.rs | 0 crates/db_views/src/structs.rs | 619 ++++++++- crates/db_views_actor/Cargo.toml | 50 - crates/db_views_actor/src/structs.rs | 259 ---- crates/db_views_moderator/Cargo.toml | 47 - crates/db_views_moderator/src/lib.rs | 3 - crates/db_views_moderator/src/structs.rs | 332 ----- crates/federate/Cargo.toml | 2 +- crates/federate/src/inboxes.rs | 2 +- crates/routes/Cargo.toml | 1 - crates/routes/src/feeds.rs | 6 +- crates/routes/src/images/download.rs | 2 +- docker/docker-compose.yml | 2 +- docker/federation/docker-compose.yml | 2 +- .../down.sql | 5 + .../up.sql | 80 ++ 126 files changed, 2418 insertions(+), 1183 deletions(-) create mode 100644 crates/db_schema/src/source/combined/search.rs rename crates/{db_views_actor/src => db_views/src/combined}/inbox_combined_view.rs (98%) create mode 100644 crates/db_views/src/combined/mod.rs rename crates/{db_views_moderator/src => db_views/src/combined}/modlog_combined_view.rs (99%) rename crates/db_views/src/{ => combined}/person_content_combined_view.rs (98%) rename crates/db_views/src/{ => combined}/person_saved_combined_view.rs (98%) rename crates/db_views/src/{ => combined}/report_combined_view.rs (99%) create mode 100644 crates/db_views/src/combined/search_combined_view.rs rename crates/db_views/src/{ => comment}/comment_view.rs (99%) create mode 100644 crates/db_views/src/comment/mod.rs rename crates/{db_views_actor/src => db_views/src/community}/community_follower_view.rs (100%) rename crates/{db_views_actor/src => db_views/src/community}/community_moderator_view.rs (100%) rename crates/{db_views_actor/src => db_views/src/community}/community_person_ban_view.rs (100%) rename crates/{db_views_actor/src => db_views/src/community}/community_view.rs (99%) rename crates/{db_views_actor/src/lib.rs => db_views/src/community/mod.rs} (56%) rename crates/db_views/src/{ => local_user}/local_user_view.rs (100%) create mode 100644 crates/db_views/src/local_user/mod.rs create mode 100644 crates/db_views/src/person/mod.rs rename crates/{db_views_actor/src => db_views/src/person}/person_view.rs (100%) create mode 100644 crates/db_views/src/post/mod.rs rename crates/db_views/src/{ => post}/post_tags_view.rs (100%) rename crates/db_views/src/{ => post}/post_view.rs (99%) create mode 100644 crates/db_views/src/private_message/mod.rs rename crates/{db_views_actor/src => db_views/src/private_message}/private_message_view.rs (100%) create mode 100644 crates/db_views/src/registration_applications/mod.rs rename crates/db_views/src/{ => registration_applications}/registration_application_view.rs (99%) rename crates/db_views/src/{ => reports}/comment_report_view.rs (100%) create mode 100644 crates/db_views/src/reports/mod.rs rename crates/db_views/src/{ => reports}/post_report_view.rs (100%) rename crates/db_views/src/{ => reports}/private_message_report_view.rs (100%) rename crates/db_views/src/{ => site}/custom_emoji_view.rs (100%) rename crates/db_views/src/{ => site}/local_image_view.rs (100%) create mode 100644 crates/db_views/src/site/mod.rs rename crates/db_views/src/{ => site}/site_view.rs (100%) rename crates/db_views/src/{ => site}/vote_view.rs (100%) delete mode 100644 crates/db_views_actor/Cargo.toml delete mode 100644 crates/db_views_actor/src/structs.rs delete mode 100644 crates/db_views_moderator/Cargo.toml delete mode 100644 crates/db_views_moderator/src/lib.rs delete mode 100644 crates/db_views_moderator/src/structs.rs create mode 100644 migrations/2024-12-12-222846_add_search_combined_table/down.sql create mode 100644 migrations/2024-12-12-222846_add_search_combined_table/up.sql diff --git a/Cargo.lock b/Cargo.lock index 4c3a87127..0289d3544 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2566,8 +2566,6 @@ dependencies = [ "lemmy_api_crud", "lemmy_db_schema", "lemmy_db_views", - "lemmy_db_views_actor", - "lemmy_db_views_moderator", "lemmy_utils", "pretty_assertions", "serial_test", @@ -2594,8 +2592,6 @@ dependencies = [ "jsonwebtoken", "lemmy_db_schema", "lemmy_db_views", - "lemmy_db_views_actor", - "lemmy_db_views_moderator", "lemmy_utils", "mime", "mime_guess", @@ -2632,7 +2628,6 @@ dependencies = [ "lemmy_api_common", "lemmy_db_schema", "lemmy_db_views", - "lemmy_db_views_actor", "lemmy_utils", "regex", "serde", @@ -2662,7 +2657,6 @@ dependencies = [ "lemmy_api_common", "lemmy_db_schema", "lemmy_db_views", - "lemmy_db_views_actor", "lemmy_utils", "moka", "pretty_assertions", @@ -2753,6 +2747,7 @@ dependencies = [ "serde_json", "serde_with", "serial_test", + "strum", "test-context", "tokio", "tracing", @@ -2760,43 +2755,6 @@ dependencies = [ "url", ] -[[package]] -name = "lemmy_db_views_actor" -version = "0.19.6-beta.7" -dependencies = [ - "chrono", - "diesel", - "diesel-async", - "i-love-jesus", - "lemmy_db_schema", - "lemmy_utils", - "pretty_assertions", - "serde", - "serde_with", - "serial_test", - "strum", - "tokio", - "ts-rs", - "url", -] - -[[package]] -name = "lemmy_db_views_moderator" -version = "0.19.6-beta.7" -dependencies = [ - "diesel", - "diesel-async", - "i-love-jesus", - "lemmy_db_schema", - "lemmy_utils", - "pretty_assertions", - "serde", - "serde_with", - "serial_test", - "tokio", - "ts-rs", -] - [[package]] name = "lemmy_federate" version = "0.19.6-beta.7" @@ -2812,7 +2770,7 @@ dependencies = [ "lemmy_api_common", "lemmy_apub", "lemmy_db_schema", - "lemmy_db_views_actor", + "lemmy_db_views", "lemmy_utils", "mockall", "moka", @@ -2847,7 +2805,6 @@ dependencies = [ "lemmy_api_common", "lemmy_db_schema", "lemmy_db_views", - "lemmy_db_views_actor", "lemmy_utils", "pretty_assertions", "prometheus", diff --git a/Cargo.toml b/Cargo.toml index e5cd2a74c..9a8bac1fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,8 +49,6 @@ members = [ "crates/db_perf", "crates/db_schema", "crates/db_views", - "crates/db_views_actor", - "crates/db_views_actor", "crates/routes", "crates/federate", ] @@ -90,8 +88,6 @@ lemmy_db_schema = { version = "=0.19.6-beta.7", path = "./crates/db_schema" } lemmy_api_common = { version = "=0.19.6-beta.7", path = "./crates/api_common" } lemmy_routes = { version = "=0.19.6-beta.7", path = "./crates/routes" } lemmy_db_views = { version = "=0.19.6-beta.7", path = "./crates/db_views" } -lemmy_db_views_actor = { version = "=0.19.6-beta.7", path = "./crates/db_views_actor" } -lemmy_db_views_moderator = { version = "=0.19.6-beta.7", path = "./crates/db_views_moderator" } lemmy_federate = { version = "=0.19.6-beta.7", path = "./crates/federate" } activitypub_federation = { version = "0.6.1", default-features = false, features = [ "actix-web", diff --git a/api_tests/package.json b/api_tests/package.json index f9c67eea7..993136742 100644 --- a/api_tests/package.json +++ b/api_tests/package.json @@ -22,16 +22,16 @@ }, "devDependencies": { "@types/jest": "^29.5.12", - "@types/node": "^22.10.6", - "@typescript-eslint/eslint-plugin": "^8.20.0", - "@typescript-eslint/parser": "^8.20.0", + "@types/node": "^22.10.7", + "@typescript-eslint/eslint-plugin": "^8.21.0", + "@typescript-eslint/parser": "^8.21.0", "eslint": "^9.18.0", - "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-prettier": "^5.2.3", "jest": "^29.5.0", - "lemmy-js-client": "0.20.0-inbox-combined.1", + "lemmy-js-client": "0.20.0-search-combined.1", "prettier": "^3.4.2", "ts-jest": "^29.1.0", "typescript": "^5.7.3", - "typescript-eslint": "^8.20.0" + "typescript-eslint": "^8.21.0" } } diff --git a/api_tests/pnpm-lock.yaml b/api_tests/pnpm-lock.yaml index 0a662e576..a0721bba3 100644 --- a/api_tests/pnpm-lock.yaml +++ b/api_tests/pnpm-lock.yaml @@ -12,38 +12,38 @@ importers: specifier: ^29.5.12 version: 29.5.14 '@types/node': - specifier: ^22.10.6 - version: 22.10.6 + specifier: ^22.10.7 + version: 22.10.7 '@typescript-eslint/eslint-plugin': - specifier: ^8.20.0 - version: 8.20.0(@typescript-eslint/parser@8.20.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3) + specifier: ^8.21.0 + version: 8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3) '@typescript-eslint/parser': - specifier: ^8.20.0 - version: 8.20.0(eslint@9.18.0)(typescript@5.7.3) + specifier: ^8.21.0 + version: 8.21.0(eslint@9.18.0)(typescript@5.7.3) eslint: specifier: ^9.18.0 version: 9.18.0 eslint-plugin-prettier: - specifier: ^5.1.3 - version: 5.2.1(eslint@9.18.0)(prettier@3.4.2) + specifier: ^5.2.3 + version: 5.2.3(eslint@9.18.0)(prettier@3.4.2) jest: specifier: ^29.5.0 - version: 29.7.0(@types/node@22.10.6) + version: 29.7.0(@types/node@22.10.7) lemmy-js-client: - specifier: 0.20.0-inbox-combined.1 - version: 0.20.0-inbox-combined.1 + specifier: 0.20.0-search-combined.1 + version: 0.20.0-search-combined.1 prettier: specifier: ^3.4.2 version: 3.4.2 ts-jest: specifier: ^29.1.0 - version: 29.2.5(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.10.6))(typescript@5.7.3) + version: 29.2.5(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.10.7))(typescript@5.7.3) typescript: specifier: ^5.7.3 version: 5.7.3 typescript-eslint: - specifier: ^8.20.0 - version: 8.20.0(eslint@9.18.0)(typescript@5.7.3) + specifier: ^8.21.0 + version: 8.21.0(eslint@9.18.0)(typescript@5.7.3) packages: @@ -422,8 +422,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@22.10.6': - resolution: {integrity: sha512-qNiuwC4ZDAUNcY47xgaSuS92cjf8JbSUoaKS77bmLG1rU7MlATVSiw/IlrjtIyyskXBZ8KkNfjK/P5na7rgXbQ==} + '@types/node@22.10.7': + resolution: {integrity: sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==} '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -434,51 +434,51 @@ packages: '@types/yargs@17.0.32': resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} - '@typescript-eslint/eslint-plugin@8.20.0': - resolution: {integrity: sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==} + '@typescript-eslint/eslint-plugin@8.21.0': + resolution: {integrity: sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/parser@8.20.0': - resolution: {integrity: sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==} + '@typescript-eslint/parser@8.21.0': + resolution: {integrity: sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/scope-manager@8.20.0': - resolution: {integrity: sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==} + '@typescript-eslint/scope-manager@8.21.0': + resolution: {integrity: sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.20.0': - resolution: {integrity: sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==} + '@typescript-eslint/type-utils@8.21.0': + resolution: {integrity: sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/types@8.20.0': - resolution: {integrity: sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==} + '@typescript-eslint/types@8.21.0': + resolution: {integrity: sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.20.0': - resolution: {integrity: sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==} + '@typescript-eslint/typescript-estree@8.21.0': + resolution: {integrity: sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/utils@8.20.0': - resolution: {integrity: sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==} + '@typescript-eslint/utils@8.21.0': + resolution: {integrity: sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/visitor-keys@8.20.0': - resolution: {integrity: sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==} + '@typescript-eslint/visitor-keys@8.21.0': + resolution: {integrity: sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} acorn-jsx@5.3.2: @@ -709,8 +709,8 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-plugin-prettier@5.2.1: - resolution: {integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==} + eslint-plugin-prettier@5.2.3: + resolution: {integrity: sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: '@types/eslint': '>=8.0.0' @@ -1157,8 +1157,8 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - lemmy-js-client@0.20.0-inbox-combined.1: - resolution: {integrity: sha512-sFJJePXdMHIVQwCa3fN+nIcIvfD7ZbBEZn08fmITXEA6/qbJLvZGWG/rEcRNkZM+lRKnhfrZihWKx1AHZE9wqA==} + lemmy-js-client@0.20.0-search-combined.1: + resolution: {integrity: sha512-K4bk6r+26lxZqL8WKiJlqfZb+ZnhNrZz1gAQTb4FQkGXQLFeqQ41ZyGmQZzjX+NPy7O9mt9d9d7zI8t/QYJ82Q==} leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} @@ -1528,8 +1528,8 @@ packages: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - typescript-eslint@8.20.0: - resolution: {integrity: sha512-Kxz2QRFsgbWj6Xcftlw3Dd154b3cEPFqQC+qMZrMypSijPd4UanKKvoKDrJ4o8AIfZFKAF+7sMaEIR8mTElozA==} + typescript-eslint@8.21.0: + resolution: {integrity: sha512-txEKYY4XMKwPXxNkN8+AxAdX6iIJAPiJbHE/FpQccs/sxw8Lf26kqwC3cn0xkHlW8kEbLhkhCsjWuMveaY9Rxw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1869,7 +1869,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 22.10.6 + '@types/node': 22.10.7 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -1882,14 +1882,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.10.6 + '@types/node': 22.10.7 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.10.6) + jest-config: 29.7.0(@types/node@22.10.7) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -1914,7 +1914,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.10.6 + '@types/node': 22.10.7 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -1932,7 +1932,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.10.6 + '@types/node': 22.10.7 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -1954,7 +1954,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.22 - '@types/node': 22.10.6 + '@types/node': 22.10.7 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -2024,7 +2024,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.10.6 + '@types/node': 22.10.7 '@types/yargs': 17.0.32 chalk: 4.1.2 @@ -2094,7 +2094,7 @@ snapshots: '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 22.10.6 + '@types/node': 22.10.7 '@types/istanbul-lib-coverage@2.0.6': {} @@ -2113,7 +2113,7 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@22.10.6': + '@types/node@22.10.7': dependencies: undici-types: 6.20.0 @@ -2125,14 +2125,14 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.20.0(@typescript-eslint/parser@8.20.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3)': + '@typescript-eslint/eslint-plugin@8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.20.0(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.20.0 - '@typescript-eslint/type-utils': 8.20.0(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/utils': 8.20.0(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.20.0 + '@typescript-eslint/parser': 8.21.0(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.21.0 + '@typescript-eslint/type-utils': 8.21.0(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/utils': 8.21.0(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.21.0 eslint: 9.18.0 graphemer: 1.4.0 ignore: 5.3.2 @@ -2142,27 +2142,27 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.20.0(eslint@9.18.0)(typescript@5.7.3)': + '@typescript-eslint/parser@8.21.0(eslint@9.18.0)(typescript@5.7.3)': dependencies: - '@typescript-eslint/scope-manager': 8.20.0 - '@typescript-eslint/types': 8.20.0 - '@typescript-eslint/typescript-estree': 8.20.0(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.20.0 + '@typescript-eslint/scope-manager': 8.21.0 + '@typescript-eslint/types': 8.21.0 + '@typescript-eslint/typescript-estree': 8.21.0(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.21.0 debug: 4.4.0 eslint: 9.18.0 typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.20.0': + '@typescript-eslint/scope-manager@8.21.0': dependencies: - '@typescript-eslint/types': 8.20.0 - '@typescript-eslint/visitor-keys': 8.20.0 + '@typescript-eslint/types': 8.21.0 + '@typescript-eslint/visitor-keys': 8.21.0 - '@typescript-eslint/type-utils@8.20.0(eslint@9.18.0)(typescript@5.7.3)': + '@typescript-eslint/type-utils@8.21.0(eslint@9.18.0)(typescript@5.7.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.20.0(typescript@5.7.3) - '@typescript-eslint/utils': 8.20.0(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/typescript-estree': 8.21.0(typescript@5.7.3) + '@typescript-eslint/utils': 8.21.0(eslint@9.18.0)(typescript@5.7.3) debug: 4.4.0 eslint: 9.18.0 ts-api-utils: 2.0.0(typescript@5.7.3) @@ -2170,12 +2170,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.20.0': {} + '@typescript-eslint/types@8.21.0': {} - '@typescript-eslint/typescript-estree@8.20.0(typescript@5.7.3)': + '@typescript-eslint/typescript-estree@8.21.0(typescript@5.7.3)': dependencies: - '@typescript-eslint/types': 8.20.0 - '@typescript-eslint/visitor-keys': 8.20.0 + '@typescript-eslint/types': 8.21.0 + '@typescript-eslint/visitor-keys': 8.21.0 debug: 4.4.0 fast-glob: 3.3.3 is-glob: 4.0.3 @@ -2186,20 +2186,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.20.0(eslint@9.18.0)(typescript@5.7.3)': + '@typescript-eslint/utils@8.21.0(eslint@9.18.0)(typescript@5.7.3)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.18.0) - '@typescript-eslint/scope-manager': 8.20.0 - '@typescript-eslint/types': 8.20.0 - '@typescript-eslint/typescript-estree': 8.20.0(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.21.0 + '@typescript-eslint/types': 8.21.0 + '@typescript-eslint/typescript-estree': 8.21.0(typescript@5.7.3) eslint: 9.18.0 typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.20.0': + '@typescript-eslint/visitor-keys@8.21.0': dependencies: - '@typescript-eslint/types': 8.20.0 + '@typescript-eslint/types': 8.21.0 eslint-visitor-keys: 4.2.0 acorn-jsx@5.3.2(acorn@8.14.0): @@ -2367,13 +2367,13 @@ snapshots: convert-source-map@2.0.0: {} - create-jest@29.7.0(@types/node@22.10.6): + create-jest@29.7.0(@types/node@22.10.7): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.10.6) + jest-config: 29.7.0(@types/node@22.10.7) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -2428,7 +2428,7 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-plugin-prettier@5.2.1(eslint@9.18.0)(prettier@3.4.2): + eslint-plugin-prettier@5.2.3(eslint@9.18.0)(prettier@3.4.2): dependencies: eslint: 9.18.0 prettier: 3.4.2 @@ -2735,7 +2735,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.10.6 + '@types/node': 22.10.7 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.1 @@ -2755,16 +2755,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@22.10.6): + jest-cli@29.7.0(@types/node@22.10.7): dependencies: '@jest/core': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.10.6) + create-jest: 29.7.0(@types/node@22.10.7) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@22.10.6) + jest-config: 29.7.0(@types/node@22.10.7) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -2774,7 +2774,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@22.10.6): + jest-config@29.7.0(@types/node@22.10.7): dependencies: '@babel/core': 7.23.9 '@jest/test-sequencer': 29.7.0 @@ -2799,7 +2799,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 22.10.6 + '@types/node': 22.10.7 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -2828,7 +2828,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.10.6 + '@types/node': 22.10.7 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -2838,7 +2838,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 22.10.6 + '@types/node': 22.10.7 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -2877,7 +2877,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.10.6 + '@types/node': 22.10.7 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -2912,7 +2912,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.10.6 + '@types/node': 22.10.7 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -2940,7 +2940,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.10.6 + '@types/node': 22.10.7 chalk: 4.1.2 cjs-module-lexer: 1.2.3 collect-v8-coverage: 1.0.2 @@ -2986,7 +2986,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.10.6 + '@types/node': 22.10.7 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -3005,7 +3005,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.10.6 + '@types/node': 22.10.7 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -3014,17 +3014,17 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 22.10.6 + '@types/node': 22.10.7 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@22.10.6): + jest@29.7.0(@types/node@22.10.7): dependencies: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@22.10.6) + jest-cli: 29.7.0(@types/node@22.10.7) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -3060,7 +3060,7 @@ snapshots: kleur@3.0.3: {} - lemmy-js-client@0.20.0-inbox-combined.1: {} + lemmy-js-client@0.20.0-search-combined.1: {} leven@3.1.0: {} @@ -3339,12 +3339,12 @@ snapshots: dependencies: typescript: 5.7.3 - ts-jest@29.2.5(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.10.6))(typescript@5.7.3): + ts-jest@29.2.5(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(jest@29.7.0(@types/node@22.10.7))(typescript@5.7.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.10.6) + jest: 29.7.0(@types/node@22.10.7) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -3368,11 +3368,11 @@ snapshots: type-fest@0.21.3: {} - typescript-eslint@8.20.0(eslint@9.18.0)(typescript@5.7.3): + typescript-eslint@8.21.0(eslint@9.18.0)(typescript@5.7.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.20.0(@typescript-eslint/parser@8.20.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/parser': 8.20.0(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/utils': 8.20.0(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/eslint-plugin': 8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/parser': 8.21.0(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/utils': 8.21.0(eslint@9.18.0)(typescript@5.7.3) eslint: 9.18.0 typescript: 5.7.3 transitivePeerDependencies: diff --git a/api_tests/prepare-drone-federation-test.sh b/api_tests/prepare-drone-federation-test.sh index f05b4328e..cb4fcf880 100755 --- a/api_tests/prepare-drone-federation-test.sh +++ b/api_tests/prepare-drone-federation-test.sh @@ -9,7 +9,7 @@ then fi export RUST_BACKTRACE=1 -export RUST_LOG="warn,lemmy_server=$LEMMY_LOG_LEVEL,lemmy_federate=$LEMMY_LOG_LEVEL,lemmy_api=$LEMMY_LOG_LEVEL,lemmy_api_common=$LEMMY_LOG_LEVEL,lemmy_api_crud=$LEMMY_LOG_LEVEL,lemmy_apub=$LEMMY_LOG_LEVEL,lemmy_db_schema=$LEMMY_LOG_LEVEL,lemmy_db_views=$LEMMY_LOG_LEVEL,lemmy_db_views_actor=$LEMMY_LOG_LEVEL,lemmy_db_views_moderator=$LEMMY_LOG_LEVEL,lemmy_routes=$LEMMY_LOG_LEVEL,lemmy_utils=$LEMMY_LOG_LEVEL,lemmy_websocket=$LEMMY_LOG_LEVEL" +export RUST_LOG="warn,lemmy_server=$LEMMY_LOG_LEVEL,lemmy_federate=$LEMMY_LOG_LEVEL,lemmy_api=$LEMMY_LOG_LEVEL,lemmy_api_common=$LEMMY_LOG_LEVEL,lemmy_api_crud=$LEMMY_LOG_LEVEL,lemmy_apub=$LEMMY_LOG_LEVEL,lemmy_db_schema=$LEMMY_LOG_LEVEL,lemmy_db_views=$LEMMY_LOG_LEVEL,lemmy_routes=$LEMMY_LOG_LEVEL,lemmy_utils=$LEMMY_LOG_LEVEL,lemmy_websocket=$LEMMY_LOG_LEVEL" export LEMMY_TEST_FAST_FEDERATION=1 # by default, the persistent federation queue has delays in the scale of 30s-5min diff --git a/api_tests/src/community.spec.ts b/api_tests/src/community.spec.ts index 62c8e7ac8..2258b020e 100644 --- a/api_tests/src/community.spec.ts +++ b/api_tests/src/community.spec.ts @@ -488,7 +488,7 @@ test("Dont receive community activities after unsubscribe", async () => { // await longDelay(); let postResBeta = searchPostLocal(beta, postRes.post_view.post); - expect((await postResBeta).posts.length).toBe(0); + expect((await postResBeta).results.length).toBe(0); }); test("Fetch community, includes posts", async () => { diff --git a/api_tests/src/shared.ts b/api_tests/src/shared.ts index 5e9513df8..88dca842e 100644 --- a/api_tests/src/shared.ts +++ b/api_tests/src/shared.ts @@ -324,9 +324,8 @@ export async function searchPostLocal( post: Post, ): Promise { let form: Search = { - q: post.name, + search_term: post.name, type_: "Posts", - sort: "TopAll", listing_type: "All", }; return api.search(form); @@ -339,7 +338,7 @@ export async function waitForPost( checker: (t: PostView | undefined) => boolean = p => !!p, ) { return waitUntil( - () => searchPostLocal(api, post).then(p => p.posts[0]), + () => searchPostLocal(api, post).then(p => p.results[0] as PostView), checker, ); } diff --git a/crates/api/Cargo.toml b/crates/api/Cargo.toml index 7fcb999fd..281f8a09f 100644 --- a/crates/api/Cargo.toml +++ b/crates/api/Cargo.toml @@ -21,8 +21,6 @@ workspace = true lemmy_utils = { workspace = true } lemmy_db_schema = { workspace = true, features = ["full"] } lemmy_db_views = { workspace = true, features = ["full"] } -lemmy_db_views_moderator = { workspace = true, features = ["full"] } -lemmy_db_views_actor = { workspace = true, features = ["full"] } lemmy_api_common = { workspace = true, features = ["full"] } activitypub_federation = { workspace = true } bcrypt = { workspace = true } diff --git a/crates/api/src/community/add_mod.rs b/crates/api/src/community/add_mod.rs index 4c5b4eae5..3417bc20a 100644 --- a/crates/api/src/community/add_mod.rs +++ b/crates/api/src/community/add_mod.rs @@ -14,8 +14,7 @@ use lemmy_db_schema::{ }, traits::{Crud, Joinable}, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::CommunityModeratorView; +use lemmy_db_views::structs::{CommunityModeratorView, LocalUserView}; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] diff --git a/crates/api/src/community/ban.rs b/crates/api/src/community/ban.rs index 547838fa7..7ba0fb79e 100644 --- a/crates/api/src/community/ban.rs +++ b/crates/api/src/community/ban.rs @@ -24,8 +24,7 @@ use lemmy_db_schema::{ }, traits::{Bannable, Crud, Followable}, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::PersonView; +use lemmy_db_views::structs::{LocalUserView, PersonView}; use lemmy_utils::{ error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::validation::is_valid_body_field, diff --git a/crates/api/src/community/block.rs b/crates/api/src/community/block.rs index d49872493..e0ac782e8 100644 --- a/crates/api/src/community/block.rs +++ b/crates/api/src/community/block.rs @@ -12,8 +12,7 @@ use lemmy_db_schema::{ }, traits::{Blockable, Followable}, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::CommunityView; +use lemmy_db_views::structs::{CommunityView, LocalUserView}; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] diff --git a/crates/api/src/community/follow.rs b/crates/api/src/community/follow.rs index d5cd3e5b1..e2ab7ee87 100644 --- a/crates/api/src/community/follow.rs +++ b/crates/api/src/community/follow.rs @@ -14,8 +14,7 @@ use lemmy_db_schema::{ traits::{Crud, Followable}, CommunityVisibility, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView}; +use lemmy_db_views::structs::{CommunityPersonBanView, CommunityView, LocalUserView}; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] diff --git a/crates/api/src/community/pending_follows/count.rs b/crates/api/src/community/pending_follows/count.rs index e8e333c84..885ce0585 100644 --- a/crates/api/src/community/pending_follows/count.rs +++ b/crates/api/src/community/pending_follows/count.rs @@ -4,8 +4,7 @@ use lemmy_api_common::{ context::LemmyContext, utils::is_mod_or_admin, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::CommunityFollowerView; +use lemmy_db_views::structs::{CommunityFollowerView, LocalUserView}; use lemmy_utils::error::LemmyResult; pub async fn get_pending_follows_count( diff --git a/crates/api/src/community/pending_follows/list.rs b/crates/api/src/community/pending_follows/list.rs index 9f300a74f..bf56478f0 100644 --- a/crates/api/src/community/pending_follows/list.rs +++ b/crates/api/src/community/pending_follows/list.rs @@ -4,8 +4,7 @@ use lemmy_api_common::{ context::LemmyContext, utils::check_community_mod_of_any_or_admin_action, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::CommunityFollowerView; +use lemmy_db_views::structs::{CommunityFollowerView, LocalUserView}; use lemmy_utils::error::LemmyResult; pub async fn get_pending_follows_list( diff --git a/crates/api/src/community/random.rs b/crates/api/src/community/random.rs index 55941229d..0d90605fa 100644 --- a/crates/api/src/community/random.rs +++ b/crates/api/src/community/random.rs @@ -10,8 +10,7 @@ use lemmy_db_schema::source::{ community::Community, local_site::LocalSite, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::CommunityView; +use lemmy_db_views::structs::{CommunityView, LocalUserView}; use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] diff --git a/crates/api/src/community/transfer.rs b/crates/api/src/community/transfer.rs index e60b50aa2..d2e0481d0 100644 --- a/crates/api/src/community/transfer.rs +++ b/crates/api/src/community/transfer.rs @@ -12,8 +12,7 @@ use lemmy_db_schema::{ }, traits::{Crud, Joinable}, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; +use lemmy_db_views::structs::{CommunityModeratorView, CommunityView, LocalUserView}; use lemmy_utils::{ error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, location_info, diff --git a/crates/api/src/local_user/add_admin.rs b/crates/api/src/local_user/add_admin.rs index 1e821bf3e..34d357417 100644 --- a/crates/api/src/local_user/add_admin.rs +++ b/crates/api/src/local_user/add_admin.rs @@ -11,8 +11,7 @@ use lemmy_db_schema::{ }, traits::Crud, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::PersonView; +use lemmy_db_views::structs::{LocalUserView, PersonView}; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] diff --git a/crates/api/src/local_user/ban_person.rs b/crates/api/src/local_user/ban_person.rs index 715bd206d..a6ec52db9 100644 --- a/crates/api/src/local_user/ban_person.rs +++ b/crates/api/src/local_user/ban_person.rs @@ -16,8 +16,7 @@ use lemmy_db_schema::{ }, traits::Crud, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::PersonView; +use lemmy_db_views::structs::{LocalUserView, PersonView}; use lemmy_utils::{ error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::validation::is_valid_body_field, diff --git a/crates/api/src/local_user/block.rs b/crates/api/src/local_user/block.rs index 3aee554d4..5c69835ba 100644 --- a/crates/api/src/local_user/block.rs +++ b/crates/api/src/local_user/block.rs @@ -7,8 +7,7 @@ use lemmy_db_schema::{ source::person_block::{PersonBlock, PersonBlockForm}, traits::Blockable, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::PersonView; +use lemmy_db_views::structs::{LocalUserView, PersonView}; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] diff --git a/crates/api/src/local_user/list_banned.rs b/crates/api/src/local_user/list_banned.rs index ba2c0d403..d4227ca37 100644 --- a/crates/api/src/local_user/list_banned.rs +++ b/crates/api/src/local_user/list_banned.rs @@ -1,7 +1,6 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{context::LemmyContext, person::BannedPersonsResponse, utils::is_admin}; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::PersonView; +use lemmy_db_views::structs::{LocalUserView, PersonView}; use lemmy_utils::error::LemmyResult; pub async fn list_banned_users( diff --git a/crates/api/src/local_user/list_saved.rs b/crates/api/src/local_user/list_saved.rs index bdb1b6b0a..97290c845 100644 --- a/crates/api/src/local_user/list_saved.rs +++ b/crates/api/src/local_user/list_saved.rs @@ -6,7 +6,7 @@ use lemmy_api_common::{ utils::check_private_instance, }; use lemmy_db_views::{ - person_saved_combined_view::PersonSavedCombinedQuery, + combined::person_saved_combined_view::PersonSavedCombinedQuery, structs::{LocalUserView, SiteView}, }; use lemmy_utils::error::LemmyResult; diff --git a/crates/api/src/local_user/notifications/list_inbox.rs b/crates/api/src/local_user/notifications/list_inbox.rs index 7d6e88468..2145619d7 100644 --- a/crates/api/src/local_user/notifications/list_inbox.rs +++ b/crates/api/src/local_user/notifications/list_inbox.rs @@ -3,8 +3,7 @@ use lemmy_api_common::{ context::LemmyContext, person::{ListInbox, ListInboxResponse}, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::inbox_combined_view::InboxCombinedQuery; +use lemmy_db_views::{combined::inbox_combined_view::InboxCombinedQuery, structs::LocalUserView}; use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] diff --git a/crates/api/src/local_user/notifications/unread_count.rs b/crates/api/src/local_user/notifications/unread_count.rs index 4fa959329..2a4738ea9 100644 --- a/crates/api/src/local_user/notifications/unread_count.rs +++ b/crates/api/src/local_user/notifications/unread_count.rs @@ -1,7 +1,6 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{context::LemmyContext, person::GetUnreadCountResponse}; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::InboxCombinedViewInternal; +use lemmy_db_views::structs::{InboxCombinedViewInternal, LocalUserView}; use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] diff --git a/crates/api/src/reports/report_combined/list.rs b/crates/api/src/reports/report_combined/list.rs index 62df91502..646d985d2 100644 --- a/crates/api/src/reports/report_combined/list.rs +++ b/crates/api/src/reports/report_combined/list.rs @@ -4,7 +4,7 @@ use lemmy_api_common::{ reports::combined::{ListReports, ListReportsResponse}, utils::check_community_mod_of_any_or_admin_action, }; -use lemmy_db_views::{report_combined_view::ReportCombinedQuery, structs::LocalUserView}; +use lemmy_db_views::{combined::report_combined_view::ReportCombinedQuery, structs::LocalUserView}; use lemmy_utils::error::LemmyResult; /// Lists reports for a community if an id is supplied diff --git a/crates/api/src/site/leave_admin.rs b/crates/api/src/site/leave_admin.rs index 042009d24..0f3fae934 100644 --- a/crates/api/src/site/leave_admin.rs +++ b/crates/api/src/site/leave_admin.rs @@ -12,8 +12,7 @@ use lemmy_db_schema::{ }, traits::Crud, }; -use lemmy_db_views::structs::{LocalUserView, SiteView}; -use lemmy_db_views_actor::structs::PersonView; +use lemmy_db_views::structs::{LocalUserView, PersonView, SiteView}; use lemmy_utils::{ error::{LemmyErrorType, LemmyResult}, VERSION, diff --git a/crates/api/src/site/mod_log.rs b/crates/api/src/site/mod_log.rs index 8c6bfdb50..ed1c15ce2 100644 --- a/crates/api/src/site/mod_log.rs +++ b/crates/api/src/site/mod_log.rs @@ -5,8 +5,7 @@ use lemmy_api_common::{ utils::{check_community_mod_of_any_or_admin_action, check_private_instance}, }; use lemmy_db_schema::source::local_site::LocalSite; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_moderator::{self, modlog_combined_view::ModlogCombinedQuery}; +use lemmy_db_views::{combined::modlog_combined_view::ModlogCombinedQuery, structs::LocalUserView}; use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] diff --git a/crates/api/src/site/purge/community.rs b/crates/api/src/site/purge/community.rs index c55f753dc..4869831e9 100644 --- a/crates/api/src/site/purge/community.rs +++ b/crates/api/src/site/purge/community.rs @@ -17,8 +17,7 @@ use lemmy_db_schema::{ }, traits::Crud, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::CommunityModeratorView; +use lemmy_db_views::structs::{CommunityModeratorView, LocalUserView}; use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] diff --git a/crates/api/src/site/registration_applications/list.rs b/crates/api/src/site/registration_applications/list.rs index 877e83796..cae0acd43 100644 --- a/crates/api/src/site/registration_applications/list.rs +++ b/crates/api/src/site/registration_applications/list.rs @@ -7,7 +7,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::source::local_site::LocalSite; use lemmy_db_views::{ - registration_application_view::RegistrationApplicationQuery, + registration_applications::registration_application_view::RegistrationApplicationQuery, structs::LocalUserView, }; use lemmy_utils::error::LemmyResult; diff --git a/crates/api_common/Cargo.toml b/crates/api_common/Cargo.toml index 5a2e9bf79..616b30667 100644 --- a/crates/api_common/Cargo.toml +++ b/crates/api_common/Cargo.toml @@ -21,8 +21,6 @@ full = [ "tracing", "rosetta-i18n", "lemmy_db_views/full", - "lemmy_db_views_actor/full", - "lemmy_db_views_moderator/full", "lemmy_utils/full", "activitypub_federation", "encoding_rs", @@ -43,8 +41,6 @@ full = [ [dependencies] lemmy_db_views = { workspace = true } -lemmy_db_views_moderator = { workspace = true } -lemmy_db_views_actor = { workspace = true } lemmy_db_schema = { workspace = true } lemmy_utils = { workspace = true } activitypub_federation = { workspace = true, optional = true } diff --git a/crates/api_common/src/build_response.rs b/crates/api_common/src/build_response.rs index 0245a0459..d7ac888a9 100644 --- a/crates/api_common/src/build_response.rs +++ b/crates/api_common/src/build_response.rs @@ -25,8 +25,7 @@ use lemmy_db_schema::{ }, traits::Crud, }; -use lemmy_db_views::structs::{CommentView, LocalUserView, PostView}; -use lemmy_db_views_actor::structs::CommunityView; +use lemmy_db_views::structs::{CommentView, CommunityView, LocalUserView, PostView}; use lemmy_utils::{ error::LemmyResult, utils::{markdown::markdown_to_html, mention::MentionData}, diff --git a/crates/api_common/src/community.rs b/crates/api_common/src/community.rs index 64643e4a8..a5fadb7fd 100644 --- a/crates/api_common/src/community.rs +++ b/crates/api_common/src/community.rs @@ -4,7 +4,7 @@ use lemmy_db_schema::{ CommunityVisibility, ListingType, }; -use lemmy_db_views_actor::structs::{ +use lemmy_db_views::structs::{ CommunityModeratorView, CommunitySortType, CommunityView, diff --git a/crates/api_common/src/lib.rs b/crates/api_common/src/lib.rs index fde2102d5..7eac9b21e 100644 --- a/crates/api_common/src/lib.rs +++ b/crates/api_common/src/lib.rs @@ -24,8 +24,6 @@ pub mod utils; pub extern crate lemmy_db_schema; pub extern crate lemmy_db_views; -pub extern crate lemmy_db_views_actor; -pub extern crate lemmy_db_views_moderator; pub extern crate lemmy_utils; pub use lemmy_utils::error::LemmyErrorType; diff --git a/crates/api_common/src/person.rs b/crates/api_common/src/person.rs index 67401663f..224363700 100644 --- a/crates/api_common/src/person.rs +++ b/crates/api_common/src/person.rs @@ -17,15 +17,13 @@ use lemmy_db_schema::{ PostSortType, }; use lemmy_db_views::structs::{ + CommunityModeratorView, + InboxCombinedPaginationCursor, + InboxCombinedView, LocalImageView, PersonContentCombinedPaginationCursor, PersonContentCombinedView, PersonSavedCombinedPaginationCursor, -}; -use lemmy_db_views_actor::structs::{ - CommunityModeratorView, - InboxCombinedPaginationCursor, - InboxCombinedView, PersonView, }; use serde::{Deserialize, Serialize}; diff --git a/crates/api_common/src/post.rs b/crates/api_common/src/post.rs index a26d6265a..68ec332f6 100644 --- a/crates/api_common/src/post.rs +++ b/crates/api_common/src/post.rs @@ -4,8 +4,13 @@ use lemmy_db_schema::{ PostFeatureType, PostSortType, }; -use lemmy_db_views::structs::{PaginationCursor, PostView, VoteView}; -use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; +use lemmy_db_views::structs::{ + CommunityModeratorView, + CommunityView, + PaginationCursor, + PostView, + VoteView, +}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; #[cfg(feature = "full")] diff --git a/crates/api_common/src/private_message.rs b/crates/api_common/src/private_message.rs index f8134ea27..aac27499a 100644 --- a/crates/api_common/src/private_message.rs +++ b/crates/api_common/src/private_message.rs @@ -1,5 +1,5 @@ use lemmy_db_schema::newtypes::{PersonId, PrivateMessageId}; -use lemmy_db_views_actor::structs::PrivateMessageView; +use lemmy_db_views::structs::PrivateMessageView; use serde::{Deserialize, Serialize}; #[cfg(feature = "full")] use ts_rs::TS; diff --git a/crates/api_common/src/send_activity.rs b/crates/api_common/src/send_activity.rs index 07203ffe4..b606c9a90 100644 --- a/crates/api_common/src/send_activity.rs +++ b/crates/api_common/src/send_activity.rs @@ -11,7 +11,7 @@ use lemmy_db_schema::{ private_message::PrivateMessage, }, }; -use lemmy_db_views_actor::structs::PrivateMessageView; +use lemmy_db_views::structs::PrivateMessageView; use lemmy_utils::error::LemmyResult; use std::sync::{LazyLock, OnceLock}; use tokio::{ diff --git a/crates/api_common/src/site.rs b/crates/api_common/src/site.rs index 8dfb1132b..25dcf90b3 100644 --- a/crates/api_common/src/site.rs +++ b/crates/api_common/src/site.rs @@ -27,22 +27,24 @@ use lemmy_db_schema::{ PostListingMode, PostSortType, RegistrationMode, + SearchSortType, SearchType, }; use lemmy_db_views::structs::{ CommentView, - LocalUserView, - PostView, - RegistrationApplicationView, - SiteView, -}; -use lemmy_db_views_actor::structs::{ CommunityFollowerView, CommunityModeratorView, CommunityView, + LocalUserView, + ModlogCombinedPaginationCursor, + ModlogCombinedView, PersonView, + PostView, + RegistrationApplicationView, + SearchCombinedPaginationCursor, + SearchCombinedView, + SiteView, }; -use lemmy_db_views_moderator::structs::{ModlogCombinedPaginationCursor, ModlogCombinedView}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; #[cfg(feature = "full")] @@ -52,9 +54,10 @@ use ts_rs::TS; #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)] #[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] -/// Searches the site, given a query string, and some optional filters. +/// Searches the site, given a search term, and some optional filters. pub struct Search { - pub q: String, + #[cfg_attr(feature = "full", ts(optional))] + pub search_term: Option, #[cfg_attr(feature = "full", ts(optional))] pub community_id: Option, #[cfg_attr(feature = "full", ts(optional))] @@ -64,14 +67,10 @@ pub struct Search { #[cfg_attr(feature = "full", ts(optional))] pub type_: Option, #[cfg_attr(feature = "full", ts(optional))] - pub sort: Option, + pub sort: Option, #[cfg_attr(feature = "full", ts(optional))] pub listing_type: Option, #[cfg_attr(feature = "full", ts(optional))] - pub page: Option, - #[cfg_attr(feature = "full", ts(optional))] - pub limit: Option, - #[cfg_attr(feature = "full", ts(optional))] pub title_only: Option, #[cfg_attr(feature = "full", ts(optional))] pub post_url_only: Option, @@ -79,19 +78,18 @@ pub struct Search { pub liked_only: Option, #[cfg_attr(feature = "full", ts(optional))] pub disliked_only: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub page_cursor: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub page_back: Option, } #[derive(Debug, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] /// The search response, containing lists of the return type possibilities -// TODO this should be redone as a list of tagged enums pub struct SearchResponse { - pub type_: SearchType, - pub comments: Vec, - pub posts: Vec, - pub communities: Vec, - pub users: Vec, + pub results: Vec, } #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)] diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index 6a3f0f337..fc572b087 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -47,14 +47,16 @@ use lemmy_db_schema::{ RegistrationMode, }; use lemmy_db_views::{ - comment_view::CommentQuery, - structs::{LocalImageView, LocalUserView, SiteView}, -}; -use lemmy_db_views_actor::structs::{ - CommunityFollowerView, - CommunityModeratorView, - CommunityPersonBanView, - CommunityView, + comment::comment_view::CommentQuery, + structs::{ + CommunityFollowerView, + CommunityModeratorView, + CommunityPersonBanView, + CommunityView, + LocalImageView, + LocalUserView, + SiteView, + }, }; use lemmy_utils::{ email::{send_email, translations::Lang}, @@ -1213,8 +1215,8 @@ mod tests { }, ModlogActionType, }; - use lemmy_db_views_moderator::{ - modlog_combined_view::ModlogCombinedQuery, + use lemmy_db_views::{ + combined::modlog_combined_view::ModlogCombinedQuery, structs::{ModRemoveCommentView, ModRemovePostView, ModlogCombinedView}, }; use pretty_assertions::assert_eq; diff --git a/crates/api_crud/Cargo.toml b/crates/api_crud/Cargo.toml index 71bcfd291..538a48216 100644 --- a/crates/api_crud/Cargo.toml +++ b/crates/api_crud/Cargo.toml @@ -16,7 +16,6 @@ workspace = true lemmy_utils = { workspace = true, features = ["full"] } lemmy_db_schema = { workspace = true, features = ["full"] } lemmy_db_views = { workspace = true, features = ["full"] } -lemmy_db_views_actor = { workspace = true, features = ["full"] } lemmy_api_common = { workspace = true, features = ["full"] } activitypub_federation = { workspace = true } bcrypt = { workspace = true } diff --git a/crates/api_crud/src/community/delete.rs b/crates/api_crud/src/community/delete.rs index 7f9d04933..1482206e8 100644 --- a/crates/api_crud/src/community/delete.rs +++ b/crates/api_crud/src/community/delete.rs @@ -11,8 +11,7 @@ use lemmy_db_schema::{ source::community::{Community, CommunityUpdateForm}, traits::Crud, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::CommunityModeratorView; +use lemmy_db_views::structs::{CommunityModeratorView, LocalUserView}; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] diff --git a/crates/api_crud/src/community/list.rs b/crates/api_crud/src/community/list.rs index 9c13ae89f..6886cf3fc 100644 --- a/crates/api_crud/src/community/list.rs +++ b/crates/api_crud/src/community/list.rs @@ -4,8 +4,10 @@ use lemmy_api_common::{ context::LemmyContext, utils::{check_private_instance, is_admin}, }; -use lemmy_db_views::structs::{LocalUserView, SiteView}; -use lemmy_db_views_actor::community_view::CommunityQuery; +use lemmy_db_views::{ + community::community_view::CommunityQuery, + structs::{LocalUserView, SiteView}, +}; use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs index 91e47d4aa..31f174980 100644 --- a/crates/api_crud/src/post/create.rs +++ b/crates/api_crud/src/post/create.rs @@ -27,8 +27,7 @@ use lemmy_db_schema::{ traits::{Crud, Likeable}, utils::diesel_url_create, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::CommunityModeratorView; +use lemmy_db_views::structs::{CommunityModeratorView, LocalUserView}; use lemmy_utils::{ error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::{ diff --git a/crates/api_crud/src/post/read.rs b/crates/api_crud/src/post/read.rs index 3b6ef9414..10986a119 100644 --- a/crates/api_crud/src/post/read.rs +++ b/crates/api_crud/src/post/read.rs @@ -12,10 +12,9 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::{ - post_view::PostQuery, - structs::{LocalUserView, PostView, SiteView}, + post::post_view::PostQuery, + structs::{CommunityModeratorView, CommunityView, LocalUserView, PostView, SiteView}, }; -use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; use lemmy_utils::error::{LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs index fd95a2b9e..1a6a78d00 100644 --- a/crates/api_crud/src/private_message/create.rs +++ b/crates/api_crud/src/private_message/create.rs @@ -21,8 +21,7 @@ use lemmy_db_schema::{ }, traits::Crud, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::PrivateMessageView; +use lemmy_db_views::structs::{LocalUserView, PrivateMessageView}; use lemmy_utils::{ error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::{markdown::markdown_to_html, validation::is_valid_body_field}, diff --git a/crates/api_crud/src/private_message/delete.rs b/crates/api_crud/src/private_message/delete.rs index d06c8bc04..30efc020c 100644 --- a/crates/api_crud/src/private_message/delete.rs +++ b/crates/api_crud/src/private_message/delete.rs @@ -9,8 +9,7 @@ use lemmy_db_schema::{ source::private_message::{PrivateMessage, PrivateMessageUpdateForm}, traits::Crud, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::PrivateMessageView; +use lemmy_db_views::structs::{LocalUserView, PrivateMessageView}; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] diff --git a/crates/api_crud/src/private_message/update.rs b/crates/api_crud/src/private_message/update.rs index 22c1da4a2..b9e4785ef 100644 --- a/crates/api_crud/src/private_message/update.rs +++ b/crates/api_crud/src/private_message/update.rs @@ -14,8 +14,7 @@ use lemmy_db_schema::{ }, traits::Crud, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::PrivateMessageView; +use lemmy_db_views::structs::{LocalUserView, PrivateMessageView}; use lemmy_utils::{ error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::validation::is_valid_body_field, diff --git a/crates/api_crud/src/site/read.rs b/crates/api_crud/src/site/read.rs index 64d2237a0..9823a0807 100644 --- a/crates/api_crud/src/site/read.rs +++ b/crates/api_crud/src/site/read.rs @@ -8,8 +8,7 @@ use lemmy_db_schema::source::{ oauth_provider::OAuthProvider, tagline::Tagline, }; -use lemmy_db_views::structs::{LocalUserView, SiteView}; -use lemmy_db_views_actor::structs::PersonView; +use lemmy_db_views::structs::{LocalUserView, PersonView, SiteView}; use lemmy_utils::{build_cache, error::LemmyResult, CacheLock, VERSION}; use std::sync::LazyLock; diff --git a/crates/api_crud/src/user/my_user.rs b/crates/api_crud/src/user/my_user.rs index f7a92eb99..66026d60b 100644 --- a/crates/api_crud/src/user/my_user.rs +++ b/crates/api_crud/src/user/my_user.rs @@ -6,8 +6,7 @@ use lemmy_db_schema::source::{ instance_block::InstanceBlock, person_block::PersonBlock, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::{CommunityFollowerView, CommunityModeratorView}; +use lemmy_db_views::structs::{CommunityFollowerView, CommunityModeratorView, LocalUserView}; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] diff --git a/crates/apub/Cargo.toml b/crates/apub/Cargo.toml index 75d16ff0f..dbe660957 100644 --- a/crates/apub/Cargo.toml +++ b/crates/apub/Cargo.toml @@ -21,7 +21,6 @@ workspace = true lemmy_utils = { workspace = true, features = ["full"] } lemmy_db_schema = { workspace = true, features = ["full"] } lemmy_db_views = { workspace = true, features = ["full"] } -lemmy_db_views_actor = { workspace = true, features = ["full"] } lemmy_api_common = { workspace = true, features = ["full"] } activitypub_federation = { workspace = true } diesel = { workspace = true } diff --git a/crates/apub/src/activities/community/report.rs b/crates/apub/src/activities/community/report.rs index 50ee60183..b9208cd7e 100644 --- a/crates/apub/src/activities/community/report.rs +++ b/crates/apub/src/activities/community/report.rs @@ -29,7 +29,7 @@ use lemmy_db_schema::{ }, traits::{Crud, Reportable}, }; -use lemmy_db_views_actor::structs::CommunityModeratorView; +use lemmy_db_views::structs::CommunityModeratorView; use lemmy_utils::error::{LemmyError, LemmyResult}; use url::Url; diff --git a/crates/apub/src/activities/create_or_update/private_message.rs b/crates/apub/src/activities/create_or_update/private_message.rs index ce04a9330..b6e7478ef 100644 --- a/crates/apub/src/activities/create_or_update/private_message.rs +++ b/crates/apub/src/activities/create_or_update/private_message.rs @@ -14,7 +14,7 @@ use activitypub_federation::{ }; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::source::activity::ActivitySendTargets; -use lemmy_db_views_actor::structs::PrivateMessageView; +use lemmy_db_views::structs::PrivateMessageView; use lemmy_utils::error::{LemmyError, LemmyResult}; use url::Url; diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index feef9cbd0..92d1897fe 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -43,7 +43,7 @@ use lemmy_db_schema::{ traits::Crud, CommunityVisibility, }; -use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView}; +use lemmy_db_views::structs::{CommunityPersonBanView, CommunityView}; use lemmy_utils::error::{FederationError, LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult}; use serde::Serialize; use tracing::info; diff --git a/crates/apub/src/api/list_comments.rs b/crates/apub/src/api/list_comments.rs index 3759d1a4b..cd01752a0 100644 --- a/crates/apub/src/api/list_comments.rs +++ b/crates/apub/src/api/list_comments.rs @@ -16,7 +16,7 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::{ - comment_view::CommentQuery, + comment::comment_view::CommentQuery, structs::{CommentView, LocalUserView, SiteView}, }; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; diff --git a/crates/apub/src/api/list_person_content.rs b/crates/apub/src/api/list_person_content.rs index 774f80766..b3f9c16b5 100644 --- a/crates/apub/src/api/list_person_content.rs +++ b/crates/apub/src/api/list_person_content.rs @@ -7,7 +7,7 @@ use lemmy_api_common::{ utils::check_private_instance, }; use lemmy_db_views::{ - person_content_combined_view::PersonContentCombinedQuery, + combined::person_content_combined_view::PersonContentCombinedQuery, structs::{LocalUserView, SiteView}, }; use lemmy_utils::error::LemmyResult; diff --git a/crates/apub/src/api/list_posts.rs b/crates/apub/src/api/list_posts.rs index 625661b7f..f85c3f879 100644 --- a/crates/apub/src/api/list_posts.rs +++ b/crates/apub/src/api/list_posts.rs @@ -15,7 +15,7 @@ use lemmy_db_schema::{ source::{community::Community, post::PostRead}, }; use lemmy_db_views::{ - post_view::PostQuery, + post::post_view::PostQuery, structs::{LocalUserView, PaginationCursor, SiteView}, }; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; diff --git a/crates/apub/src/api/read_community.rs b/crates/apub/src/api/read_community.rs index f94769158..46195a545 100644 --- a/crates/apub/src/api/read_community.rs +++ b/crates/apub/src/api/read_community.rs @@ -11,8 +11,7 @@ use lemmy_db_schema::source::{ community::Community, local_site::LocalSite, }; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; +use lemmy_db_views::structs::{CommunityModeratorView, CommunityView, LocalUserView}; use lemmy_utils::error::{LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] diff --git a/crates/apub/src/api/read_person.rs b/crates/apub/src/api/read_person.rs index b79871b93..e38f6568c 100644 --- a/crates/apub/src/api/read_person.rs +++ b/crates/apub/src/api/read_person.rs @@ -6,8 +6,7 @@ use lemmy_api_common::{ person::{GetPersonDetails, GetPersonDetailsResponse}, utils::{check_private_instance, is_admin, read_site_for_actor}, }; -use lemmy_db_views::structs::{LocalUserView, SiteView}; -use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonView}; +use lemmy_db_views::structs::{CommunityModeratorView, LocalUserView, PersonView, SiteView}; use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] diff --git a/crates/apub/src/api/resolve_object.rs b/crates/apub/src/api/resolve_object.rs index 8d2cd384f..9b4d4551c 100644 --- a/crates/apub/src/api/resolve_object.rs +++ b/crates/apub/src/api/resolve_object.rs @@ -11,8 +11,7 @@ use lemmy_api_common::{ utils::check_private_instance, }; use lemmy_db_schema::{source::local_site::LocalSite, utils::DbPool}; -use lemmy_db_views::structs::{CommentView, LocalUserView, PostView}; -use lemmy_db_views_actor::structs::{CommunityView, PersonView}; +use lemmy_db_views::structs::{CommentView, CommunityView, LocalUserView, PersonView, PostView}; use lemmy_utils::error::{LemmyErrorExt2, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] diff --git a/crates/apub/src/api/search.rs b/crates/apub/src/api/search.rs index 0ae7053d3..41e1ded39 100644 --- a/crates/apub/src/api/search.rs +++ b/crates/apub/src/api/search.rs @@ -4,19 +4,13 @@ use actix_web::web::{Json, Query}; use lemmy_api_common::{ context::LemmyContext, site::{Search, SearchResponse}, - utils::{check_conflicting_like_filters, check_private_instance, is_admin}, + utils::{check_conflicting_like_filters, check_private_instance}, }; -use lemmy_db_schema::{source::community::Community, utils::post_to_comment_sort_type, SearchType}; +use lemmy_db_schema::source::community::Community; use lemmy_db_views::{ - comment_view::CommentQuery, - post_view::PostQuery, + combined::search_combined_view::SearchCombinedQuery, structs::{LocalUserView, SiteView}, }; -use lemmy_db_views_actor::{ - community_view::CommunityQuery, - person_view::PersonQuery, - structs::CommunitySortType, -}; use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] @@ -28,154 +22,43 @@ pub async fn search( let local_site = SiteView::read_local(&mut context.pool()).await?; check_private_instance(&local_user_view, &local_site.local_site)?; + check_conflicting_like_filters(data.liked_only, data.disliked_only)?; - let is_admin = local_user_view - .as_ref() - .map(|luv| is_admin(luv).is_ok()) - .unwrap_or_default(); - - let mut posts = Vec::new(); - let mut comments = Vec::new(); - let mut communities = Vec::new(); - let mut users = Vec::new(); - - // TODO no clean / non-nsfw searching rn - - let Query(Search { - q, - community_id, - community_name, - creator_id, - type_, - sort, - listing_type, - page, - limit, - title_only, - post_url_only, - liked_only, - disliked_only, - }) = data; - - let q = q.clone(); - let search_type = type_.unwrap_or(SearchType::All); - let community_id = if let Some(name) = &community_name { + let community_id = if let Some(name) = &data.community_name { Some( resolve_actor_identifier::(name, &context, &local_user_view, false) .await?, ) .map(|c| c.id) } else { - community_id + data.community_id }; - let local_user = local_user_view.as_ref().map(|l| &l.local_user); + let search_term = data.search_term.clone(); - check_conflicting_like_filters(liked_only, disliked_only)?; + // parse pagination token + let page_after = if let Some(pa) = &data.page_cursor { + Some(pa.read(&mut context.pool()).await?) + } else { + None + }; + let page_back = data.page_back; - let posts_query = PostQuery { - sort, - listing_type, + let results = SearchCombinedQuery { + search_term, community_id, - creator_id, - local_user, - search_term: Some(q.clone()), - page, - limit, - title_only, - url_only: post_url_only, - liked_only, - disliked_only, - ..Default::default() - }; + creator_id: data.creator_id, + type_: data.type_, + sort: data.sort, + listing_type: data.listing_type, + title_only: data.title_only, + post_url_only: data.post_url_only, + liked_only: data.liked_only, + disliked_only: data.disliked_only, + page_after, + page_back, + } + .list(&mut context.pool(), &local_user_view) + .await?; - let comment_query = CommentQuery { - sort: sort.map(post_to_comment_sort_type), - listing_type, - search_term: Some(q.clone()), - community_id, - creator_id, - local_user, - page, - limit, - liked_only, - disliked_only, - ..Default::default() - }; - - let community_query = CommunityQuery { - sort: sort.map(CommunitySortType::from), - listing_type, - search_term: Some(q.clone()), - title_only, - local_user, - is_mod_or_admin: is_admin, - page, - limit, - ..Default::default() - }; - - let person_query = PersonQuery { - sort, - search_term: Some(q.clone()), - listing_type, - page, - limit, - }; - - match search_type { - SearchType::Posts => { - posts = posts_query - .list(&local_site.site, &mut context.pool()) - .await?; - } - SearchType::Comments => { - comments = comment_query - .list(&local_site.site, &mut context.pool()) - .await?; - } - SearchType::Communities => { - communities = community_query - .list(&local_site.site, &mut context.pool()) - .await?; - } - SearchType::Users => { - users = person_query.list(&mut context.pool()).await?; - } - SearchType::All => { - // If the community or creator is included, dont search communities or users - let community_or_creator_included = - community_id.is_some() || community_name.is_some() || creator_id.is_some(); - - posts = posts_query - .list(&local_site.site, &mut context.pool()) - .await?; - - comments = comment_query - .list(&local_site.site, &mut context.pool()) - .await?; - - communities = if community_or_creator_included { - vec![] - } else { - community_query - .list(&local_site.site, &mut context.pool()) - .await? - }; - - users = if community_or_creator_included { - vec![] - } else { - person_query.list(&mut context.pool()).await? - }; - } - }; - - // Return the jwt - Ok(Json(SearchResponse { - type_: search_type, - comments, - posts, - communities, - users, - })) + Ok(Json(SearchResponse { results })) } diff --git a/crates/apub/src/api/user_settings_backup.rs b/crates/apub/src/api/user_settings_backup.rs index d98df25ad..3180223c5 100644 --- a/crates/apub/src/api/user_settings_backup.rs +++ b/crates/apub/src/api/user_settings_backup.rs @@ -323,8 +323,7 @@ pub(crate) mod tests { }, traits::{Crud, Followable}, }; - use lemmy_db_views::structs::LocalUserView; - use lemmy_db_views_actor::structs::CommunityFollowerView; + use lemmy_db_views::structs::{CommunityFollowerView, LocalUserView}; use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use serial_test::serial; use std::time::Duration; diff --git a/crates/apub/src/collections/community_follower.rs b/crates/apub/src/collections/community_follower.rs index a4f5debbc..7293b2297 100644 --- a/crates/apub/src/collections/community_follower.rs +++ b/crates/apub/src/collections/community_follower.rs @@ -10,7 +10,7 @@ use activitypub_federation::{ }; use lemmy_api_common::{context::LemmyContext, utils::generate_followers_url}; use lemmy_db_schema::aggregates::structs::CommunityAggregates; -use lemmy_db_views_actor::structs::CommunityFollowerView; +use lemmy_db_views::structs::CommunityFollowerView; use lemmy_utils::error::LemmyError; use url::Url; diff --git a/crates/apub/src/collections/community_moderators.rs b/crates/apub/src/collections/community_moderators.rs index c7b925f97..51913724c 100644 --- a/crates/apub/src/collections/community_moderators.rs +++ b/crates/apub/src/collections/community_moderators.rs @@ -14,7 +14,7 @@ use lemmy_db_schema::{ source::community::{CommunityModerator, CommunityModeratorForm}, traits::Joinable, }; -use lemmy_db_views_actor::structs::CommunityModeratorView; +use lemmy_db_views::structs::CommunityModeratorView; use lemmy_utils::error::{LemmyError, LemmyResult}; use url::Url; diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs index 01199bc2b..5e89f07f9 100644 --- a/crates/apub/src/collections/community_outbox.rs +++ b/crates/apub/src/collections/community_outbox.rs @@ -19,7 +19,7 @@ use activitypub_federation::{ use futures::future::join_all; use lemmy_api_common::{context::LemmyContext, utils::generate_outbox_url}; use lemmy_db_schema::{source::site::Site, utils::FETCH_LIMIT_MAX, PostSortType}; -use lemmy_db_views::post_view::PostQuery; +use lemmy_db_views::post::post_view::PostQuery; use lemmy_utils::error::{LemmyError, LemmyResult}; use url::Url; diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index dbcc51258..f4dd23d5b 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -23,7 +23,7 @@ use actix_web::{ }; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{source::community::Community, traits::ApubActor, CommunityVisibility}; -use lemmy_db_views_actor::structs::CommunityFollowerView; +use lemmy_db_views::structs::CommunityFollowerView; use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use serde::Deserialize; diff --git a/crates/apub/src/http/mod.rs b/crates/apub/src/http/mod.rs index 52e036376..dcfe16df6 100644 --- a/crates/apub/src/http/mod.rs +++ b/crates/apub/src/http/mod.rs @@ -18,7 +18,7 @@ use lemmy_db_schema::{ source::{activity::SentActivity, community::Community}, CommunityVisibility, }; -use lemmy_db_views_actor::structs::CommunityFollowerView; +use lemmy_db_views::structs::CommunityFollowerView; use lemmy_utils::error::{FederationError, LemmyErrorExt, LemmyErrorType, LemmyResult}; use serde::{Deserialize, Serialize}; use std::{ops::Deref, time::Duration}; diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index 25ff7a91f..a3606b8c9 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -40,7 +40,7 @@ use lemmy_db_schema::{ traits::{ApubActor, Crud}, CommunityVisibility, }; -use lemmy_db_views_actor::structs::CommunityFollowerView; +use lemmy_db_views::structs::CommunityFollowerView; use lemmy_utils::{ error::{LemmyError, LemmyResult}, spawn_try_task, diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index 73d937ad5..e5ea5838a 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -36,7 +36,7 @@ use lemmy_db_schema::{ }, traits::Crud, }; -use lemmy_db_views_actor::structs::CommunityModeratorView; +use lemmy_db_views::structs::CommunityModeratorView; use lemmy_utils::{ error::{LemmyError, LemmyResult}, spawn_try_task, diff --git a/crates/db_perf/src/main.rs b/crates/db_perf/src/main.rs index 71554219b..aac40d660 100644 --- a/crates/db_perf/src/main.rs +++ b/crates/db_perf/src/main.rs @@ -22,7 +22,7 @@ use lemmy_db_schema::{ utils::{build_db_pool, get_conn, now}, PostSortType, }; -use lemmy_db_views::{post_view::PostQuery, structs::PaginationCursor}; +use lemmy_db_views::{post::post_view::PostQuery, structs::PaginationCursor}; use lemmy_utils::error::{LemmyErrorExt2, LemmyResult}; use std::num::NonZeroU32; use url::Url; @@ -160,7 +160,7 @@ async fn try_main() -> LemmyResult<()> { .list(&site()?, &mut conn.into()) .await?; - if let Some(post_view) = post_views.into_iter().last() { + if let Some(post_view) = post_views.into_iter().next_back() { println!("👀 getting pagination cursor data for next page"); let cursor_data = PaginationCursor::after_post(&post_view) .read(&mut conn.into(), None) diff --git a/crates/db_schema/replaceable_schema/triggers.sql b/crates/db_schema/replaceable_schema/triggers.sql index ad61f0485..adf06c32b 100644 --- a/crates/db_schema/replaceable_schema/triggers.sql +++ b/crates/db_schema/replaceable_schema/triggers.sql @@ -927,3 +927,126 @@ CREATE TRIGGER require_uplete FOR EACH STATEMENT EXECUTE FUNCTION r.require_uplete (); +-- search: (post, comment, community, person) +CREATE PROCEDURE r.create_search_combined_trigger (table_name text) +LANGUAGE plpgsql +AS $a$ +BEGIN + EXECUTE replace($b$ CREATE FUNCTION r.search_combined_thing_insert ( ) + RETURNS TRIGGER + LANGUAGE plpgsql + AS $$ + BEGIN + -- TODO need to figure out how to do the other columns here + INSERT INTO search_combined (published, thing_id) + VALUES (NEW.published, NEW.id); + RETURN NEW; + END $$; + CREATE TRIGGER search_combined + AFTER INSERT ON thing + FOR EACH ROW + EXECUTE FUNCTION r.search_combined_thing_insert ( ); + $b$, + 'thing', + table_name); +END; +$a$; + +CALL r.create_search_combined_trigger ('post'); + +CALL r.create_search_combined_trigger ('comment'); + +CALL r.create_search_combined_trigger ('community'); + +CALL r.create_search_combined_trigger ('person'); + +-- You also need to triggers to update the `score` column. +-- post | post_aggregates::score +-- comment | comment_aggregates::score +-- community | community_aggregates::users_active_monthly +-- person | person_aggregates::post_score +-- +-- Post score +CREATE FUNCTION r.search_combined_post_score_update () + RETURNS TRIGGER + LANGUAGE plpgsql + AS $$ +BEGIN + UPDATE + search_combined + SET + score = NEW.score + WHERE + post_id = NEW.post_id; + RETURN NULL; +END +$$; + +CREATE TRIGGER search_combined_post_score + AFTER UPDATE OF score ON post_aggregates + FOR EACH ROW + EXECUTE FUNCTION r.search_combined_post_score_update (); + +-- Comment score +CREATE FUNCTION r.search_combined_comment_score_update () + RETURNS TRIGGER + LANGUAGE plpgsql + AS $$ +BEGIN + UPDATE + search_combined + SET + score = NEW.score + WHERE + comment_id = NEW.comment_id; + RETURN NULL; +END +$$; + +CREATE TRIGGER search_combined_comment_score + AFTER UPDATE OF score ON comment_aggregates + FOR EACH ROW + EXECUTE FUNCTION r.search_combined_comment_score_update (); + +-- Person score +CREATE FUNCTION r.search_combined_person_score_update () + RETURNS TRIGGER + LANGUAGE plpgsql + AS $$ +BEGIN + UPDATE + search_combined + SET + score = NEW.post_score + WHERE + person_id = NEW.person_id; + RETURN NULL; +END +$$; + +CREATE TRIGGER search_combined_person_score + AFTER UPDATE OF post_score ON person_aggregates + FOR EACH ROW + EXECUTE FUNCTION r.search_combined_person_score_update (); + +-- Community score +CREATE FUNCTION r.search_combined_community_score_update () + RETURNS TRIGGER + LANGUAGE plpgsql + AS $$ +BEGIN + UPDATE + search_combined + SET + score = NEW.users_active_month + WHERE + community_id = NEW.community_id; + RETURN NULL; +END +$$; + +CREATE TRIGGER search_combined_community_score + AFTER UPDATE OF users_active_month ON community_aggregates + FOR EACH ROW + EXECUTE FUNCTION r.search_combined_community_score_update (); + diff --git a/crates/db_schema/src/impls/images.rs b/crates/db_schema/src/impls/images.rs index df894f68d..d253e998c 100644 --- a/crates/db_schema/src/impls/images.rs +++ b/crates/db_schema/src/impls/images.rs @@ -66,7 +66,7 @@ impl LocalImage { } pub async fn delete_by_url(pool: &mut DbPool<'_>, url: &DbUrl) -> Result { - let alias = url.as_str().split('/').last().ok_or(NotFound)?; + let alias = url.as_str().split('/').next_back().ok_or(NotFound)?; Self::delete_by_alias(pool, alias).await } } diff --git a/crates/db_schema/src/lib.rs b/crates/db_schema/src/lib.rs index fd270e6d5..980e6ac8d 100644 --- a/crates/db_schema/src/lib.rs +++ b/crates/db_schema/src/lib.rs @@ -101,6 +101,19 @@ pub enum CommentSortType { Controversial, } +#[derive( + EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, Hash, +)] +#[cfg_attr(feature = "full", derive(TS))] +#[cfg_attr(feature = "full", ts(export))] +/// The search sort types. +pub enum SearchSortType { + #[default] + New, + Top, + Old, +} + #[derive( EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, Hash, )] @@ -166,11 +179,14 @@ pub enum PostListingMode { SmallCard, } -#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] +#[derive( + EnumString, Display, Debug, Serialize, Deserialize, Default, Clone, Copy, PartialEq, Eq, Hash, +)] #[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] /// The type of content returned from a search. pub enum SearchType { + #[default] All, Comments, Posts, diff --git a/crates/db_schema/src/newtypes.rs b/crates/db_schema/src/newtypes.rs index 66a02a79b..91ba5b362 100644 --- a/crates/db_schema/src/newtypes.rs +++ b/crates/db_schema/src/newtypes.rs @@ -220,6 +220,11 @@ pub struct ModlogCombinedId(i32); /// The inbox combined id pub struct InboxCombinedId(i32); +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType))] +/// The search combined id +pub struct SearchCombinedId(i32); + #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] #[cfg_attr(feature = "full", derive(DieselNewType, TS))] #[cfg_attr(feature = "full", ts(export))] diff --git a/crates/db_schema/src/schema.rs b/crates/db_schema/src/schema.rs index 80a1b4c6f..84a14bf22 100644 --- a/crates/db_schema/src/schema.rs +++ b/crates/db_schema/src/schema.rs @@ -995,6 +995,18 @@ diesel::table! { } } +diesel::table! { + search_combined (id) { + id -> Int4, + published -> Timestamptz, + score -> Int8, + post_id -> Nullable, + comment_id -> Nullable, + community_id -> Nullable, + person_id -> Nullable, + } +} + diesel::table! { secret (id) { id -> Int4, @@ -1196,6 +1208,10 @@ diesel::joinable!(report_combined -> comment_report (comment_report_id)); diesel::joinable!(report_combined -> community_report (community_report_id)); diesel::joinable!(report_combined -> post_report (post_report_id)); diesel::joinable!(report_combined -> private_message_report (private_message_report_id)); +diesel::joinable!(search_combined -> comment (comment_id)); +diesel::joinable!(search_combined -> community (community_id)); +diesel::joinable!(search_combined -> person (person_id)); +diesel::joinable!(search_combined -> post (post_id)); diesel::joinable!(site -> instance (instance_id)); diesel::joinable!(site_aggregates -> site (site_id)); diesel::joinable!(site_language -> language (language_id)); @@ -1274,6 +1290,7 @@ diesel::allow_tables_to_appear_in_same_query!( registration_application, remote_image, report_combined, + search_combined, secret, sent_activity, site, diff --git a/crates/db_schema/src/source/combined/mod.rs b/crates/db_schema/src/source/combined/mod.rs index 2555ef5be..458fdd519 100644 --- a/crates/db_schema/src/source/combined/mod.rs +++ b/crates/db_schema/src/source/combined/mod.rs @@ -3,3 +3,4 @@ pub mod modlog; pub mod person_content; pub mod person_saved; pub mod report; +pub mod search; diff --git a/crates/db_schema/src/source/combined/search.rs b/crates/db_schema/src/source/combined/search.rs new file mode 100644 index 000000000..fe7387fea --- /dev/null +++ b/crates/db_schema/src/source/combined/search.rs @@ -0,0 +1,28 @@ +use crate::newtypes::{CommentId, CommunityId, PersonId, PostId, SearchCombinedId}; +#[cfg(feature = "full")] +use crate::schema::search_combined; +use chrono::{DateTime, Utc}; +#[cfg(feature = "full")] +use i_love_jesus::CursorKeysModule; +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; + +#[skip_serializing_none] +#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)] +#[cfg_attr( + feature = "full", + derive(Identifiable, Queryable, Selectable, CursorKeysModule) +)] +#[cfg_attr(feature = "full", diesel(table_name = search_combined))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", cursor_keys_module(name = search_combined_keys))] +/// A combined table for a search (posts, comments, communities, persons) +pub struct SearchCombined { + pub id: SearchCombinedId, + pub published: DateTime, + pub score: i64, + pub post_id: Option, + pub comment_id: Option, + pub community_id: Option, + pub person_id: Option, +} diff --git a/crates/db_views/Cargo.toml b/crates/db_views/Cargo.toml index f7d4b1d7a..1e1f3315d 100644 --- a/crates/db_views/Cargo.toml +++ b/crates/db_views/Cargo.toml @@ -42,6 +42,7 @@ actix-web = { workspace = true, optional = true } i-love-jesus = { workspace = true, optional = true } chrono = { workspace = true } derive-new.workspace = true +strum = { workspace = true } [dev-dependencies] serial_test = { workspace = true } diff --git a/crates/db_views_actor/src/inbox_combined_view.rs b/crates/db_views/src/combined/inbox_combined_view.rs similarity index 98% rename from crates/db_views_actor/src/inbox_combined_view.rs rename to crates/db_views/src/combined/inbox_combined_view.rs index 8dec579e2..0e51c4ab3 100644 --- a/crates/db_views_actor/src/inbox_combined_view.rs +++ b/crates/db_views/src/combined/inbox_combined_view.rs @@ -40,7 +40,9 @@ use lemmy_db_schema::{ post, post_actions, post_aggregates, + post_tag, private_message, + tag, }, source::{ combined::inbox::{inbox_combined_keys as key, InboxCombined}, @@ -244,6 +246,15 @@ impl InboxCombinedQuery { let community_join = post::community_id.eq(community::id); + let post_tags = post_tag::table + .inner_join(tag::table) + .select(diesel::dsl::sql::( + "json_agg(tag.*)", + )) + .filter(post_tag::post_id.eq(post::id)) + .filter(tag::deleted.eq(false)) + .single_value(); + let mut query = inbox_combined::table .left_join(comment_reply::table) .left_join(person_comment_mention::table) @@ -308,6 +319,7 @@ impl InboxCombinedQuery { post_actions::hidden.nullable().is_not_null(), post_actions::like_score.nullable(), image_details::all_columns.nullable(), + post_tags, private_message::all_columns.nullable(), // Shared post::all_columns.nullable(), @@ -520,6 +532,7 @@ impl InternalToCombinedView for InboxCombinedViewInternal { hidden: v.post_hidden, my_vote: v.my_post_vote, image_details: v.image_details, + post_tags: v.post_tags, banned_from_community: v.banned_from_community, })) } else if let Some(private_message) = v.private_message { @@ -538,7 +551,7 @@ impl InternalToCombinedView for InboxCombinedViewInternal { #[expect(clippy::indexing_slicing)] mod tests { use crate::{ - inbox_combined_view::InboxCombinedQuery, + combined::inbox_combined_view::InboxCombinedQuery, structs::{InboxCombinedView, InboxCombinedViewInternal, PrivateMessageView}, }; use lemmy_db_schema::{ diff --git a/crates/db_views/src/combined/mod.rs b/crates/db_views/src/combined/mod.rs new file mode 100644 index 000000000..d6f4576ee --- /dev/null +++ b/crates/db_views/src/combined/mod.rs @@ -0,0 +1,12 @@ +#[cfg(feature = "full")] +pub mod inbox_combined_view; +#[cfg(feature = "full")] +pub mod modlog_combined_view; +#[cfg(feature = "full")] +pub mod person_content_combined_view; +#[cfg(feature = "full")] +pub mod person_saved_combined_view; +#[cfg(feature = "full")] +pub mod report_combined_view; +#[cfg(feature = "full")] +pub mod search_combined_view; diff --git a/crates/db_views_moderator/src/modlog_combined_view.rs b/crates/db_views/src/combined/modlog_combined_view.rs similarity index 99% rename from crates/db_views_moderator/src/modlog_combined_view.rs rename to crates/db_views/src/combined/modlog_combined_view.rs index cc349ec00..a492be07c 100644 --- a/crates/db_views_moderator/src/modlog_combined_view.rs +++ b/crates/db_views/src/combined/modlog_combined_view.rs @@ -592,7 +592,7 @@ impl InternalToCombinedView for ModlogCombinedViewInternal { #[expect(clippy::indexing_slicing)] mod tests { - use crate::{modlog_combined_view::ModlogCombinedQuery, structs::ModlogCombinedView}; + use crate::{combined::modlog_combined_view::ModlogCombinedQuery, structs::ModlogCombinedView}; use lemmy_db_schema::{ newtypes::PersonId, source::{ diff --git a/crates/db_views/src/person_content_combined_view.rs b/crates/db_views/src/combined/person_content_combined_view.rs similarity index 98% rename from crates/db_views/src/person_content_combined_view.rs rename to crates/db_views/src/combined/person_content_combined_view.rs index d28dc7bdf..65b4d43e6 100644 --- a/crates/db_views/src/person_content_combined_view.rs +++ b/crates/db_views/src/combined/person_content_combined_view.rs @@ -3,7 +3,7 @@ use crate::structs::{ LocalUserView, PersonContentCombinedPaginationCursor, PersonContentCombinedView, - PersonContentViewInternal, + PersonContentCombinedViewInternal, PostView, }; use diesel::{ @@ -235,7 +235,9 @@ impl PersonContentCombinedQuery { // Tie breaker .then_desc(key::id); - let res = query.load::(conn).await?; + let res = query + .load::(conn) + .await?; // Map the query results to the enum let out = res @@ -247,7 +249,7 @@ impl PersonContentCombinedQuery { } } -impl InternalToCombinedView for PersonContentViewInternal { +impl InternalToCombinedView for PersonContentCombinedViewInternal { type CombinedView = PersonContentCombinedView; fn map_to_enum(self) -> Option { @@ -299,7 +301,7 @@ impl InternalToCombinedView for PersonContentViewInternal { mod tests { use crate::{ - person_content_combined_view::PersonContentCombinedQuery, + combined::person_content_combined_view::PersonContentCombinedQuery, structs::PersonContentCombinedView, }; use lemmy_db_schema::{ diff --git a/crates/db_views/src/person_saved_combined_view.rs b/crates/db_views/src/combined/person_saved_combined_view.rs similarity index 98% rename from crates/db_views/src/person_saved_combined_view.rs rename to crates/db_views/src/combined/person_saved_combined_view.rs index 590094279..4ac7ee3fe 100644 --- a/crates/db_views/src/person_saved_combined_view.rs +++ b/crates/db_views/src/combined/person_saved_combined_view.rs @@ -1,7 +1,7 @@ use crate::structs::{ LocalUserView, PersonContentCombinedView, - PersonContentViewInternal, + PersonContentCombinedViewInternal, PersonSavedCombinedPaginationCursor, }; use diesel::{ @@ -236,7 +236,9 @@ impl PersonSavedCombinedQuery { // Tie breaker .then_desc(key::id); - let res = query.load::(conn).await?; + let res = query + .load::(conn) + .await?; // Map the query results to the enum let out = res @@ -253,7 +255,7 @@ impl PersonSavedCombinedQuery { mod tests { use crate::{ - person_saved_combined_view::PersonSavedCombinedQuery, + combined::person_saved_combined_view::PersonSavedCombinedQuery, structs::{LocalUserView, PersonContentCombinedView}, }; use lemmy_db_schema::{ diff --git a/crates/db_views/src/report_combined_view.rs b/crates/db_views/src/combined/report_combined_view.rs similarity index 99% rename from crates/db_views/src/report_combined_view.rs rename to crates/db_views/src/combined/report_combined_view.rs index 201e02728..0fce1afcb 100644 --- a/crates/db_views/src/report_combined_view.rs +++ b/crates/db_views/src/combined/report_combined_view.rs @@ -491,7 +491,7 @@ impl InternalToCombinedView for ReportCombinedViewInternal { mod tests { use crate::{ - report_combined_view::ReportCombinedQuery, + combined::report_combined_view::ReportCombinedQuery, structs::{ CommentReportView, LocalUserView, diff --git a/crates/db_views/src/combined/search_combined_view.rs b/crates/db_views/src/combined/search_combined_view.rs new file mode 100644 index 000000000..13054da0a --- /dev/null +++ b/crates/db_views/src/combined/search_combined_view.rs @@ -0,0 +1,1194 @@ +use crate::structs::{ + CommentView, + CommunityView, + LocalUserView, + PersonView, + PostView, + SearchCombinedPaginationCursor, + SearchCombinedView, + SearchCombinedViewInternal, +}; +use diesel::{ + dsl::not, + result::Error, + BoolExpressionMethods, + ExpressionMethods, + JoinOnDsl, + NullableExpressionMethods, + PgTextExpressionMethods, + QueryDsl, + SelectableHelper, +}; +use diesel_async::RunQueryDsl; +use i_love_jesus::PaginatedQueryBuilder; +use lemmy_db_schema::{ + aliases::creator_community_actions, + newtypes::{CommunityId, PersonId}, + schema::{ + comment, + comment_actions, + comment_aggregates, + community, + community_actions, + community_aggregates, + image_details, + local_user, + person, + person_actions, + person_aggregates, + post, + post_actions, + post_aggregates, + post_tag, + search_combined, + tag, + }, + source::{ + combined::search::{search_combined_keys as key, SearchCombined}, + community::CommunityFollower, + }, + traits::InternalToCombinedView, + utils::{ + actions, + actions_alias, + functions::coalesce, + fuzzy_search, + get_conn, + DbPool, + ReverseTimestampKey, + }, + ListingType, + SearchSortType, + SearchType, +}; +use lemmy_utils::error::LemmyResult; +use SearchSortType::*; + +impl SearchCombinedPaginationCursor { + // get cursor for page that starts immediately after the given post + pub fn after_post(view: &SearchCombinedView) -> SearchCombinedPaginationCursor { + let (prefix, id) = match view { + SearchCombinedView::Post(v) => ('P', v.post.id.0), + SearchCombinedView::Comment(v) => ('C', v.comment.id.0), + SearchCombinedView::Community(v) => ('O', v.community.id.0), + SearchCombinedView::Person(v) => ('E', v.person.id.0), + }; + // hex encoding to prevent ossification + SearchCombinedPaginationCursor(format!("{prefix}{id:x}")) + } + + pub async fn read(&self, pool: &mut DbPool<'_>) -> Result { + let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into()); + let mut query = search_combined::table + .select(SearchCombined::as_select()) + .into_boxed(); + let (prefix, id_str) = self.0.split_at_checked(1).ok_or_else(err_msg)?; + let id = i32::from_str_radix(id_str, 16).map_err(|_err| err_msg())?; + query = match prefix { + "P" => query.filter(search_combined::post_id.eq(id)), + "C" => query.filter(search_combined::comment_id.eq(id)), + "O" => query.filter(search_combined::community_id.eq(id)), + "E" => query.filter(search_combined::person_id.eq(id)), + _ => return Err(err_msg()), + }; + let token = query.first(&mut get_conn(pool).await?).await?; + + Ok(PaginationCursorData(token)) + } +} + +#[derive(Clone)] +pub struct PaginationCursorData(SearchCombined); + +#[derive(Default)] +pub struct SearchCombinedQuery { + pub search_term: Option, + pub community_id: Option, + pub creator_id: Option, + pub type_: Option, + pub sort: Option, + pub listing_type: Option, + pub title_only: Option, + pub post_url_only: Option, + pub liked_only: Option, + pub disliked_only: Option, + pub page_after: Option, + pub page_back: Option, +} + +impl SearchCombinedQuery { + pub async fn list( + self, + pool: &mut DbPool<'_>, + user: &Option, + ) -> LemmyResult> { + let my_person_id = user.as_ref().map(|u| u.local_user.person_id); + let item_creator = person::id; + + let conn = &mut get_conn(pool).await?; + + let post_tags = post_tag::table + .inner_join(tag::table) + .select(diesel::dsl::sql::( + "json_agg(tag.*)", + )) + .filter(post_tag::post_id.eq(post::id)) + .filter(tag::deleted.eq(false)) + .single_value(); + + let item_creator_join = search_combined::person_id + .eq(item_creator.nullable()) + .or( + search_combined::comment_id + .is_not_null() + .and(comment::creator_id.eq(item_creator)), + ) + .or( + search_combined::post_id + .is_not_null() + .and(post::creator_id.eq(item_creator)), + ) + .and(not(person::deleted)); + + let comment_join = search_combined::comment_id + .eq(comment::id.nullable()) + .and(not(comment::removed)) + .and(not(comment::deleted)); + + let post_join = search_combined::post_id + .eq(post::id.nullable()) + .or(comment::post_id.eq(post::id)) + .and(not(post::removed)) + .and(not(post::deleted)); + + let community_join = search_combined::community_id + .eq(community::id.nullable()) + .or(post::community_id.eq(community::id)) + .and(not(community::removed)) + .and(not(community::deleted)); + + // Notes: since the post_id and comment_id are optional columns, + // many joins must use an OR condition. + // For example, the creator must be the person table joined to either: + // - post.creator_id + // - comment.creator_id + let mut query = search_combined::table + // The comment + .left_join(comment::table.on(comment_join)) + // The post + .left_join(post::table.on(post_join)) + // The item creator + .left_join(person::table.on(item_creator_join)) + // The community + .left_join(community::table.on(community_join)) + .left_join(actions_alias( + creator_community_actions, + item_creator, + community::id, + )) + .left_join( + local_user::table.on( + item_creator + .eq(local_user::person_id) + .and(local_user::admin.eq(true)), + ), + ) + .left_join(actions( + community_actions::table, + my_person_id, + community::id, + )) + .left_join(actions(post_actions::table, my_person_id, post::id)) + .left_join(actions(person_actions::table, my_person_id, item_creator)) + .left_join( + person_aggregates::table + .on(search_combined::person_id.eq(person_aggregates::person_id.nullable())), + ) + .left_join(post_aggregates::table.on(post::id.eq(post_aggregates::post_id))) + .left_join( + comment_aggregates::table + .on(search_combined::comment_id.eq(comment_aggregates::comment_id.nullable())), + ) + .left_join( + community_aggregates::table + .on(search_combined::community_id.eq(community_aggregates::community_id.nullable())), + ) + .left_join(actions(comment_actions::table, my_person_id, comment::id)) + .left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()))) + .select(( + // Post-specific + post::all_columns.nullable(), + post_aggregates::all_columns.nullable(), + coalesce( + post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(), + post_aggregates::comments, + ) + .nullable(), + post_actions::saved.nullable().is_not_null(), + post_actions::read.nullable().is_not_null(), + post_actions::hidden.nullable().is_not_null(), + post_actions::like_score.nullable(), + image_details::all_columns.nullable(), + post_tags, + // Comment-specific + comment::all_columns.nullable(), + comment_aggregates::all_columns.nullable(), + comment_actions::saved.nullable().is_not_null(), + comment_actions::like_score.nullable(), + // Community-specific + community::all_columns.nullable(), + community_aggregates::all_columns.nullable(), + community_actions::blocked.nullable().is_not_null(), + CommunityFollower::select_subscribed_type(), + // Person + person_aggregates::all_columns.nullable(), + // // Shared + person::all_columns.nullable(), + local_user::admin.nullable().is_not_null(), + creator_community_actions + .field(community_actions::became_moderator) + .nullable() + .is_not_null(), + creator_community_actions + .field(community_actions::received_ban) + .nullable() + .is_not_null(), + person_actions::blocked.nullable().is_not_null(), + community_actions::received_ban.nullable().is_not_null(), + )) + .into_boxed(); + + // The filters + + // The search term + if let Some(search_term) = &self.search_term { + if self.post_url_only.unwrap_or_default() { + query = query.filter(post::url.eq(search_term)); + } else { + let searcher = fuzzy_search(search_term); + + let name_or_title_filter = post::name + .ilike(searcher.clone()) + .or(comment::content.ilike(searcher.clone())) + .or(community::name.ilike(searcher.clone())) + .or(community::title.ilike(searcher.clone())) + .or(person::name.ilike(searcher.clone())) + .or(person::display_name.ilike(searcher.clone())); + + let body_or_description_filter = post::body + .ilike(searcher.clone()) + .or(community::description.ilike(searcher.clone())); + + query = if self.title_only.unwrap_or_default() { + query.filter(name_or_title_filter) + } else { + query.filter(name_or_title_filter.or(body_or_description_filter)) + } + } + } + + // Community id + if let Some(community_id) = self.community_id { + query = query.filter(community::id.eq(community_id)); + } + + // Creator id + if let Some(creator_id) = self.creator_id { + query = query.filter(item_creator.eq(creator_id)); + } + + // Liked / disliked filter + if let Some(my_id) = my_person_id { + let not_creator_filter = item_creator.ne(my_id); + let liked_disliked_filter = |score: i16| { + search_combined::post_id + .is_not_null() + .and(post_actions::like_score.eq(score)) + .or( + search_combined::comment_id + .is_not_null() + .and(comment_actions::like_score.eq(score)), + ) + }; + + if self.liked_only.unwrap_or_default() { + query = query + .filter(not_creator_filter) + .filter(liked_disliked_filter(1)); + } else if self.disliked_only.unwrap_or_default() { + query = query + .filter(not_creator_filter) + .filter(liked_disliked_filter(-1)); + } + }; + + // Type + query = match self.type_.unwrap_or_default() { + SearchType::All => query, + SearchType::Posts => query.filter(search_combined::post_id.is_not_null()), + SearchType::Comments => query.filter(search_combined::comment_id.is_not_null()), + SearchType::Communities => query.filter(search_combined::community_id.is_not_null()), + SearchType::Users => query.filter(search_combined::person_id.is_not_null()), + }; + + // Listing type + let is_subscribed = community_actions::followed.is_not_null(); + match self.listing_type.unwrap_or_default() { + ListingType::Subscribed => query = query.filter(is_subscribed), + ListingType::Local => { + query = query.filter( + community::local + .eq(true) + .and(community::hidden.eq(false).or(is_subscribed)) + .or(search_combined::person_id.is_not_null().and(person::local)), + ); + } + ListingType::All => { + query = query.filter( + community::hidden + .eq(false) + .or(is_subscribed) + .or(search_combined::person_id.is_not_null()), + ) + } + ListingType::ModeratorView => { + query = query.filter(community_actions::became_moderator.is_not_null()); + } + } + + let mut query = PaginatedQueryBuilder::new(query); + + let page_after = self.page_after.map(|c| c.0); + + if self.page_back.unwrap_or_default() { + query = query.before(page_after).limit_and_offset_from_end(); + } else { + query = query.after(page_after); + } + + query = match self.sort.unwrap_or_default() { + New => query.then_desc(key::published), + Old => query.then_desc(ReverseTimestampKey(key::published)), + Top => query.then_desc(key::score), + }; + // finally use unique id as tie breaker + query = query.then_desc(key::id); + + let res = query.load::(conn).await?; + + // Map the query results to the enum + let out = res + .into_iter() + .filter_map(InternalToCombinedView::map_to_enum) + .collect(); + + Ok(out) + } +} + +impl InternalToCombinedView for SearchCombinedViewInternal { + type CombinedView = SearchCombinedView; + + fn map_to_enum(self) -> Option { + // Use for a short alias + let v = self; + + if let (Some(comment), Some(counts), Some(creator), Some(post), Some(community)) = ( + v.comment, + v.comment_counts, + v.item_creator.clone(), + v.post.clone(), + v.community.clone(), + ) { + Some(SearchCombinedView::Comment(CommentView { + comment, + counts, + post, + community, + creator, + creator_banned_from_community: v.item_creator_banned_from_community, + creator_is_moderator: v.item_creator_is_moderator, + creator_is_admin: v.item_creator_is_admin, + creator_blocked: v.item_creator_blocked, + subscribed: v.subscribed, + saved: v.comment_saved, + my_vote: v.my_comment_vote, + banned_from_community: v.banned_from_community, + })) + } else if let ( + Some(post), + Some(counts), + Some(creator), + Some(community), + Some(unread_comments), + ) = ( + v.post, + v.post_counts, + v.item_creator.clone(), + v.community.clone(), + v.post_unread_comments, + ) { + Some(SearchCombinedView::Post(PostView { + post, + community, + counts, + unread_comments, + creator, + creator_banned_from_community: v.item_creator_banned_from_community, + creator_is_moderator: v.item_creator_is_moderator, + creator_is_admin: v.item_creator_is_admin, + creator_blocked: v.item_creator_blocked, + subscribed: v.subscribed, + saved: v.post_saved, + read: v.post_read, + hidden: v.post_hidden, + my_vote: v.my_post_vote, + image_details: v.image_details, + banned_from_community: v.banned_from_community, + tags: v.post_tags, + })) + } else if let (Some(community), Some(counts)) = (v.community, v.community_counts) { + Some(SearchCombinedView::Community(CommunityView { + community, + counts, + subscribed: v.subscribed, + blocked: v.community_blocked, + banned_from_community: v.banned_from_community, + })) + } else if let (Some(person), Some(counts)) = (v.item_creator, v.item_creator_counts) { + Some(SearchCombinedView::Person(PersonView { + person, + counts, + is_admin: v.item_creator_is_admin, + })) + } else { + None + } + } +} + +#[cfg(test)] +#[expect(clippy::indexing_slicing)] +mod tests { + + use crate::{ + combined::search_combined_view::SearchCombinedQuery, + structs::{LocalUserView, SearchCombinedView}, + }; + use lemmy_db_schema::{ + assert_length, + source::{ + comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm}, + community::{Community, CommunityInsertForm}, + instance::Instance, + local_user::{LocalUser, LocalUserInsertForm}, + local_user_vote_display_mode::LocalUserVoteDisplayMode, + person::{Person, PersonInsertForm}, + post::{Post, PostInsertForm, PostLike, PostLikeForm, PostUpdateForm}, + }, + traits::{Crud, Likeable}, + utils::{build_db_pool_for_tests, DbPool}, + SearchSortType, + SearchType, + }; + use lemmy_utils::error::LemmyResult; + use pretty_assertions::assert_eq; + use serial_test::serial; + use url::Url; + + struct Data { + instance: Instance, + timmy: Person, + timmy_view: LocalUserView, + sara: Person, + community: Community, + community_2: Community, + timmy_post: Post, + timmy_post_2: Post, + sara_post: Post, + timmy_comment: Comment, + sara_comment: Comment, + sara_comment_2: Comment, + } + + async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult { + let instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; + + let sara_form = PersonInsertForm::test_form(instance.id, "sara_pcv"); + let sara = Person::create(pool, &sara_form).await?; + + let timmy_form = PersonInsertForm::test_form(instance.id, "timmy_pcv"); + let timmy = Person::create(pool, &timmy_form).await?; + let timmy_local_user_form = LocalUserInsertForm::test_form(timmy.id); + let timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![]).await?; + let timmy_view = LocalUserView { + local_user: timmy_local_user, + local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), + person: timmy.clone(), + counts: Default::default(), + }; + + let community_form = CommunityInsertForm { + description: Some("ask lemmy things".into()), + ..CommunityInsertForm::new( + instance.id, + "asklemmy".to_string(), + "Ask Lemmy".to_owned(), + "pubkey".to_string(), + ) + }; + let community = Community::create(pool, &community_form).await?; + + let community_form_2 = CommunityInsertForm::new( + instance.id, + "startrek_ds9".to_string(), + "Star Trek - Deep Space Nine".to_owned(), + "pubkey".to_string(), + ); + let community_2 = Community::create(pool, &community_form_2).await?; + + let timmy_post_form = PostInsertForm { + body: Some("postbody inside here".into()), + url: Some(Url::parse("https://google.com")?.into()), + ..PostInsertForm::new("timmy post prv".into(), timmy.id, community.id) + }; + let timmy_post = Post::create(pool, &timmy_post_form).await?; + + let timmy_post_form_2 = PostInsertForm::new("timmy post prv 2".into(), timmy.id, community.id); + let timmy_post_2 = Post::create(pool, &timmy_post_form_2).await?; + + let sara_post_form = PostInsertForm::new("sara post prv".into(), sara.id, community_2.id); + let sara_post = Post::create(pool, &sara_post_form).await?; + + let timmy_comment_form = + CommentInsertForm::new(timmy.id, timmy_post.id, "timmy comment prv gold".into()); + let timmy_comment = Comment::create(pool, &timmy_comment_form, None).await?; + + let sara_comment_form = + CommentInsertForm::new(sara.id, sara_post.id, "sara comment prv gold".into()); + let sara_comment = Comment::create(pool, &sara_comment_form, None).await?; + + let sara_comment_form_2 = + CommentInsertForm::new(sara.id, timmy_post_2.id, "sara comment prv 2".into()); + let sara_comment_2 = Comment::create(pool, &sara_comment_form_2, None).await?; + + // Timmy likes and dislikes a few things + let timmy_like_post_form = PostLikeForm::new(timmy_post.id, timmy.id, 1); + PostLike::like(pool, &timmy_like_post_form).await?; + + let timmy_like_sara_post_form = PostLikeForm::new(sara_post.id, timmy.id, 1); + PostLike::like(pool, &timmy_like_sara_post_form).await?; + + let timmy_dislike_post_form = PostLikeForm::new(timmy_post_2.id, timmy.id, -1); + PostLike::like(pool, &timmy_dislike_post_form).await?; + + let timmy_like_comment_form = CommentLikeForm { + person_id: timmy.id, + comment_id: timmy_comment.id, + score: 1, + }; + CommentLike::like(pool, &timmy_like_comment_form).await?; + + let timmy_like_sara_comment_form = CommentLikeForm { + person_id: timmy.id, + comment_id: sara_comment.id, + score: 1, + }; + CommentLike::like(pool, &timmy_like_sara_comment_form).await?; + + let timmy_dislike_sara_comment_form = CommentLikeForm { + person_id: timmy.id, + comment_id: sara_comment_2.id, + score: -1, + }; + CommentLike::like(pool, &timmy_dislike_sara_comment_form).await?; + + Ok(Data { + instance, + timmy, + timmy_view, + sara, + community, + community_2, + timmy_post, + timmy_post_2, + sara_post, + timmy_comment, + sara_comment, + sara_comment_2, + }) + } + + async fn cleanup(data: Data, pool: &mut DbPool<'_>) -> LemmyResult<()> { + Instance::delete(pool, data.instance.id).await?; + + Ok(()) + } + + #[tokio::test] + #[serial] + async fn combined() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests(); + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + // search + let search = SearchCombinedQuery::default().list(pool, &None).await?; + assert_length!(10, search); + + // Make sure the types are correct + if let SearchCombinedView::Comment(v) = &search[0] { + assert_eq!(data.sara_comment_2.id, v.comment.id); + assert_eq!(data.timmy_post_2.id, v.post.id); + assert_eq!(data.community.id, v.community.id); + } else { + panic!("wrong type"); + } + + if let SearchCombinedView::Comment(v) = &search[1] { + assert_eq!(data.sara_comment.id, v.comment.id); + assert_eq!(data.sara_post.id, v.post.id); + assert_eq!(data.community_2.id, v.community.id); + } else { + panic!("wrong type"); + } + + if let SearchCombinedView::Comment(v) = &search[2] { + assert_eq!(data.timmy_comment.id, v.comment.id); + assert_eq!(data.timmy_post.id, v.post.id); + assert_eq!(data.community.id, v.community.id); + } else { + panic!("wrong type"); + } + + if let SearchCombinedView::Post(v) = &search[3] { + assert_eq!(data.sara_post.id, v.post.id); + assert_eq!(data.community_2.id, v.community.id); + } else { + panic!("wrong type"); + } + + if let SearchCombinedView::Post(v) = &search[4] { + assert_eq!(data.timmy_post_2.id, v.post.id); + assert_eq!(data.community.id, v.community.id); + } else { + panic!("wrong type"); + } + + if let SearchCombinedView::Post(v) = &search[5] { + assert_eq!(data.timmy_post.id, v.post.id); + assert_eq!(data.community.id, v.community.id); + } else { + panic!("wrong type"); + } + + if let SearchCombinedView::Community(v) = &search[6] { + assert_eq!(data.community_2.id, v.community.id); + } else { + panic!("wrong type"); + } + + if let SearchCombinedView::Community(v) = &search[7] { + assert_eq!(data.community.id, v.community.id); + } else { + panic!("wrong type"); + } + + if let SearchCombinedView::Person(v) = &search[8] { + assert_eq!(data.timmy.id, v.person.id); + } else { + panic!("wrong type"); + } + + if let SearchCombinedView::Person(v) = &search[9] { + assert_eq!(data.sara.id, v.person.id); + } else { + panic!("wrong type"); + } + + // Filtered by community id + let search_by_community = SearchCombinedQuery { + community_id: Some(data.community.id), + ..Default::default() + } + .list(pool, &None) + .await?; + assert_length!(5, search_by_community); + + // Filtered by creator_id + let search_by_creator = SearchCombinedQuery { + creator_id: Some(data.timmy.id), + ..Default::default() + } + .list(pool, &None) + .await?; + assert_length!(4, search_by_creator); + + // Using a term + let search_by_name = SearchCombinedQuery { + search_term: Some("gold".into()), + ..Default::default() + } + .list(pool, &None) + .await?; + + assert_length!(2, search_by_name); + + // Liked / disliked only + let search_liked_only = SearchCombinedQuery { + liked_only: Some(true), + ..Default::default() + } + .list(pool, &Some(data.timmy_view.clone())) + .await?; + + assert_length!(2, search_liked_only); + + let search_disliked_only = SearchCombinedQuery { + disliked_only: Some(true), + ..Default::default() + } + .list(pool, &Some(data.timmy_view.clone())) + .await?; + + assert_length!(1, search_disliked_only); + + // Test sorts + // Test Old sort + let search_old_sort = SearchCombinedQuery { + sort: Some(SearchSortType::Old), + ..Default::default() + } + .list(pool, &Some(data.timmy_view.clone())) + .await?; + if let SearchCombinedView::Person(v) = &search_old_sort[0] { + assert_eq!(data.sara.id, v.person.id); + } else { + panic!("wrong type"); + } + assert_length!(10, search_old_sort); + + // Remove a post and delete a comment + Post::update( + pool, + data.timmy_post_2.id, + &PostUpdateForm { + removed: Some(true), + ..Default::default() + }, + ) + .await?; + + Comment::update( + pool, + data.sara_comment.id, + &CommentUpdateForm { + deleted: Some(true), + ..Default::default() + }, + ) + .await?; + + // 2 things got removed, but the post also has another comment which got removed + let search = SearchCombinedQuery::default().list(pool, &None).await?; + assert_length!(7, search); + + cleanup(data, pool).await?; + + Ok(()) + } + + #[tokio::test] + #[serial] + async fn community() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests(); + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + // Community search + let community_search = SearchCombinedQuery { + type_: Some(SearchType::Communities), + ..Default::default() + } + .list(pool, &None) + .await?; + assert_length!(2, community_search); + + // Make sure the types are correct + if let SearchCombinedView::Community(v) = &community_search[0] { + assert_eq!(data.community_2.id, v.community.id); + } else { + panic!("wrong type"); + } + + if let SearchCombinedView::Community(v) = &community_search[1] { + assert_eq!(data.community.id, v.community.id); + } else { + panic!("wrong type"); + } + + // Filtered by id + let community_search_by_id = SearchCombinedQuery { + community_id: Some(data.community.id), + type_: Some(SearchType::Communities), + ..Default::default() + } + .list(pool, &None) + .await?; + assert_length!(1, community_search_by_id); + + // Using a term + let community_search_by_name = SearchCombinedQuery { + search_term: Some("things".into()), + type_: Some(SearchType::Communities), + ..Default::default() + } + .list(pool, &None) + .await?; + + assert_length!(1, community_search_by_name); + if let SearchCombinedView::Community(v) = &community_search_by_name[0] { + // The asklemmy community + assert_eq!(data.community.id, v.community.id); + } else { + panic!("wrong type"); + } + + // Test title only search to make sure 'ask lemmy things' doesn't get returned + // Using a term + let community_search_title_only = SearchCombinedQuery { + search_term: Some("things".into()), + type_: Some(SearchType::Communities), + title_only: Some(true), + ..Default::default() + } + .list(pool, &None) + .await?; + + assert!(community_search_title_only.is_empty()); + + cleanup(data, pool).await?; + + Ok(()) + } + + #[tokio::test] + #[serial] + async fn person() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests(); + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + // Person search + let person_search = SearchCombinedQuery { + type_: Some(SearchType::Users), + ..Default::default() + } + .list(pool, &None) + .await?; + assert_length!(2, person_search); + + // Make sure the types are correct + if let SearchCombinedView::Person(v) = &person_search[0] { + assert_eq!(data.timmy.id, v.person.id); + } else { + panic!("wrong type"); + } + + if let SearchCombinedView::Person(v) = &person_search[1] { + assert_eq!(data.sara.id, v.person.id); + } else { + panic!("wrong type"); + } + + // Filtered by creator_id + let person_search_by_id = SearchCombinedQuery { + creator_id: Some(data.sara.id), + type_: Some(SearchType::Users), + ..Default::default() + } + .list(pool, &None) + .await?; + assert_length!(1, person_search_by_id); + if let SearchCombinedView::Person(v) = &person_search_by_id[0] { + assert_eq!(data.sara.id, v.person.id); + } else { + panic!("wrong type"); + } + + // Using a term + let person_search_by_name = SearchCombinedQuery { + search_term: Some("tim".into()), + type_: Some(SearchType::Users), + ..Default::default() + } + .list(pool, &None) + .await?; + + assert_length!(1, person_search_by_name); + if let SearchCombinedView::Person(v) = &person_search_by_name[0] { + assert_eq!(data.timmy.id, v.person.id); + } else { + panic!("wrong type"); + } + + // Test Top sorting (uses post score) + let person_search_sort_top = SearchCombinedQuery { + type_: Some(SearchType::Users), + sort: Some(SearchSortType::Top), + ..Default::default() + } + .list(pool, &None) + .await?; + assert_length!(2, person_search_sort_top); + + // Sara should be first, as she has a higher score + if let SearchCombinedView::Person(v) = &person_search_sort_top[0] { + assert_eq!(data.sara.id, v.person.id); + } else { + panic!("wrong type"); + } + + cleanup(data, pool).await?; + + Ok(()) + } + + #[tokio::test] + #[serial] + async fn post() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests(); + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + // post search + let post_search = SearchCombinedQuery { + type_: Some(SearchType::Posts), + ..Default::default() + } + .list(pool, &None) + .await?; + assert_length!(3, post_search); + + // Make sure the types are correct + if let SearchCombinedView::Post(v) = &post_search[0] { + assert_eq!(data.sara_post.id, v.post.id); + assert_eq!(data.community_2.id, v.community.id); + } else { + panic!("wrong type"); + } + + if let SearchCombinedView::Post(v) = &post_search[1] { + assert_eq!(data.timmy_post_2.id, v.post.id); + assert_eq!(data.community.id, v.community.id); + } else { + panic!("wrong type"); + } + + if let SearchCombinedView::Post(v) = &post_search[2] { + assert_eq!(data.timmy_post.id, v.post.id); + assert_eq!(data.community.id, v.community.id); + } else { + panic!("wrong type"); + } + + // Filtered by id + let post_search_by_community = SearchCombinedQuery { + community_id: Some(data.community.id), + type_: Some(SearchType::Posts), + ..Default::default() + } + .list(pool, &None) + .await?; + assert_length!(2, post_search_by_community); + + // Using a term + let post_search_by_name = SearchCombinedQuery { + search_term: Some("sara".into()), + type_: Some(SearchType::Posts), + ..Default::default() + } + .list(pool, &None) + .await?; + + assert_length!(1, post_search_by_name); + + // Test title only search to make sure 'postbody' doesn't show up + // Using a term + let post_search_title_only = SearchCombinedQuery { + search_term: Some("postbody".into()), + type_: Some(SearchType::Posts), + title_only: Some(true), + ..Default::default() + } + .list(pool, &None) + .await?; + + assert!(post_search_title_only.is_empty()); + + // Test title only search to make sure 'postbody' doesn't show up + // Using a term + let post_search_url_only = SearchCombinedQuery { + search_term: data.timmy_post.url.as_ref().map(ToString::to_string), + type_: Some(SearchType::Posts), + post_url_only: Some(true), + ..Default::default() + } + .list(pool, &None) + .await?; + + assert_length!(1, post_search_url_only); + + // Liked / disliked only + let post_search_liked_only = SearchCombinedQuery { + type_: Some(SearchType::Posts), + liked_only: Some(true), + ..Default::default() + } + .list(pool, &Some(data.timmy_view.clone())) + .await?; + + // Should only be 1 not 2, because liked only ignores your own content + assert_length!(1, post_search_liked_only); + + let post_search_disliked_only = SearchCombinedQuery { + type_: Some(SearchType::Posts), + disliked_only: Some(true), + ..Default::default() + } + .list(pool, &Some(data.timmy_view.clone())) + .await?; + + // Should be zero because you disliked your own post + assert_length!(0, post_search_disliked_only); + + // Test top sort + let post_search_sort_top = SearchCombinedQuery { + type_: Some(SearchType::Posts), + sort: Some(SearchSortType::Top), + ..Default::default() + } + .list(pool, &None) + .await?; + assert_length!(3, post_search_sort_top); + + // Timmy_post_2 has a dislike, so it should be last + if let SearchCombinedView::Post(v) = &post_search_sort_top[2] { + assert_eq!(data.timmy_post_2.id, v.post.id); + assert_eq!(data.community.id, v.community.id); + } else { + panic!("wrong type"); + } + + cleanup(data, pool).await?; + + Ok(()) + } + + #[tokio::test] + #[serial] + async fn comment() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests(); + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + // comment search + let comment_search = SearchCombinedQuery { + type_: Some(SearchType::Comments), + ..Default::default() + } + .list(pool, &None) + .await?; + assert_length!(3, comment_search); + + // Make sure the types are correct + if let SearchCombinedView::Comment(v) = &comment_search[0] { + assert_eq!(data.sara_comment_2.id, v.comment.id); + assert_eq!(data.timmy_post_2.id, v.post.id); + assert_eq!(data.community.id, v.community.id); + } else { + panic!("wrong type"); + } + + if let SearchCombinedView::Comment(v) = &comment_search[1] { + assert_eq!(data.sara_comment.id, v.comment.id); + assert_eq!(data.sara_post.id, v.post.id); + assert_eq!(data.community_2.id, v.community.id); + } else { + panic!("wrong type"); + } + + if let SearchCombinedView::Comment(v) = &comment_search[2] { + assert_eq!(data.timmy_comment.id, v.comment.id); + assert_eq!(data.timmy_post.id, v.post.id); + assert_eq!(data.community.id, v.community.id); + } else { + panic!("wrong type"); + } + + // Filtered by id + let comment_search_by_community = SearchCombinedQuery { + community_id: Some(data.community.id), + type_: Some(SearchType::Comments), + ..Default::default() + } + .list(pool, &None) + .await?; + assert_length!(2, comment_search_by_community); + + // Using a term + let comment_search_by_name = SearchCombinedQuery { + search_term: Some("gold".into()), + type_: Some(SearchType::Comments), + ..Default::default() + } + .list(pool, &None) + .await?; + + assert_length!(2, comment_search_by_name); + + // Liked / disliked only + let comment_search_liked_only = SearchCombinedQuery { + type_: Some(SearchType::Comments), + liked_only: Some(true), + ..Default::default() + } + .list(pool, &Some(data.timmy_view.clone())) + .await?; + + assert_length!(1, comment_search_liked_only); + + let comment_search_disliked_only = SearchCombinedQuery { + type_: Some(SearchType::Comments), + disliked_only: Some(true), + ..Default::default() + } + .list(pool, &Some(data.timmy_view.clone())) + .await?; + + assert_length!(1, comment_search_disliked_only); + + // Test top sort + let comment_search_sort_top = SearchCombinedQuery { + type_: Some(SearchType::Comments), + sort: Some(SearchSortType::Top), + ..Default::default() + } + .list(pool, &None) + .await?; + assert_length!(3, comment_search_sort_top); + + // Sara comment 2 is disliked, so should be last + if let SearchCombinedView::Comment(v) = &comment_search_sort_top[2] { + assert_eq!(data.sara_comment_2.id, v.comment.id); + assert_eq!(data.timmy_post_2.id, v.post.id); + assert_eq!(data.community.id, v.community.id); + } else { + panic!("wrong type"); + } + + cleanup(data, pool).await?; + + Ok(()) + } +} diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment/comment_view.rs similarity index 99% rename from crates/db_views/src/comment_view.rs rename to crates/db_views/src/comment/comment_view.rs index 28e1f91be..13a849449 100644 --- a/crates/db_views/src/comment_view.rs +++ b/crates/db_views/src/comment/comment_view.rs @@ -374,7 +374,7 @@ fn handle_deleted(mut c: CommentView, is_admin: bool) -> CommentView { mod tests { use crate::{ - comment_view::{CommentQuery, CommentSortType, CommentView, DbPool}, + comment::comment_view::{CommentQuery, CommentSortType, CommentView, DbPool}, structs::LocalUserView, }; use lemmy_db_schema::{ diff --git a/crates/db_views/src/comment/mod.rs b/crates/db_views/src/comment/mod.rs new file mode 100644 index 000000000..b6f4b0b75 --- /dev/null +++ b/crates/db_views/src/comment/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "full")] +pub mod comment_view; diff --git a/crates/db_views_actor/src/community_follower_view.rs b/crates/db_views/src/community/community_follower_view.rs similarity index 100% rename from crates/db_views_actor/src/community_follower_view.rs rename to crates/db_views/src/community/community_follower_view.rs diff --git a/crates/db_views_actor/src/community_moderator_view.rs b/crates/db_views/src/community/community_moderator_view.rs similarity index 100% rename from crates/db_views_actor/src/community_moderator_view.rs rename to crates/db_views/src/community/community_moderator_view.rs diff --git a/crates/db_views_actor/src/community_person_ban_view.rs b/crates/db_views/src/community/community_person_ban_view.rs similarity index 100% rename from crates/db_views_actor/src/community_person_ban_view.rs rename to crates/db_views/src/community/community_person_ban_view.rs diff --git a/crates/db_views_actor/src/community_view.rs b/crates/db_views/src/community/community_view.rs similarity index 99% rename from crates/db_views_actor/src/community_view.rs rename to crates/db_views/src/community/community_view.rs index 1a8e3c4cd..7ef746e53 100644 --- a/crates/db_views_actor/src/community_view.rs +++ b/crates/db_views/src/community/community_view.rs @@ -266,7 +266,7 @@ impl CommunityQuery<'_> { mod tests { use crate::{ - community_view::CommunityQuery, + community::community_view::CommunityQuery, structs::{CommunitySortType, CommunityView}, }; use lemmy_db_schema::{ diff --git a/crates/db_views_actor/src/lib.rs b/crates/db_views/src/community/mod.rs similarity index 56% rename from crates/db_views_actor/src/lib.rs rename to crates/db_views/src/community/mod.rs index 3f3991734..53d55c3cf 100644 --- a/crates/db_views_actor/src/lib.rs +++ b/crates/db_views/src/community/mod.rs @@ -6,10 +6,3 @@ pub mod community_moderator_view; pub mod community_person_ban_view; #[cfg(feature = "full")] pub mod community_view; -#[cfg(feature = "full")] -pub mod inbox_combined_view; -#[cfg(feature = "full")] -pub mod person_view; -#[cfg(feature = "full")] -pub mod private_message_view; -pub mod structs; diff --git a/crates/db_views/src/lib.rs b/crates/db_views/src/lib.rs index 74a1cd5c6..594303fda 100644 --- a/crates/db_views/src/lib.rs +++ b/crates/db_views/src/lib.rs @@ -2,33 +2,23 @@ extern crate serial_test; #[cfg(feature = "full")] -pub mod comment_report_view; +pub mod combined; #[cfg(feature = "full")] -pub mod comment_view; +pub mod comment; #[cfg(feature = "full")] -pub mod custom_emoji_view; +pub mod community; #[cfg(feature = "full")] -pub mod local_image_view; +pub mod local_user; #[cfg(feature = "full")] -pub mod local_user_view; +pub mod person; #[cfg(feature = "full")] -pub mod person_content_combined_view; +pub mod post; #[cfg(feature = "full")] -pub mod person_saved_combined_view; +pub mod private_message; #[cfg(feature = "full")] -pub mod post_report_view; +pub mod registration_applications; #[cfg(feature = "full")] -pub mod post_tags_view; +pub mod reports; #[cfg(feature = "full")] -pub mod post_view; -#[cfg(feature = "full")] -pub mod private_message_report_view; -#[cfg(feature = "full")] -pub mod registration_application_view; -#[cfg(feature = "full")] -pub mod report_combined_view; -#[cfg(feature = "full")] -pub mod site_view; +pub mod site; pub mod structs; -#[cfg(feature = "full")] -pub mod vote_view; diff --git a/crates/db_views/src/local_user_view.rs b/crates/db_views/src/local_user/local_user_view.rs similarity index 100% rename from crates/db_views/src/local_user_view.rs rename to crates/db_views/src/local_user/local_user_view.rs diff --git a/crates/db_views/src/local_user/mod.rs b/crates/db_views/src/local_user/mod.rs new file mode 100644 index 000000000..ffe33f138 --- /dev/null +++ b/crates/db_views/src/local_user/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "full")] +pub mod local_user_view; diff --git a/crates/db_views/src/person/mod.rs b/crates/db_views/src/person/mod.rs new file mode 100644 index 000000000..5feb31647 --- /dev/null +++ b/crates/db_views/src/person/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "full")] +pub mod person_view; diff --git a/crates/db_views_actor/src/person_view.rs b/crates/db_views/src/person/person_view.rs similarity index 100% rename from crates/db_views_actor/src/person_view.rs rename to crates/db_views/src/person/person_view.rs diff --git a/crates/db_views/src/post/mod.rs b/crates/db_views/src/post/mod.rs new file mode 100644 index 000000000..fec40b095 --- /dev/null +++ b/crates/db_views/src/post/mod.rs @@ -0,0 +1,4 @@ +#[cfg(feature = "full")] +pub mod post_tags_view; +#[cfg(feature = "full")] +pub mod post_view; diff --git a/crates/db_views/src/post_tags_view.rs b/crates/db_views/src/post/post_tags_view.rs similarity index 100% rename from crates/db_views/src/post_tags_view.rs rename to crates/db_views/src/post/post_tags_view.rs diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post/post_view.rs similarity index 99% rename from crates/db_views/src/post_view.rs rename to crates/db_views/src/post/post_view.rs index f04747cfd..7065b4637 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post/post_view.rs @@ -638,7 +638,7 @@ impl<'a> PostQuery<'a> { #[cfg(test)] mod tests { use crate::{ - post_view::{PaginationCursorData, PostQuery, PostView}, + post::post_view::{PaginationCursorData, PostQuery, PostView}, structs::{LocalUserView, PostTags}, }; use chrono::Utc; @@ -1604,7 +1604,7 @@ mod tests { listed_post_ids.extend(post_listings.iter().map(|p| p.post.id)); - if let Some(p) = post_listings.into_iter().last() { + if let Some(p) = post_listings.into_iter().next_back() { page_after = Some(p.counts); } else { break; diff --git a/crates/db_views/src/private_message/mod.rs b/crates/db_views/src/private_message/mod.rs new file mode 100644 index 000000000..71bde5dd1 --- /dev/null +++ b/crates/db_views/src/private_message/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "full")] +pub mod private_message_view; diff --git a/crates/db_views_actor/src/private_message_view.rs b/crates/db_views/src/private_message/private_message_view.rs similarity index 100% rename from crates/db_views_actor/src/private_message_view.rs rename to crates/db_views/src/private_message/private_message_view.rs diff --git a/crates/db_views/src/registration_applications/mod.rs b/crates/db_views/src/registration_applications/mod.rs new file mode 100644 index 000000000..b71cca970 --- /dev/null +++ b/crates/db_views/src/registration_applications/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "full")] +pub mod registration_application_view; diff --git a/crates/db_views/src/registration_application_view.rs b/crates/db_views/src/registration_applications/registration_application_view.rs similarity index 99% rename from crates/db_views/src/registration_application_view.rs rename to crates/db_views/src/registration_applications/registration_application_view.rs index 87bbd5b01..cdb6af61d 100644 --- a/crates/db_views/src/registration_application_view.rs +++ b/crates/db_views/src/registration_applications/registration_application_view.rs @@ -137,7 +137,7 @@ impl RegistrationApplicationQuery { #[cfg(test)] mod tests { - use crate::registration_application_view::{ + use crate::registration_applications::registration_application_view::{ RegistrationApplicationQuery, RegistrationApplicationView, }; diff --git a/crates/db_views/src/comment_report_view.rs b/crates/db_views/src/reports/comment_report_view.rs similarity index 100% rename from crates/db_views/src/comment_report_view.rs rename to crates/db_views/src/reports/comment_report_view.rs diff --git a/crates/db_views/src/reports/mod.rs b/crates/db_views/src/reports/mod.rs new file mode 100644 index 000000000..aa42ff6e9 --- /dev/null +++ b/crates/db_views/src/reports/mod.rs @@ -0,0 +1,6 @@ +#[cfg(feature = "full")] +pub mod comment_report_view; +#[cfg(feature = "full")] +pub mod post_report_view; +#[cfg(feature = "full")] +pub mod private_message_report_view; diff --git a/crates/db_views/src/post_report_view.rs b/crates/db_views/src/reports/post_report_view.rs similarity index 100% rename from crates/db_views/src/post_report_view.rs rename to crates/db_views/src/reports/post_report_view.rs diff --git a/crates/db_views/src/private_message_report_view.rs b/crates/db_views/src/reports/private_message_report_view.rs similarity index 100% rename from crates/db_views/src/private_message_report_view.rs rename to crates/db_views/src/reports/private_message_report_view.rs diff --git a/crates/db_views/src/custom_emoji_view.rs b/crates/db_views/src/site/custom_emoji_view.rs similarity index 100% rename from crates/db_views/src/custom_emoji_view.rs rename to crates/db_views/src/site/custom_emoji_view.rs diff --git a/crates/db_views/src/local_image_view.rs b/crates/db_views/src/site/local_image_view.rs similarity index 100% rename from crates/db_views/src/local_image_view.rs rename to crates/db_views/src/site/local_image_view.rs diff --git a/crates/db_views/src/site/mod.rs b/crates/db_views/src/site/mod.rs new file mode 100644 index 000000000..f2c318409 --- /dev/null +++ b/crates/db_views/src/site/mod.rs @@ -0,0 +1,8 @@ +#[cfg(feature = "full")] +pub mod custom_emoji_view; +#[cfg(feature = "full")] +pub mod local_image_view; +#[cfg(feature = "full")] +pub mod site_view; +#[cfg(feature = "full")] +pub mod vote_view; diff --git a/crates/db_views/src/site_view.rs b/crates/db_views/src/site/site_view.rs similarity index 100% rename from crates/db_views/src/site_view.rs rename to crates/db_views/src/site/site_view.rs diff --git a/crates/db_views/src/vote_view.rs b/crates/db_views/src/site/vote_view.rs similarity index 100% rename from crates/db_views/src/vote_view.rs rename to crates/db_views/src/site/vote_view.rs diff --git a/crates/db_views/src/structs.rs b/crates/db_views/src/structs.rs index 43eba36c7..09a868be9 100644 --- a/crates/db_views/src/structs.rs +++ b/crates/db_views/src/structs.rs @@ -12,17 +12,44 @@ use lemmy_db_schema::{ }, source::{ comment::Comment, + comment_reply::CommentReply, comment_report::CommentReport, community::Community, community_report::CommunityReport, custom_emoji::CustomEmoji, custom_emoji_keyword::CustomEmojiKeyword, images::{ImageDetails, LocalImage}, + instance::Instance, local_site::LocalSite, local_site_rate_limit::LocalSiteRateLimit, local_user::LocalUser, local_user_vote_display_mode::LocalUserVoteDisplayMode, + mod_log::{ + admin::{ + AdminAllowInstance, + AdminBlockInstance, + AdminPurgeComment, + AdminPurgeCommunity, + AdminPurgePerson, + AdminPurgePost, + }, + moderator::{ + ModAdd, + ModAddCommunity, + ModBan, + ModBanFromCommunity, + ModFeaturePost, + ModHideCommunity, + ModLockPost, + ModRemoveComment, + ModRemoveCommunity, + ModRemovePost, + ModTransferCommunity, + }, + }, person::Person, + person_comment_mention::PersonCommentMention, + person_post_mention::PersonPostMention, post::Post, post_report::PostReport, private_message::PrivateMessage, @@ -347,7 +374,7 @@ pub enum ReportCombinedView { #[cfg_attr(feature = "full", derive(Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] /// A combined person_content view -pub struct PersonContentViewInternal { +pub struct PersonContentCombinedViewInternal { // Post-specific pub post_counts: PostAggregates, pub post_unread_comments: i64, @@ -384,6 +411,596 @@ pub enum PersonContentCombinedView { Comment(CommentView), } +#[derive(Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// A community follower. +pub struct CommunityFollowerView { + pub community: Community, + pub follower: Person, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// A community moderator. +pub struct CommunityModeratorView { + pub community: Community, + pub moderator: Person, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +/// A community person ban. +pub struct CommunityPersonBanView { + pub community: Community, + pub person: Person, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// A community view. +pub struct CommunityView { + pub community: Community, + pub subscribed: SubscribedType, + pub blocked: bool, + pub counts: CommunityAggregates, + pub banned_from_community: bool, +} + +/// The community sort types. See here for descriptions: https://join-lemmy.org/docs/en/users/03-votes-and-ranking.html +#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "full", derive(TS))] +#[cfg_attr(feature = "full", ts(export))] +pub enum CommunitySortType { + #[default] + Active, + Hot, + New, + Old, + TopDay, + TopWeek, + TopMonth, + TopYear, + TopAll, + MostComments, + NewComments, + TopHour, + TopSixHour, + TopTwelveHour, + TopThreeMonths, + TopSixMonths, + TopNineMonths, + Controversial, + Scaled, + NameAsc, + NameDesc, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// A person comment mention view. +pub struct PersonCommentMentionView { + pub person_comment_mention: PersonCommentMention, + pub comment: Comment, + pub creator: Person, + pub post: Post, + pub community: Community, + pub recipient: Person, + pub counts: CommentAggregates, + pub creator_banned_from_community: bool, + pub banned_from_community: bool, + pub creator_is_moderator: bool, + pub creator_is_admin: bool, + pub subscribed: SubscribedType, + pub saved: bool, + pub creator_blocked: bool, + #[cfg_attr(feature = "full", ts(optional))] + pub my_vote: Option, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// A person post mention view. +pub struct PersonPostMentionView { + pub person_post_mention: PersonPostMention, + pub post: Post, + pub creator: Person, + pub community: Community, + #[cfg_attr(feature = "full", ts(optional))] + pub image_details: Option, + pub recipient: Person, + pub counts: PostAggregates, + pub creator_banned_from_community: bool, + pub banned_from_community: bool, + pub creator_is_moderator: bool, + pub creator_is_admin: bool, + pub subscribed: SubscribedType, + pub saved: bool, + pub read: bool, + pub hidden: bool, + pub creator_blocked: bool, + #[cfg_attr(feature = "full", ts(optional))] + pub my_vote: Option, + pub unread_comments: i64, + pub post_tags: PostTags, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// A comment reply view. +pub struct CommentReplyView { + pub comment_reply: CommentReply, + pub comment: Comment, + pub creator: Person, + pub post: Post, + pub community: Community, + pub recipient: Person, + pub counts: CommentAggregates, + pub creator_banned_from_community: bool, + pub banned_from_community: bool, + pub creator_is_moderator: bool, + pub creator_is_admin: bool, + pub subscribed: SubscribedType, + pub saved: bool, + pub creator_blocked: bool, + #[cfg_attr(feature = "full", ts(optional))] + pub my_vote: Option, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// A person view. +pub struct PersonView { + pub person: Person, + pub counts: PersonAggregates, + pub is_admin: bool, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +pub struct PendingFollow { + pub person: Person, + pub community: Community, + pub is_new_instance: bool, + pub subscribed: SubscribedType, +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// A private message view. +pub struct PrivateMessageView { + pub private_message: PrivateMessage, + pub creator: Person, + pub recipient: Person, +} + +/// like PaginationCursor but for the report_combined table +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "full", derive(TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct InboxCombinedPaginationCursor(pub String); + +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +/// A combined inbox view +pub struct InboxCombinedViewInternal { + // Comment reply + pub comment_reply: Option, + // Person comment mention + pub person_comment_mention: Option, + // Person post mention + pub person_post_mention: Option, + pub post_counts: Option, + pub post_unread_comments: Option, + pub post_saved: bool, + pub post_read: bool, + pub post_hidden: bool, + pub my_post_vote: Option, + pub image_details: Option, + pub post_tags: PostTags, + // Private message + pub private_message: Option, + // Shared + pub post: Option, + pub community: Option, + pub comment: Option, + pub comment_counts: Option, + pub comment_saved: bool, + pub my_comment_vote: Option, + pub subscribed: SubscribedType, + pub item_creator: Person, + pub item_recipient: Person, + pub item_creator_is_admin: bool, + pub item_creator_is_moderator: bool, + pub item_creator_banned_from_community: bool, + pub item_creator_blocked: bool, + pub banned_from_community: bool, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS))] +#[cfg_attr(feature = "full", ts(export))] +// Use serde's internal tagging, to work easier with javascript libraries +#[serde(tag = "type_")] +pub enum InboxCombinedView { + CommentReply(CommentReplyView), + CommentMention(PersonCommentMentionView), + PostMention(PersonPostMentionView), + PrivateMessage(PrivateMessageView), +} +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When someone is added as a community moderator. +pub struct ModAddCommunityView { + pub mod_add_community: ModAddCommunity, + #[cfg_attr(feature = "full", ts(optional))] + pub moderator: Option, + pub community: Community, + pub other_person: Person, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When someone is added as a site moderator. +pub struct ModAddView { + pub mod_add: ModAdd, + #[cfg_attr(feature = "full", ts(optional))] + pub moderator: Option, + pub other_person: Person, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When someone is banned from a community. +pub struct ModBanFromCommunityView { + pub mod_ban_from_community: ModBanFromCommunity, + #[cfg_attr(feature = "full", ts(optional))] + pub moderator: Option, + pub community: Community, + pub other_person: Person, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When someone is banned from the site. +pub struct ModBanView { + pub mod_ban: ModBan, + #[cfg_attr(feature = "full", ts(optional))] + pub moderator: Option, + pub other_person: Person, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When a community is hidden from public view. +pub struct ModHideCommunityView { + pub mod_hide_community: ModHideCommunity, + #[cfg_attr(feature = "full", ts(optional))] + pub admin: Option, + pub community: Community, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When a moderator locks a post (prevents new comments being made). +pub struct ModLockPostView { + pub mod_lock_post: ModLockPost, + #[cfg_attr(feature = "full", ts(optional))] + pub moderator: Option, + pub other_person: Person, + pub post: Post, + pub community: Community, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When a moderator removes a comment. +pub struct ModRemoveCommentView { + pub mod_remove_comment: ModRemoveComment, + #[cfg_attr(feature = "full", ts(optional))] + pub moderator: Option, + pub other_person: Person, + pub comment: Comment, + pub post: Post, + pub community: Community, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When a moderator removes a community. +pub struct ModRemoveCommunityView { + pub mod_remove_community: ModRemoveCommunity, + #[cfg_attr(feature = "full", ts(optional))] + pub moderator: Option, + pub community: Community, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When a moderator removes a post. +pub struct ModRemovePostView { + pub mod_remove_post: ModRemovePost, + #[cfg_attr(feature = "full", ts(optional))] + pub moderator: Option, + pub other_person: Person, + pub post: Post, + pub community: Community, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When a moderator features a post on a community (pins it to the top). +pub struct ModFeaturePostView { + pub mod_feature_post: ModFeaturePost, + #[cfg_attr(feature = "full", ts(optional))] + pub moderator: Option, + pub other_person: Person, + pub post: Post, + pub community: Community, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When a moderator transfers a community to a new owner. +pub struct ModTransferCommunityView { + pub mod_transfer_community: ModTransferCommunity, + #[cfg_attr(feature = "full", ts(optional))] + pub moderator: Option, + pub community: Community, + pub other_person: Person, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When an admin purges a comment. +pub struct AdminPurgeCommentView { + pub admin_purge_comment: AdminPurgeComment, + #[cfg_attr(feature = "full", ts(optional))] + pub admin: Option, + pub post: Post, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When an admin purges a community. +pub struct AdminPurgeCommunityView { + pub admin_purge_community: AdminPurgeCommunity, + #[cfg_attr(feature = "full", ts(optional))] + pub admin: Option, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When an admin purges a person. +pub struct AdminPurgePersonView { + pub admin_purge_person: AdminPurgePerson, + #[cfg_attr(feature = "full", ts(optional))] + pub admin: Option, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When an admin purges a post. +pub struct AdminPurgePostView { + pub admin_purge_post: AdminPurgePost, + #[cfg_attr(feature = "full", ts(optional))] + pub admin: Option, + pub community: Community, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When an admin purges a post. +pub struct AdminBlockInstanceView { + pub admin_block_instance: AdminBlockInstance, + pub instance: Instance, + #[cfg_attr(feature = "full", ts(optional))] + pub admin: Option, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When an admin purges a post. +pub struct AdminAllowInstanceView { + pub admin_allow_instance: AdminAllowInstance, + pub instance: Instance, + #[cfg_attr(feature = "full", ts(optional))] + pub admin: Option, +} + +/// like PaginationCursor but for the modlog_combined +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "full", derive(TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct ModlogCombinedPaginationCursor(pub String); + +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +/// A combined modlog view +pub struct ModlogCombinedViewInternal { + // Specific + pub admin_allow_instance: Option, + pub admin_block_instance: Option, + pub admin_purge_comment: Option, + pub admin_purge_community: Option, + pub admin_purge_person: Option, + pub admin_purge_post: Option, + pub mod_add: Option, + pub mod_add_community: Option, + pub mod_ban: Option, + pub mod_ban_from_community: Option, + pub mod_feature_post: Option, + pub mod_hide_community: Option, + pub mod_lock_post: Option, + pub mod_remove_comment: Option, + pub mod_remove_community: Option, + pub mod_remove_post: Option, + pub mod_transfer_community: Option, + // Specific fields + + // Shared + pub moderator: Option, + pub other_person: Option, + pub instance: Option, + pub community: Option, + pub post: Option, + pub comment: Option, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS))] +#[cfg_attr(feature = "full", ts(export))] +// Use serde's internal tagging, to work easier with javascript libraries +#[serde(tag = "type_")] +pub enum ModlogCombinedView { + AdminAllowInstance(AdminAllowInstanceView), + AdminBlockInstance(AdminBlockInstanceView), + AdminPurgeComment(AdminPurgeCommentView), + AdminPurgeCommunity(AdminPurgeCommunityView), + AdminPurgePerson(AdminPurgePersonView), + AdminPurgePost(AdminPurgePostView), + ModAdd(ModAddView), + ModAddCommunity(ModAddCommunityView), + ModBan(ModBanView), + ModBanFromCommunity(ModBanFromCommunityView), + ModFeaturePost(ModFeaturePostView), + ModHideCommunity(ModHideCommunityView), + ModLockPost(ModLockPostView), + ModRemoveComment(ModRemoveCommentView), + ModRemoveCommunity(ModRemoveCommunityView), + ModRemovePost(ModRemovePostView), + ModTransferCommunity(ModTransferCommunityView), +} + +/// like PaginationCursor but for the modlog_combined +// TODO get rid of all these pagination cursors +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "full", derive(TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct SearchCombinedPaginationCursor(pub String); + +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +/// A combined search view +pub struct SearchCombinedViewInternal { + // Post-specific + pub post: Option, + pub post_counts: Option, + pub post_unread_comments: Option, + pub post_saved: bool, + pub post_read: bool, + pub post_hidden: bool, + pub my_post_vote: Option, + pub image_details: Option, + pub post_tags: PostTags, + // // Comment-specific + pub comment: Option, + pub comment_counts: Option, + pub comment_saved: bool, + pub my_comment_vote: Option, + // // Community-specific + pub community: Option, + pub community_counts: Option, + pub community_blocked: bool, + pub subscribed: SubscribedType, + // Person + pub item_creator_counts: Option, + // Shared + pub item_creator: Option, + pub item_creator_is_admin: bool, + pub item_creator_is_moderator: bool, + pub item_creator_banned_from_community: bool, + pub item_creator_blocked: bool, + pub banned_from_community: bool, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS))] +#[cfg_attr(feature = "full", ts(export))] +// Use serde's internal tagging, to work easier with javascript libraries +#[serde(tag = "type_")] +pub enum SearchCombinedView { + Post(PostView), + Comment(CommentView), + Community(CommunityView), + Person(PersonView), +} + #[derive(Clone, serde::Serialize, serde::Deserialize, Debug, PartialEq, Default)] #[cfg_attr(feature = "full", derive(TS, FromSqlRow, AsExpression))] #[serde(transparent)] diff --git a/crates/db_views_actor/Cargo.toml b/crates/db_views_actor/Cargo.toml deleted file mode 100644 index 34d64d0b0..000000000 --- a/crates/db_views_actor/Cargo.toml +++ /dev/null @@ -1,50 +0,0 @@ -[package] -name = "lemmy_db_views_actor" -version.workspace = true -edition.workspace = true -description.workspace = true -license.workspace = true -homepage.workspace = true -documentation.workspace = true -repository.workspace = true - -[lib] -doctest = false - -[lints] -workspace = true - -[features] -full = [ - "lemmy_db_schema/full", - "lemmy_utils/full", - "i-love-jesus", - "diesel", - "diesel-async", - "ts-rs", -] - -[dependencies] -lemmy_db_schema = { workspace = true } -diesel = { workspace = true, features = [ - "chrono", - "postgres", - "serde_json", -], optional = true } -diesel-async = { workspace = true, features = [ - "deadpool", - "postgres", -], optional = true } -serde = { workspace = true } -serde_with = { workspace = true } -ts-rs = { workspace = true, optional = true } -chrono.workspace = true -strum = { workspace = true } -lemmy_utils = { workspace = true, optional = true } -i-love-jesus = { workspace = true, optional = true } - -[dev-dependencies] -serial_test = { workspace = true } -tokio = { workspace = true } -pretty_assertions = { workspace = true } -url.workspace = true diff --git a/crates/db_views_actor/src/structs.rs b/crates/db_views_actor/src/structs.rs deleted file mode 100644 index b1f75c86d..000000000 --- a/crates/db_views_actor/src/structs.rs +++ /dev/null @@ -1,259 +0,0 @@ -#[cfg(feature = "full")] -use diesel::Queryable; -use lemmy_db_schema::{ - aggregates::structs::{CommentAggregates, CommunityAggregates, PersonAggregates, PostAggregates}, - source::{ - comment::Comment, - comment_reply::CommentReply, - community::Community, - images::ImageDetails, - person::Person, - person_comment_mention::PersonCommentMention, - person_post_mention::PersonPostMention, - post::Post, - private_message::PrivateMessage, - }, - SubscribedType, -}; -use serde::{Deserialize, Serialize}; -use serde_with::skip_serializing_none; -#[cfg(feature = "full")] -use ts_rs::TS; - -#[derive(Debug, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// A community follower. -pub struct CommunityFollowerView { - pub community: Community, - pub follower: Person, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// A community moderator. -pub struct CommunityModeratorView { - pub community: Community, - pub moderator: Person, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -/// A community person ban. -pub struct CommunityPersonBanView { - pub community: Community, - pub person: Person, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// A community view. -pub struct CommunityView { - pub community: Community, - pub subscribed: SubscribedType, - pub blocked: bool, - pub counts: CommunityAggregates, - pub banned_from_community: bool, -} - -/// The community sort types. See here for descriptions: https://join-lemmy.org/docs/en/users/03-votes-and-ranking.html -#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "full", derive(TS))] -#[cfg_attr(feature = "full", ts(export))] -pub enum CommunitySortType { - #[default] - Active, - Hot, - New, - Old, - TopDay, - TopWeek, - TopMonth, - TopYear, - TopAll, - MostComments, - NewComments, - TopHour, - TopSixHour, - TopTwelveHour, - TopThreeMonths, - TopSixMonths, - TopNineMonths, - Controversial, - Scaled, - NameAsc, - NameDesc, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// A person comment mention view. -pub struct PersonCommentMentionView { - pub person_comment_mention: PersonCommentMention, - pub comment: Comment, - pub creator: Person, - pub post: Post, - pub community: Community, - pub recipient: Person, - pub counts: CommentAggregates, - pub creator_banned_from_community: bool, - pub banned_from_community: bool, - pub creator_is_moderator: bool, - pub creator_is_admin: bool, - pub subscribed: SubscribedType, - pub saved: bool, - pub creator_blocked: bool, - #[cfg_attr(feature = "full", ts(optional))] - pub my_vote: Option, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// A person post mention view. -pub struct PersonPostMentionView { - pub person_post_mention: PersonPostMention, - pub post: Post, - pub creator: Person, - pub community: Community, - #[cfg_attr(feature = "full", ts(optional))] - pub image_details: Option, - pub recipient: Person, - pub counts: PostAggregates, - pub creator_banned_from_community: bool, - pub banned_from_community: bool, - pub creator_is_moderator: bool, - pub creator_is_admin: bool, - pub subscribed: SubscribedType, - pub saved: bool, - pub read: bool, - pub hidden: bool, - pub creator_blocked: bool, - #[cfg_attr(feature = "full", ts(optional))] - pub my_vote: Option, - pub unread_comments: i64, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// A comment reply view. -pub struct CommentReplyView { - pub comment_reply: CommentReply, - pub comment: Comment, - pub creator: Person, - pub post: Post, - pub community: Community, - pub recipient: Person, - pub counts: CommentAggregates, - pub creator_banned_from_community: bool, - pub banned_from_community: bool, - pub creator_is_moderator: bool, - pub creator_is_admin: bool, - pub subscribed: SubscribedType, - pub saved: bool, - pub creator_blocked: bool, - #[cfg_attr(feature = "full", ts(optional))] - pub my_vote: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// A person view. -pub struct PersonView { - pub person: Person, - pub counts: PersonAggregates, - pub is_admin: bool, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -pub struct PendingFollow { - pub person: Person, - pub community: Community, - pub is_new_instance: bool, - pub subscribed: SubscribedType, -} - -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// A private message view. -pub struct PrivateMessageView { - pub private_message: PrivateMessage, - pub creator: Person, - pub recipient: Person, -} - -/// like PaginationCursor but for the report_combined table -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "full", derive(TS))] -#[cfg_attr(feature = "full", ts(export))] -pub struct InboxCombinedPaginationCursor(pub String); - -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -/// A combined inbox view -pub struct InboxCombinedViewInternal { - // Comment reply - pub comment_reply: Option, - // Person comment mention - pub person_comment_mention: Option, - // Person post mention - pub person_post_mention: Option, - pub post_counts: Option, - pub post_unread_comments: Option, - pub post_saved: bool, - pub post_read: bool, - pub post_hidden: bool, - pub my_post_vote: Option, - pub image_details: Option, - // Private message - pub private_message: Option, - // Shared - pub post: Option, - pub community: Option, - pub comment: Option, - pub comment_counts: Option, - pub comment_saved: bool, - pub my_comment_vote: Option, - pub subscribed: SubscribedType, - pub item_creator: Person, - pub item_recipient: Person, - pub item_creator_is_admin: bool, - pub item_creator_is_moderator: bool, - pub item_creator_banned_from_community: bool, - pub item_creator_blocked: bool, - pub banned_from_community: bool, -} - -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS))] -#[cfg_attr(feature = "full", ts(export))] -// Use serde's internal tagging, to work easier with javascript libraries -#[serde(tag = "type_")] -pub enum InboxCombinedView { - CommentReply(CommentReplyView), - CommentMention(PersonCommentMentionView), - PostMention(PersonPostMentionView), - PrivateMessage(PrivateMessageView), -} diff --git a/crates/db_views_moderator/Cargo.toml b/crates/db_views_moderator/Cargo.toml deleted file mode 100644 index a7257c4f1..000000000 --- a/crates/db_views_moderator/Cargo.toml +++ /dev/null @@ -1,47 +0,0 @@ -[package] -name = "lemmy_db_views_moderator" -version.workspace = true -edition.workspace = true -description.workspace = true -license.workspace = true -homepage.workspace = true -documentation.workspace = true -repository.workspace = true - -[lib] -doctest = false - -[lints] -workspace = true - -[features] -full = [ - "lemmy_db_schema/full", - "lemmy_utils", - "i-love-jesus", - "diesel", - "diesel-async", - "ts-rs", -] - -[dependencies] -lemmy_db_schema = { workspace = true } -lemmy_utils = { workspace = true, optional = true } -i-love-jesus = { workspace = true, optional = true } -diesel = { workspace = true, features = [ - "chrono", - "postgres", - "serde_json", -], optional = true } -diesel-async = { workspace = true, features = [ - "deadpool", - "postgres", -], optional = true } -serde = { workspace = true } -serde_with = { workspace = true } -ts-rs = { workspace = true, optional = true } - -[dev-dependencies] -serial_test = { workspace = true } -tokio = { workspace = true } -pretty_assertions = { workspace = true } diff --git a/crates/db_views_moderator/src/lib.rs b/crates/db_views_moderator/src/lib.rs deleted file mode 100644 index 1cc21da27..000000000 --- a/crates/db_views_moderator/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[cfg(feature = "full")] -pub mod modlog_combined_view; -pub mod structs; diff --git a/crates/db_views_moderator/src/structs.rs b/crates/db_views_moderator/src/structs.rs deleted file mode 100644 index 513a79705..000000000 --- a/crates/db_views_moderator/src/structs.rs +++ /dev/null @@ -1,332 +0,0 @@ -#[cfg(feature = "full")] -use diesel::Queryable; -use lemmy_db_schema::source::{ - comment::Comment, - community::Community, - instance::Instance, - mod_log::{ - admin::{ - AdminAllowInstance, - AdminBlockInstance, - AdminPurgeComment, - AdminPurgeCommunity, - AdminPurgePerson, - AdminPurgePost, - }, - moderator::{ - ModAdd, - ModAddCommunity, - ModBan, - ModBanFromCommunity, - ModFeaturePost, - ModHideCommunity, - ModLockPost, - ModRemoveComment, - ModRemoveCommunity, - ModRemovePost, - ModTransferCommunity, - }, - }, - person::Person, - post::Post, -}; -use serde::{Deserialize, Serialize}; -use serde_with::skip_serializing_none; -#[cfg(feature = "full")] -use ts_rs::TS; - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When someone is added as a community moderator. -pub struct ModAddCommunityView { - pub mod_add_community: ModAddCommunity, - #[cfg_attr(feature = "full", ts(optional))] - pub moderator: Option, - pub community: Community, - pub other_person: Person, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When someone is added as a site moderator. -pub struct ModAddView { - pub mod_add: ModAdd, - #[cfg_attr(feature = "full", ts(optional))] - pub moderator: Option, - pub other_person: Person, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When someone is banned from a community. -pub struct ModBanFromCommunityView { - pub mod_ban_from_community: ModBanFromCommunity, - #[cfg_attr(feature = "full", ts(optional))] - pub moderator: Option, - pub community: Community, - pub other_person: Person, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When someone is banned from the site. -pub struct ModBanView { - pub mod_ban: ModBan, - #[cfg_attr(feature = "full", ts(optional))] - pub moderator: Option, - pub other_person: Person, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When a community is hidden from public view. -pub struct ModHideCommunityView { - pub mod_hide_community: ModHideCommunity, - #[cfg_attr(feature = "full", ts(optional))] - pub admin: Option, - pub community: Community, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When a moderator locks a post (prevents new comments being made). -pub struct ModLockPostView { - pub mod_lock_post: ModLockPost, - #[cfg_attr(feature = "full", ts(optional))] - pub moderator: Option, - pub other_person: Person, - pub post: Post, - pub community: Community, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When a moderator removes a comment. -pub struct ModRemoveCommentView { - pub mod_remove_comment: ModRemoveComment, - #[cfg_attr(feature = "full", ts(optional))] - pub moderator: Option, - pub other_person: Person, - pub comment: Comment, - pub post: Post, - pub community: Community, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When a moderator removes a community. -pub struct ModRemoveCommunityView { - pub mod_remove_community: ModRemoveCommunity, - #[cfg_attr(feature = "full", ts(optional))] - pub moderator: Option, - pub community: Community, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When a moderator removes a post. -pub struct ModRemovePostView { - pub mod_remove_post: ModRemovePost, - #[cfg_attr(feature = "full", ts(optional))] - pub moderator: Option, - pub other_person: Person, - pub post: Post, - pub community: Community, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When a moderator features a post on a community (pins it to the top). -pub struct ModFeaturePostView { - pub mod_feature_post: ModFeaturePost, - #[cfg_attr(feature = "full", ts(optional))] - pub moderator: Option, - pub other_person: Person, - pub post: Post, - pub community: Community, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When a moderator transfers a community to a new owner. -pub struct ModTransferCommunityView { - pub mod_transfer_community: ModTransferCommunity, - #[cfg_attr(feature = "full", ts(optional))] - pub moderator: Option, - pub community: Community, - pub other_person: Person, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When an admin purges a comment. -pub struct AdminPurgeCommentView { - pub admin_purge_comment: AdminPurgeComment, - #[cfg_attr(feature = "full", ts(optional))] - pub admin: Option, - pub post: Post, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When an admin purges a community. -pub struct AdminPurgeCommunityView { - pub admin_purge_community: AdminPurgeCommunity, - #[cfg_attr(feature = "full", ts(optional))] - pub admin: Option, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When an admin purges a person. -pub struct AdminPurgePersonView { - pub admin_purge_person: AdminPurgePerson, - #[cfg_attr(feature = "full", ts(optional))] - pub admin: Option, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When an admin purges a post. -pub struct AdminPurgePostView { - pub admin_purge_post: AdminPurgePost, - #[cfg_attr(feature = "full", ts(optional))] - pub admin: Option, - pub community: Community, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When an admin purges a post. -pub struct AdminBlockInstanceView { - pub admin_block_instance: AdminBlockInstance, - pub instance: Instance, - #[cfg_attr(feature = "full", ts(optional))] - pub admin: Option, -} - -#[skip_serializing_none] -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -#[cfg_attr(feature = "full", ts(export))] -/// When an admin purges a post. -pub struct AdminAllowInstanceView { - pub admin_allow_instance: AdminAllowInstance, - pub instance: Instance, - #[cfg_attr(feature = "full", ts(optional))] - pub admin: Option, -} - -/// like PaginationCursor but for the modlog_combined -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "full", derive(TS))] -#[cfg_attr(feature = "full", ts(export))] -pub struct ModlogCombinedPaginationCursor(pub String); - -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(Queryable))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -/// A combined modlog view -pub struct ModlogCombinedViewInternal { - // Specific - pub admin_allow_instance: Option, - pub admin_block_instance: Option, - pub admin_purge_comment: Option, - pub admin_purge_community: Option, - pub admin_purge_person: Option, - pub admin_purge_post: Option, - pub mod_add: Option, - pub mod_add_community: Option, - pub mod_ban: Option, - pub mod_ban_from_community: Option, - pub mod_feature_post: Option, - pub mod_hide_community: Option, - pub mod_lock_post: Option, - pub mod_remove_comment: Option, - pub mod_remove_community: Option, - pub mod_remove_post: Option, - pub mod_transfer_community: Option, - // Specific fields - - // Shared - pub moderator: Option, - pub other_person: Option, - pub instance: Option, - pub community: Option, - pub post: Option, - pub comment: Option, -} - -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS))] -#[cfg_attr(feature = "full", ts(export))] -// Use serde's internal tagging, to work easier with javascript libraries -#[serde(tag = "type_")] -pub enum ModlogCombinedView { - AdminAllowInstance(AdminAllowInstanceView), - AdminBlockInstance(AdminBlockInstanceView), - AdminPurgeComment(AdminPurgeCommentView), - AdminPurgeCommunity(AdminPurgeCommunityView), - AdminPurgePerson(AdminPurgePersonView), - AdminPurgePost(AdminPurgePostView), - ModAdd(ModAddView), - ModAddCommunity(ModAddCommunityView), - ModBan(ModBanView), - ModBanFromCommunity(ModBanFromCommunityView), - ModFeaturePost(ModFeaturePostView), - ModHideCommunity(ModHideCommunityView), - ModLockPost(ModLockPostView), - ModRemoveComment(ModRemoveCommentView), - ModRemoveCommunity(ModRemoveCommunityView), - ModRemovePost(ModRemovePostView), - ModTransferCommunity(ModTransferCommunityView), -} diff --git a/crates/federate/Cargo.toml b/crates/federate/Cargo.toml index 7ea46de80..fb5e436b0 100644 --- a/crates/federate/Cargo.toml +++ b/crates/federate/Cargo.toml @@ -18,7 +18,7 @@ workspace = true lemmy_api_common.workspace = true lemmy_apub.workspace = true lemmy_db_schema = { workspace = true, features = ["full"] } -lemmy_db_views_actor.workspace = true +lemmy_db_views.workspace = true lemmy_utils.workspace = true activitypub_federation.workspace = true diff --git a/crates/federate/src/inboxes.rs b/crates/federate/src/inboxes.rs index ec96b1d6c..a3997bc3b 100644 --- a/crates/federate/src/inboxes.rs +++ b/crates/federate/src/inboxes.rs @@ -6,7 +6,7 @@ use lemmy_db_schema::{ source::{activity::SentActivity, site::Site}, utils::{ActualDbPool, DbPool}, }; -use lemmy_db_views_actor::structs::CommunityFollowerView; +use lemmy_db_views::structs::CommunityFollowerView; use lemmy_utils::error::LemmyResult; use reqwest::Url; use std::{ diff --git a/crates/routes/Cargo.toml b/crates/routes/Cargo.toml index 0edf436cd..85a066964 100644 --- a/crates/routes/Cargo.toml +++ b/crates/routes/Cargo.toml @@ -18,7 +18,6 @@ workspace = true [dependencies] lemmy_utils = { workspace = true, features = ["full"] } lemmy_db_views = { workspace = true } -lemmy_db_views_actor = { workspace = true } lemmy_db_schema = { workspace = true } lemmy_api_common = { workspace = true, features = ["full"] } activitypub_federation = { workspace = true } diff --git a/crates/routes/src/feeds.rs b/crates/routes/src/feeds.rs index 4368aa172..401c29d89 100644 --- a/crates/routes/src/feeds.rs +++ b/crates/routes/src/feeds.rs @@ -13,10 +13,10 @@ use lemmy_db_schema::{ PostSortType, }; use lemmy_db_views::{ - post_view::PostQuery, - structs::{PostView, SiteView}, + combined::inbox_combined_view::InboxCombinedQuery, + post::post_view::PostQuery, + structs::{InboxCombinedView, PostView, SiteView}, }; -use lemmy_db_views_actor::{inbox_combined_view::InboxCombinedQuery, structs::InboxCombinedView}; use lemmy_utils::{ cache_header::cache_1hour, error::{LemmyError, LemmyErrorType, LemmyResult}, diff --git a/crates/routes/src/images/download.rs b/crates/routes/src/images/download.rs index 76f09a8d1..c4317c4dd 100644 --- a/crates/routes/src/images/download.rs +++ b/crates/routes/src/images/download.rs @@ -125,5 +125,5 @@ pub(super) async fn do_get_image( pub(super) fn file_type(file_type: Option, name: &str) -> String { file_type .clone() - .unwrap_or_else(|| name.split('.').last().unwrap_or("jpg").to_string()) + .unwrap_or_else(|| name.split('.').next_back().unwrap_or("jpg").to_string()) } diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index dc978244e..ed4325dec 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -38,7 +38,7 @@ services: hostname: lemmy restart: unless-stopped environment: - - RUST_LOG="warn,lemmy_server=debug,lemmy_api=debug,lemmy_api_common=debug,lemmy_api_crud=debug,lemmy_apub=debug,lemmy_db_schema=debug,lemmy_db_views=debug,lemmy_db_views_actor=debug,lemmy_db_views_moderator=debug,lemmy_routes=debug,lemmy_utils=debug,lemmy_websocket=debug" + - RUST_LOG="warn,lemmy_server=debug,lemmy_api=debug,lemmy_api_common=debug,lemmy_api_crud=debug,lemmy_apub=debug,lemmy_db_schema=debug,lemmy_db_views=debug,lemmy_routes=debug,lemmy_utils=debug,lemmy_websocket=debug" - RUST_BACKTRACE=full ports: # prometheus metrics can be enabled with the `prometheus` config option. they are available on diff --git a/docker/federation/docker-compose.yml b/docker/federation/docker-compose.yml index bc4b5ea7f..711e4eae0 100644 --- a/docker/federation/docker-compose.yml +++ b/docker/federation/docker-compose.yml @@ -16,7 +16,7 @@ x-lemmy-default: &lemmy-default dockerfile: docker/Dockerfile environment: - RUST_BACKTRACE=1 - - RUST_LOG="warn,lemmy_server=debug,lemmy_api=debug,lemmy_api_common=debug,lemmy_api_crud=debug,lemmy_apub=debug,lemmy_db_schema=debug,lemmy_db_views=debug,lemmy_db_views_actor=debug,lemmy_db_views_moderator=debug,lemmy_routes=debug,lemmy_utils=debug,lemmy_websocket=debug" + - RUST_LOG="warn,lemmy_server=debug,lemmy_api=debug,lemmy_api_common=debug,lemmy_api_crud=debug,lemmy_apub=debug,lemmy_db_schema=debug,lemmy_db_views=debug,lemmy_routes=debug,lemmy_utils=debug,lemmy_websocket=debug" restart: always x-postgres-default: &postgres-default diff --git a/migrations/2024-12-12-222846_add_search_combined_table/down.sql b/migrations/2024-12-12-222846_add_search_combined_table/down.sql new file mode 100644 index 000000000..477bb9b63 --- /dev/null +++ b/migrations/2024-12-12-222846_add_search_combined_table/down.sql @@ -0,0 +1,5 @@ +ALTER TABLE person_aggregates + DROP COLUMN published; + +DROP TABLE search_combined; + diff --git a/migrations/2024-12-12-222846_add_search_combined_table/up.sql b/migrations/2024-12-12-222846_add_search_combined_table/up.sql new file mode 100644 index 000000000..f8edc7454 --- /dev/null +++ b/migrations/2024-12-12-222846_add_search_combined_table/up.sql @@ -0,0 +1,80 @@ +-- Creates combined tables for +-- Search: (post, comment, community, person) +CREATE TABLE search_combined ( + id serial PRIMARY KEY, + published timestamptz NOT NULL, + -- This is used for the top sort + -- For persons: its post score + -- For comments: score, + -- For posts: score, + -- For community: users active monthly + score bigint NOT NULL DEFAULT 0, + post_id int UNIQUE REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE, + comment_id int UNIQUE REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE, + community_id int UNIQUE REFERENCES community ON UPDATE CASCADE ON DELETE CASCADE, + person_id int UNIQUE REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE, + -- Make sure only one of the columns is not null + CHECK (num_nonnulls (post_id, comment_id, community_id, person_id) = 1) +); + +CREATE INDEX idx_search_combined_published ON search_combined (published DESC, id DESC); + +CREATE INDEX idx_search_combined_published_asc ON search_combined (reverse_timestamp_sort (published) DESC, id DESC); + +CREATE INDEX idx_search_combined_score ON search_combined (score DESC, id DESC); + +-- Add published to person_aggregates (it was missing for some reason) +ALTER TABLE person_aggregates + ADD COLUMN published timestamptz NOT NULL DEFAULT now(); + +UPDATE + person_aggregates pa +SET + published = p.published +FROM + person p +WHERE + pa.person_id = p.id; + +-- Updating the history +INSERT INTO search_combined (published, score, post_id, comment_id, community_id, person_id) +SELECT + published, + score, + post_id, + NULL::int, + NULL::int, + NULL::int +FROM + post_aggregates +UNION ALL +SELECT + published, + score, + NULL::int, + comment_id, + NULL::int, + NULL::int +FROM + comment_aggregates +UNION ALL +SELECT + published, + users_active_month, + NULL::int, + NULL::int, + community_id, + NULL::int +FROM + community_aggregates +UNION ALL +SELECT + published, + post_score, + NULL::int, + NULL::int, + NULL::int, + person_id +FROM + person_aggregates; +