/** * Behaves similar to a find('all') style query. * Additional values processed * from the $options array: * * headline = Array of fields to highlight with matches from the search. Virtual fields will * be present for the query. They will be in the form name_headline where name is the original * field and the new field has the extra markup. * weights = Array of weights to use when ranking various levels * fn_rank = Name of ranking function to use (default 'ts_rank') * * Example: * * $Book->search("title:Wind", array('headline' => array('title'), weights => array('A' => 1.25))); * * @param Model $model * The model currently being operated on * @param string $text_query * An english query. Basic support provided for and, or, - operator as well * as some weighted properties. * @param array $options * Available options for find('all') plus those listed above. * @return Results of query expression, null for failure */ public function search(Model $model, $text_query, $options = array()) { try { $ts_query = new CakeTsQueryBuilder($text_query, $this->settings[$model->alias]['weights']); if ($this->settings[$model->alias]['column'] != null) { $query = 'to_tsquery(\'' . $this->regconfig . '\', \'' . pg_escape_string($ts_query->getQuery()) . '\')'; // Generate additional WHERE clause if (!isset($options['conditions'])) { $options['conditions'] = array(); } $options['conditions'][] = $model->alias . '.' . $this->settings[$model->alias]['column'] . ' @@@ to_tsquery(\'' . $this->regconfig . '\', \'' . pg_escape_string($ts_query->getQuery()) . '\')'; // Create highlight columns desired if (isset($options['headline'])) { foreach ($options['headline'] as $highlight) { $model->virtualFields[$highlight . '_headline'] = 'ts_headline(' . $model->alias . '.' . $highlight . ',' . $query . ', \'HighlightAll=TRUE\')'; } // If the user is restricting the fields, include matching, generated headlines if (isset($options['fields'])) { foreach ($options['fields'] as $field) { if (in_array($field, $options['headline'])) { $options['fields'][] = $field . '_headline'; } } } } // Merge user provided weights with default weights. $weights = array('D' => 0.1, 'C' => 0.2, 'B' => 0.4, 'A' => 1.0); if (isset($options['weights'])) { $weights = array_merge($weights, $options['weights']); $weights = array_intersect_key($weights, array('A' => null, 'B' => null, 'C' => null, 'D' => null)); ksort($weights, SORT_STRING); $weights = array_reverse($weights); } // Identify correct/preferred rank function $fn_rank = 'ts_rank'; if (isset($options['fn_rank'])) { $fn_rank = $options['fn_rank']; } // Add ranking virtual field $model->virtualFields[$this->settings[$model->alias]['column'] . '_rank'] = 'ts_rank(\'{' . implode(',', $weights) . '}\', ' . $this->settings[$model->alias]['column'] . ',' . $query . ')'; if (!isset($options['order'])) { $options['order'] = array(); } // Sort by rank as the first argument array_unshift($options['order'], $model->alias . '.' . $this->settings[$model->alias]['column'] . '_rank DESC'); } return $model->find('all', $options); } catch (\Exception $e) { $this->log('Failure searching. ' . $e->__toString()); } finally { if ($this->settings[$model->alias]['column'] != null) { unset($model->virtualFields[$this->settings[$model->alias]['column'] . '_rank']); } if (isset($options['headline'])) { foreach ($options['headline'] as $highlight) { unset($model->virtualFields[$highlight . '_headline']); } } } return array(); }
public function testNegativeGroupedByWithQuotes() { $query = new CakeTsQueryBuilder('sam or -(jim or -"has bacon")'); $this->assertEquals("'sam' | !('jim' | !'has bacon')", $query->getQuery()); }