00001 <?php
00002
00003
00004
00005
00006
00007
00008 abstract class DataObjectSearchIndexSphinxBase extends DataObjectSphinxBase implements ISearchIndex {
00009 public $id;
00010 public $item_id;
00011 public $item_model;
00012
00013 public $title;
00014 public $teaser;
00015 public $text;
00016 public $meta;
00017 public $modificationdate;
00018 public $creationdate;
00019
00020 protected $matching = self::MATCH_NARROW;
00021
00022
00023
00024
00025
00026
00027 protected function create_table_object() {
00028 $this->set_matching(self::MATCH_NARROW);
00029 return new DBTable(
00030 Config::get_value(ConfigSearchIndex::TABLE_NAME),
00031 array_merge(
00032 array(
00033 new DBFieldInt('id', null, DBFieldInt::PRIMARY_KEY),
00034 new DBFieldInt('item_id', null, DBFieldInt::UNSIGNED | DBDriverSphinx::SPHINX_ATTRIBUTE),
00035 new DBFieldInt('item_model', null, DBFieldInt::UNSIGNED | DBDriverSphinx::SPHINX_ATTRIBUTE),
00036 new DBFieldText('title', 200, null, DBField::NOT_NULL),
00037 new DBFieldText('teaser', DBFieldText::BLOB_LENGTH_SMALL, null, DBField::NONE),
00038 new DBFieldTextHtml('text', DBFieldText::BLOB_LENGTH_LARGE, null, DBField::NONE),
00039 new DBFieldText('meta', DBFieldText::BLOB_LENGTH_SMALL, null, DBField::NONE),
00040 new DBFieldDateTime('creationdate', DBFieldDateTime::NOW, DBFieldDateTime::NOT_NULL | DBDriverSphinx::SPHINX_ATTRIBUTE),
00041 new DBFieldDateTime('modificationdate', DBFieldDateTime::NOW, DBFieldDateTime::NOT_NULL | DBDriverSphinx::SPHINX_ATTRIBUTE)
00042 ),
00043 $this->get_additional_field_definitions()
00044 ),
00045 'id',
00046 array(),
00047 array(),
00048 DBDriverSphinx::DEFAULT_CONNECTION_NAME
00049 );
00050 }
00051
00052
00053
00054
00055
00056
00057 protected function get_additional_field_definitions() {
00058 return array();
00059 }
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070 public function set_search($search) {
00071 $this->sphinx_all_fields = $search;
00072 }
00073
00074
00075
00076
00077
00078
00079 public function exclude_models($models) {
00080 $exclude = $this->extract_model_ids($models);
00081 if (count($exclude)) {
00082 $this->add_where('item_model', DBWhere::OP_NOT_IN, $exclude);
00083 }
00084 }
00085
00086
00087
00088
00089
00090
00091 public function limit_to_models($models) {
00092 $include = $this->extract_model_ids($models);
00093 switch(count($include)) {
00094 case 0:
00095
00096 $this->add_where('item_model', '=', 0);
00097 break;
00098 default:
00099 $this->add_where('item_model', DBWhere::OP_IN, $include);
00100 break;
00101 }
00102 }
00103
00104
00105
00106
00107 protected function extract_model_ids($models) {
00108 $ret = array();
00109 foreach(Arr::force($models, false) as $model) {
00110 $ret[] = SearchIndexRepository::get_model_id($model);
00111 }
00112
00113 $ret = array_filter($ret);
00114 return $ret;
00115 }
00116
00117
00118
00119
00120 public function sort_by_relevance() {
00121 $this->sort('relevance_w', self::DESC);
00122 }
00123
00124
00125
00126
00127 public function set_matching($matching) {
00128 $this->matching = $matching;
00129 }
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143 public function count() {
00144 try {
00145 $count = parent::count();
00146 }
00147 catch (Exception $ex) {
00148 $this->set_sphinx_feature(DBDriverSphinx::FEATURE_STRIP_OPERATORS, true);
00149 $count = parent::count();
00150 }
00151 return min($count, APP_SPHINX_MAX_MATCHES);
00152 }
00153
00154
00155
00156
00157 public function get_sortable_columns() {
00158 return array(
00159 'relevance' => new DBSortColumn('relevance_w', 'Relevanz', DBSortColumn::TYPE_MATCH, DBSortColumn::ORDER_FORWARD, true)
00160 );
00161 }
00162
00163
00164
00165
00166 public function get_sort_default_column() {
00167 return 'relevance';
00168 }
00169
00170
00171
00172
00173 public function execute() {
00174 $ret = array();
00175 $log_fail = Config::has_feature(Config::LOG_FAILED_QUERIES);
00176 try {
00177
00178 Config::set_feature(Config::LOG_FAILED_QUERIES, false);
00179 $found = $this->find();
00180 Config::set_feature(Config::LOG_FAILED_QUERIES, $log_fail);
00181 }
00182 catch (Exception $ex) {
00183 Config::set_feature(Config::LOG_FAILED_QUERIES, $log_fail);
00184
00185 $this->set_sphinx_feature(DBDriverSphinx::FEATURE_STRIP_OPERATORS, true);
00186 $found = $this->find();
00187 }
00188 if ($found) {
00189 while ($this->fetch()) {
00190 $model = $this->resolve_model($this->item_model);
00191 $p = false;
00192 if ($model) {
00193 $p = DB::get_item_by_pk($model, $this->item_id);
00194 }
00195 if ($p) {
00196 $p->relevance_w = $this->relevance_w;
00197 $ret[] = $p;
00198 }
00199 }
00200 }
00201 return $ret;
00202 }
00203
00204
00205
00206
00207 protected function resolve_model($id) {
00208 return SearchIndexRepository::get_model_for_id($id);
00209 }
00210
00211
00212
00213
00214
00215
00216
00217 protected function configure_select_query($query, $policy) {
00218 $this->set_field_weights();
00219 $this->sphinx_all_fields = $this->preprocess_query($this->sphinx_all_fields);
00220 switch($this->matching) {
00221 case self::MATCH_WIDE:
00222 $this->set_sphinx_feature(DBDriverSphinx::FEATURE_MATCH_MODE, DBDriverSphinx::MATCH_OR);
00223 break;
00224 default:
00225 $this->set_sphinx_feature(DBDriverSphinx::FEATURE_MATCH_MODE, DBDriverSphinx::MATCH_EX);
00226 break;
00227 }
00228 parent::configure_select_query($query, $policy);
00229
00230 $query->set_fields(array(
00231 '*',
00232 $this->compute_relevance_w() => 'relevance_w'
00233 ));
00234 }
00235
00236
00237
00238
00239
00240
00241
00242 protected function preprocess_query($query) {
00243
00244 $query = String::preg_replace('@(\w)\-@', '$1 ', $query);
00245 return $query;
00246 }
00247
00248
00249
00250
00251 protected function set_field_weights() {
00252 $this->set_sphinx_feature(DBDriverSphinx::FEATURE_WEIGHTS, array('title' => 5, 'teaser' => 3, 'text' => 1));
00253 }
00254
00255
00256
00257
00258
00259
00260 protected function compute_relevance_w() {
00261 $model_weight = $this->compute_model_weight(3000);
00262 $age_weight = $this->compute_age_weight(100);
00263 return "@weight + ($age_weight) + ($model_weight)";
00264 }
00265
00266
00267
00268
00269
00270
00271
00272
00273 protected function compute_age_weight($weight_base) {
00274 $ret = '0';
00275 if ($this->matching == self::MATCH_NARROW) {
00276 $month = 30 * GyroDate::ONE_DAY;
00277 $now = time();
00278 $weight_base = String::number($weight_base, 2, true);
00279
00280 $ret = "-$weight_base * ($now - modificationdate) / $month";
00281 }
00282 return $ret;
00283 }
00284
00285
00286
00287
00288
00289
00290
00291 protected function compute_model_weight($weight_base) {
00292 $weight_if_expression = $weight_base;
00293
00294 foreach(SearchIndexRepository::get_model_rules() as $rule) {
00295 $model_id = $rule->model_id;
00296 $model_weight = String::number($weight_base * $rule->weight, 2, true);
00297 $weight_if_expression = "IF(item_model = $model_id, $model_weight, $weight_if_expression)";
00298 }
00299 return $weight_if_expression;
00300 }
00301 }