Example #1
0
 /**
  * Allows for category groups filtering: (1|2|3) && (4|5|6)
  *
  * @access     public
  * @return     void
  */
 public function filter($entry_ids)
 {
     // --------------------------------------
     // See if there are groups present, with correct values
     // --------------------------------------
     $groups = array_filter($this->params->get_prefixed('category:'), 'low_param_is_numeric');
     // --------------------------------------
     // Bail out if there are no groups
     // --------------------------------------
     if (empty($groups)) {
         return $entry_ids;
     }
     // --------------------------------------
     // Log it
     // --------------------------------------
     $this->_log('Applying ' . __CLASS__);
     // --------------------------------------
     // Loop through groups, compose SQL
     // --------------------------------------
     foreach ($groups as $key => $val) {
         // Prep the value
         $val = $this->params->prep($key, $val);
         // Get the parameter
         list($ids, $in) = low_explode_param($val);
         // Match all?
         $all = (bool) strpos($val, '&');
         // One query per group
         ee()->db->select('entry_id')->from('category_posts')->{$in ? 'where_in' : 'where_not_in'}('cat_id', $ids);
         // Limit by already existing ids
         if ($entry_ids) {
             ee()->db->where_in('entry_id', $entry_ids);
         }
         // Do the having-trick to account for *all* given entry ids
         if ($in && $all) {
             ee()->db->select('COUNT(*) AS num')->group_by('entry_id')->having('num', count($ids));
         }
         // Execute query
         $query = ee()->db->get();
         // And get the entry ids
         $entry_ids = low_flatten_results($query->result_array(), 'entry_id');
         $entry_ids = array_unique($entry_ids);
         // Bail out if there aren't any matches
         if (is_array($entry_ids) && empty($entry_ids)) {
             break;
         }
     }
     return $entry_ids;
 }
Example #2
0
 function low_merge_params($haystack, $needles, $as_param = FALSE)
 {
     // Prep the haystack
     if (!is_array($haystack)) {
         // Explode the param, forget about the 'not '
         list($haystack, ) = low_explode_param($haystack);
     }
     // Prep the needles
     if (!is_array($needles)) {
         list($needles, $in) = low_explode_param($needles);
     } else {
         $in = TRUE;
     }
     // Choose function to merge
     $method = $in ? 'array_intersect' : 'array_diff';
     // Do the merge thing
     $merged = $method($haystack, $needles);
     // Change back to parameter syntax if necessary
     if ($as_param) {
         $merged = low_implode_param($merged);
     }
     return $merged;
 }
Example #3
0
 /**
  * Search parameters for (parents|children):field params and return set of ids that match it
  *
  * @access      public
  * @return      void
  */
 public function filter($entry_ids)
 {
     // --------------------------------------
     // Check prefixed parameters needed
     // --------------------------------------
     $rels = array_filter(array_merge($this->params->get_prefixed('parent:'), $this->params->get_prefixed('child:')), 'low_param_is_numeric');
     // --------------------------------------
     // Don't do anything if nothing's there
     // --------------------------------------
     if (empty($rels)) {
         return $entry_ids;
     }
     // --------------------------------------
     // Log it
     // --------------------------------------
     $this->_log('Applying ' . __CLASS__);
     // --------------------------------------
     // Loop through relationships
     // --------------------------------------
     foreach ($rels as $key => $val) {
         // List out match
         list($type, $field) = explode(':', $key, 2);
         // Get the field id, skip if non-existent
         if (!($field_id = $this->_get_field_id($field))) {
             continue;
         }
         // Prep the value
         $val = $this->params->prep($key, $val);
         // Get the parameter
         list($ids, $in) = low_explode_param($val);
         // Match all?
         $all = (bool) strpos($val, '&');
         // Init vars
         $rel_ids = $table = FALSE;
         $get_children = $type == 'parent';
         // Native relationship field
         if ($this->_is_rel_field($field)) {
             // Account for new EE rels
             $prefix = version_compare(APP_VER, '2.6.0', '<') ? 'rel_' : '';
             // Set the table & attributes
             $table = 'relationships';
             $select = $get_children ? $prefix . 'child_id' : $prefix . 'parent_id';
             $where = $get_children ? $prefix . 'parent_id' : $prefix . 'child_id';
         } elseif ($this->_is_playa_field($field_id)) {
             // Set the table
             $table = 'playa_relationships';
             $select = $get_children ? 'child_entry_id' : 'parent_entry_id';
             $where = $get_children ? 'parent_entry_id' : 'child_entry_id';
             // Focus on specific field
             ee()->db->where('parent_field_id', $field_id);
         }
         // Execute query
         if ($table) {
             ee()->db->select($select . ' AS entry_id')->from($table)->{$in ? 'where_in' : 'where_not_in'}($where, $ids);
             // Limit by already existing ids
             if ($entry_ids) {
                 ee()->db->where_in($select, $entry_ids);
             }
             // Do the having-trick to account for *all* given entry ids
             if ($in && $all) {
                 ee()->db->select('COUNT(*) AS num')->group_by($select)->having('num', count($ids));
             }
             // Execute query
             $query = ee()->db->get();
             // And get the entry ids
             $entry_ids = low_flatten_results($query->result_array(), 'entry_id');
             $entry_ids = array_unique($entry_ids);
             // Bail out if there aren't any matches
             if (is_array($entry_ids) && empty($entry_ids)) {
                 break;
             }
         }
     }
     return $entry_ids;
 }
