mroonga の検索クエリサンプル

2014年10月23日更新 view: 381 view
photoBy: https://fbcdn-profile-a.akamaihd.net/hprofile-ak-…

mroonga_command で音速の検索

cakephp用の書き方です。
Recruit ってのが model。

参考

http://groonga.org/ja/docs/reference/commands/select.html

http://groonga.org/ja/docs/reference/functions/between.html

http://qiita.com/naoa/items/258305428aa72dd0185e

基礎知識

query と filter がある。
query は 全文検索、filterはそれ以外で使う。
よって、メインはfilterで検索することになる。

query 引数で全文検索条件を指定する場合は、 match_columns 引数と一緒に使います。 match_columns はどのカラムまたはインデックスを使って query を検索するかを指定します。

最初にインデックスをFULLTEXT貼ること。
でないとうまくヒットしないときがある。

ラッパーモード

area フィールドに 名古屋市東北区 , 名古屋市 北区 , 名古屋市北区 というのが入っているとする。
以下のようにすると

  • 名古屋市東北区は拾わない
  • 名古屋市南区か、名古屋市北区 のみ拾ってくれる。
SELECT COUNT(*) AS `count` FROM `qjiin`.`qjiin_recruits` AS `Recruit` WHERE MATCH(area) AGAINST( '(+\"名古屋市南区\") (+\"名古屋市北区\")' IN BOOLEAN MODE)

cakephpだと

$cond['AND']['MATCH(area) AGAINST( ? IN BOOLEAN MODE)'] = array('(+"名古屋市南区") (+"名古屋市北区")');

cakephpでwプラグマ

title,body フィールドから ラーメンに マッチするものを マッチしたスコア順に取得。
但し、タイトルのが重要なのでタイトルには10の重み付けをしておく。

        $cond = array();
        $cond['AND']['MATCH(title,body) AGAINST( ? IN BOOLEAN MODE)'] = array('*W1:10,2:1 +ラーメン');

        $order = "MATCH (title,body) AGAINST('*W1:10,2:1 +ラーメン' IN BOOLEAN MODE) DESC";
        $res = $this->Article->find('all', array('conditions' => $cond, 'order' => $order, 'limit' => 10));
        //新着順にしたい場合は普通に order id desc
        //$res = $this->Article->find('all', array('conditions' => $cond, 'order' => 'id desc', 'limit' => 10));

クエリサンプル

area に 名古屋 を含むを検索

//SELECT mroonga_command('select qjiin_recruits --match_columns area --query 名古屋')    
$res = $this->Recruit->query("SELECT mroonga_command('select qjiin_recruits --match_columns area --query 名古屋')");

@ は名古屋を全文検索

//SELECT mroonga_command('select qjiin_recruits --filter \'area @ "名古屋"\' ')    
$res = $this->Recruit->query("SELECT mroonga_command('select qjiin_recruits --filter \'area @ \"名古屋\"\' ')");

== 名古屋を完全一致検索

//SELECT mroonga_command('select qjiin_recruits --filter \'area == "名古屋"\' ')    
$res = $this->Recruit->query("SELECT mroonga_command('select qjiin_recruits --filter \'area == \"名古屋\"\' ')");

&& は複数条件 arega から 鹿児島を 全文検索し、なおかつ pref_id が 47のものを探してくる。
|| にすれば or になる。

//SELECT mroonga_command('select qjiin_recruits --filter \'area @ "鹿児島" && pref_id == 47\' ')    
$res = $this->Recruit->query("SELECT mroonga_command('select qjiin_recruits --filter \'area @ \"鹿児島\" && pref_id == 47\' ')");

カウント (大なり小なり)

//SELECT mroonga_command('select qjiin_recruits --limit 0 --filter \'pref_id >= 45 && pref_id <= 47\' --output_columns _key')
$res = $this->Recruit->query("SELECT mroonga_command('select qjiin_recruits --limit 0 --filter \'pref_id >= 45 && pref_id <= 47\' --output_columns _key')");
スポンサードリンク

pref もしくは area の中から 名古屋市に フレーズマッチするものをカウント

$tmp = $this->Recruit->query("SELECT mroonga_command('select --table qjiin_recruits --match_columns pref||area --query \"名古屋市\")");

一応between (激重い)

$res = $this->Recruit->query("SELECT mroonga_command('select qjiin_recruits --filter \'between(pref_id,45,\"include\",47,\"include\")\' ')");

複合クエリ

正社員か、契約社員を フレーズ検索

$res = $this->Recruit->query("SELECT mroonga_command('select --table qjiin_recruits --filter \'work @ \"契約社員\" || work @ \"正社員\"\' --limit 0')");

preg,area のどちらかが 名古屋にフレーズ一致
pref_id が 24
の数をカウント

$tmp = $this->Recruit->query("SELECT mroonga_command('select --table qjiin_recruits --match_columns pref||area --query \"名古屋市\" --filter \"pref_id == 24\" --limit 0')");

