Commit c00e7231 authored by Bas de Nooijer's avatar Bas de Nooijer

Merge branch 'atomic-updates' of github.com:relwell/solarium into develop

Refactored the code into the existing document class, to prevent introducing the complexity of two document types.
Moved the document class into a subfolder, updated all references
Added / updated unittests

Conflicts:
	library/Solarium/QueryType/Update/RequestBuilder.php
parents 9ee61252 10cab55f
......@@ -45,7 +45,7 @@ namespace Solarium\QueryType\Extract;
use Solarium\Core\Query\Query as BaseQuery;
use Solarium\Core\Client\Client;
use Solarium\QueryType\Update\ResponseParser as UpdateResponseParser;
use Solarium\QueryType\Update\Query\Document;
use Solarium\QueryType\Update\Query\Document\Document;
/**
* Extract query
......@@ -69,7 +69,7 @@ class Query extends BaseQuery
protected $options = array(
'handler' => 'update/extract',
'resultclass' => 'Solarium\QueryType\Extract\Result',
'documentclass' => 'Solarium\QueryType\Update\Query\Document',
'documentclass' => 'Solarium\QueryType\Update\Query\Document\Document',
'omitheader' => true,
);
......
......@@ -68,7 +68,7 @@ class ResponseParser extends ResponseParserAbstract implements ResponseParserInt
// create document instances
$documentClass = $query->getOption('documentclass');
$classes = class_implements($documentClass);
if (!in_array('Solarium\QueryType\Select\Result\DocumentInterface',$classes) && !in_array('Solarium\QueryType\Update\Query\DocumentInterface',$classes)) {
if (!in_array('Solarium\QueryType\Select\Result\DocumentInterface',$classes) && !in_array('Solarium\QueryType\Update\Query\Document\DocumentInterface',$classes)) {
throw new RuntimeException('The result document class must implement a document interface');
}
......
......@@ -38,7 +38,7 @@
*/
namespace Solarium\QueryType\Update\Query\Command;
use Solarium\QueryType\Update\Query\Query as UpdateQuery;
use Solarium\QueryType\Update\Query\Document;
use Solarium\QueryType\Update\Query\Document\Document;
use Solarium\Exception\RuntimeException;
/**
......
......@@ -36,8 +36,9 @@
/**
* @namespace
*/
namespace Solarium\QueryType\Update\Query;
namespace Solarium\QueryType\Update\Query\Document;
use Solarium\QueryType\Select\Result\AbstractDocument;
use Solarium\Exception\RuntimeException;
/**
* Read/Write Solr document
......@@ -51,9 +52,31 @@ use Solarium\QueryType\Select\Result\AbstractDocument;
* is not recommended. Most Solr indexes have fields that are indexed and not
* stored. You will loose that data because it is impossible to retrieve it from
* Solr. Always update from the original data source.
*
* Atomic updates are also support, using the field modifiers
*/
class Document extends AbstractDocument implements DocumentInterface
{
/**
* Directive to set a value using atomic updates
*
* @var string
*/
const MODIFIER_SET = 'set';
/**
* Directive to increment an integer value using atomic updates
*
* @var string
*/
const MODIFIER_INC = 'inc';
/**
* Directive to append a value (e.g. multivalued fields) using atomic updates
*
* @var string
*/
const MODIFIER_ADD = 'add';
/**
* Document boost value
......@@ -62,6 +85,20 @@ class Document extends AbstractDocument implements DocumentInterface
*/
protected $boost = null;
/**
* Allows us to determine what kind of atomic update we want to set
*
* @var array
*/
protected $modifiers = array();
/**
* This field needs to be explicitly set to observe the rules of atomic updates
*
* @var string
*/
protected $key;
/**
* Field boosts
*
......@@ -76,11 +113,13 @@ class Document extends AbstractDocument implements DocumentInterface
*
* @param array $fields
* @param array $boosts
* @param array $modifiers
*/
public function __construct(array $fields = array(), array $boosts = array())
public function __construct(array $fields = array(), array $boosts = array(), array $modifiers = array())
{
$this->fields = $fields;
$this->fieldBoosts = $boosts;
$this->modifiers = $modifiers;
}
/**
......@@ -92,12 +131,13 @@ class Document extends AbstractDocument implements DocumentInterface
* @param string $key
* @param mixed $value
* @param float $boost
* @param string $modifier
* @return self Provides fluent interface
*/
public function addField($key, $value, $boost = null)
public function addField($key, $value, $boost = null, $modifier = null)
{
if (!isset($this->fields[$key])) {
$this->setField($key, $value, $boost);
$this->setField($key, $value, $boost, $modifier);
} else {
// convert single value to array if needed
if (!is_array($this->fields[$key])) {
......@@ -106,6 +146,9 @@ class Document extends AbstractDocument implements DocumentInterface
$this->fields[$key][] = $value;
$this->setFieldBoost($key, $boost);
if ($modifier !== null) {
$this->setFieldModifier($key, $modifier);
}
}
return $this;
......@@ -121,15 +164,19 @@ class Document extends AbstractDocument implements DocumentInterface
* @param string $key
* @param mixed $value
* @param float $boost
* @param string $modifier
* @return self Provides fluent interface
*/
public function setField($key, $value, $boost = null)
public function setField($key, $value, $boost = null, $modifier = null)
{
if ($value === null) {
$this->removeField($key);
} else {
$this->fields[$key] = $value;
$this->setFieldBoost($key, $boost);
if ($modifier !== null) {
$this->setFieldModifier($key, $modifier);
}
}
return $this;
......@@ -225,6 +272,7 @@ class Document extends AbstractDocument implements DocumentInterface
{
$this->fields = array();
$this->fieldBoosts = array();
$this->modifiers = array();
return $this;
}
......@@ -261,4 +309,68 @@ class Document extends AbstractDocument implements DocumentInterface
$this->removeField($name);
}
/**
* Sets the uniquely identifying key for use in atomic updating
*
* You can set an existing field as key by supplying that field name as key, or add a new field by also supplying a
* value.
*
* @param string $key
* @param mixed $value
* @return self Provides fluent interface
*/
public function setKey($key, $value = null)
{
$this->key = $key;
if ($value !== null) {
$this->addField($key, $value);
}
return $this;
}
/**
* Sets the modifier type for the provided field
*
* @param string $key
* @param string $modifier
* @throws RuntimeException
* @return self
*/
public function setFieldModifier($key, $modifier = null)
{
if (! in_array($modifier, array(self::MODIFIER_ADD, self::MODIFIER_INC, self::MODIFIER_SET)) ) {
throw new RuntimeException('Attempt to set an atomic update modifier that is not supported');
}
$this->modifiers[$key] = $modifier;
return $this;
}
/**
* Returns the appropriate modifier for atomic updates.
*
* @param string $key
* @return null|string
*/
public function getFieldModifier($key)
{
return isset($this->modifiers[$key]) ? $this->modifiers[$key] : null;
}
/**
* Get fields
*
* Adds validation for atomicUpdates
*
* @return array
*/
public function getFields()
{
if (count($this->modifiers) > 0 && ($this->key == null || !isset($this->fields[$this->key]))) {
throw new RuntimeException(
'A document that uses modifiers (atomic updates) must have a key defined before it is used'
);
}
return parent::getFields();
}
}
......@@ -36,7 +36,7 @@
/**
* @namespace
*/
namespace Solarium\QueryType\Update\Query;
namespace Solarium\QueryType\Update\Query\Document;
/**
* Solr update document interface
......@@ -48,7 +48,9 @@ interface DocumentInterface
* Constructor
*
* @param array $fields
* @param array $boosts
* @param array $modifiers
*/
public function __construct(array $fields = array(), array $boosts = array());
public function __construct(array $fields = array(), array $boosts = array(), array $modifiers = array());
}
......@@ -106,7 +106,7 @@ class Query extends BaseQuery
protected $options = array(
'handler' => 'update',
'resultclass' => 'Solarium\QueryType\Update\Result',
'documentclass' => 'Solarium\QueryType\Update\Query\Document',
'documentclass' => 'Solarium\QueryType\Update\Query\Document\Document',
'omitheader' => false,
);
......
......@@ -124,12 +124,13 @@ class RequestBuilder extends BaseRequestBuilder
foreach ($doc->getFields() as $name => $value) {
$boost = $doc->getFieldBoost($name);
$modifier = $doc->getFieldModifier($name);
if (is_array($value)) {
foreach ($value as $multival) {
$xml .= $this->buildFieldXml($name, $boost, $multival);
$xml .= $this->buildFieldXml($name, $boost, $multival, $modifier);
}
} else {
$xml .= $this->buildFieldXml($name, $boost, $value);
$xml .= $this->buildFieldXml($name, $boost, $value, $modifier);
}
}
......@@ -149,12 +150,14 @@ class RequestBuilder extends BaseRequestBuilder
* @param string $name
* @param float $boost
* @param mixed $value
* @param string $modifier
* @return string
*/
protected function buildFieldXml($name, $boost, $value)
protected function buildFieldXml($name, $boost, $value, $modifier = null)
{
$xml = '<field name="' . $name . '"';
$xml .= $this->attrib('boost', $boost);
$xml .= $this->attrib('update', $modifier);
$xml .= '>' . htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8');
$xml .= '</field>';
......
......@@ -30,7 +30,7 @@
*/
namespace Solarium\Tests\Plugin\BufferedAdd;
use Solarium\QueryType\Update\Query\Document;
use Solarium\QueryType\Update\Query\Document\Document;
use Solarium\Plugin\BufferedAdd\BufferedAdd;
use Solarium\Core\Client\Client;
......
......@@ -33,7 +33,7 @@ namespace Solarium\Tests\QueryType\Analysis\RequestBuilder;
use Solarium\QueryType\Analysis\Query\Document;
use Solarium\QueryType\Analysis\RequestBuilder\Document as DocumentBuilder;
use Solarium\Core\Client\Request;
use Solarium\QueryType\Update\Query\Document as InputDocument;
use Solarium\QueryType\Update\Query\Document\Document as InputDocument;
class DocumentTest extends \PHPUnit_Framework_TestCase
{
......
......@@ -31,7 +31,7 @@
namespace Solarium\Tests\QueryType\Extract;
use Solarium\Core\Client\Client;
use Solarium\QueryType\Update\Query\Document;
use Solarium\QueryType\Update\Query\Document\Document;
use Solarium\QueryType\Extract\Query;
class QueryTest extends \PHPUnit_Framework_TestCase
......
......@@ -33,7 +33,7 @@ namespace Solarium\Tests\QueryType\Select\ResponseParser;
use Solarium\QueryType\Select\Query\Query;
use Solarium\QueryType\Select\Result\FacetSet;
use Solarium\QueryType\Select\ResponseParser\ResponseParser;
use Solarium\QueryType\Update\Query\Document;
use Solarium\QueryType\Update\Query\Document\Document;
class ResponseParserTest extends \PHPUnit_Framework_TestCase
{
......@@ -54,7 +54,7 @@ class ResponseParserTest extends \PHPUnit_Framework_TestCase
)
);
$query = new Query(array('documentclass' => 'Solarium\QueryType\Update\Query\Document'));
$query = new Query(array('documentclass' => 'Solarium\QueryType\Update\Query\Document\Document'));
$query->getFacetSet();
$resultStub = $this->getMock('Solarium\QueryType\Select\Result\Result', array(), array(), '', false);
......@@ -132,7 +132,7 @@ class ResponseParserTest extends \PHPUnit_Framework_TestCase
)
);
$query = new Query(array('documentclass' => 'Solarium\QueryType\Update\Query\Document'));
$query = new Query(array('documentclass' => 'Solarium\QueryType\Update\Query\Document\Document'));
$query->getFacetSet();
$resultStub = $this->getMock('Solarium\QueryType\Select\Result\Result', array(), array(), '', false);
......
......@@ -32,7 +32,7 @@
namespace Solarium\Tests\QueryType\Update\Query\Command;
use Solarium\QueryType\Update\Query\Command\Add;
use Solarium\QueryType\Update\Query\Query;
use Solarium\QueryType\Update\Query\Document;
use Solarium\QueryType\Update\Query\Document\Document;
class AddTest extends \PHPUnit_Framework_TestCase
{
......
......@@ -30,11 +30,14 @@
*/
namespace Solarium\Tests\QueryType\Update\Query;
use Solarium\QueryType\Update\Query\Document;
use Solarium\QueryType\Update\Query\Document\Document;
class DocumentTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Document
*/
protected $doc;
protected $fields = array(
......@@ -48,11 +51,13 @@ class DocumentTest extends \PHPUnit_Framework_TestCase
$this->doc = new Document($this->fields);
}
public function testConstructorWithFieldsAndBoosts()
public function testConstructorWithFieldsAndBoostsAndModifiers()
{
$fields = array('id' => 1, 'name' => 'testname');
$boosts = array('name' => 2.7);
$doc = new Document($fields, $boosts);
$modifiers = array('name' => Document::MODIFIER_SET);
$doc = new Document($fields, $boosts, $modifiers);
$doc->setKey('id');
$this->assertEquals(
$fields,
......@@ -63,6 +68,11 @@ class DocumentTest extends \PHPUnit_Framework_TestCase
2.7,
$doc->getFieldBoost('name')
);
$this->assertEquals(
Document::MODIFIER_SET,
$doc->getFieldModifier('name')
);
}
public function testAddFieldNoBoost()
......@@ -118,6 +128,24 @@ class DocumentTest extends \PHPUnit_Framework_TestCase
);
}
public function testAddFieldWithModifier()
{
$this->doc->clear();
$this->doc->setKey('id', 1);
$this->doc->addField('myfield', 'myvalue', null, Document::MODIFIER_ADD);
$this->doc->addField('myfield', 'myvalue2', null, Document::MODIFIER_ADD);
$this->assertEquals(
array('id' => 1, 'myfield' => array('myvalue', 'myvalue2')),
$this->doc->getFields()
);
$this->assertEquals(
Document::MODIFIER_ADD,
$this->doc->getFieldModifier('myfield')
);
}
public function testSetField()
{
$this->doc->setField('name', 'newname');
......@@ -131,6 +159,23 @@ class DocumentTest extends \PHPUnit_Framework_TestCase
);
}
public function testSetFieldWithModifier()
{
$this->doc->clear();
$this->doc->setKey('id', 1);
$this->doc->setField('myfield', 'myvalue', null, Document::MODIFIER_ADD);
$this->assertEquals(
array('id' => 1, 'myfield' => 'myvalue'),
$this->doc->getFields()
);
$this->assertEquals(
Document::MODIFIER_ADD,
$this->doc->getFieldModifier('myfield')
);
}
public function testSetFieldWithFalsyValue()
{
$falsy_value = '';
......@@ -325,4 +370,58 @@ class DocumentTest extends \PHPUnit_Framework_TestCase
);
}
public function testSetAndGetFieldModifier()
{
$this->doc->setFieldModifier('name', Document::MODIFIER_ADD);
$this->assertEquals(
Document::MODIFIER_ADD,
$this->doc->getFieldModifier('name')
);
$this->assertEquals(
null,
$this->doc->getFieldModifier('non-existing-field')
);
}
public function testClearFieldsModifierRemoval()
{
$this->doc->setFieldModifier('name', Document::MODIFIER_ADD);
$this->doc->clear();
$this->assertEquals(
null,
$this->doc->getFieldBoost('name')
);
}
public function testSetFieldModifierWithInvalidValue()
{
$this->setExpectedException('Solarium\Exception\RuntimeException');
$this->doc->setFieldModifier('name', 'invalid_modifier_value');
}
public function testSetAndGetFieldsUsingModifiers()
{
$this->doc->clear();
$this->doc->setKey('id', 1);
$this->doc->setField('name', 'newname', null, Document::MODIFIER_SET);
$this->assertEquals(
array('id' => 1, 'name' => 'newname'),
$this->doc->getFields()
);
}
public function testSetAndGetFieldsUsingModifiersWithoutKey()
{
$this->doc->clear();
$this->doc->setField('id', 1);
$this->doc->setField('name', 'newname', null, Document::MODIFIER_SET);
$this->setExpectedException('Solarium\Exception\RuntimeException');
$this->doc->getFields();
}
}
......@@ -34,7 +34,7 @@ use Solarium\Core\Client\Client;
use Solarium\QueryType\Update\Query\Query;
use Solarium\QueryType\Update\Query\Command\Rollback;
use Solarium\QueryType\Update\Query\Command\Commit;
use Solarium\QueryType\Update\Query\Document;
use Solarium\QueryType\Update\Query\Document\Document;
class QueryTest extends \PHPUnit_Framework_TestCase
{
......
......@@ -38,7 +38,7 @@ use Solarium\QueryType\Update\Query\Command\Delete as DeleteCommand;
use Solarium\QueryType\Update\Query\Command\Optimize as OptimizeCommand;
use Solarium\QueryType\Update\Query\Command\Commit as CommitCommand;
use Solarium\QueryType\Update\Query\Command\Rollback as RollbackCommand;
use Solarium\QueryType\Update\Query\Document;
use Solarium\QueryType\Update\Query\Document\Document;
class RequestBuilderTest extends \PHPUnit_Framework_TestCase
{
......@@ -159,6 +159,41 @@ class RequestBuilderTest extends \PHPUnit_Framework_TestCase
);
}
public function testBuildAddXmlWithFieldModifiers()
{
$doc = new Document();
$doc->setKey('id',1);
$doc->addField('category', 123, null, Document::MODIFIER_ADD);
$doc->addField('name', 'test', 2.5, Document::MODIFIER_SET);
$doc->setField('stock', 2, null, Document::MODIFIER_INC);
$command = new AddCommand();
$command->addDocument($doc);
$this->assertEquals(
'<add><doc><field name="id">1</field><field name="category" update="add">123</field><field name="name" boost="2.5" update="set">test</field><field name="stock" update="inc">2</field></doc></add>',
$this->builder->buildAddXml($command)
);
}
public function testBuildAddXmlWithFieldModifiersAndMultivalueFields()
{
$doc = new Document();
$doc->setKey('id',1);
$doc->addField('category', 123, null, Document::MODIFIER_ADD);
$doc->addField('category', 234, null, Document::MODIFIER_ADD);
$doc->addField('name', 'test', 2.3, Document::MODIFIER_SET);
$doc->setField('stock', 2, null, Document::MODIFIER_INC);
$command = new AddCommand();
$command->addDocument($doc);
$this->assertEquals(
'<add><doc><field name="id">1</field><field name="category" update="add">123</field><field name="category" update="add">234</field><field name="name" boost="2.3" update="set">test</field><field name="stock" update="inc">2</field></doc></add>',
$this->builder->buildAddXml($command)
);
}
public function testBuildDeleteXml()
{
$command = new DeleteCommand;
......
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