Example #4
0
 /**
  * Allows for tag filtering: (1|2|3) && (4|5|6)
  *
  * @access     public
  * @return     void
  */
 public function filter($entry_ids)
 {
     // -------------------------------------------
     // Make sure addons-library is loaded
     // -------------------------------------------
     ee()->load->library('addons');
     // -------------------------------------------
     // Solspace Tag or DevDemon Tagger?
     // -------------------------------------------
     if (ee()->addons->is_package('tag')) {
         $tables = array('tag_tags', 'tag_entries');
     } elseif (ee()->addons->is_package('tagger')) {
         $tables = array('tagger', 'tagger_links');
     }
     // --------------------------------------
     // See if there are tag params present
     // --------------------------------------
     $tag_names = $this->params->get_prefixed('tag_name');
     $tag_ids = $this->params->get_prefixed('tag_id');
     // --------------------------------------
     // Bail out if there are no tags
     // --------------------------------------
     if (empty($tables) || empty($tag_names) && empty($tag_ids)) {
         return $entry_ids;
     }
     // --------------------------------------
     // Log it
     // --------------------------------------
     $this->_log('Applying ' . __CLASS__);
     // -------------------------------------------
     // Check tag names and convert to tag IDs
     // -------------------------------------------
     if ($tag_names) {
         $unique_tags = array();
         foreach ($tag_names as $key => $val) {
             // Get the tags
             list($tags, $in) = low_explode_param($val);
             $unique_tags = array_merge($unique_tags, $tags);
         }
         // Remove duplicates and convert
         $unique_tags = array_unique($unique_tags);
         $unique_tags = array_map(array($this, '_convert_tag'), $unique_tags);
         // Get IDs for unique tags
         $query = ee()->db->select('tag_id, tag_name')->from($tables[0])->where_in('site_id', $this->params->site_ids())->where_in('tag_name', $unique_tags)->get();
         // clean up
         unset($unique_tags);
         // Get tag map: [tag name] => tag_id
         $tag_map = low_flatten_results($query->result_array(), 'tag_id', 'tag_name');
         // Now, loop through original tags thing and convert to tag IDs
         foreach ($tag_names as $key => $val) {
             // Initiate tag ids
             $ids = array();
             // Read parameter value
             list($tags, $in) = low_explode_param($val);
             // Loop through tags and map them to IDs
             foreach ($tags as $tag) {
                 $tag = $this->_convert_tag($tag);
                 if (isset($tag_map[$tag])) {
                     $ids[] = $tag_map[$tag];
                 }
             }
             if ($ids) {
                 // Check separator and implode back to parameter
                 $sep = strpos($val, '&') === FALSE ? '|' : '&';
                 $str = implode($sep, $ids);
                 // Add negator back
                 if (!$in) {
                     $str = 'not ' . $ids;
                 }
                 // Add final parameter string to IDs
                 $tag_ids[$key] = $str;
             }
         }
     }
     // --------------------------------------
     // Get channel IDs before starting the query
     // --------------------------------------
     $channel_ids = ee()->low_search_collection_model->get_channel_ids($this->params->get('collection'));
     // --------------------------------------
     // Loop through groups, compose SQL
     // --------------------------------------
     foreach ($tag_ids as $key => $val) {
         // Prep the value
         $val = $this->params->prep($key, $val);
         // Get the parameter
         list($ids, $in) = low_explode_param($val);
         // Match all?
         $all = (bool) strpos($val, '&');
         // One query per group
         ee()->db->distinct()->select('entry_id')->from($tables[1])->where_in('site_id', $this->params->site_ids())->{$in ? 'where_in' : 'where_not_in'}('tag_id', $ids);
         // Limit by already existing ids
         if ($entry_ids) {
             ee()->db->where_in('entry_id', $entry_ids);
         }
         // Limit by channel ID
         if ($channel_ids) {
             ee()->db->where_in('channel_id', $channel_ids);
         }
         // Do the having-trick to account for *all* given entry ids
         if ($in && $all) {
             ee()->db->select('COUNT(*) AS num')->group_by('entry_id')->having('num', count($ids));
         }
         // Execute query
         $query = ee()->db->get();
         // And get the entry ids
         $entry_ids = low_flatten_results($query->result_array(), 'entry_id');
         // Bail out if there aren't any matches
         if (is_array($entry_ids) && empty($entry_ids)) {
             break;
         }
     }
     return $entry_ids;
 }
 /**
  * Get collection IDs by parameter on a site ID
  *
  * @access      public
  * @param       int      Site ID
  * @return      array
  */
 public function get_by_param($param)
 {
     list($ids, $in) = low_explode_param($param);
     $attr = low_array_is_numeric($ids) ? $this->pk() : 'collection_name';
     return $this->_get_by_attr($ids, $attr, $in);
 }
