railsのmysqlでemojiを使う方法
emojiを使おうと思ったら、色々と問題があるようなので、まとめます。
文字コード問題
まず、emojiはmysqlのutf8では使用できません。 mysqlでemojiを使用するにはutf8mb4にする必要があります。
これは何をしようとしているのかいうと、emojiの文字コードは1文字に4バイト使用します。 通常のutf8では、1文字に対して3バイトまでしか使用できません。 なので、mysqlで保存するためには、文字コードを変更する必要があります。
collation問題
こちらは初期設定のutf8mb4の設定でいくと、寿司ビール(🍣🍺)問題になります。 🍣と🍺が同じものとして扱われます。 こちらを別々に検索できるようにするための設定が必要になります。
これに関連してハハパパ問題というのがあります。
utf8でもきちんと設定しておかないと、はまったりします。
ハハパパ問題(参考)
ハハパパ問題は、「ハハ」「パパ」が同一の値として扱われます。 そんなことがあるのか!って思いましたが、railsのmysqlではdefaultでこの設定になっています。 何も知らないと検索結果おかしい・・・みたいな罠にはまります。 では、どのようなcollationがあるかを見ていきます。
- utf8_bin
- utf8_general_ci
- utf8_unicode_ci
utf8_bin 文字コードが完全に一致するもののみ返す 一番厳しいですね
utf8_general_ci アルファベットの大文字・小文字は区別しない 他は全て区別する
utf8_unicode_ci 大文字小文字/全角半角を区別しない。
「あああ」は「アアア」「ァァァ」「ぁぁぁ」にマッチする。
railsはdefaultでutf8_unicode_ciになっています。 なので、この設定は変更しましょう。
show full columns from topics;
でcollationの確認をすることができます。
mysql> show full columns from topics; +------------------------+--------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+ | Field | Type | Collation | Null | Key | Default | Extra | Privileges | Comment | +------------------------+--------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+ | id | int(11) | NULL | NO | PRI | NULL | auto_increment | select,insert,update,references | | | title | varchar(255) | utf8_unicode_ci | NO | | NULL | | select,insert,update,references | | | body | text | utf8_unicode_ci | NO | | NULL | | select,insert,update,references | | | name | varchar(255) | utf8_unicode_ci | NO | | NULL | | select,insert,update,references | | | category_id | int(11) | NULL | NO | MUL | NULL | | select,insert,update,references | | | thumbnail_updated_at | datetime | NULL | YES | | NULL | | select,insert,update,references | | | thumbnail_file_size | int(11) | NULL | YES | | NULL | | select,insert,update,references | | | thumbnail_content_type | varchar(255) | utf8_unicode_ci | YES | | NULL | | select,insert,update,references | | | thumbnail_file_name | varchar(255) | utf8_unicode_ci | YES | | NULL | | select,insert,update,references | | | created_at | datetime | NULL | NO | | NULL | | select,insert,update,references | | | updated_at | datetime | NULL | NO | | NULL | | select,insert,update,references | | +------------------------+--------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+ 11 rows in set (0.00 sec)
何もしていないと、無事にutf8_unicode_ci
になって、ウワって結果になると思います。
default: &default adapter: mysql2 collation: utf8_general_ci
これは初期設定でやるべきことですね。
もし、運用フェーズで気づいたなら、mysql側で変更しましょう。
index問題
最大のインデックス長が757バイトになっています。 utf8では255文字で、utf8mb4では191文字になります。
解決方法としては、二つあります。
- インデックス長を増やす
- インデックス長を減らす
減らす方法はデフォルトの動きではならなくなるので、インデックス長を増やす方が良いです。
mysql側の設定
my.cnfを設定します。
[mysqld]
innodb_file_per_table
innodb_file_format=barracuda
innodb_large_prefix = 1
rails側の設定
database.ymlの設定
default: &default adapter: mysql2 charset: utf8mb4 encoding: utf8mb4 collation: utf8mb4_bin pool: 5 timeout: 5000
絵文字を区別したいのであれば、collationの設定をutf8mb4_bin
にしないといけません。
区別する必要がないのであれば、utf8mb4_general_ci
で大丈夫です。
ただし、このままだとmigrationの設定がうまくいかないので、挙動を変更するようにします。
config/initializers/ar_innodb_row_format.rb
ActiveSupport.on_load :active_record do module ActiveRecord::ConnectionAdapters class AbstractMysqlAdapter def create_table_with_innodb_row_format(table_name, options = {}) table_options = options.merge(:options => 'ENGINE=InnoDB ROW_FORMAT=DYNAMIC') create_table_without_innodb_row_format(table_name, table_options) do |td| yield td if block_given? end end alias_method_chain :create_table, :innodb_row_format end end end
以上です。
参考