* @license http://github.com/basdenooijer/solarium/raw/master/COPYING * @link http://www.solarium-project.org/ * * @package Solarium * @subpackage Query */ /** * Select Query * * Can be used to select documents and/or facets from Solr. This querytype has * lots of options and there are many Solarium subclasses for it. * See the Solr documentation and the relevant Solarium classes for more info. * * @package Solarium * @subpackage Query */ class Solarium_Query_Select extends Solarium_Query { /** * Solr sort mode descending */ const SORT_DESC = 'desc'; /** * Solr sort mode ascending */ const SORT_ASC = 'asc'; /** * Solr query operator AND */ const QUERY_OPERATOR_AND = 'AND'; /** * Solr query operator OR */ const QUERY_OPERATOR_OR = 'OR'; /** * Query component facetset */ const COMPONENT_FACETSET = 'facetset'; /** * Query component dismax */ const COMPONENT_DISMAX = 'dismax'; /** * Query component morelikethis */ const COMPONENT_MORELIKETHIS = 'morelikethis'; /** * Query component highlighting */ const COMPONENT_HIGHLIGHTING = 'highlighting'; /** * Query component spellcheck */ const COMPONENT_SPELLCHECK = 'spellcheck'; /** * Query component grouping */ const COMPONENT_GROUPING = 'grouping'; /** * Query component distributed search */ const COMPONENT_DISTRIBUTEDSEARCH = 'distributedsearch'; /** * Query component stats */ const COMPONENT_STATS = 'stats'; /** * Query component debug */ const COMPONENT_DEBUG = 'debug'; /** * Get type for this query * * @return string */ public function getType() { return Solarium_Client::QUERYTYPE_SELECT; } /** * Default options * * @var array */ protected $_options = array( 'handler' => 'select', 'resultclass' => 'Solarium_Result_Select', 'documentclass' => 'Solarium_Document_ReadOnly', 'query' => '*:*', 'start' => 0, 'rows' => 10, 'fields' => '*,score', ); /** * Default select query component types * * @var array */ protected $_componentTypes = array( self::COMPONENT_FACETSET => array( 'component' => 'Solarium_Query_Select_Component_FacetSet', 'requestbuilder' => 'Solarium_Client_RequestBuilder_Select_Component_FacetSet', 'responseparser' => 'Solarium_Client_ResponseParser_Select_Component_FacetSet', ), self::COMPONENT_DISMAX => array( 'component' => 'Solarium_Query_Select_Component_DisMax', 'requestbuilder' => 'Solarium_Client_RequestBuilder_Select_Component_DisMax', 'responseparser' => null, ), self::COMPONENT_MORELIKETHIS => array( 'component' => 'Solarium_Query_Select_Component_MoreLikeThis', 'requestbuilder' => 'Solarium_Client_RequestBuilder_Select_Component_MoreLikeThis', 'responseparser' => 'Solarium_Client_ResponseParser_Select_Component_MoreLikeThis', ), self::COMPONENT_HIGHLIGHTING => array( 'component' => 'Solarium_Query_Select_Component_Highlighting', 'requestbuilder' => 'Solarium_Client_RequestBuilder_Select_Component_Highlighting', 'responseparser' => 'Solarium_Client_ResponseParser_Select_Component_Highlighting', ), self::COMPONENT_GROUPING => array( 'component' => 'Solarium_Query_Select_Component_Grouping', 'requestbuilder' => 'Solarium_Client_RequestBuilder_Select_Component_Grouping', 'responseparser' => 'Solarium_Client_ResponseParser_Select_Component_Grouping', ), self::COMPONENT_SPELLCHECK => array( 'component' => 'Solarium_Query_Select_Component_Spellcheck', 'requestbuilder' => 'Solarium_Client_RequestBuilder_Select_Component_Spellcheck', 'responseparser' => 'Solarium_Client_ResponseParser_Select_Component_Spellcheck', ), self::COMPONENT_DISTRIBUTEDSEARCH => array( 'component' => 'Solarium_Query_Select_Component_DistributedSearch', 'requestbuilder' => 'Solarium_Client_RequestBuilder_Select_Component_DistributedSearch', 'responseparser' => null, ), self::COMPONENT_STATS => array( 'component' => 'Solarium_Query_Select_Component_Stats', 'requestbuilder' => 'Solarium_Client_RequestBuilder_Select_Component_Stats', 'responseparser' => 'Solarium_Client_ResponseParser_Select_Component_Stats', ), self::COMPONENT_DEBUG => array( 'component' => 'Solarium_Query_Select_Component_Debug', 'requestbuilder' => 'Solarium_Client_RequestBuilder_Select_Component_Debug', 'responseparser' => 'Solarium_Client_ResponseParser_Select_Component_Debug', ), ); /** * Fields to fetch * * @var array */ protected $_fields = array(); /** * Items to sort on * * @var array */ protected $_sorts = array(); /** * Filterqueries * * @var array */ protected $_filterQueries = array(); /** * Search components * * @var array */ protected $_components = array(); /** * Initialize options * * Several options need some extra checks or setup work, for these options * the setters are called. * * @return void */ protected function _init() { foreach ($this->_options AS $name => $value) { switch ($name) { case 'query': $this->setQuery($value); break; case 'filterquery': $this->addFilterQueries($value); break; case 'sort': $this->addSorts($value); break; case 'fields': $this->addFields($value); break; case 'rows': $this->setRows((int)$value); break; case 'start': $this->setStart((int)$value); break; case 'component': $this->_createComponents($value); break; } } } /** * Set the query string * * Overwrites the current value. You are responsible for the correct * escaping of user input. * * @param string $query * @param array $bind Bind values for placeholders in the query string * @return Solarium_Query_Select Provides fluent interface */ public function setQuery($query, $bind = null) { if (!is_null($bind)) { $query = $this->getHelper()->assemble($query, $bind); } return $this->_setOption('query', trim($query)); } /** * Get the query string * * @return string */ public function getQuery() { return $this->getOption('query'); } /** * Set default query operator * * Use one of the constants as value * * @param string $operator * @return Solarium_Query_Select Provides fluent interface */ public function setQueryDefaultOperator($operator) { return $this->_setOption('querydefaultoperator', $operator); } /** * Get the default query operator * * @return null|string */ public function getQueryDefaultOperator() { return $this->getOption('querydefaultoperator'); } /** * Set default query field * * @param string $field * @return Solarium_Query_Select Provides fluent interface */ public function setQueryDefaultField($field) { return $this->_setOption('querydefaultfield', $field); } /** * Get the default query field * * @return null|string */ public function getQueryDefaultField() { return $this->getOption('querydefaultfield'); } /** * Set the start offset * * @param integer $start * @return Solarium_Query_Select Provides fluent interface */ public function setStart($start) { return $this->_setOption('start', $start); } /** * Get the start offset * * @return integer */ public function getStart() { return $this->getOption('start'); } /** * Set a custom resultclass * * @param string $value classname * @return Solarium_Query_Select Provides fluent interface */ public function setResultClass($value) { return $this->_setOption('resultclass', $value); } /** * Get the current resultclass option * * The value is a classname, not an instance * * @return string */ public function getResultClass() { return $this->getOption('resultclass'); } /** * Set a custom document class * * @param string $value classname * @return Solarium_Query */ public function setDocumentClass($value) { return $this->_setOption('documentclass', $value); } /** * Get the current documentclass option * * The value is a classname, not an instance * * @return string */ public function getDocumentClass() { return $this->getOption('documentclass'); } /** * Set the number of rows to fetch * * @param integer $rows * @return Solarium_Query_Select Provides fluent interface */ public function setRows($rows) { return $this->_setOption('rows', $rows); } /** * Get the number of rows * * @return integer */ public function getRows() { return $this->getOption('rows'); } /** * Specify a field to return in the resultset * * @param string $field * @return Solarium_Query_Select Provides fluent interface */ public function addField($field) { $this->_fields[$field] = true; return $this; } /** * Specify multiple fields to return in the resultset * * @param string|array $fields can be an array or string with comma * separated fieldnames * * @return Solarium_Query_Select Provides fluent interface */ public function addFields($fields) { if (is_string($fields)) { $fields = explode(',', $fields); $fields = array_map('trim', $fields); } foreach ($fields AS $field) { $this->addField($field); } return $this; } /** * Remove a field from the field list * * @param string $field * @return Solarium_Query_Select Provides fluent interface */ public function removeField($field) { if (isset($this->_fields[$field])) { unset($this->_fields[$field]); } return $this; } /** * Remove all fields from the field list. * * @return Solarium_Query_Select Provides fluent interface */ public function clearFields() { $this->_fields = array(); return $this; } /** * Get the list of fields * * @return array */ public function getFields() { return array_keys($this->_fields); } /** * Set multiple fields * * This overwrites any existing fields * * @param array $fields * @return Solarium_Query_Select Provides fluent interface */ public function setFields($fields) { $this->clearFields(); $this->addFields($fields); return $this; } /** * Add a sort * * @param string $sort * @param string $order * @return Solarium_Query_Select Provides fluent interface */ public function addSort($sort, $order) { $this->_sorts[$sort] = $order; return $this; } /** * Add multiple sorts * * The input array must contain sort items as keys and the order as values. * * @param array $sorts * @return Solarium_Query_Select Provides fluent interface */ public function addSorts(array $sorts) { foreach ($sorts AS $sort => $order) { $this->addSort($sort, $order); } return $this; } /** * Remove a sort * * @param string $sort * @return Solarium_Query_Select Provides fluent interface */ public function removeSort($sort) { if (isset($this->_sorts[$sort])) { unset($this->_sorts[$sort]); } return $this; } /** * Remove all sorts * * @return Solarium_Query_Select Provides fluent interface */ public function clearSorts() { $this->_sorts = array(); return $this; } /** * Get a list of the sorts * * @return array */ public function getSorts() { return $this->_sorts; } /** * Set multiple sorts * * This overwrites any existing sorts * * @param array $sorts * @return Solarium_Query_Select Provides fluent interface */ public function setSorts($sorts) { $this->clearSorts(); $this->addSorts($sorts); return $this; } /** * Create a filterquery instance * * If you supply a string as the first arguments ($options) it will be used as the key for the filterquery * and it will be added to this query. * If you supply an options array/object that contains a key the filterquery will also be added to the query. * * When no key is supplied the filterquery cannot be added, in that case you will need to add it manually * after setting the key, by using the addFilterQuery method. * * @param mixed $options * @return Solarium_Query_Select_FilterQuery */ public function createFilterQuery($options = null) { if (is_string($options)) { $fq = new Solarium_Query_Select_FilterQuery; $fq->setKey($options); } else { $fq = new Solarium_Query_Select_FilterQuery($options); } if ($fq->getKey() !== null) { $this->addFilterQuery($fq); } return $fq; } /** * Add a filter query * * Supports a filterquery instance or a config array, in that case a new * filterquery instance wil be created based on the options. * * @param Solarium_Query_Select_FilterQuery|array $filterQuery * @return Solarium_Query_Select Provides fluent interface */ public function addFilterQuery($filterQuery) { if (is_array($filterQuery)) { $filterQuery = new Solarium_Query_Select_FilterQuery($filterQuery); } $key = $filterQuery->getKey(); if (0 === strlen($key)) { throw new Solarium_Exception('A filterquery must have a key value'); } //double add calls for the same FQ are ignored, but non-unique keys cause an exception //@todo add trigger_error with a notice for double add calls? if (array_key_exists($key, $this->_filterQueries) && $this->_filterQueries[$key] !== $filterQuery) { throw new Solarium_Exception('A filterquery must have a unique key value within a query'); } else { $this->_filterQueries[$key] = $filterQuery; } return $this; } /** * Add multiple filterqueries * * @param array $filterQueries * @return Solarium_Query_Select Provides fluent interface */ public function addFilterQueries(array $filterQueries) { foreach ($filterQueries AS $key => $filterQuery) { // in case of a config array: add key to config if (is_array($filterQuery) && !isset($filterQuery['key'])) { $filterQuery['key'] = $key; } $this->addFilterQuery($filterQuery); } return $this; } /** * Get a filterquery * * @param string $key * @return string */ public function getFilterQuery($key) { if (isset($this->_filterQueries[$key])) { return $this->_filterQueries[$key]; } else { return null; } } /** * Get all filterqueries * * @return array */ public function getFilterQueries() { return $this->_filterQueries; } /** * Remove a single filterquery * * You can remove a filterquery by passing it's key, or by passing the filterquery instance * * @param string|Solarium_Query_Select_FilterQuery $filterQuery * @return Solarium_Query_Select Provides fluent interface */ public function removeFilterQuery($filterQuery) { if (is_object($filterQuery)) { $filterQuery = $filterQuery->getKey(); } if (isset($this->_filterQueries[$filterQuery])) { unset($this->_filterQueries[$filterQuery]); } return $this; } /** * Remove all filterqueries * * @return Solarium_Query_Select Provides fluent interface */ public function clearFilterQueries() { $this->_filterQueries = array(); return $this; } /** * Set multiple filterqueries * * This overwrites any existing filterqueries * * @param array $filterQueries */ public function setFilterQueries($filterQueries) { $this->clearFilterQueries(); $this->addFilterQueries($filterQueries); } /** * Get all registered component types * * @return array */ public function getComponentTypes() { return $this->_componentTypes; } /** * Register a component type * * @param string $key * @param string $component * @param string $requestBuilder * @param string $responseParser * @return Solarium_Query_Select Provides fluent interface */ public function registerComponentType($key, $component, $requestBuilder=null, $responseParser=null) { $this->_componentTypes[$key] = array( 'component' => $component, 'requestbuilder' => $requestBuilder, 'responseparser' => $responseParser, ); return $this; } /** * Get all registered components * * @return array */ public function getComponents() { return $this->_components; } /** * Get a component instance by key * * You can optionally supply an autoload class to create a new component * instance if there is no registered component for the given key yet. * * @param string $key Use one of the constants * @param string $autoload Class to autoload if component needs to be created * @param array $config Configuration to use for autoload * @return object|null */ public function getComponent($key, $autoload = false, $config = null) { if (isset($this->_components[$key])) { return $this->_components[$key]; } else { if ($autoload == true) { if (!isset($this->_componentTypes[$key])) { throw new Solarium_Exception('Cannot autoload unknown component: ' . $key); } $className = $this->_componentTypes[$key]['component']; $component = new $className($config); $this->setComponent($key, $component); return $component; } return null; } } /** * Set a component instance * * This overwrites any existing component registered with the same key. * * @param string $key * @param object|null $value * @return Solarium_Query_Select Provides fluent interface */ public function setComponent($key, $value) { $this->_components[$key] = $value; return $this; } /** * Remove a component instance * * You can remove a component by passing it's key or the component instance * * @param string|Solarium_Query_Select_Component $component * @return Solarium_Query_Select Provides fluent interface */ public function removeComponent($component) { if (is_object($component)) { foreach ($this->_components as $key => $instance) { if ($instance === $component) { unset($this->_components[$key]); break; } } } else { if (isset($this->_components[$component])) { unset($this->_components[$component]); } } return $this; } /** * Build component instances based on config * * @param array $configs * @return void */ protected function _createComponents($configs) { foreach ($configs AS $type => $config) { $this->getComponent($type, true, $config); } } /** * Get a MoreLikeThis component instance * * This is a convenience method that maps presets to getComponent * * @return Solarium_Query_Select_Component_MoreLikeThis */ public function getMoreLikeThis() { return $this->getComponent(Solarium_Query_Select::COMPONENT_MORELIKETHIS, true); } /** * Get a FacetSet component instance * * This is a convenience method that maps presets to getComponent * * @return Solarium_Query_Select_Component_FacetSet */ public function getFacetSet() { return $this->getComponent(Solarium_Query_Select::COMPONENT_FACETSET, true); } /** * Get a DisMax component instance * * This is a convenience method that maps presets to getComponent * * @return Solarium_Query_Select_Component_DisMax */ public function getDisMax() { return $this->getComponent(Solarium_Query_Select::COMPONENT_DISMAX, true); } /** * Get a highlighting component instance * * This is a convenience method that maps presets to getComponent * * @return Solarium_Query_Select_Component_Highlighting */ public function getHighlighting() { return $this->getComponent(Solarium_Query_Select::COMPONENT_HIGHLIGHTING, true); } /** * Get a grouping component instance * * This is a convenience method that maps presets to getComponent * * @return Solarium_Query_Select_Component_Grouping */ public function getGrouping() { return $this->getComponent(Solarium_Query_Select::COMPONENT_GROUPING, true); } /** * Get a spellcheck component instance * * This is a convenience method that maps presets to getComponent * * @return Solarium_Query_Select_Component_Spellcheck */ public function getSpellcheck() { return $this->getComponent(Solarium_Query_Select::COMPONENT_SPELLCHECK, true); } /** * Get a DistributedSearch component instance * * This is a convenience method that maps presets to getComponent * * @return Solarium_Query_Select_Component_DistributedSearch */ public function getDistributedSearch() { return $this->getComponent(Solarium_Query_Select::COMPONENT_DISTRIBUTEDSEARCH, true); } /** * Get a Stats component instance * * This is a convenience method that maps presets to getComponent * * @return Solarium_Query_Select_Component_Stats */ public function getStats() { return $this->getComponent(Solarium_Query_Select::COMPONENT_STATS, true); } /** * Get a Debug component instance * * This is a convenience method that maps presets to getComponent * * @return Solarium_Query_Select_Component_Debug */ public function getDebug() { return $this->getComponent(Solarium_Query_Select::COMPONENT_DEBUG, true); } }