From 624f31ffaf8bd3b8e825e9263c663a77113a8159 Mon Sep 17 00:00:00 2001 From: naskya Date: Sat, 28 Oct 2023 20:19:25 +0900 Subject: [PATCH] feat: use PGroonga for full-text search Co-authored-by: tamaina --- README.md | 89 +++++++++++++++++-- docker-compose.yml | 35 +------- neko/flags/.gitignore | 4 + neko/messages/install_pgroonga | 7 ++ neko/revert.sql | 6 ++ .../migration-neko/1698420787202-pgroonga.js | 27 ++++++ packages/backend/src/models/entities/note.ts | 1 + .../src/models/entities/user-profile.ts | 1 + packages/backend/src/models/entities/user.ts | 1 + .../src/server/api/endpoints/notes/search.ts | 2 +- .../src/server/api/endpoints/users/search.ts | 8 +- update.sh | 12 +++ 12 files changed, 149 insertions(+), 44 deletions(-) create mode 100644 neko/flags/.gitignore create mode 100644 neko/messages/install_pgroonga create mode 100644 packages/backend/migration-neko/1698420787202-pgroonga.js diff --git a/README.md b/README.md index fa4b8d431..a927716ac 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ ## 主要な変更点 +- 全文検索のエンジンを [PGroonga](https://pgroonga.github.io/) に変更 + - PGroonga のインストールが必要になります!詳しくは[この投稿](https://post.naskya.net/notes/9ldi29amfanomef5)をご覧ください + - Meilisearch, Elasticsearch, Sonic は非推奨となります - 「秘密」という公開範囲を追加 - 宛先無しのダイレクト投稿を言い換えているだけです - 既存の投稿を削除せずに後から秘密にすることもできます @@ -139,15 +142,14 @@ ## インストール -[Firefish のインストールスクリプト](https://git.joinfirefish.org/firefish/ubuntu-bash-install)のプロンプトで尋ねられるリポジトリの URL にこのリポジトリの URL を使ってください。 +ToDo (#82) -``` -Repository url where you want to install: -> https://code.naskya.net/naskya/firefish -``` +遠回りな方法ですが、公式のインストールスクリプトを使いたい場合にはそれを用いて本家の Firefish をインストールしてから下記の手順でこのフォークに移行できます。 ## アップデート +重要なお知らせがある場合にはアップデートスクリプトを通じてお伝えするので、必ず `update.sh` を用いてアップデートしてください。 + 1. サーバーのバックアップを取る 1. サーバーを停止する ```sh @@ -188,6 +190,64 @@ Repository url where you want to install: $ cp -r calckey.old/custom calckey $ cp -r calckey.old/.config calckey ``` +1. 全文検索エンジン(Meilisearch, Sonic, Elasticsearch のいずれか)を使用している場合には、`.config/default.yml` からその設定を削除またはコメントアウトする + 先頭に `#` をつけると設定をコメントアウトできます。 + ```yaml + #sonic: + # host: localhost + # port: 1491 + # auth: SecretPassword + # collection: notes + # bucket: default + ``` + 全文検索エンジンは停止またはアンインストールしてしまってよいです。本家の Firefish に戻るつもりがあるなら停止を、そうでなければアンインストールをおすすめします。 + 停止コマンドの例 + ```sh + $ sudo systemctl disable --now sonic + ``` +1. PostgreSQL のバージョンを確認する + ```sh + $ psql --version + ``` +1. PGroonga をインストールする + インストールコマンドの例(詳しくは[この投稿](https://post.naskya.net/notes/9ldi29amfanomef5)を参考にしてください) + ```sh + $ sudo apt install -y software-properties-common + $ sudo add-apt-repository -y universe + $ sudo add-apt-repository -y ppa:groonga/ppa + $ sudo apt install -y wget lsb-release + $ wget https://packages.groonga.org/ubuntu/groonga-apt-source-latest-$(lsb_release --codename --short).deb + $ sudo apt install -y -V ./groonga-apt-source-latest-$(lsb_release --codename --short).deb + $ echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release --codename --short)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list + $ wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - + $ sudo apt update + $ sudo apt install -y -V postgresql-14-pgdg-pgroonga + ^^^^^^^^^^^^^ + for PostgreSQL 14.x + ``` +1. `.config/default.yml` に書かれているデータベースの名前を確認する(以下の例では `mk1`) + ```yaml + db: + host: localhost + port: 5432 + db: mk1 # <--- + ``` +1. PostgreSQL のプロンプトを起動する(`mk1` の部分は自分のデータベース名に変えて実行) + ```sh + $ sudo -iu postgres psql --dbname=mk1 + ``` + 以下のような表示が出てコマンドの入力待ちになります。 + ``` + psql (16.0) + Type "help" for help. + + mk1=# + ``` +1. 以下のコマンドを実行して PGroonga の拡張機能を有効にする + ``` + CREATE EXTENSION pgroonga; + ``` +1. `\q` というコマンドを実行するか Ctrl+D を押して PostgreSQL のプロンプトを終了する 1. 新しい Firefish のディレクトリに入ってビルドする ```sh $ cd calckey @@ -218,9 +278,24 @@ Repository url where you want to install: ```sh $ ./update.sh ``` -1. このフォークで加えられたデータベースへの変更を取り消す(`dbname`(以下の例では `mk1`)には `.config/default.yml` に記載されている PostgreSQL のデータベース名(`db:` の後に書かれているもの)を指定する) +1. `.config/default.yml` に書かれているデータベースの名前を確認する(以下の例では `mk1`) + ```yaml + db: + host: localhost + port: 5432 + db: mk1 # <--- + ``` +1. このフォークで加えられたデータベースへの変更を取り消す(`mk1` の部分は自分のデータベース名に変更する) ```sh - $ sudo -iu postgres psql --dbname=mk1 --file=neko/revert.sql + $ sudo -iu postgres psql --file=neko/revert.sql --dbname=mk1 + ``` +1. PGroonga をアンインストールする + アンインストールするコマンドの例 + ```sh + $ sudo apt purge --remove postgresql-14-pgdg-pgroonga + $ sudo add-apt-repository --remove ppa:groonga/ppa + $ sudo apt-key del ACCC4CF8 + $ sudo apt update ``` 1. Firefish がインストールされているディレクトリの親ディレクトリ (e.g., `/home/calckey`) に行く ```sh diff --git a/docker-compose.yml b/docker-compose.yml index e40751300..70c6e8f54 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,15 +2,13 @@ version: "3" services: web: - image: registry.joinfirefish.org/firefish/firefish + image: firefish + build: . container_name: firefish_web restart: unless-stopped depends_on: - db - redis -### Uncomment one of the following to use a search engine -# - meilisearch -# - sonic ports: - "3000:3000" networks: @@ -33,7 +31,7 @@ services: db: restart: unless-stopped - image: docker.io/postgres:12.2-alpine + image: docker.io/groonga/pgroonga:latest-alpine-12 container_name: firefish_db networks: - calcnet @@ -42,33 +40,6 @@ services: volumes: - ./db:/var/lib/postgresql/data -### Only one of the below should be used. -### Meilisearch is better overall, but resource-intensive. Sonic is a very light full text search engine. - -# meilisearch: -# container_name: meilisearch -# image: getmeili/meilisearch:v1.1.1 -# environment: -# - MEILI_ENV=${MEILI_ENV:-development} -# ports: -# - "7700:7700" -# networks: -# - calcnet -# volumes: -# - ./meili_data:/meili_data -# restart: unless-stopped - -# sonic: -# restart: unless-stopped -# image: docker.io/valeriansaliou/sonic:v1.4.0 -# logging: -# driver: none -# networks: -# - calcnet -# volumes: -# - ./sonic:/var/lib/sonic/store -# - ./sonic/config.cfg:/etc/sonic.cfg - networks: calcnet: # web: diff --git a/neko/flags/.gitignore b/neko/flags/.gitignore new file mode 100644 index 000000000..5e7d2734c --- /dev/null +++ b/neko/flags/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/neko/messages/install_pgroonga b/neko/messages/install_pgroonga new file mode 100644 index 000000000..21f7dc748 --- /dev/null +++ b/neko/messages/install_pgroonga @@ -0,0 +1,7 @@ + ------------------------------------------------------------------------- +| This Firefish fork now requires PGroonga (https://pgroonga.github.io/), | +| so PLEASE INSTALL PGroonga BEFORE UPGRADING!!! | +| | +| For the detailed explanation, see: | +| https://post.naskya.net/notes/9ldi29amfanomef5 | + ------------------------------------------------------------------------- diff --git a/neko/revert.sql b/neko/revert.sql index 4a94496a6..387bd582c 100644 --- a/neko/revert.sql +++ b/neko/revert.sql @@ -1,3 +1,9 @@ +-- pgroonga +DROP INDEX "public"."IDX_f27f5d88941e57442be75ba9c8"; +DROP INDEX "public"."IDX_065d4d8f3b5adb4a08841eae3c"; +DROP INDEX "public"."IDX_fcb770976ff8240af5799e3ffc"; +DROP EXTENSION pgroonga CASCADE; + -- emoji-moderator ALTER TABLE "user" DROP COLUMN "emojiModPerm"; DROP TYPE "public"."user_emojimodperm_enum"; diff --git a/packages/backend/migration-neko/1698420787202-pgroonga.js b/packages/backend/migration-neko/1698420787202-pgroonga.js new file mode 100644 index 000000000..dcdfd47b3 --- /dev/null +++ b/packages/backend/migration-neko/1698420787202-pgroonga.js @@ -0,0 +1,27 @@ +export class Pgroonga1698420787202 { + name = "Pgroonga1698420787202"; + + async up(queryRunner) { + await queryRunner.query( + `CREATE INDEX "IDX_f27f5d88941e57442be75ba9c8" ON "note" USING "pgroonga" ("text")`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_065d4d8f3b5adb4a08841eae3c" ON "user" USING "pgroonga" ("name" pgroonga_varchar_full_text_search_ops_v2)`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_fcb770976ff8240af5799e3ffc" ON "user_profile" USING "pgroonga" ("description" pgroonga_varchar_full_text_search_ops_v2) `, + ); + } + + async down(queryRunner) { + await queryRunner.query( + `DROP INDEX "public"."IDX_fcb770976ff8240af5799e3ffc"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_065d4d8f3b5adb4a08841eae3c"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_f27f5d88941e57442be75ba9c8"`, + ); + } +} diff --git a/packages/backend/src/models/entities/note.ts b/packages/backend/src/models/entities/note.ts index 2b9de38cb..e4e57e8e7 100644 --- a/packages/backend/src/models/entities/note.ts +++ b/packages/backend/src/models/entities/note.ts @@ -61,6 +61,7 @@ export class Note { }) public threadId: string | null; + @Index() // USING pgroonga @Column("text", { nullable: true, }) diff --git a/packages/backend/src/models/entities/user-profile.ts b/packages/backend/src/models/entities/user-profile.ts index b6d9f6957..cb9161ce1 100644 --- a/packages/backend/src/models/entities/user-profile.ts +++ b/packages/backend/src/models/entities/user-profile.ts @@ -38,6 +38,7 @@ export class UserProfile { }) public birthday: string | null; + @Index() // USING pgroonga pgroonga_varchar_full_text_search_ops_v2 @Column("varchar", { length: 2048, nullable: true, diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts index 685b78ef0..a316f5126 100644 --- a/packages/backend/src/models/entities/user.ts +++ b/packages/backend/src/models/entities/user.ts @@ -64,6 +64,7 @@ export class User { }) public usernameLower: string; + @Index() // USING pgroonga pgroonga_varchar_full_text_search_ops_v2 @Column("varchar", { length: 128, nullable: true, diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 093aad657..716349fbb 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -91,7 +91,7 @@ export default define(meta, paramDef, async (ps, me) => { } query - .andWhere("note.text ILIKE :q", { q: `%${sqlLikeEscape(ps.query)}%` }) + .andWhere("note.text &@~ :q", { q: `${sqlLikeEscape(ps.query)}` }) .andWhere("note.visibility = 'public'") .innerJoinAndSelect("note.user", "user") .leftJoinAndSelect("user.avatar", "avatar") diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index 2d84d5bfe..dd0f8c4f8 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -78,8 +78,8 @@ export default define(meta, paramDef, async (ps, me) => { const nameQuery = Users.createQueryBuilder("user") .where( new Brackets((qb) => { - qb.where("user.name ILIKE :query", { - query: `%${sqlLikeEscape(ps.query)}%`, + qb.where("user.name &@~ :query", { + query: `${sqlLikeEscape(ps.query)}`, }); // Also search username if it qualifies as username @@ -115,8 +115,8 @@ export default define(meta, paramDef, async (ps, me) => { if (users.length < ps.limit) { const profQuery = UserProfiles.createQueryBuilder("prof") .select("prof.userId") - .where("prof.description ILIKE :query", { - query: `%${sqlLikeEscape(ps.query)}%`, + .where("prof.description &@~ :query", { + query: `${sqlLikeEscape(ps.query)}`, }); if (ps.origin === "local") { diff --git a/update.sh b/update.sh index 6aa3f15af..2f2a3c02b 100755 --- a/update.sh +++ b/update.sh @@ -62,6 +62,18 @@ else say "This script seems to be up-to-date!\n" fi +## show messages +for message in neko/messages/*; do + file=$(basename -- "${message}") + if [[ ! -f "neko/flags/${file}" ]]; then + say "There is an important notice!" + cat "${message}" + touch "neko/flags/${file}" + say "To read this again, run: \$ cat ${message}" + exit 1 + fi +done + ## write version info say "Writing version info to package.json..."