Commit 200e6067 authored by Bas de Nooijer's avatar Bas de Nooijer

Merge branch 'release/1.0.0'

parents b270e9e7 6754be6e
Solarium is a PHP Solr client that not only facilitates Solr communication but
also tries to accurately model Solr concepts.
Please see the wiki for a more detailed description.
Please see the project website for a more detailed description.
Project page:
Project website:
http://www.solarium-project.org/
GitHub project:
http://github.com/basdenooijer/solarium
License:
See the COPYING file or view online
https://github.com/basdenooijer/solarium/blob/master/COPYING
Wiki:
https://github.com/basdenooijer/solarium/wiki
Issue tracker:
http://github.com/basdenooijer/solarium/issues
......@@ -19,4 +19,4 @@ Contributors:
https://github.com/basdenooijer/solarium/contributors
API docs:
http://solarium.raspberry.nl/api/
\ No newline at end of file
http://api.solarium-project.org/
\ No newline at end of file
<?php
/**
* Copyright 2011 Bas de Nooijer. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this listof conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are
* those of the authors and should not be interpreted as representing official
* policies, either expressed or implied, of the copyright holder.
*
* @copyright Copyright 2011 Bas de Nooijer <solarium@raspberry.nl>
* @license http://github.com/basdenooijer/solarium/raw/master/COPYING
*
* @package Solarium
*/
/**
* Autoloader
*
* This class is included to allow for easy usage of Solarium. If you already
* have your own autoloader that follows the Zend Framework class/file naming
* you don't need this.
*
* @package Solarium
*/
class Solarium_Autoloader
{
/**
* Register the Solarium autoloader
*
* The autoloader only acts for classnames that start with 'Solarium'. It
* will be appended to any other autoloaders already registered.
*
* Using this autoloader will disable any existing __autoload function. If
* you want to use multiple autoloaders please use spl_autoload_register.
*
* @static
* @return void
*/
static public function register()
{
spl_autoload_register(array(new self, 'load'));
}
/**
* Autoload a class
*
* This method is automatically called after registering this autoloader.
* The autoloader only acts for classnames that start with 'Solarium'.
*
* @static
* @param string $class
* @return void
*/
static public function load($class)
{
if (substr($class, 0, 8) == 'Solarium') {
$class = str_replace(
array('Solarium', '_'),
array('', '/'),
$class
);
$file = dirname(__FILE__) . '/' . $class . '.php';
require($file);
}
}
}
\ No newline at end of file
......@@ -283,6 +283,32 @@ class Solarium_Client extends Solarium_Configurable
return $this->_adapter;
}
/**
* Set adapter options
*
* @param array|object $options
* @return Solarium_Client Provides fluent interface
*/
public function setAdapterOptions($options)
{
// covert config object into an array if needed
if (is_object($options)) {
$options = $options->toArray();
}
return $this->_setOption('adapteroptions', $options);
}
/**
* Get adapteroptions
*
* @return array
*/
public function getAdapterOptions()
{
return $this->getOption('adapteroptions');
}
/**
* Execute a ping query
*
......
......@@ -77,9 +77,18 @@ class Solarium_Client_Adapter_ZendHttp extends Solarium_Client_Adapter_Http
{
parent::setOptions($options);
if (null !== $this->_zendHttp
&& isset($this->_options['adapteroptions'])) {
$this->_zendHttp->setOptions($this->_options['adapteroptions']);
// forward options to zendHttp instance
if (null !== $this->_zendHttp) {
// forward timeout setting
$this->_zendHttp->setConfig(
array('timeout' => $this->getOption('timeout'))
);
// forward adapter options if available
if (isset($this->_options['adapteroptions'])) {
$this->_zendHttp->setConfig($this->_options['adapteroptions']);
}
}
return $this;
......
......@@ -171,7 +171,7 @@ abstract class Solarium_Client_Request
return 'http://' . $this->_options['host'] . ':'
. $this->_options['port'] . $this->_options['path']
. $core . $this->_query->getOption('path') . '?'
. $core . '/' . $this->_query->getHandler() . '?'
. $queryString;
}
......
......@@ -128,10 +128,15 @@ class Solarium_Client_Request_Update extends Solarium_Client_Request
$xml .= '>';
foreach ($doc->getFields() AS $name => $value) {
$xml .= '<field name="' . $name . '"';
$xml .= $this->attrib('boost', $doc->getFieldBoost($name));
$xml .= '>' . htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8')
. '</field>';
$boost = $doc->getFieldBoost($name);
if(is_array($value))
{
foreach ($value AS $multival) {
$xml .= $this->_buildFieldXml($name, $boost, $multival);
}
} else {
$xml .= $this->_buildFieldXml($name, $boost, $value);
}
}
$xml .= '</doc>';
......@@ -142,6 +147,26 @@ class Solarium_Client_Request_Update extends Solarium_Client_Request
return $xml;
}
/**
* Build XML for a field
*
* Used in the add command
*
* @param string $name
* @param float $boost
* @param mixed $value
* @return string
*/
protected function _buildFieldXml($name, $boost, $value)
{
$xml = '<field name="' . $name . '"';
$xml .= $this->attrib('boost', $boost);
$xml .= '>' . htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8');
$xml .= '</field>';
return $xml;
}
/**
* Build XML for a delete command
*
......
......@@ -44,7 +44,8 @@
* @package Solarium
* @subpackage Document
*/
class Solarium_Document_ReadOnly implements IteratorAggregate, Countable
class Solarium_Document_ReadOnly
implements IteratorAggregate, Countable, ArrayAccess
{
/**
......@@ -129,4 +130,49 @@ class Solarium_Document_ReadOnly implements IteratorAggregate, Countable
return count($this->_fields);
}
/**
* ArrayAccess implementation
*
* @param miex $offset
* @param mixed $value
* @return void
*/
public function offsetSet($offset, $value)
{
$this->__set($offset, $value);
}
/**
* ArrayAccess implementation
*
* @param mixed $offset
* @return bool
*/
public function offsetExists($offset)
{
return ($this->__get($offset) !== null);
}
/**
* ArrayAccess implementation
*
* @param mixed $offset
* @return void
*/
public function offsetUnset($offset)
{
$this->__set($offset, null);
}
/**
* ArrayAccess implementation
*
* @param mixed $offset
* @return mixed|null
*/
public function offsetGet($offset)
{
return $this->__get($offset);
}
}
\ No newline at end of file
......@@ -112,7 +112,9 @@ class Solarium_Document_ReadWrite extends Solarium_Document_ReadOnly
/**
* Set a field value
*
* If a field already has a value it will be overwritten.
* If a field already has a value it will be overwritten. You cannot use
* this method for a multivalue field.
* If you supply NULL as the value the field will be removed
*
* @param string $key
* @param mixed $value
......@@ -121,8 +123,12 @@ class Solarium_Document_ReadWrite extends Solarium_Document_ReadOnly
*/
public function setField($key, $value, $boost = null)
{
$this->_fields[$key] = $value;
$this->setFieldBoost($key, $boost);
if ($value == null) {
$this->removeField($key);
} else {
$this->_fields[$key] = $value;
$this->setFieldBoost($key, $boost);
}
return $this;
}
......@@ -202,8 +208,12 @@ class Solarium_Document_ReadWrite extends Solarium_Document_ReadOnly
* Magic method for setting fields as properties of this document
* object, by field name.
*
* If you supply NULL as the value the field will be removed
* If you supply an array a multivalue field will be created.
* In all cases any existing (multi)value will be overwritten.
*
* @param string $name
* @param string $value
* @param string|null $value
* @return void
*/
public function __set($name, $value)
......@@ -211,4 +221,17 @@ class Solarium_Document_ReadWrite extends Solarium_Document_ReadOnly
$this->setField($name, $value);
}
/**
* Unset field value
*
* Magic method for removing fields by unsetting object properties
*
* @param string $name
* @return void
*/
public function __unset($name)
{
$this->removeField($name);
}
}
\ No newline at end of file
......@@ -65,7 +65,7 @@ class Solarium_Escape
*/
static public function term($input)
{
$pattern = '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/';
$pattern = '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/';
return preg_replace($pattern, '\\\$1', $input);
}
......
......@@ -45,24 +45,24 @@ class Solarium_Query extends Solarium_Configurable
{
/**
* Set path option
* Set handler option
*
* @param string $path
* @param string $handler
* @return Solarium_Query Provides fluent interface
*/
public function setPath($path)
public function setHandler($handler)
{
return $this->_setOption('path', $path);
return $this->_setOption('handler', $handler);
}
/**
* Get path option
* Get handler option
*
* @return string
*/
public function getPath()
public function getHandler()
{
return $this->getOption('path');
return $this->getOption('handler');
}
/**
......
......@@ -54,7 +54,7 @@ class Solarium_Query_Ping extends Solarium_Query
* @var array
*/
protected $_options = array(
'path' => '/admin/ping',
'handler' => 'admin/ping',
);
}
\ No newline at end of file
......@@ -60,7 +60,7 @@ class Solarium_Query_Select extends Solarium_Query
* @var array
*/
protected $_options = array(
'path' => '/select',
'handler' => 'select',
'resultclass' => 'Solarium_Result_Select',
'documentclass' => 'Solarium_Document_ReadOnly',
'query' => '*:*',
......@@ -453,7 +453,13 @@ class Solarium_Query_Select extends Solarium_Query
*/
public function addFilterQueries(array $filterQueries)
{
foreach ($filterQueries AS $filterQuery) {
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);
}
......@@ -560,7 +566,13 @@ class Solarium_Query_Select extends Solarium_Query
*/
public function addFacets(array $facets)
{
foreach ($facets AS $facet) {
foreach ($facets AS $key => $facet) {
// in case of a config array: add key to config
if (is_array($facet) && !isset($facet['key'])) {
$facet['key'] = $key;
}
$this->addFacet($facet);
}
......
......@@ -76,7 +76,11 @@ abstract class Solarium_Query_Select_Facet extends Solarium_Configurable
{
foreach ($this->_options AS $name => $value) {
switch ($name) {
case 'key':
$this->setKey($value);
break;
case 'exclude':
if(!is_array($value)) $value = array($value);
$this->setExcludes($value);
break;
}
......
......@@ -70,8 +70,12 @@ class Solarium_Query_Select_FilterQuery extends Solarium_Configurable
foreach ($this->_options AS $name => $value) {
switch ($name) {
case 'tag':
if(!is_array($value)) $value = array($value);
$this->addTags($value);
break;
case 'key':
$this->setKey($value);
break;
case 'query':
$this->setQuery($value);
break;
......
......@@ -54,10 +54,8 @@ class Solarium_Query_Update extends Solarium_Query
* @var array
*/
protected $_options = array(
'path' => '/update',
'resultclass' => 'Solarium_Result_Update',
'override' => null,
'commitwithin' => null,
'handler' => 'update',
'resultclass' => 'Solarium_Result_Update',
);
/**
......@@ -70,6 +68,41 @@ class Solarium_Query_Update extends Solarium_Query
*/
protected $_commands = array();
/**
* Initialize options
*
* Several options need some extra checks or setup work, for these options
* the setters are called.
*
* @return void
*/
protected function _init()
{
if (isset($this->_options['command'])) {
foreach ($this->_options['command'] as $key => $value) {
switch ($value['type']) {
case 'delete':
$command = new Solarium_Query_Update_Command_Delete($value);
break;
case 'commit':
$command = new Solarium_Query_Update_Command_Commit($value);
break;
case 'optimize':
$command = new Solarium_Query_Update_Command_Optimize($value);
break;
case 'rollback':
$command = new Solarium_Query_Update_Command_Rollback($value);
break;
case 'add':
throw new Solarium_Exception("Adding documents is not supported in configuration, use the API for this");
}
$this->add($key, $command);
}
}
}
/**
* Get all commands for this update query
*
......
......@@ -69,7 +69,7 @@ class Solarium_Version
*
* @var string
*/
const VERSION = '1.2.3';
const VERSION = '1.0.0';
/**
......
......@@ -103,6 +103,19 @@ class Solarium_Client_Request_UpdateTest extends PHPUnit_Framework_TestCase
);
}
public function testBuildAddXmlMultivalueField()
{
$command = new Solarium_Query_Update_Command_Add;
$command->addDocument(new Solarium_Document_ReadWrite(array('id' => array(1,2,3), 'text' => 'test < 123 > test')));
$request = new Solarium_Client_Request_Update($this->_options, $this->_query);
$this->assertEquals(
'<add><doc><field name="id">1</field><field name="id">2</field><field name="id">3</field><field name="text">test &lt; 123 &gt; test</field></doc></add>',
$request->buildAddXml($command)
);
}
public function testBuildAddXmlSingleDocumentWithBoost()
{
$doc = new Solarium_Document_ReadWrite(array('id' => 1));
......
......@@ -44,7 +44,7 @@ class Solarium_Client_RequestTest extends PHPUnit_Framework_TestCase
protected function _getRequest($options, $class = 'Solarium_Client_Request_Ping')
{
$query = new Solarium_Query;
$query->setPath('/mypath');
$query->setHandler('mypath');
return new $class($options, $query);
}
......
......@@ -165,7 +165,27 @@ class Solarium_ClientTest extends PHPUnit_Framework_TestCase
$client->setAdapter($observer);
$client->update($query);
}
public function testSetAndGetAdapterOptions()
{
$options = array('useragent' => 'myAgent');
$client = new Solarium_Client();
$client->setAdapterOptions($options);
$this->assertEquals($options, $client->getAdapterOptions());
}
public function testSetAndGetAdapterOptionsWithObject()
{
$options = array('useragent' => 'myAgent');
$optionObject = new myConfig($options);
$client = new Solarium_Client();
$client->setAdapterOptions($optionObject);
$this->assertEquals($options, $client->getAdapterOptions());
}
}
class MyAdapter extends Solarium_Client_Adapter_Http{
......@@ -182,4 +202,19 @@ class MyAdapter extends Solarium_Client_Adapter_Http{
{
}
}
class myConfig{
protected $_options;
public function __construct($options)
{
$this->_options = $options;
}
public function toArray()
{
return $this->_options;
}
}
\ No newline at end of file
......@@ -83,6 +83,48 @@ class Solarium_Document_ReadOnlyTest extends PHPUnit_Framework_TestCase
$this->assertEquals($this->_fields, $fields);
}
public function testArrayGet()
{
$this->assertEquals(
$this->_fields['categories'],
$this->_doc['categories']
);
}
public function testArrayGetInvalidField()
{
$this->assertEquals(
null,
$this->_doc['invalidfield']
);
}
public function testArrayIsset()
{
$this->assertTrue(
isset($this->_doc['categories'])
);
}
public function testArrayIssetInvalidField()
{
$this->assertFalse(
isset($this->_doc['invalidfield'])
);
}
public function testArraySet()
{
$this->setExpectedException('Solarium_Exception');
$this->_doc['newField'] = 'new value';
}
public function testArrayUnset()
{
$this->setExpectedException('Solarium_Exception');
unset($this->_doc['newField']);
}
public function testCount()
{
$this->assertEquals(count($this->_fields), count($this->_doc));
......
......@@ -141,6 +141,19 @@ class Solarium_Document_ReadWriteTest extends PHPUnit_Framework_TestCase
);
}
public function testRemoveFieldBySettingToNull()
{
$this->_doc->setField('name', NULL);
$expectedFields = $this->_fields;
unset($expectedFields['name']);
$this->assertEquals(
$expectedFields,
$this->_doc->getFields()
);
}
public function testRemoveFieldBoostRemoval()
{
$this->_doc->setFieldBoost('name',3.2);
......@@ -198,5 +211,66 @@ class Solarium_Document_ReadWriteTest extends PHPUnit_Framework_TestCase
$this->_doc->name
);
}
public function testSetAndGetMultivalueFieldByProperty()
{
$values = array('test1', 'test2', 'test3');
$this->_doc->multivaluefield = $values;
$this->assertEquals(
$values,
$this->_doc->multivaluefield
);
}
public function testSetAndGetMultivalueFieldByPropertyOverwrite()
{
$values = array('test1', 'test2', 'test3');
$this->_doc->name = $values;
$this->assertEquals(
$values,
$this->_doc->name
);
}
public function testUnsetFieldByProperty()
{
unset($this->_doc->name);
$expectedFields = $this->_fields;
unset($expectedFields['name']);
$this->assertEquals(
$expectedFields,
$this->_doc->getFields()
);
}
public function testSetFieldAsArray()
{
$this->_doc['name'] = 'newname';
$expectedFields = $this->_fields;
$expectedFields['name'] = 'newname';
$this->assertEquals(
$expectedFields,
$this->_doc->getFields()
);
}
public function testRemoveFieldAsArray()
{
unset($this->_doc['name']);
$expectedFields = $this->_fields;
unset($expectedFields['name']);
$this->assertEquals(
$expectedFields,
$this->_doc->getFields()
);
}
}
......@@ -83,6 +83,12 @@ class Solarium_Query_Select_FacetTest extends PHPUnit_Framework_TestCase
$this->_facet = new TestFacet(array('exclude' => array('e1','e2')));
$this->assertEquals(array('e1','e2'), $this->_facet->getExcludes());
}
public function testConstructorWithConfigSingleValueExclude()
{
$this->_facet = new TestFacet(array('exclude' => 'e1'));
$this->assertEquals(array('e1'), $this->_facet->getExcludes());
}
}
class TestFacet extends Solarium_Query_Select_Facet{
......
......@@ -83,4 +83,22 @@ class Solarium_Query_Select_FilterQueryTest extends PHPUnit_Framework_TestCase
$this->_filterQuery->setTags(array('t3','t4'));
$this->assertEquals(array('t3','t4'), $this->_filterQuery->getTags());
}
public function testConstructorWithConfig()
{
$fq = new Solarium_Query_Select_FilterQuery(array('tag' => array('t1','t2'),'key' => 'k1','query'=> 'id:[10 TO 20]'));
$this->assertEquals(array('t1','t2'), $fq->getTags());
$this->assertEquals('k1', $fq->getKey());
$this->assertEquals('id:[10 TO 20]', $fq->getQuery());
}
public function testConstructorWithConfigSingleValueTag()
{
$fq = new Solarium_Query_Select_FilterQuery(array('tag' => 't1','key' => 'k1','query'=> 'id:[10 TO 20]'));
$this->assertEquals(array('t1'), $fq->getTags());
$this->assertEquals('k1', $fq->getKey());
$this->assertEquals('id:[10 TO 20]', $fq->getQuery());
}
}
......@@ -490,11 +490,12 @@ class Solarium_Query_SelectTest extends PHPUnit_Framework_TestCase
'rows' => 100,
'start' => 200,
'filterquery' => array(
array('key' => 'pub', 'tag' => array('pub'),'query' => 'published:true')
array('key' => 'pub', 'tag' => array('pub'),'query' => 'published:true'),
'online' => array('tag' => 'onl','query' => 'online:true')
),
'facet' => array(
array('type' => 'field', 'key' => 'categories', 'field' => 'category'),
array('type' => 'query', 'key' => 'category13', 'query' => 'category:13')
'category13' => array('type' => 'query', 'query' => 'category:13')
),
);
$query = new Solarium_Query_Select($config);
......
......@@ -270,4 +270,93 @@ class Solarium_Query_UpdateTest extends PHPUnit_Framework_TestCase
);
}
public function testConstructorWithConfig()
{
$config = array(
'command' => array(
'key1' => array(
'type' => 'delete',
'query' => 'population:[* TO 1000]',
'id' => array(1,2),
),
'key2' => array(
'type' => 'commit',
'waitflush' => true,
'waitsearcher' => false,
'expungedeletes' => true,
),
'key3' => array(
'type' => 'optimize',
'waitflush' => true,
'waitsearcher' => false,
'maxsegments' => 5,
),
'key4' => array(
'type' => 'rollback',
)
)
);
$query = new Solarium_Query_Update($config);
$commands = $query->getCommands();
$delete = $commands['key1'];
$this->assertEquals(
array(1,2),
$delete->getIds()
);
$this->assertEquals(
array('population:[* TO 1000]'),
$delete->getQueries()
);
$commit = $commands['key2'];
$this->assertEquals(
true,
$commit->getWaitFlush()
);
$this->assertEquals(
false,
$commit->getWaitSearcher()
);
$this->assertEquals(
true,
$commit->getExpungeDeletes()
);
$optimize = $commands['key3'];
$this->assertEquals(
true,
$optimize->getWaitFlush()
);
$this->assertEquals(
false,
$optimize->getWaitSearcher()
);
$this->assertEquals(
5,
$optimize->getMaxSegments()
);
$rollback = $commands['key4'];
$this->assertEquals(
'Solarium_Query_Update_Command_Rollback',
get_class($rollback)
);
}
public function testConstructorWithConfigAddCommand()
{
$config = array(
'command' => array(
'key1' => array(
'type' => 'add',
),
)
);
$this->setExpectedException('Solarium_Exception');
new Solarium_Query_Update($config);
}
}
......@@ -32,11 +32,11 @@
class Solarium_QueryTest extends PHPUnit_Framework_TestCase
{
public function testSetAndGetPath()
public function testSetAndGetHandler()
{
$query = new Solarium_Query;
$query->setPath('mypath');
$this->assertEquals('mypath', $query->getPath());
$query->setHandler('myhandler');
$this->assertEquals('myhandler', $query->getHandler());
}
public function testSetAndGetResultClass()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment