View source  
  <?php
class apachesolr_views_query extends views_plugin_query implements Drupal_Solr_Query_Interface {
  
  protected static $idCount = 0;
  
  public $id;
  
  protected $_facets = array();
  
  protected $_query;
  
  protected $_solr_service;
  
  protected $_params = array(
    'facet' => 'true',
    'facet.mincount' => 1,
    'facet.sort' => 'true',
  );
  
  protected $_used_fields = array(
    'id',
    'nid',
    'url',
    'uid',
  );
  
  protected $_query_template = '';
  
  protected $_view_arguments = array();
  
  protected $_view_filters = array();
  
  protected $_base_path = '';
  
  protected $_subqueries = array();
  
  protected $_extra_query = array();
  
  protected $_available_sorts = array();
  
  protected $_sorts = array();
  
  protected $_current_display = '';
  
  protected $_current_views_name = '';
  
  protected $_query_fields = array();
  
  protected $_boost_functions = array();
  
  protected $_boost_queries = array();
  protected $base_table;
  protected $sql_base_table;
  protected $base_field;
  protected $_db_query = NULL;
  protected $has_sql_fields = FALSE;
  
  public function init($base_table, $base_field, $options) {
    $this->base_table = $base_table;
    $this->base_field = $base_field;
    $this->sql_base_table = substr($base_table, 11);
    $this->_solr_service = apachesolr_get_solr();
    module_load_include('inc', 'views', 'plugins/views_plugin_query_default');
    $this->_db_query = new views_plugin_query_default();
    $this->_db_query
      ->init($this->sql_base_table, $base_field, $options);
    $this->id = ++self::$idCount;
    $data = views_fetch_data($base_table);
    foreach ($data as $field_name => $field) {
      if (!empty($field['sort'])) {
        $this->_available_sorts[$field_name] = array(
          'name' => $field['title'],
          'default' => 'asc',
        );
      }
    }
  }
  
  public function __clone() {
    $this->id = ++self::$idCount;
  }
  
  public function build(&$view) {
    $view
      ->init_pager();
    if ($this->pager
      ->use_pager()) {
      $this->pager
        ->set_current_page($view->current_page);
    }
    
    $this->pager
      ->query();
    
    if (empty($this->_query_template) || $this->_query_template == 'dismax') {
      
      if (empty($this->_query_fields)) {
        $qf = variable_get('apachesolr_search_query_fields', array());
        foreach ($qf as $field_name => $boost) {
          if (!empty($boost)) {
            
            if ($field_name == 'body') {
              $boost *= 40.0;
            }
            $this
              ->add_query_field($field_name, $boost);
          }
        }
      }
      
      if (empty($this->_boost_functions)) {
        $this->_params['bf'] = array();
        $solr = $this->_solr_service;
        $total = 0;
        if (isset($solr)) {
          try {
            $data = $solr
              ->getLuke();
          } catch (Exception $e) {
            watchdog('apachesolr_views', $e
              ->getMessage());
          }
          if (isset($data) && isset($data->index->numDocs)) {
            $total = $data->index->numDocs;
          }
        }
        if (empty($total)) {
          $total = db_result(db_query("SELECT COUNT(nid) FROM {node}"));
        }
        
        $date_settings = variable_get('apachesolr_search_date_boost', '4:200.0');
        list($date_steepness, $date_boost) = explode(':', $date_settings);
        if (!empty($date_boost)) {
          $values = array(
            $date_steepness,
            $total,
            $total,
            $date_boost,
          );
          $this
            ->add_boost_function("recip(rord(created),%f,%d,%d)^%f", $values);
        }
        
        $comment_settings = variable_get('apachesolr_search_comment_boost', '0:0');
        list($comment_steepness, $comment_boost) = explode(':', $comment_settings);
        if ($comment_boost) {
          $values = array(
            $comment_steepness,
            $total,
            $total,
            $comment_boost,
          );
          $this
            ->add_boost_function("recip(rord(comment_count),%f,%d,%d)^%f", $values);
        }
        
        $changed_settings = variable_get('apachesolr_search_changed_boost', '0:0');
        list($changed_steepness, $changed_boost) = explode(':', $changed_settings);
        if ($changed_boost) {
          $values = array(
            $changed_steepness,
            $total,
            $total,
            $changed_boost,
          );
          $this
            ->add_boost_function("recip(rord(last_comment_or_change),%f,%d,%d)^%f", $values);
        }
      }
      
      if (empty($this->_boost_queries)) {
        
        $sticky_boost = variable_get('apachesolr_search_sticky_boost', 0);
        if ($sticky_boost) {
          $this
            ->add_boost_query('sticky', 'true', $sticky_boost);
        }
        
        $promote_boost = variable_get('apachesolr_search_promote_boost', 0);
        if ($promote_boost) {
          $this
            ->add_boost_query('promote', 'true', $promote_boost);
        }
        
        $type_boosts = variable_get('apachesolr_search_type_boosts', array());
        if (!empty($type_boosts)) {
          foreach ($type_boosts as $type => $boost) {
            
            if (!empty($boost)) {
              $this
                ->add_boost_query('type', $type, $boost);
            }
          }
        }
      }
    }
    try {
      
      apachesolr_search_add_facet_params($this->_params, $this);
      if ($this->_query_template == 'dismax' || empty($this->_query_template)) {
        
        $this->_params['qf'] = array();
        foreach ($this->_query_fields as $field_name => $boost) {
          $this->_params['qf'][] = "{$field_name}^{$boost}";
        }
        
        $this->_params['bf'] = $this->_boost_functions;
        
        foreach ($this->_boost_queries as $field_name => $fields) {
          foreach ($fields as $field_value => $boost) {
            $this->_params['bq'][] = "{$field_name}:{$field_value}^{$boost}";
          }
        }
      }
      
      $this->_params['fq'] = $this
        ->rebuild_fq();
      
      foreach ($this->_subqueries as $query_data) {
        
        $sub_query_string = $query_data['query']
          ->get_query_basic();
        if ($sub_query_string) {
          $this->_query .= " {$query_data['q_op']} ({$sub_query_string})";
        }
        
        $subfq = $query_data['query']
          ->get_fq();
        if (!empty($subfq)) {
          $operator = $query_data['fq_op'];
          $this->_params['fq'][] = "(" . implode(" {$operator} ", $subfq) . ")";
        }
      }
      
      $this->_params['fl'] = implode(',', $this->_used_fields);
      
      $sort_strings = array();
      foreach ($this->_sorts as $name => $direction) {
        $sort_strings[] = "{$name} {$direction}";
      }
      $this->_params['sort'] = implode(',', $sort_strings);
      
      if (!empty($this->_query_template)) {
        $this->_params['qt'] = $this->_query_template;
      }
    } catch (Exception $e) {
      watchdog('apachesolr_views', $e
        ->getMessage());
    }
  }
  
  public function alter(&$view) {
    foreach (module_implements('apachesolr_modify_query') as $module) {
      $function = $module . '_apachesolr_modify_query';
      $function($this, $this->_params, 'apachesolr_views_query');
    }
  }
  
  public function execute(&$view) {
    $start_time = views_microtime();
    $view->result = array();
    $this->_view_arguments = $view->argument;
    $this->_view_filters = $view->filter;
    $this->_base_path = $view
      ->get_path();
    $this->_current_display = $view->current_display;
    $this->_current_views_name = $view->name;
    
    $this->pager
      ->pre_execute($this->_query, $args);
    try {
      
      if (empty($this->_query)) {
        watchdog('Apachesolr Views', 'Warning, no query string set');
      }
      $response = $this->_solr_service
        ->search($this->_query, $this->offset, $this->limit, $this->_params);
      $view->total_rows = $total = $response->response->numFound;
      
      if (!empty($this->_base_path)) {
        apachesolr_static_response_cache($response);
        apachesolr_has_searched(TRUE);
        apachesolr_current_query($this);
      }
      if ($total > 0) {
        $results = $response->response->docs;
        
        $date_fields = array(
          'created',
          'changed',
        );
        foreach (array_values($date_fields) as $field) {
          if (empty($this->_used_fields[$field])) {
            unset($date_fields[$field]);
          }
        }
        if (!empty($date_fields)) {
          foreach ($results as $doc) {
            foreach ($date_fields as $field) {
              $doc->{$field} = strtotime($doc->{$field});
            }
          }
        }
        $base_fields = array();
        foreach ($results as $doc) {
          $base_fields[] = $doc->{$this->base_field};
        }
        
        if ($this->has_sql_fields) {
          $sql_table = $this->_db_query
            ->ensure_table($this->sql_base_table);
          $replace = array_fill(0, sizeof($base_fields), '%d');
          $in = '(' . implode(", ", $replace) . ')';
          $this->_db_query
            ->add_where(0, "{$sql_table}.{$this->base_field} IN {$in}", $base_fields);
          $base_alias = $this->_db_query
            ->add_field($sql_table, $this->base_field);
          $sql_query = $this->_db_query
            ->query();
          $sql_args = $this->_db_query
            ->get_where_args();
          $sql_result = db_query($sql_query, $sql_args);
          
          while ($sql_row = db_fetch_object($sql_result)) {
            foreach ($results as $key => $doc) {
              if ($doc->{$this->base_field} == $sql_row->{$base_alias}) {
                foreach ($sql_row as $field => $value) {
                  $results[$key]->{$field} = $value;
                }
              }
            }
          }
        }
        $view->result = $results;
      }
    } catch (Exception $e) {
      watchdog('Apache Solr', $e
        ->getMessage(), NULL, WATCHDOG_ERROR);
      apachesolr_failure(t('Solr search'), is_null($query) ? $this->_keys : $query
        ->get_query_basic());
    }
    $this->pager->total_items = $view->total_rows;
    $this->pager
      ->update_page_info();
    $this->pager
      ->post_execute($view->result);
    $view->execute_time = views_microtime() - $start_time;
  }
  
