作物名検索語の前処理
- このフォーラムに新しいトピックを立てることはできません
- このフォーラムではゲスト投稿が禁止されています
OhYeah!
投稿数: 983
オンライン

ACFinder 公式サイトの1行コメントにあった想定外の作物が検索されてしまう件ですが、拗音・促音・濁音・半濁音・長音などの表記のユレに対応できるようにしたこと、作物の別名が登録されたことに起因します。全体的には便利になったはずですが、指摘のような弊害も発生してしまいます。
ACFinde の場合は「作物名完全一致」でこの弊害を回避していますが、携帯農薬検索では異なるアプローチをとっています。以前にも書いたような気がしますが、どのトピックか分からなくなったので、再度書いておきます。
具体的には、検索語を正規化(拗音・促音は大文字に変換、濁音・半濁音は清音に変換、長音は削除して「ひらがな」に統一)したあと、下記のような処理をして、目的外の作物を検索しないようにしています。Delphi だと文字列に case 文が使えないので、if else を多用することになりそうですが…。
ACFinde の場合は「作物名完全一致」でこの弊害を回避していますが、携帯農薬検索では異なるアプローチをとっています。以前にも書いたような気がしますが、どのトピックか分からなくなったので、再度書いておきます。
具体的には、検索語を正規化(拗音・促音は大文字に変換、濁音・半濁音は清音に変換、長音は削除して「ひらがな」に統一)したあと、下記のような処理をして、目的外の作物を検索しないようにしています。Delphi だと文字列に case 文が使えないので、if else を多用することになりそうですが…。
function conv_sakumotsu($str) {
switch ($str) {
case 'とまと': return '(?<!に)とまと';
case 'さくら': return 'さくら(?!そ|ん|す)';
case 'いね': return '(?<!か)いね(?!か|科|こ)|すいとう|りくとう';
case 'むき': return '(?<!と)むき';
case 'きひ': return '(?<!う)きひ';
case 'うめ': return '(?<!ゆ|よ)うめ';
case 'かき': return '(?<!さ)かき(?!る)';
case 'くり': return '(?<!す|み|つ|よ)くり(?!さ|ん|ふ)';
case 'なし': return '(?<!る|ち|ね)なし';
case 'もも': return '(?<!す|ま|い|ら)もも';
case 'ゆす': return 'ゆす(?!き)';
case 'うと': return '(?<!お|か|ふ|よ)うと(?!う)';
case 'かふ': return '(?<!り|た|ね|や)かふ(?!た)';
case 'ける': return '(?<!つ)ける';
case 'せり': return '(?<!は|う)せり';
case 'なす': return '(?<!た|な|ま|ひ)なす(?!な)';
case 'ねき': return '(?<!ま|と)ねき';
case 'はす': return 'はす(?!い|か|も)';
case 'ふき': return '(?<!い|わ)ふき';
case 'きく': return '(?<!ん|あ|う)きく(?!に|ち|な)';
case 'はら': return '(?<!す)はら';
case 'ゆり': return '(?<!ち)ゆり(?!の)';
case 'らん': return '(?<!か|く|し|す|と|ま|ふ|ん|お)らん';
case 'かし': return '(?<!め|ら)かし(?!す|つ|ゆ|よ|ら|ろ|わ)';
case 'きり': return '(?<!つ)きり(?!え|は)';
case 'すき': return '(?<!あ|お|み|ゆ)すき';
case 'つけ': return '(?<!け|ぬ|め|き)つけ(?!も)';
case 'ふう': return '(?<!う|ん)ふう(?!と)';
case 'ふな': return '(?<!み)ふな';
case 'まき': return 'まき(?!い|さ|ち|つ|な|ま|ろ|\(|\))';
case 'まつ': return '(?<!か|そ|と|ら|ろ)まつ(?!し|な|は|も)';
case 'やし': return '(?<!ち|ひ|ら)やし';
case 'しは': return '(?<!ろ)しは(?!い)';
case 'ちや': return '(?<!ほ|ん)ちや(?!い|ひ|と)';
case 'こま': return '(?<!え)こま(?!つ)';
}
$str = str_replace('しかまき', 'ちよくは', $str);
$str = str_replace('ちかまき', 'ちよくは', $str);
$str = str_replace('おおつふ', 'たいりゆう', $str);
$str = str_replace('こつふ', 'しようりゆう', $str);
$str = str_replace('かつか', 'かくか', $str);
$str = preg_replace('/(?<!ら)たねなし|種無(し)?/', 'むかく', $str);
$str = preg_replace('/たねあり|種有(り)?/', 'ゆうかく', $str);
$str = str_replace('非結球', 'ひけつきゆう', $str);
$str = str_replace('半結球', 'はんけつきゆう', $str);
$str = str_replace('西洋', 'せいよう', $str);
$str = str_replace('食用', 'しよくよう', $str);
$str = str_replace('ろち', 'ろし', $str);
$str = preg_replace('/(.*)?((露地|ろし|施設|しせつ|水耕|すいこう)(栽培|さいはい)?)(.*)?/', '\1\5\(\2', $str);
return $str;
}
投票数:2
平均点:5.00
kabe
投稿数: 231