Example #6
0
 /**
  * Display entries in order
  *
  * @access      public
  * @return      string
  */
 public function entries()
 {
     // Set low_reorder param so extension kicks in
     ee()->TMPL->tagparams['low_reorder'] = 'yes';
     // --------------------------------------
     // Initiate set to get set_id, cat_id and entry_ids
     // --------------------------------------
     $this->_init_set();
     $this->_prep_no_results();
     $this->_remove_var_prefix();
     // --------------------------------------
     // Cache the set_id and cat_it
     // --------------------------------------
     low_set_cache($this->package, 'set_id', $this->set_id);
     low_set_cache($this->package, 'cat_id', $this->cat_id);
     // --------------------------------------
     // Check fallback parameter
     // --------------------------------------
     $fallback = ee()->TMPL->fetch_param('fallback') == 'yes' ? '_channel_entries' : '_empty_set';
     // --------------------------------------
     // Check if that results into entry_ids
     // --------------------------------------
     if (empty($this->entry_ids)) {
         return $this->{$fallback}();
     }
     // --------------------------------------
     // Check existing entry_id parameter
     // --------------------------------------
     if ($entry_ids = ee()->TMPL->fetch_param('entry_id')) {
         $this->_log('entry_id parameter found, filtering ordered entries accordingly');
         // Get the parameter value
         list($ids, $in) = low_explode_param($entry_ids);
         // Either remove $ids from $entry_ids OR limit $entry_ids to $ids
         $method = $in ? 'array_intersect' : 'array_diff';
         // Get list of entry ids that should be listed
         $this->entry_ids = $method($this->entry_ids, $ids);
     }
     // If that results in empty ids, bail out again
     if (empty($this->entry_ids)) {
         return $this->{$fallback}();
     }
     // --------------------------------------
     // Add fixed_order to parameters
     // --------------------------------------
     $orderby = ee()->TMPL->fetch_param('orderby');
     $param = empty($orderby) ? 'fixed_order' : 'entry_id';
     $this->set['parameters'][$param] = implode('|', $this->entry_ids);
     // --------------------------------------
     // Set template parameters
     // --------------------------------------
     // Check whether to force template params or not
     $force = ee()->TMPL->fetch_param('force_set_params', 'no') == 'yes';
     // Set the params
     $this->_set_template_parameters($force);
     // --------------------------------------
     // Use channel module to generate entries
     // --------------------------------------
     return $this->_channel_entries();
 }
