環境
C#、Linq to Entity、SQL Serverです。
はじめに
以下を参考にさせていただきました。(感謝)
karak 異体字同一視検索
異体字とは
標準の字体と同じ意味・発音を持つが、表記に差異がある文字のこと。手書きによる個人差から生じたもの、新字・旧字(簡体・繁体)の違いによるものがある。
目的
峰さん、嶺さん、峯さん、いっぱいいますね。
いざ検索しようとして、みねってどんな字だったっけ?全然ヒットしない!
という事もあろうかと思います。
同様に浜さん、濱さん、濵さん、なかなか検索ヒットできません。
さらにたとえば濵嵜(はまざき)さんという人を検索したい場合、なかなか変換ではでてこないですし、ひらがなも、はまざき・はまさき どっちだったけ?となりがちです。
という時に、「浜」でも「濱」「濵」さんが検索ヒットできるように異体字を同一視して検索できるように実装しました。
異体字辞書の作成
まずは異体字の辞書を用意しました。
DB化するか悩んだのですが、漢字であるしほぼ変わらないであろうことからコードに書いてます。
具体的には以下のような感じです。
var variants = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("濱", "濱|頻|濵|浜|滨"),
new KeyValuePair<string, string>("浜", "浜|濵|濱"),
new KeyValuePair<string, string>("領", "領|阾|袊|嶺|岭|领"),
new KeyValuePair<string, string>("岭", "岭|領|岺|嶺"),
new KeyValuePair<string, string>("嶺", "嶺|領|岭"),
...
}
検索用文字列の変換
浜で検索した場合に正規表現で検索したいので、
浜→[浜|濵|濱]と変換します。
たとえば濵嵜(はまざき)さんという人を検索したい場合は検索ワードとしては以下で正規表現で検索するといい感じになりそうです。
[浜|濵|濱][崎|隑|陭|徛|﨑|㟢|碕|埼|嵜]
浜崎 → [浜|濵|濱][崎|隑|陭|徛|﨑|㟢|碕|埼|嵜] という変換は文字列を1文字ずつ先ほどの辞書を元に変換します。
public static string get(string word)
{
var result = "";
//異体字一覧(辞書)を取得する
var vari = Dictionaly.GetDictionaly();
//文字列を1文字ずつに分割して処理する
foreach (char c in word)
{
//文字が異体字リストにあれば、異体字を取得する。
//ex. 浜 → [浜|濵|濱]
var f = vari.FirstOrDefault(x => x.Key == c.ToString());
//[]で囲む
if (f.Key != null)
{
result += "[" + f.Value + "]";
}
else
{
result += c;
}
}
//結果を返す
return result;
}
検索
Linq to Entityに投げ込みます。
Linq to Entityでは正規表現をそのまま検索できないので、SqlFunctionsを使います。
SqlFunctionsはSystem.Data.Objects.SqlClientにあります。
using System.Data.Objects.SqlClient;
//regStringsが検索ワード([浜|濵|濱][崎|隑|陭|徛|﨑|㟢|碕|埼|嵜])等
var query = from c in db.Entity
where SqlFunctions.PatIndex("%" + regStrings + "%", c.Modid) == 1
select c;
参考
・Linq to Entityでの正規表現について(stackoverflow)
他にも検索の幅を増やしたい場合には、LinqKit等を使えばより柔軟になりそうです。