_params; } /** * Remove a Solr param. * * @param string $key * Param name to remove * @param string $value * (optional) Solr param value to be removed. If null given, remove all * params matching $key. */ public function removeParam($key, $value = NULL) { if (! $value) { unset($this->_param[$key]); } else { if (is_array($this->_params[$key])) { foreach ($this->_params[$key] as $_key => $_value) { if ($_value == $value) { unset($this->_params[$_key]); } } } else if($this->_params[$key] == $value) { unset($this->_param[$key]); } } } /** * Add a Solr param to our query * * @param string $key * Solr param name * @param array|string $value * Solr param value, likely more a string any other type. If an array is * given, this will add a set of values for the given param name. */ public function addParam($key, $value) { if (isset($this->_params[$key])) { if (is_array($value)) { // array_values() will ensure we have numeric keys and ensure the // array_merge() expected behavior. if (is_array($this->_params[$key])) { $this->_params = array_merge($this->_params[$key], array_values($value)); } else { $this->_params = array_merge(array($this->_params[$key]), array_values($value)); } } else { if (is_array($this->_params[$key])) { $this->_params[$key][] = $value; } else { $this->_params[$key] = array($this->_params[$key], $value); } } } else { // Whatever is a single value or an array, this will do the right thing. $this->_params[$key] = $value; } } /** * Merge params array with existing one. * * @param array $params = array() * New params to add * @param boolean $override = FALSE * If set to TRUE, incomming params key will override existing ones */ public function addParams($params = array(), $override = FALSE) { foreach ($params as $key => &$value) { if ($override) { $this->removeParam($key); } $this->addParam($key, $value); } } /** * Construct the object * * @param array $params = array() * Apache Solr module formated array of params */ public function __construct($params = array()) { $this->_params = $params; } } /** * Error when building a wrong query. */ class SolrQueryException extends Exception {} /** * Abstract implementation of complex query tree. */ abstract class AbstractAssistantSolrQueryString { /** * Sub queries storage. * * @var array */ protected $_subQueries = array(); /** * Default operator. Can be 'AND' or 'OR'. * * @var string */ protected $_operator = 'OR'; /** * Set default operator. * * @param string $operator * Can be 'AND' or 'OR'. */ public function setOperator($operator) { if ($operator == 'AND' || $operator == 'OR') { $this->_operator = $operator; } } /** * Add subquery to query * * @param AbstractAssistantSolrQueryString $subQuery * AbstractAssistantSolrQueryString instance */ public function addSubQuery(AbstractAssistantSolrQueryString $query) { $this->_subQueries[] = $query; } /** * Build RAW output (speciliazed method). * * @return string */ protected abstract function _renderRawOutput(); /** * Build RAW query string * * @return string */ public final function __toString() { $output = trim($this->_renderRawOutput()); if (! empty($this->_subQueries)) { $subOutput = implode(' ' . $this->_operator . ' ', $this->_subQueries); if (! empty($output)) { $output .= ' ' . $this->_operator . ' (' . $subOutput . ')'; } else { $output .= $subOutput; } } return $output; } } /** * Query string implementation. */ class AssistantSolrQueryString extends AbstractAssistantSolrQueryString { /** * Key/value pairs, keys are field string on which to apply the search token * values are the search token. * Key can be a numeric value, in this case it will be ignored and the value * will be apply as a default search pattern. * This will be used to construct the Solr Query string. */ protected $_q = NULL; /** * Add query keyword to given query array. * * @param string $value * Keywords * @param string $field = NULL * (optional) Field name where to append the query string * Note that if this field already exists, string will be concatenated to * existing one. */ public function addQuery($value, $field = NULL) { if (empty($field)) { $this->_q[] = $value; } else if (isset($destination[$field])) { $this->_q[$field] .= " " . $value; } else { $this->_q[$field] = $value; } } /** * Add '"' chars if necessary to a token value. * @return unknown_type */ protected static function _escapeToken($token) { if (preg_match("/[\., -:]/", $token)) { return '"' . $token . '"'; } else { return $token; } } /** * @override _renderRawOutput() * @see AbstractAssistantSolrQueryString#_renderRawOutput() */ protected function _renderRawOutput() { $queryString = array(); if (! empty($this->_q)) { foreach ($this->_q as $field => &$token) { // Skip numeric keys, and append the value as default search keyword if (is_numeric($field)) { $queryString[] = self::_escapeToken($token); } else { $queryString[] = $field . ':' . self::_escapeToken($token); } } return implode(" ", $queryString); } return ""; } } /** * Because Solr_Base_Query does not allows us to do full Solr queries, we done * our own simple implementation that fits to our needs. * * TODO: The AssistantSolrParams extends is ugly * * TODO: Cache the computed query strings until modifications are done. */ class AssistantSolrQuery { /** * Solr params array */ protected $_params = array(); /** * Solr Query string builder * * @var array * Array of AssistantSolrQueryString */ protected $_queries = array(); public function supportedQueryStrings() { return array('q', 'fq', 'bq'); } /** * Tells if query name is supported. * * @param $query * @return unknown_type */ public function isSupported($queryName) { return in_array($queryName, $this->supportedQueryStrings()); } /** * Query getter */ public function __get($name) { if (! $this->isSupported($name)) { throw new SolrQueryException("Unsupported query string"); } if (empty($this->_queries[$name])) { $this->_queries[$name] = new AssistantSolrQueryString(); } return $this->_queries[$name]; } /** * Query setter */ public function __set($name, $value) { if (! $this->isSupported($name)) { throw new SolrQueryException("Unsupported query string"); } if (! $value instanceof AssistantSolrQueryString) { throw new SolrQueryException("Value is not a AssistantSolrQueryString instance"); } $this->_queries[$name] = $value; } /** * Rebuild params using setted query strings */ protected function _rebuildParams() { foreach ($this->supportedQueryStrings() as $name) { // String explicit cast calls the __toString() magic method. if ($queryString = (string) $this->{$name}) { $this->_params->removeParam($name); $this->_params->addParam($name, $queryString); } } } /** * Get params rebuilt using queries */ public function getParams() { $this->_rebuildParams(); return $this->_params->getParams(); } /** * Construct the object * * @param array $params = array() * Apache Solr module formated array of params * @param AssistantSolrQueryString $q = NULL * A AssistantSolrQueryString instance for Solr Query string construction * @param AssistantSolrQueryString $fq = NULL * A AssistantSolrQueryString instance for Solr FilterQuery string construction */ public function __construct($params = array()) { $this->_params = new AssistantSolrParams($params); } }