  function set_limit($limit) {
    $this->limit = $limit;
  }
  
  function set_offset($offset) {
    $this->offset = $offset;
  }
  
  function set_keys($keys) {
    $this->keys = $keys;
  }
  
  function get_keys() {
    return $this->keys;
  }
  
  public function add_solr_field($field) {
    if (!in_array($field, $this->_used_fields)) {
      $this->_used_fields[] = $field;
    }
    return $field;
  }
  
  public function add_field($table, $field, $alias = '', $params = array()) {
    $this->has_sql_fields = TRUE;
    return $this->_db_query
      ->add_field($table, $field, $alias, $params);
  }
  
  public function ensure_table($table, $relationship) {
    if ($table != $this->base_table) {
      if (substr($table, 0, strlen($this->base_table)) == $this->base_table) {
        $real_table = substr($table, strlen($this->base_table) + 1);
      }
      else {
        $real_table = $table;
      }
      return $this->_db_query
        ->ensure_table($real_table, $relationship);
    }
  }
  
  public function add_sort($field, $order, $single = FALSE) {
    if ($single) {
      $this->_sorts = array();
    }
    
    $this->_sorts[$field] = strtolower($order);
  }
  
  function get_param($param_name) {
    return $this->_params[$param_name];
  }
  
  function set_param($param_name, $param_value) {
    $this->_params[$param_name] = $param_value;
  }
  
  public function add_filter($type, $value, $exclude = FALSE) {
    $this->_facets[$type][] = array(
      'value' => $value,
      'exclude' => $exclude,
    );
  }
  
  public function set_query($query) {
    return $this->_query = $query;
  }
  
  public function get_query_basic() {
    return $this->_query;
  }
  
  public function remove_keys() {
    $this->_query = '';
  }
  
  function has_filter($field, $value) {
    if (isset($this->_facets[$field])) {
      foreach ($this->_facets[$field] as $definition) {
        if ($definition['value'] == $value && $definition['exclude'] == FALSE) {
          return TRUE;
        }
      }
    }
    return FALSE;
  }
  
  function remove_filter($field, $value = NULL) {
    if (!empty($value)) {
      if (isset($this->_facets[$field])) {
        $removal_key = FALSE;
        foreach ($this->_facets[$field] as $key => $definition) {
          if ($definition['value'] == $value) {
            $removal_key = $key;
            break;
          }
        }
        
        if ($removal_key !== FALSE) {
          unset($this->_facets[$field][$removal_key]);
        }
      }
    }
    else {
      if (isset($this->_facets[$field])) {
        unset($this->_facets[$field]);
      }
    }
  }
  
  function get_path($new_keys = NULL) {
    if (isset($new_keys)) {
      $this
        ->set_query($new_keys);
    }
    $wildcard_count = 0;
    
    if (empty($this->_view_arguments)) {
      return $this->_base_path;
    }
    foreach ($this->_view_arguments as $field => $argument) {
      
      $path_part = $this
        ->part_of_facet_block($argument) ? $this
        ->argument_part($field) : $argument->argument;
      if (!empty($path_part)) {
        $path_parts[$argument->position + 1] = $path_part;
        $wildcard_count = 0;
      }
      else {
        $path_parts[$argument->position + 1] = $argument->options['wildcard'];
        $wildcard_count++;
      }
    }
    $arguments = explode('/', $this->_base_path);
    $path = '';
    foreach ($arguments as $arg) {
      if ($arg == '%') {
        $part = array_shift($path_parts);
      }
      else {
        $part = $arg;
      }
      $path .= "/{$part}";
    }
    if (count($path_parts) && $wildcard_count != count($path_parts)) {
      $path .= "/" . implode('/', array_slice($path_parts, 0, count($path_parts) - $wildcard_count));
    }
    $path = substr($path, 1);
    if (trim($path) == trim(variable_get('site_frontpage', 'node'))) {
      return '<front>';
    }
    return $path;
  }
  
  protected function argument_part($field) {
    $argument_path = '';
    if (empty($this->_facets[$field])) {
      if ($field == 'text' && !empty($this->_query)) {
        
        return $this->_query;
      }
      else {
        return '';
      }
    }
    foreach ($this->_facets[$field] as $defintion) {
      if (!$defintion['exclude']) {
        $argument_path .= ',' . $defintion['value'];
      }
    }
    return drupal_substr($argument_path, 1);
  }
  