Example #7
0
 /**
  * Create a list of where-clauses for given search parameters
  *
  * @access     private
  * @param      array
  * @param      string
  * @return     array
  */
 private function _search_where($search = array(), $prefix = '')
 {
     // --------------------------------------
     // Initiate where array
     // --------------------------------------
     $where = array();
     // --------------------------------------
     // Get field ids for given search fields
     // --------------------------------------
     $fields = $this->_get_channel_fields();
     // --------------------------------------
     // Loop through search filters and create where clause accordingly
     // --------------------------------------
     foreach ($search as $key => $val) {
         // Skip non-existent fields
         if (!isset($fields[$key])) {
             continue;
         }
         // Initiate some vars
         $exact = $all = FALSE;
         $field = $prefix . 'field_id_' . $fields[$key];
         // Exact matches
         if (substr($val, 0, 1) == '=') {
             $val = substr($val, 1);
             $exact = TRUE;
         }
         // All items? -> && instead of |
         if (strpos($val, '&&') !== FALSE) {
             $all = TRUE;
         }
         // Convert parameter to bool and array
         list($items, $in) = low_explode_param($val);
         // Init sql for where clause
         $sql = array();
         // Loop through each sub-item of the filter an create sub-clause
         foreach ($items as $item) {
             // Convert IS_EMPTY constant to empty string
             $empty = $item == 'IS_EMPTY';
             $item = str_replace('IS_EMPTY', '', $item);
             // greater/less than matches
             if (preg_match('/^([<>]=?)(\\d+)$/', $item, $matches)) {
                 $gtlt = $matches[1];
                 $item = $matches[2];
             } else {
                 $gtlt = FALSE;
             }
             // whole word? Regexp search
             if (substr($item, -2) == '\\W') {
                 $operand = $in ? 'REGEXP' : 'NOT REGEXP';
                 $item = '[[:<:]]' . preg_quote(substr($item, 0, -2)) . '[[:>:]]';
             } else {
                 // Not a whole word
                 if ($exact || $empty) {
                     // Use exact operand if empty or = was the first char in param
                     $operand = $in ? '=' : '!=';
                     $item = "'" . ee()->db->escape_str($item) . "'";
                 } elseif ($gtlt !== FALSE) {
                     $operand = $gtlt;
                     $item = "'" . ee()->db->escape_str($item) . "'";
                 } else {
                     // Use like operand in all other cases
                     $operand = $in ? 'LIKE' : 'NOT LIKE';
                     $item = "'%" . ee()->db->escape_str($item) . "%'";
                 }
             }
             // Add sub-clause to this statement
             $sql[] = sprintf("(%s %s %s)", $field, $operand, $item);
         }
         // Inclusive or exclusive
         $andor = $all ? ' AND ' : ' OR ';
         // Add complete clause to where array
         $where[] = '(' . implode($andor, $sql) . ')';
     }
     // --------------------------------------
     // Where now contains a list of clauses
     // --------------------------------------
     return $where;
 }
Example #8
0
 /**
  * Catch search form submission
  *
  * @access      public
  * @return      void
  */
 public function catch_search()
 {
     // --------------------------------------
     // Initiate data array; will be encrypted
     // and put in the URI later
     // --------------------------------------
     $data = array();
     if ($params = ee()->input->post('params')) {
         $data = low_search_decode($params);
     }
     // --------------------------------------
     // Check other data
     // --------------------------------------
     foreach (array_merge($_GET, $_POST) as $key => $val) {
         // Keys to skip
         if (in_array($key, array('ACT', 'XID', 'csrf_token', 'params', 'site_id'))) {
             continue;
         }
         // Add post var to data
         $data[$key] = is_array($val) ? implode('|', array_filter($val, 'low_not_empty')) : $val;
     }
     // --------------------------------------
     // Clean up the data array
     // --------------------------------------
     $data = array_filter($data, 'low_not_empty');
     // --------------------------------------
     // 'low_search_catch_search' extension hook
     //  - Check incoming data and optionally change it
     // --------------------------------------
     if (ee()->extensions->active_hook('low_search_catch_search') === TRUE) {
         $data = ee()->extensions->call('low_search_catch_search', $data);
         if (ee()->extensions->end_script === TRUE) {
             return;
         }
         // Clean again to be sure
         $data = array_filter($data, 'low_not_empty');
     }
     // --------------------------------------
     // Check for required parameter
     // --------------------------------------
     if (isset($data['required'])) {
         // Init errors
         $errors = array();
         // Get required as array
         list($required, $in) = low_explode_param($data['required']);
         foreach ($required as $req) {
             // Break out when empty
             // @TODO: enhance for multiple fields
             if (empty($data[$req])) {
                 $errors[] = $req;
             }
         }
         // Go back
         if ($errors) {
             ee()->session->set_flashdata('errors', $errors);
             $this->_go_back('fields_missing');
         }
         // remove from data
         unset($data['required']);
     }
     // --------------------------------------
     // Optionally log search query
     // --------------------------------------
     $this->_log_search($data);
     // --------------------------------------
     // Result URI: result page & cleaned up data, encoded
     // --------------------------------------
     $url = $this->_create_url($data, '&');
     // --------------------------------------
     // Redirect to result page
     // --------------------------------------
     // Empty out flashdata to avoid serving of JSON for ajax request
     if (AJAX_REQUEST && count(ee()->session->flashdata)) {
         ee()->session->flashdata = array();
     }
     ee()->functions->redirect($url);
 }
