高パフォーマンスでシンプルなタグ機能Rails用gem

gem, rails, tag

Railsでタグ機能を追加する際に定番のgemといえるのがacts-as-taggable-onです。
このgemを使うと一般的に必要とされている機能がだいたい手に入るので非常に重宝します。
しかしタグの結びつけと検索だけしたい、などの限定的な場合オーバースペック気味に感じる場合があります。
今回はpostgresのarray機能を使った検索に特化したタグ用gemを作ってみました。

コンセプト

acts-as-taggable-onを使った際最初に気になったのはmany-to-manyのスキーマを必要とすることでした。
正直に実装するのであれば確かにそうなるのですが、タグやタグ付けされる対象が多くなってくるとassociationがえらい数になるのは目に見えてみます。

そもそも、タグに関する機能として考えられるのは

  1. タグ付け
  2. タグによる検索
  3. タグ名補完
  4. タグ列挙
  5. ページング
  6. タグ付け数によるソート
  7. タグクラウド

あたりだと考えられます。

これら全ての機能が必要であればacts-as-taggable-onの実装のようにmany-to-manyが必要でしょう。
特に5,6,7あたりは無いと実現できません。

しかし今回僕が必要としたのは1,2だけでした。
タグ付けされる対象にタグを突っ込むカラム一つあればできるのではないでしょうか?
もっとシンプルに出来そうな気がしますね。

実装

ここで僕が注目したのはpostgresのarray型です。
postgresのarrayはindexも作ることができて、タグ機能への応用ができそうです。
僕はheroku上で動かすことを想定しているのでpostgres限定の動作でも何ら問題ありません。

ということで作りました。
その名もacts-as-taggable-array-on

機能

詳しくはREADMEを見てもらえればわかると思います。
メインのファイルは50行足らずの小さなライブラリです。

用意した機能は主に

  1. 指定したタグを含む検索
  2. 指定したタグを含まない検索
  3. タグ列挙
  4. タグクラウド

です。

ベンチマーク

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
% rake bench:write bench:find_by_id bench:find_by_tag
Deleted all ActsAsTaggableOn::Tag
Deleted all ActsAsTaggableOn::Tagging
Deleted all TaggableUser
Deleted all TaggableArrayUser
Finsihed to clean


###################################################################

bench:write
Rehearsal ---------------------------------------------------------
Using Taggable          6.950000   0.420000   7.370000 (  9.223704)
Using Postgres Arrays   0.710000   0.090000   0.800000 (  1.184734)
------------------------------------------------ total: 8.170000sec

                            user     system      total        real
Using Taggable          5.800000   0.340000   6.140000 (  7.842051)
Using Postgres Arrays   0.680000   0.090000   0.770000 (  1.117812)

###################################################################

bench:find_by_id
Rehearsal ---------------------------------------------------------
Using Taggable          1.490000   0.110000   1.600000 (  2.079776)
Using Postgres Arrays   0.240000   0.030000   0.270000 (  0.419430)
------------------------------------------------ total: 1.870000sec

                            user     system      total        real
Using Taggable          1.440000   0.100000   1.540000 (  2.023188)
Using Postgres Arrays   0.250000   0.040000   0.290000 (  0.434233)

###################################################################

bench:find_by_tag
Rehearsal ---------------------------------------------------------
Using Taggable          0.600000   0.040000   0.640000 (  1.107227)
Using Postgres Arrays   0.060000   0.000000   0.060000 (  0.060019)
------------------------------------------------ total: 0.700000sec

                            user     system      total        real
Using Taggable          0.600000   0.040000   0.640000 (  1.100302)
Using Postgres Arrays   0.030000   0.000000   0.030000 (  0.033001)
rake bench:write bench:find_by_id bench:find_by_tag  20.29s user 1.52s system 77% cpu 28.322 total

なんだか思ったより速い

まとめ

github: acts-as-taggable-array-on
benchmark: acts-as-taggable-benchmark

Comments