* @license http://github.com/basdenooijer/solarium/raw/master/COPYING
* @link http://www.solarium-project.org/
*
* @package Solarium
* @subpackage Client
*/
/**
* Main interface for interaction with Solr
*
* The client is the main interface for usage of the Solarium library.
* You can use it to get query instances and to execute them.
* It also allows to register plugins and querytypes to customize Solarium.
* Finally, it also gives access to the adapter, which holds the Solr connection settings.
*
* Example usage with default settings:
*
* $client = new Solarium_Client;
* $query = $client->createSelect();
* $result = $client->select($query);
*
*
* @package Solarium
* @subpackage Client
*/
class Solarium_Client extends Solarium_Configurable
{
/**
* Querytype select
*/
const QUERYTYPE_SELECT = 'select';
/**
* Querytype update
*/
const QUERYTYPE_UPDATE = 'update';
/**
* Querytype ping
*/
const QUERYTYPE_PING = 'ping';
/**
* Querytype morelikethis
*/
const QUERYTYPE_MORELIKETHIS = 'mlt';
/**
* Querytype analysis field
*/
const QUERYTYPE_ANALYSIS_FIELD = 'analysis-field';
/**
* Querytype analysis document
*/
const QUERYTYPE_ANALYSIS_DOCUMENT = 'analysis-document';
/**
* Querytype terms
*/
const QUERYTYPE_TERMS = 'terms';
/**
* Querytype suggester
*/
const QUERYTYPE_SUGGESTER = 'suggester';
/**
* Default options
*
* @var array
*/
protected $_options = array(
'adapter' => 'Solarium_Client_Adapter_Http',
);
/**
* Querytype mappings
*
* These can be customized using {@link registerQueryType()}
*/
protected $_queryTypes = array(
self::QUERYTYPE_SELECT => array(
'query' => 'Solarium_Query_Select',
'requestbuilder' => 'Solarium_Client_RequestBuilder_Select',
'responseparser' => 'Solarium_Client_ResponseParser_Select'
),
self::QUERYTYPE_UPDATE => array(
'query' => 'Solarium_Query_Update',
'requestbuilder' => 'Solarium_Client_RequestBuilder_Update',
'responseparser' => 'Solarium_Client_ResponseParser_Update'
),
self::QUERYTYPE_PING => array(
'query' => 'Solarium_Query_Ping',
'requestbuilder' => 'Solarium_Client_RequestBuilder_Ping',
'responseparser' => 'Solarium_Client_ResponseParser_Ping'
),
self::QUERYTYPE_MORELIKETHIS => array(
'query' => 'Solarium_Query_MoreLikeThis',
'requestbuilder' => 'Solarium_Client_RequestBuilder_MoreLikeThis',
'responseparser' => 'Solarium_Client_ResponseParser_MoreLikeThis'
),
self::QUERYTYPE_ANALYSIS_DOCUMENT => array(
'query' => 'Solarium_Query_Analysis_Document',
'requestbuilder' => 'Solarium_Client_RequestBuilder_Analysis_Document',
'responseparser' => 'Solarium_Client_ResponseParser_Analysis_Document'
),
self::QUERYTYPE_ANALYSIS_FIELD => array(
'query' => 'Solarium_Query_Analysis_Field',
'requestbuilder' => 'Solarium_Client_RequestBuilder_Analysis_Field',
'responseparser' => 'Solarium_Client_ResponseParser_Analysis_Field'
),
self::QUERYTYPE_TERMS => array(
'query' => 'Solarium_Query_Terms',
'requestbuilder' => 'Solarium_Client_RequestBuilder_Terms',
'responseparser' => 'Solarium_Client_ResponseParser_Terms'
),
self::QUERYTYPE_SUGGESTER => array(
'query' => 'Solarium_Query_Suggester',
'requestbuilder' => 'Solarium_Client_RequestBuilder_Suggester',
'responseparser' => 'Solarium_Client_ResponseParser_Suggester'
),
);
/**
* Plugin types
*
* @var array
*/
protected $_pluginTypes = array(
'loadbalancer' => 'Solarium_Plugin_Loadbalancer',
'postbigrequest' => 'Solarium_Plugin_PostBigRequest',
'customizerequest' => 'Solarium_Plugin_CustomizeRequest',
'parallelexecution' => 'Solarium_Plugin_ParallelExecution',
'bufferedadd' => 'Solarium_Plugin_BufferedAdd',
'prefetchiterator' => 'Solarium_Plugin_PrefetchIterator',
);
/**
* Registered plugin instances
*
* @var array
*/
protected $_pluginInstances = array();
/**
* Adapter instance
*
* If an adapter instance is set using {@link setAdapter()} this var will
* contain a reference to that instance.
*
* In all other cases the adapter is lazy-loading, it will be instantiated
* on first use by {@link getAdapter()} based on the 'adapter' entry in
* {@link $_options}. This option can be set using {@link setAdapter()}
*
* @var Solarium_Client_Adapter
*/
protected $_adapter;
/**
* Request builder instances
*
* @var array
*/
protected $_requestBuilders;
/**
* Initialization hook
*/
protected function _init()
{
foreach ($this->_options AS $name => $value) {
switch ($name) {
case 'querytype':
$this->registerQueryTypes($value);
break;
case 'plugin':
$this->registerPlugins($value);
break;
}
}
}
/**
* Set the adapter
*
* The adapter has to be a class that extends
* {@link Solarium_Client_Adapter}.
*
* If a string is passed it is assumed to be the classname and it will be
* instantiated on first use. This requires the availability of the class
* through autoloading or a manual require before calling this method.
* Any existing adapter instance will be removed by this method, this way an
* instance of the new adapter type will be created upon the next usage of
* the adapter (lazy-loading)
*
* If an adapter instance is passed it will replace the current adapter
* immediately, bypassing the lazy loading.
*
* @param string|Solarium_Client_Adapter $adapter
* @return Solarium_Client Provides fluent interface
*/
public function setAdapter($adapter)
{
if (is_string($adapter)) {
$this->_adapter = null;
return $this->_setOption('adapter', $adapter);
} else {
// forward options
$adapter->setOptions($this->_options);
// overwrite existing adapter
$this->_adapter = $adapter;
return $this;
}
}
/**
* Create an adapter instance
*
* The 'adapter' entry in {@link $_options} will be used to create an
* adapter instance. This entry can be the default value of
* {@link $_options}, a value passed to the constructor or a value set by
* using {@link setAdapter()}
*
* This method is used for lazy-loading the adapter upon first use in
* {@link getAdapter()}
*
* @return void
*/
protected function _createAdapter()
{
$adapterClass = $this->getOption('adapter');
$this->_adapter = new $adapterClass;
$this->_adapter->setOptions($this->getOption('adapteroptions'));
}
/**
* Get the adapter instance
*
* If {@see $_adapter} doesn't hold an instance a new one will be created by
* calling {@see _createAdapter()}
*
* @param boolean $autoload
* @return Solarium_Client_Adapter
*/
public function getAdapter($autoload = true)
{
if (null === $this->_adapter && $autoload) {
$this->_createAdapter();
}
return $this->_adapter;
}
/**
* Register a querytype
*
* You can also use this method to override any existing querytype with a new mapping.
* This requires the availability of the classes through autoloading or a manual
* require before calling this method.
*
* @param string $type
* @param string $query
* @param string|object $requestBuilder
* @param string|object $responseParser
* @return Solarium_Client Provides fluent interface
*/
public function registerQueryType($type, $query, $requestBuilder, $responseParser)
{
$this->_queryTypes[$type] = array(
'query' => $query,
'requestbuilder' => $requestBuilder,
'responseparser' => $responseParser,
);
return $this;
}
/**
* Register multiple querytypes
*
* @param array $queryTypes
* @return Solarium_Client Provides fluent interface
*/
public function registerQueryTypes($queryTypes)
{
foreach ($queryTypes as $type => $queryType) {
if (!isset($queryType['type'])) $queryType['type'] = $type;
$this->registerQueryType(
$queryType['type'],
$queryType['query'],
$queryType['requestbuilder'],
$queryType['responseparser']
);
}
}
/**
* Get all registered querytypes
*
* @return array
*/
public function getQueryTypes()
{
return $this->_queryTypes;
}
/**
* Register a plugin
*
* You can supply a plugin instance or a plugin classname as string.
* This requires the availability of the class through autoloading
* or a manual require.
*
* @param string $key
* @param string|Solarium_Plugin_Abstract $plugin
* @param array $options
* @return Solarium_Client Provides fluent interface
*/
public function registerPlugin($key, $plugin, $options = array())
{
if (is_string($plugin)) {
$plugin = new $plugin;
}
if (!($plugin instanceof Solarium_Plugin_Abstract)) {
throw new Solarium_Exception('All plugins must extend Solarium_Plugin_Abstract');
}
$plugin->init($this, $options);
$this->_pluginInstances[$key] = $plugin;
return $this;
}
/**
* Register multiple plugins
*
* @param array $plugins
* @return Solarium_Client Provides fluent interface
*/
public function registerPlugins($plugins)
{
foreach ($plugins as $key => $plugin) {
if (!isset($plugin['key'])) $plugin['key'] = $key;
$this->registerPlugin(
$plugin['key'],
$plugin['plugin'],
$plugin['options']
);
}
return $this;
}
/**
* Get all registered plugins
*
* @return array
*/
public function getPlugins()
{
return $this->_pluginInstances;
}
/**
* Get a plugin instance
*
* @param string $key
* @param boolean $autocreate
* @return Solarium_Plugin_Abstract|null
*/
public function getPlugin($key, $autocreate = true)
{
if (isset($this->_pluginInstances[$key])) {
return $this->_pluginInstances[$key];
} elseif ($autocreate) {
if (array_key_exists($key, $this->_pluginTypes)) {
$this->registerPlugin($key, $this->_pluginTypes[$key]);
return $this->_pluginInstances[$key];
} else {
throw new Solarium_Exception('Cannot autoload plugin of unknown type: ' . $key);
}
} else {
return null;
}
}
/**
* Remove a plugin instance
*
* You can remove a plugin by passing the plugin key, or the plugin instance
*
* @param string|Solarium_Plugin_Abstract $plugin
* @return Solarium_Client Provides fluent interface
*/
public function removePlugin($plugin)
{
if (is_object($plugin)) {
foreach ($this->_pluginInstances as $key => $instance) {
if ($instance === $plugin) {
unset($this->_pluginInstances[$key]);
break;
}
}
} else {
if (isset($this->_pluginInstances[$plugin])) {
unset($this->_pluginInstances[$plugin]);
}
}
return $this;
}
/**
* Trigger external events for plugins
*
* This methods adds 'namespacing' to the event name to prevent conflicts with Solariums internal event keys.
*
* Based on the event name you can always tell if an event was internal (Solarium base classes)
* or external (plugins, even if it's a plugin included with Solarium).
*
* External events always have the 'event' prefix in the event name.
*
* @param string $event
* @param array $params
* @param bool $resultOverride
* @return void|mixed
*/
public function triggerEvent($event, $params = array(), $resultOverride = false)
{
// Add namespacing
$event = 'event'.$event;
return $this->_callPlugins($event, $params, $resultOverride);
}
/**
* Forward events to plugins
*
* @param string $event
* @param array $params
* @param bool $resultOverride
* @return void|mixed
*/
protected function _callPlugins($event, $params, $resultOverride = false)
{
foreach ($this->_pluginInstances AS $plugin) {
if (method_exists($plugin, $event)) {
$result = call_user_func_array(array($plugin, $event), $params);
if ($result !== null && $resultOverride) {
return $result;
}
}
}
}
/**
* Creates a request based on a query instance
*
* @param Solarium_Query $query
* @return Solarium_Client_Request
*/
public function createRequest($query)
{
$pluginResult = $this->_callPlugins('preCreateRequest', array($query), true);
if($pluginResult !== null) return $pluginResult;
$queryType = $query->getType();
if (!isset($this->_queryTypes[$queryType])) {
throw new Solarium_Exception('No requestbuilder registered for querytype: '. $queryType);
}
$requestBuilder = $this->_queryTypes[$queryType]['requestbuilder'];
if (is_string($requestBuilder)) {
$requestBuilder = new $requestBuilder;
}
$request = $requestBuilder->build($query);
$this->_callPlugins('postCreateRequest', array($query, $request));
return $request;
}
/**
* Creates a result object
*
* @param Solarium_Query $query
* @param array Solarium_Client_Response $response
* @return Solarium_Result
*/
public function createResult($query, $response)
{
$pluginResult = $this->_callPlugins('preCreateResult', array($query, $response), true);
if($pluginResult !== null) return $pluginResult;
$resultClass = $query->getResultClass();
$result = new $resultClass($this, $query, $response);
$this->_callPlugins('postCreateResult', array($query, $response, $result));
return $result;
}
/**
* Execute a query
*
* @param Solarium_Query
* @return Solarium_Result
*/
public function execute($query)
{
$pluginResult = $this->_callPlugins('preExecute', array($query), true);
if($pluginResult !== null) return $pluginResult;
$request = $this->createRequest($query);
$response = $this->executeRequest($request);
$result = $this->createResult($query, $response);
$this->_callPlugins('postExecute', array($query, $result));
return $result;
}
/**
* Execute a request and return the response
*
* @param Solarium_Client_Request
* @return Solarium_Client_Response
*/
public function executeRequest($request)
{
$pluginResult = $this->_callPlugins('preExecuteRequest', array($request), true);
if ($pluginResult !== null) {
$response = $pluginResult; //a plugin result overrules the standard execution result
} else {
$response = $this->getAdapter()->execute($request);
}
$this->_callPlugins('postExecuteRequest', array($request, $response));
return $response;
}
/**
* Execute a ping query
*
* Example usage:
*
* $client = new Solarium_Client;
* $query = $client->createPing();
* $result = $client->ping($query);
*
*
* @see Solarium_Query_Ping
*
* @internal This is a convenience method that forwards the query to the
* execute method, thus allowing for an easy to use and clean API.
*
* @param Solarium_Query_Ping $query
* @return Solarium_Result_Ping
*/
public function ping($query)
{
return $this->execute($query);
}
/**
* Execute an update query
*
* Example usage:
*
* $client = new Solarium_Client;
* $query = $client->createUpdate();
* $update->addOptimize();
* $result = $client->update($update);
*
*
* @see Solarium_Query_Update
* @see Solarium_Result_Update
*
* @internal This is a convenience method that forwards the query to the
* execute method, thus allowing for an easy to use and clean API.
*
* @param Solarium_Query_Update $query
* @return Solarium_Result_Update
*/
public function update($query)
{
return $this->execute($query);
}
/**
* Execute a select query
*
* Example usage:
*
* $client = new Solarium_Client;
* $query = $client->createSelect();
* $result = $client->select($query);
*
*
* @see Solarium_Query_Select
* @see Solarium_Result_Select
*
* @internal This is a convenience method that forwards the query to the
* execute method, thus allowing for an easy to use and clean API.
*
* @param Solarium_Query_Select $query
* @return Solarium_Result_Select
*/
public function select($query)
{
return $this->execute($query);
}
/**
* Execute a MoreLikeThis query
*
* Example usage:
*
* $client = new Solarium_Client;
* $query = $client->createMoreLikeThis();
* $result = $client->moreLikeThis($query);
*
*
* @see Solarium_Query_MoreLikeThis
* @see Solarium_Result_MoreLikeThis
*
* @internal This is a convenience method that forwards the query to the
* execute method, thus allowing for an easy to use and clean API.
*
* @param Solarium_Query_MoreLikeThis $query
* @return Solarium_Result_MoreLikeThis
*/
public function moreLikeThis($query)
{
return $this->execute($query);
}
/**
* Execute an analysis query
*
* @internal This is a convenience method that forwards the query to the
* execute method, thus allowing for an easy to use and clean API.
*
* @param Solarium_Query_Analysis_Document|Solarium_Query_Analysis_Field $query
* @return Solarium_Result_Analysis_Document|Solarium_Result_Analysis_Field
*/
public function analyze($query)
{
return $this->execute($query);
}
/**
* Execute a terms query
*
* @internal This is a convenience method that forwards the query to the
* execute method, thus allowing for an easy to use and clean API.
*
* @param Solarium_Query_Terms $query
* @return Solarium_Result_Terms
*/
public function terms($query)
{
return $this->execute($query);
}
/**
* Execute a suggester query
*
* @internal This is a convenience method that forwards the query to the
* execute method, thus allowing for an easy to use and clean API.
*
* @param Solarium_Query_Suggester $query
* @return Solarium_Result_Suggester
*/
public function suggester($query)
{
return $this->execute($query);
}
/**
* Create a query instance
*
* @param string $type
* @param array $options
* @return Solarium_Query
*/
public function createQuery($type, $options = null)
{
$type = strtolower($type);
$pluginResult = $this->_callPlugins('preCreateQuery', array($type, $options), true);
if($pluginResult !== null) return $pluginResult;
if (!isset($this->_queryTypes[$type])) {
throw new Solarium_Exception('Unknown querytype: '. $type);
}
$class = $this->_queryTypes[$type]['query'];
$query = new $class($options);
$this->_callPlugins('postCreateQuery', array($type, $options, $query));
return $query;
}
/**
* Create a select query instance
*
* @param mixed $options
* @return Solarium_Query_Select
*/
public function createSelect($options = null)
{
return $this->createQuery(self::QUERYTYPE_SELECT, $options);
}
/**
* Create a MoreLikeThis query instance
*
* @param mixed $options
* @return Solarium_Query_MoreLikeThis
*/
public function createMoreLikeThis($options = null)
{
return $this->createQuery(self::QUERYTYPE_MORELIKETHIS, $options);
}
/**
* Create an update query instance
*
* @param mixed $options
* @return Solarium_Query_Update
*/
public function createUpdate($options = null)
{
return $this->createQuery(self::QUERYTYPE_UPDATE, $options);
}
/**
* Create a ping query instance
*
* @param mixed $options
* @return Solarium_Query_Ping
*/
public function createPing($options = null)
{
return $this->createQuery(self::QUERYTYPE_PING, $options);
}
/**
* Create an analysis field query instance
*
* @param mixed $options
* @return Solarium_Query_Analysis_Field
*/
public function createAnalysisField($options = null)
{
return $this->createQuery(self::QUERYTYPE_ANALYSIS_FIELD, $options);
}
/**
* Create an analysis document query instance
*
* @param mixed $options
* @return Solarium_Query_Analysis_Document
*/
public function createAnalysisDocument($options = null)
{
return $this->createQuery(self::QUERYTYPE_ANALYSIS_DOCUMENT, $options);
}
/**
* Create a terms query instance
*
* @param mixed $options
* @return Solarium_Query_Terms
*/
public function createTerms($options = null)
{
return $this->createQuery(self::QUERYTYPE_TERMS, $options);
}
/**
* Create a suggester query instance
*
* @param mixed $options
* @return Solarium_Query_Suggester
*/
public function createSuggester($options = null)
{
return $this->createQuery(self::QUERYTYPE_SUGGESTER, $options);
}
}