Example #9
0
 /**
  * Get WHERE clause for given field and parameter value
  *
  * @access     private
  * @return     void
  */
 public function _get_where($field, $val)
 {
     // Initiate some vars
     $exact = $all = $starts = $ends = FALSE;
     // Exact matches
     if (substr($val, 0, 1) == '=') {
         $val = substr($val, 1);
         $exact = TRUE;
     }
     // Starts with matches
     if (substr($val, 0, 1) == '^') {
         $val = substr($val, 1);
         $starts = TRUE;
     }
     // Ends with matches
     if (substr($val, -1) == '$') {
         $val = rtrim($val, '$');
         $ends = TRUE;
     }
     // All items? -> && instead of |
     if (strpos($val, '&&') !== FALSE) {
         $all = TRUE;
         $val = str_replace('&&', '|', $val);
     }
     // Convert parameter to bool and array
     list($items, $in) = low_explode_param($val);
     // Init sql for where clause
     $sql = array();
     // Loop through each sub-item of the filter an create sub-clause
     foreach ($items as $item) {
         // Convert IS_EMPTY constant to empty string
         $empty = $item == 'IS_EMPTY';
         $item = str_replace('IS_EMPTY', '', $item);
         // whole word? Regexp search
         if (substr($item, -2) == '\\W') {
             $operand = $in ? 'REGEXP' : 'NOT REGEXP';
             $item = preg_quote(substr($item, 0, -2));
             $item = str_replace("'", "\\'", $item);
             $item = "'[[:<:]]{$item}[[:>:]]'";
         } else {
             if (preg_match('/^([<>]=?)([\\d\\.]+)$/', $item, $match)) {
                 // Numeric operator!
                 $operand = $match[1];
                 $item = $match[2];
             } elseif ($exact || $empty || $starts && $ends) {
                 // Use exact operand if empty or = was the first char in param
                 $operand = $in ? '=' : '!=';
                 $item = "'" . ee()->db->escape_str($item) . "'";
             } else {
                 // Use like operand in all other cases
                 $operand = $in ? 'LIKE' : 'NOT LIKE';
                 $item = '%' . ee()->db->escape_like_str($item) . '%';
                 // Allow for starts/ends with matching
                 if ($starts) {
                     $item = ltrim($item, '%');
                 }
                 if ($ends) {
                     $item = rtrim($item, '%');
                 }
                 $item = "'{$item}'";
             }
         }
         // Add sub-clause to this statement
         $sql[] = sprintf("(%s %s %s)", $field, $operand, $item);
     }
     // Inclusive or exclusive
     $andor = $all ? ' AND ' : ' OR ';
     // Get complete clause, with parenthesis and everything
     $where = count($sql) == 1 ? $sql[0] : '(' . implode($andor, $sql) . ')';
     return $where;
 }
Example #10
0
 /**
  * Check if a value is present in a parameter
  */
 public function in_param($val, $param)
 {
     $it = FALSE;
     if ($param = $this->get($param)) {
         list($fields, $in) = low_explode_param($param);
         $it = in_array($val, $fields);
     }
     return $it;
 }