  protected function part_of_facet_block($argument) {
    foreach (apachesolr_get_enabled_facets() as $module => $module_facets) {
      foreach ($module_facets as $delta => $facet_field) {
        $facet_arguments[] = $facet_field;
      }
    }
    
    if (!empty($facet_arguments) && $argument->field == "tid") {
      return true;
    }
    
    return in_array($argument->field, $facet_arguments);
  }
  
  function get_url_queryvalues() {
    
    $query_values = array();
    foreach ($this->_view_filters as $filter) {
      if ($filter->options['exposed']) {
        if ($filter->field == 'text' && !empty($this->_query)) {
          
          $query_values[$filter->options['expose']['identifier']] = $this->_query;
        }
        elseif ($this->_facets[$filter->field]) {
          $query_values[$filter->options['expose']['identifier']] = $this->_facets[$filter->field];
        }
        elseif (!empty($_GET[$filter->options['expose']['identifier']]) && $filter->field != 'text') {
          $query_values[$filter->options['expose']['identifier']] = $_GET[$filter->options['expose']['identifier']];
        }
      }
    }
    foreach ($this->_extra_query as $field => $value) {
      $query_values[$field] = $value;
    }
    return $query_values;
  }
  
  function set_solrsort($field, $direction) {
    $this
      ->add_sort($field, $direction);
  }
  
  function get_solrsort() {
    return $this->_sorts;
  }
  
  function get_filters($name = NULL) {
    $filters = array();
    foreach ($this->_facets as $type => $fields) {
      foreach ($fields as $data) {
        $filters[] = array(
          '#name' => $data['exclude'] ? "NOT {$type}" : $type,
          '#value' => $data['value'],
        );
      }
    }
    
    if (!empty($name)) {
      foreach ($filters as $key => $filter) {
        if ($filter['#name'] != $name) {
          unset($filters[$key]);
        }
      }
    }
    return $filters;
  }
  function get_fq() {
    return $this
      ->rebuild_fq();
  }
  
  function get_display_id() {
    return $this->_current_display;
  }
  
  function get_views_name() {
    return $this->_current_views_name;
  }
  
  function add_subquery(Drupal_Solr_Query_Interface $query, $fq_operator = 'OR', $q_operator = 'AND') {
    $this->_subqueries[$query->id] = array(
      'query' => $query,
      'fq_op' => $fq_operator,
      'q_op' => $q_operator,
    );
  }
  
  public static function escape_term($term) {
    $term = trim($term);
    if (empty($term)) {
      return '';
    }
    if ($term[0] == '"' && $term[strlen($term) - 1] == '"' || $term[0] == '(' && $term[strlen($term) - 1] == ')') {
      return $term;
    }
    if (strpos($term, ' TO ') !== FALSE) {
      return $term;
    }
    if (strpos($term, ' ') !== FALSE) {
      return Drupal_Apache_Solr_Service::phrase($term);
    }
    return Drupal_Apache_Solr_Service::escape($term);
  }
  
  public function change_query_template($qt) {
    $this->_query_template = $qt;
  }
  
  function add_url_query_param($field, $value) {
    $this->_extra_query[$field] = $value;
  }
  public function get_available_sorts() {
    return $this->_available_sorts;
  }
  
  function remove_subquery(Drupal_Solr_Query_Interface $query) {
    unset($this->_subqueries[$query->id]);
  }
  
  function remove_subqueries() {
    $this->_subqueries = array();
  }
  
  function set_available_sort($field, $sort) {
    
  }
  
  function add_query_field($field_name, $boost = 1.0) {
    $this->_query_fields[$field_name] = $boost;
  }
  
  function remove_query_field($field_name) {
    unset($this->_query_fields[$field_name]);
  }
  
  function add_boost_function($function, array $values) {
    $this->_boost_functions[] = vsprintf($function, $values);
  }
  
  function add_boost_query($field_name, $field_value, $boost_value) {
    $this->_boost_queries[$field_name][$field_value] = $boost_value;
  }
  
  function remove_boost_query($field_name, $field_value = NULL) {
    if ($field_value) {
      unset($this->_boost_queries[$field_name][$field_value]);
    }
    else {
      unset($this->_boost_queries[$field_name]);
    }
  }
  
  protected function rebuild_fq() {
    $query_filters = array();
    foreach ($this->_facets as $type => $facets) {
      $filter_string = '';
      foreach ($facets as $definition) {
        if ($definition['exclude']) {
          $type_string = "NOT {$type}";
        }
        else {
          $type_string = $type;
        }
        $query_filters[] = "{$type_string}:" . $definition['value'];
      }
    }
    $query_filters[] = "entity:" . $this->sql_base_table;
    return $query_filters;
  }
}