preg,area のどちらかが 名古屋にフレーズ一致
pref_id が 10以上、45以下のもの
id が 1044862のもの
の数をカウント

$tmp = $this->Recruit->query("SELECT mroonga_command('select --table qjiin_recruits --match_columns pref||area --query \"名古屋市\" --filter \"pref_id >= 10 && pref_id <= 45 && id==1044862\" --limit 0')");

検索を変換してみる

フィールド renketu から 正社員を検索

ラッパー

SELECT mroonga_command('select --table qjiin_recruits --filter \'renketu @ "正社員"\' --limit 0 --output_columns _key')

ストレージ

SELECT COUNT(*) AS `count` FROM `qjiin`.`qjiin_recruits` AS `Recruit` WHERE MATCH(renketu) AGAINST('(+\"正社員\")' IN BOOLEAN MODE)

ストレージ(cakephp)

$word = '正社員';
$hoge = $this->Model->query("SELECT mroonga_command('select --table qjiin_recruits --filter \'renketu @ \"".$word."\"\' --limit 0 --output_columns _key')");

and検索 renketu から 清掃、ビルの両方のキーワードを持つものをそれぞれフレーズ検索

ストレージ

$tmp = $this->Recruit->query("SELECT mroonga_command('select qjiin_recruits --filter \'renketu @ \"清掃\" && renketu @ \"ビル\" \' --limit 0 --drilldown yearly_income --output_columns _key')");

ラッパー (cakephp)

$cond['AND']['MATCH(renketu) AGAINST(? IN BOOLEAN MODE)'] = '(+"ビル")';
$cond['AND']['MATCH(renketu) AGAINST(? IN BOOLEAN MODE) '] = '(+"清掃")';//同じキー名なので、 in boolean mode ) の後に半角スペースをつけておく。
$count = $this->Recruit->find('count',array('conditions' => $cond));

ドリルダウン検索

ドリルダウン検索って?

絞り込んだ後、そのテーブルが持っている他のフィールドを数えたりするもの。
よくある 東海(5) みたいなこの何件持っているかを簡単に検索できる。

$tmp = $this->Recruit->query("SELECT mroonga_command('select --table qjiin_recruits --match_columns pref||area --query \"名古屋市\" --limit 0 --drilldown nensyuu --drilldown_limit 100')");
        

これで、nensyuu フィールドを年収別に 100件どれだけ件数があるかを取得してくれます。

100万円(3)
300万円(6)

みたいな感じに。

複雑なクエリ

created のタイムスタンプが 1396770363 より新しいもので、
id created フィールドのみ、
指定した距離以内のものを距離が近い順に取得

$location = array(
      'lat' => $res['Recruit']['lat'],//緯度
      'lng' => $res['Recruit']['lng'],//経度
      'distance' => 50000//50km
     );
$tmp = $this->Recruit->query('
             SELECT mroonga_command("select qjiin_recruits --output_columns id,created --filter \'created > 1396770363 && geo_in_circle(location, \"'.$location['lat'].','.$location['lng'].'\", '.$location['distance'].')\' --sortby \'geo_distance(location, \"'.$location['lat'].'x'.$location['lng'].'\")\'")
             ');

まずは、距離順。同じ距離なら新しいデータ created を優先して取得

$tmp = $this->Recruit->query('
             SELECT mroonga_command("select qjiin_recruits --filter \'geo_in_circle(location, \"'.$location['lat'].','.$location['lng'].'\", '.$location['distance'].')\' --sortby \'geo_distance(location, \"'.$location['lat'].'x'.$location['lng'].'\")\' created")
             ');

解析

jsonで来るので、上記の $res を

$tmp = array_shift($tmp[0][0]);//配列を取り出し
$tmp = json_decode($tmp);
$tmp = array_shift($tmp);
$res = Set::reverse($tmp);
pr($res);

で解析できます。

キーワードの重み付け

スコアを取得

$tmp = $this->Article->query(
    'SELECT mroonga_command("select mroonga_test --output_columns title,body,_score --filter \'title @ \"mroonga\" || body @ \"実践\" \'");'
);

スコア順に並び替え

$tmp = $this->Article->query(
    'SELECT mroonga_command("select mroonga_test --output_columns title,body,_score --filter \'title @ \"mroonga\" || body @ \"実践\" \' --sortby -_score");'
);

ドリルダウン検索の結果を取得

        $tmp = $this->Article->query(
            'SELECT mroonga_command("select matomater_articles --filter \'title @ \"'.$keyword.'\" || body @ \"ラーメン\"\' --drilldown category_id --limit 1000");'
        );

        $tmp = array_shift($tmp[0][0]);//配列を取り出し

        $tmp = json_decode($tmp);
        $drill_down = $tmp[1];

        pd($drill_down);

 

スポンサードリンク

関連記事

関連カテゴリ

コロ助

web関連の記事や制作系の記事をどんどんまとめていきます。 宜しくお願いします!

ピックアップ

パソコン・ソフトウェア ランキング

9月19日 ( 水 ) にアクセスが多かった記事はこちら!