当面の課題なんですが、なかなか手をつけられずにいます。
早くなんとかしないと。
早くなんとかしないと。
投票数:2
平均点:0.00
kabe
投稿数: 231

投票数:0
平均点:0.00
OhYeah!
投稿数: 983
オンライン

お疲れ様です。「なす」とか「くり」とかで他の作物を検索することはなくなりましたね。
ただ、作物名直接入力の場合、たとえば「スイートコーン」と入力した場合に、「未成熟トウモロコシ」のみに登録がある農薬しか表示されないのがちょっと…。「作物名完全一致」チェックボックスは不要になるので、ここを上位・下位分類一括検索にしませんか? 携帯農薬検索では、下記のような方法で、第4階層の作物名を指定した場合は上位・下位を全て検索、第5階層の栽培条件及び第3階層の小分類以前を指定した場合は上位分類のみを検索するようにしています。最後の $cond の値が、作物を検索する SQL になります。
この場合、作物タブでは作物選択ダイアログボックスの「上位分類を含む」チェックボックスも不要になりますが、定型処理の作物選択ダイアログボックスでは必要なので、作物タブから呼んだときのみ非表示とかにしておいてもらえると助かります。
「"{$cid2}00"」 は 「$cid2."00"」と等価です。
ただ、作物名直接入力の場合、たとえば「スイートコーン」と入力した場合に、「未成熟トウモロコシ」のみに登録がある農薬しか表示されないのがちょっと…。「作物名完全一致」チェックボックスは不要になるので、ここを上位・下位分類一括検索にしませんか? 携帯農薬検索では、下記のような方法で、第4階層の作物名を指定した場合は上位・下位を全て検索、第5階層の栽培条件及び第3階層の小分類以前を指定した場合は上位分類のみを検索するようにしています。最後の $cond の値が、作物を検索する SQL になります。
この場合、作物タブでは作物選択ダイアログボックスの「上位分類を含む」チェックボックスも不要になりますが、定型処理の作物選択ダイアログボックスでは必要なので、作物タブから呼んだときのみ非表示とかにしておいてもらえると助かります。
$db = dbOpen();
$sql = "select level, idsaku, sakumotsu from m_sakumotsu where strconv(sakumotsu||'、'||betsumei) regexp '$kw' and sakumotsu not like '%除く%' order by idsaku";
$res = $db->query($sql);
$crops = $res->fetchAll(PDO::FETCH_ASSOC);
dbCloseStatement($res);
dbClose($db);
$cid = $crops[0]['idsaku'];
$cid2 = substr($cid, 0, 8);
$level = $crops[0]['level'];
$cond = _concat('|', substr($cid, 0, 2)."000000", substr($cid, 0, 4)."0000", substr($cid, 0, 5)."000");
if ($level == 3) $cond = _concat('|', $cond, "{$cid2}");
if ($level == 4) {
$cond = preg_replace("/(~|\|){$cid2}/", '', $cond);
$cond = _concat('|', $cond, "{$cid2}00", $cid);
}
$db = dbOpen();
$except = _concat('|', substr($cid, 0, 4)."000000", substr($cid, 0, 5)."00000", "{$cid2}00");
$sql = "select concat('|', sakumotsu) from sakumotsu where idsaku regexp '$except'";
$res = $db->query($sql);
if (!$res) sqlerror($sql, $db);
$except = $res->fetchColumn(0);
dbCloseStatement($res);
dbClose($db);
$except = '\((.*、)?('.str_replace(array('(', ')', '[', ']', '類'), array('\(?', '\)?', '\[?', '\]?', '(類)?'), $except).')(、.*)?を除く';
$crop = $crops[$cno]['sakumotsu'];
if (strpos($crop, '露地') !== false) $except .= '|施設|水耕';
if (strpos($crop, '施設') !== false) $except .= '|露地';
if (strpos($crop, '水耕') !== false) $except .= '|露地';
$cond = "select sakumotsu from m_sakumotsu where idsaku regexp '^($cond)' and sakumotsu not regexp '$except'";
_concat は、拡張 SQLite 関数の CONCAT と同じ機能の PHP ユーザ関数です。「"{$cid2}00"」 は 「$cid2."00"」と等価です。
投票数:6
平均点:6.67