「オープンデータ」のような半濁点を含むカタカナがうまく検索できない
PostgreSQL 9.2.4 + Rails 4.1を使っています。
DB内のあるカラムに「空間オープンデータを用いたソフトウェア」という文言が入っています。
このとき、以下のようなSQLを発行してもデータが返ってきません(0件)。(実際には画面からフリー入力された文字列をActiveRecordのクエリに渡しています)
select *
from groups g
where
g.overview like '%オープンデータ%'
しかし、「タ」だけ検索すると返ってきます。
select *
from groups g
where
g.overview like '%タ%'
「ソフトウェア」や「空間」でも返ってきます。
select *
from groups g
where
g.overview like '%ソフトウェア%'
select *
from groups g
where
g.overview like '%空間%'
「テ」でも大丈夫です。
select *
from groups g
where
g.overview like '%デ%'
しかし、「デ」や「プ」ではダメです。
select *
from groups g
where
g.overview like '%デ%'
select *
from groups g
where
g.overview like '%プ%'
どうも濁点や半濁点が入るとおかしくなる気がします。
UnicodeのNFCやNFDが関連してそうな気がしますが、具体的な原因はよくわかりません。
ちなみに SHOW LC_COLLATE
の結果はja_JP.UTF-8
になっています。
原因や解決策(入力された文字を見た目通りに検索する方法)をご存知の方がいたら教えてください。
EDIT
原因はわかりました。
実際には直接SQLを発行しているのではなく、Railsアプリ内で検索しています。
で、キーボードから直接入力した文字列と、DBから取得した文字列をunpackしてコードポイントを比較してみると、両者が異なっていました。
(DBに格納されている文字列は濁点や半濁点が1文字として扱われているようです)
# キーボードから直接入力する
irb(main):017:0> o = '空間オープンデータを用いたソフトウェア'
=> "空間オープンデータを用いたソフトウェア"
irb(main):018:0> o.unpack("U*")
=> [31354, 38291, 12458, 12540, 12503, 12531, 12487, 12540, 12479, 12434, 29992, 12356, 12383, 12477, 12501, 12488, 12454, 12455, 12450]
# DBから取得する(gはid指定で取得したActiveRecordのオブジェクト)
irb(main):019:0> g.overview
=> "空間オープンデータを用いたソフトウェア"
irb(main):020:0> g.overview.unpack("U*")
=> [31354, 38291, 12458, 12540, 12501, 12442, 12531, 12486, 12441, 12540, 12479, 12434, 29992, 12356, 12383, 12477, 12501, 12488, 12454, 12455, 12450]
# 両者は同一ではない
irb(main):021:0> o == g.overview
=> false
# キーボードから直接入力した文字列はヒットしない
irb(main):024:0> Group.where(overview: o).count
(0.7ms) SELECT COUNT(*) FROM "groups" WHERE "groups"."deleted_at" IS NULL AND "groups"."overview" = '空間オープンデータを用いたソフトウェア'
=> 0
# DBから取得した文字列はヒットする
irb(main):025:0> Group.where(overview: g.overview).count
(0.6ms) SELECT COUNT(*) FROM "groups" WHERE "groups"."deleted_at" IS NULL AND "groups"."overview" = '空間オープンデータを用いたソフトウェア'
=> 1
見た目には全く同じに見えますが、内部的なコードポイントが異なるため、ヒットする文字列とヒットしない文字列が分かれてしまうようです。(DBはNFDで、画面からの入力した値はNFCになっている)
さて、原因はわかりましたが、最終的なゴールは内部的なコードポイントに左右されず、人間の目に見えるとおりに検索をヒットさせることです。
なんらかの方法で画面からの入力値とDB内の値やクエリのパラメータを一致させる必要があります。
どうすればズレを無くして一致させることができるでしょうか?
画面からはNFCで入力されるので、DB側もNFCで統一するのがベストかなと思います。
- 既存のデータをNFCにコンバートする
- 新しく登録されるデータもNFCにする
といったアプローチが必要になりそうですが、実際にこうした対応をやっている人がいたら対応方法を教えていただきたいです。
よろしくお願いします。
EDIT2
原因は(何らかの特殊な事情で?)NFDの文字列が入力されてしまったことにあるようです。
問題のあるデータからのコピペではなく、キーボードからの打ち直しをしてみると、ちゃんと検索できました。
なので、DBやRailsの問題というよりも、入力した人間に原因がある可能性が高いです。
というわけで、ここではこれ以上の深掘りはできないので、いったん解決済みとしたいと思います。