友人Yii(ActiveDataProvider)とPostgreSQLでのテキスト検索の作り方

YiiプロジェクトでのPostgreSQL tsearch2の使用


どのサイトも主にテキストです。 テキストを便利に編集するために、多くの場合、テキストはデータベースに保存されます。 同時に、テキストフィールドのコンテンツの便利な検索などの追加機能が表示されます。 古き良きLIKEは良いですが、常にではありません。 PostgreSQLにはtsearch2のようなより高度なものがあります。 Yiiフレームワークでそれを使用する方法は、猫の下で教えます。

前文


かつて私が作成したサイトの1つで全文検索を実装する必要がありました。 tsearch2を使用するには、サイトに包括的なガイドがあるため、長時間Googleで検索してその仕組みを考える必要はありません。 LIKEと比較してtsearch2を使用することの魅力は何ですか、私も説明する必要はないと思います。 このサイトには情報がいっぱいです。 したがって、アプリケーションでSQLクエリを自分で明示的に記述した場合、問題はありませんが、私はYiiを使用し、開発者が推奨する方法ですべてを実行したいと考えています。 ただし、これが唯一の理由ではありません。 CListViewウィジェットを使用するには、CActiveDataProviderクラスのインスタンスを準備する必要があります。 これが楽しみの始まりです。

解決策


CActiveDataProviderを使用するには、CDbCriteriaクラスのオブジェクトを初期化する必要がありますが、FROMフィールドを変更して、tsearch2エンジンへの要求を生成する関数呼び出しを追加することはできません。

SELECT ... FROM ...、 to_tsquery $ sh_string AS q ...


残っているのは、findAllBySqlメソッド呼び出しで生成されたモデルを使用することだけですが、CActiveDataProviderの組み込みメカニズムはfindAllのみをサポートします。 それでは、純粋なSQLクエリから取得したモデルをCActiveDataProviderに変換するにはどうすればよいでしょうか? ソリューションはStackoverflowで見つかりましたが、問題はそこで終わりませんでした。 テキストが保存されている私のテーブルでは、キーフィールドはidではなくtxt_idと呼ばれていました。そのため、回答からのテキストに小さな、しかし非常に重要な修正を行わなければなりませんでした。 これは私が得たものです。
コントローラー内:

$ _shString = '' ;
if isset $ _GET [ 'sh' ] ))
{
$ _shString = implode '&' explode '' $ _GET [ 'sh' ] )) ;
$ _qry = "
選択
txt_id
ts_headline(txt、q、 'StartSel = <strong>、StopSel = </ strong>、MaxWords = 35、MinWords = 15')AS txt、
ts_rank(fti_txt、q)ASランク
から
テキスト、to_tsquery(:sh)AS q
どこ
user_id =:uid AND fti_txt @@ q
ORDER BYランクDESC
" ;

$ _model = Texts :: model -> findAllBySql $ _qry array ':uid' => Yii :: app -> user- > id ':sh' => $ _shString ;
}


ビューで:

$ this- > ウィジェット 'zii.widgets.CListView' 配列
'id' => '検索結果'
'dataProvider' => 新しい CArrayDataProvider $ model array 'keyField' => 'txt_id'
' itemView ' => '_text'
;


同じ編集が適用されます
配列 'keyField' => 'txt_id'


UPD。 2012-04-02


尊敬されているRiveがコメントで書いたように、問題はこのアプローチではすべての検索結果が最初とCListViewのページをナビゲートするときに選択されることです。 解決策を求めて、私はフレームワークフォーラムで質問をしなければなりませんでしたが、短いが完全な回答がすぐに届きました! 読むのが面倒すぎる私は答えの本質を言うでしょう: CSqlDataProviderを使用する

私のコードはこのようになりました。 コントローラー内:
$ _shString = '' ;
if isset $ _GET [ 'sh' ] ))
{
$ _shString = implode '&' explode '' $ _GET [ 'sh' ] )) ;

$ _cnt = Yii :: app -> db- > createCommand 'SELECT count(*)FROM texts、to_tsquery(:sh)AS q WHERE user_id =:uid AND fti_txt @@ q' -> queryScalar array ':uid' => Yii :: app -> user- > id ':sh' => $ _shString ;

$ _qry = "
選択
txt_id
ts_headline(txt、q、 'StartSel = <strong>、StopSel = </ strong>、MaxWords = 35、MinWords = 15')AS txt、
ts_rank(fti_txt、q)ASランク
から
テキスト、to_tsquery(:sh)AS q
どこ
user_id =:uid AND fti_txt @@ q
ORDER BYランクDESC
" ;

$ dataProvider = new CSqlDataProvider $ _qry 配列
'totalItemCount' => $ _cnt
'params' => array ':uid' => Yii :: app -> user- > id ':sh' => $ _shString
'keyField' => 'txt_id'
'ページネーション' => 配列
'pageSize' => 20

;
}

さらに視野:
$ this- > ウィジェット 'zii.widgets.CListView' 配列
'id' => '検索結果'
'dataProvider' => $ dataProvider
' itemView ' => '_text'
;

ご覧のとおり、リストの各要素を描画するために、補助ビュー_text.phpが使用されます。ここに、コンテンツの最も単純なバージョンを示します。
<div class = "view">
<?php
// CHtmlをエコー:: encode($ data-> txt);
// echo $ data-> txt;
echo $ data [ 'txt' ] ;
?>
</ div>


特に注意すべき点は2つあります。
  1. CSqlDataProviderのインスタンスを作成する場合、リクエストによって返されるレコードの総数を個別に検索して渡す必要があります。 最初は、同様の重大度の2つのリクエストが実行されます。 これは公式文書に記載されています。
  2. プロバイダ内のクエリ結果に基づいて「パス」中に受信した$データコンポーネントは、オブジェクトではなく配列の形式を持っています。 無駄ではないソース_text.phpを引用しましたが、ここではecho $ data ['txt']を使用する必要があります。 echo $ data-> txtの代わりに;

この経験が誰かに役立つことを願っています!

Source: https://habr.com/ru/post/J140912/


All Articles