Example #11
0
 /**
  * Allows for keywords="" parameter, along with its associated params
  *
  * @access     private
  * @return     void
  */
 public function filter($entry_ids)
 {
     // --------------------------------------
     // Log it
     // --------------------------------------
     $this->_log('Applying ' . __CLASS__);
     // --------------------------------------
     // Set internal collections array based on param
     // --------------------------------------
     $this->_collections = ($cols = $this->params->get('collection')) ? ee()->low_search_collection_model->get_by_param($cols) : array();
     // --------------------------------------
     // Check validity of search mode and loose ends
     // --------------------------------------
     $this->_search_mode();
     $this->_loose_ends();
     // --------------------------------------
     // Check keywords and set the search terms
     // --------------------------------------
     $this->_set_terms();
     // --------------------------------------
     // Only perform actual search if keywords are given
     // --------------------------------------
     if (empty($this->_terms)) {
         $this->_log('No keyword search');
         $this->_prep_params();
         return $entry_ids;
     }
     // --------------------------------------
     // Reset results
     // --------------------------------------
     $this->_results = array();
     // --------------------------------------
     // Begin composing query
     // --------------------------------------
     ee()->db->select($this->_fulltext ? "entry_id, collection_id, MATCH(index_text) AGAINST('{$this->_query()}') AS score" : 'entry_id, collection_id, index_text', FALSE)->from(ee()->low_search_index_model->table());
     // --------------------------------------
     // Filters used by both searches
     // --------------------------------------
     // Limit query by collection
     if ($this->_collections) {
         ee()->db->where_in('collection_id', low_flatten_results($this->_collections, 'collection_id'));
     }
     // Limit query by site
     if ($site_ids = $this->params->site_ids()) {
         ee()->db->where_in('site_id', array_values($site_ids));
     }
     // If entry ids were given, limit to those
     if ($entry_ids) {
         ee()->db->where_in('entry_id', $entry_ids);
     }
     // Add where clause
     ee()->db->where($this->_fulltext ? $this->_fulltext_keywords() : $this->_fallback_keywords(), NULL, FALSE);
     if ($this->_fulltext) {
         // Actual fulltext search
         ee()->db->order_by('score', 'desc');
         // Limit by min_score
         if ($this->params->get('min_score')) {
             list($score, $include) = $this->_min_score();
             $oper = $include ? '>=' : '>';
             ee()->db->having("score {$oper}", $score);
         }
     }
     // --------------------------------------
     // Extra search stuff
     // --------------------------------------
     if ($add_to_query = $this->params->get_prefixed('keywords-query:', TRUE)) {
         foreach ($add_to_query as $field => $val) {
             if (ee()->db->field_exists($field, ee()->low_search_index_model->table())) {
                 list($items, $in) = low_explode_param($val);
                 ee()->db->{$in ? 'where_in' : 'where_not_in'}($field, $val);
             } else {
                 $this->_log("Field {$field} does not exist in " . ee()->low_search_index_model->table());
             }
         }
     }
     // --------------------------------------
     // Perform the search
     // --------------------------------------
     $this->_log('Starting search ' . ($this->_fulltext ? '(fulltext)' : '(fallback)'));
     $query = ee()->db->get();
     // --------------------------------------
     // If the search had no results, return no results bit
     // --------------------------------------
     if ($query->num_rows == 0) {
         $this->_log('Searched but found nothing. Returning no results.');
         return array();
     }
     // --------------------------------------
     // If we do have results, continue
     // --------------------------------------
     $this->_results = $this->_fulltext ? low_associate_results($query->result_array(), 'entry_id') : $this->_get_fallback_results($query);
     // Bail out if no entry falls above the min_score threshold
     if (empty($this->_results)) {
         $this->_log('No valid results after scoring');
         return array();
     }
     // --------------------------------------
     // Modify scores for each collection
     // --------------------------------------
     if ($modifiers = array_unique(low_flatten_results($this->_collections, 'modifier'))) {
         if (!(count($modifiers) == 1 && $modifiers[0] == 1.0)) {
             $this->_log('Applying collection modifier to search results');
             foreach ($this->_results as &$row) {
                 if ($mod = (double) $this->_collections[$row['collection_id']]['modifier']) {
                     $row['score'] = $row['score'] * $mod;
                 }
             }
         }
     }
     // -------------------------------------
     // 'low_search_modify_score' hook.
     //  - Modify scoring for keyword searches
     // -------------------------------------
     if (ee()->extensions->active_hook('low_search_modify_score') === TRUE) {
         $this->_results = ee()->extensions->call('low_search_modify_score', $this->_results);
         if (empty($this->_results) || ee()->extensions->end_script === TRUE) {
             return array();
         }
     }
     // --------------------------------------
     // Sort by score
     // --------------------------------------
     uasort($this->_results, 'low_by_score');
     // --------------------------------------
     // Add results to cache, so extension can look this up
     // --------------------------------------
     return array_keys($this->_results);
 }