Added finder capability for an index

This commit is contained in:
Tim Nagel 2012-01-03 21:20:38 +11:00
parent fdc0d3c227
commit 9c4ef3d8bd
10 changed files with 283 additions and 2 deletions

View file

@ -0,0 +1,48 @@
<?php
namespace FOQ\ElasticaBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
use InvalidArgumentException;
/**
* Registers Transformer implementations into the TransformerCollection.
*
* @author Tim Nagel <tim@nagel.com.au>
*/
class TransformerPass implements CompilerPassInterface
{
/**
* {@inheritDoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('foq_elastica.elastica_to_model_transformer.collection.prototype')) {
return;
}
$transformers = array();
foreach ($container->findTaggedServiceIds('foq_elastica.elastica_to_model_transformer') as $id => $tags) {
foreach ($tags as $tag) {
if (empty($tag['index']) || empty($tag['type'])) {
throw new InvalidArgumentException('The Transformer must have both a type and an index defined.');
}
$transformers[$tag['index']][$tag['type']]= new Reference($id);
}
}
foreach ($transformers as $index => $indexTransformers) {
if (!$container->hasDefinition(sprintf('foq_elastica.elastica_to_model_transformer.collection.%s', $index))) {
continue;
}
$index = $container->getDefinition(sprintf('foq_elastica.elastica_to_model_transformer.collection.%s', $index));
$index->replaceArgument(0, $indexTransformers);
}
}
}

View file

@ -71,6 +71,7 @@ class Configuration
->performNoDeepMerging()
->children()
->scalarNode('client')->end()
->scalarNode('finder')->end()
->arrayNode('type_prototype')
->children()
->arrayNode('persistence')

View file

@ -113,6 +113,9 @@ class FOQElasticaExtension extends Extension
'mappings' => array()
)
);
if (isset($index['finder'])) {
$this->loadIndexFinder($container, $name, $indexId);
}
if (!empty($index['settings'])) {
$this->indexConfigs[$name]['config']['settings'] = $index['settings'];
}
@ -122,6 +125,32 @@ class FOQElasticaExtension extends Extension
return $indexIds;
}
/**
* Loads the configured index finders.
*
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
* @param string $name The index name
* @param string $indexId The index service identifier
* @return string
*/
protected function loadIndexFinder(ContainerBuilder $container, $name, $indexId)
{
$abstractTransformerId = 'foq_elastica.elastica_to_model_transformer.collection.prototype';
$transformerId = sprintf('foq_elastica.elastica_to_model_transformer.collection.%s', $name);
$transformerDef = new DefinitionDecorator($abstractTransformerId);
$container->setDefinition($transformerId, $transformerDef);
$abstractFinderId = 'foq_elastica.finder.prototype';
$finderId = sprintf('foq_elastica.finder.%s', $name);
$finderDef = new DefinitionDecorator($abstractFinderId);
$finderDef->replaceArgument(0, new Reference($indexId));
$finderDef->replaceArgument(1, new Reference($transformerId));
$container->setDefinition($finderId, $finderDef);
return $finderId;
}
/**
* Loads the configured types.
*
@ -203,6 +232,7 @@ class FOQElasticaExtension extends Extension
$abstractId = sprintf('foq_elastica.elastica_to_model_transformer.prototype.%s', $typeConfig['driver']);
$serviceId = sprintf('foq_elastica.elastica_to_model_transformer.%s.%s', $indexName, $typeName);
$serviceDef = new DefinitionDecorator($abstractId);
$serviceDef->addTag('foq_elastica.elastica_to_model_transformer', array('type' => $typeName, 'index' => $indexName));
// Doctrine has a mandatory service as first argument
$argPos = ('propel' === $typeConfig['driver']) ? 0 : 1;
@ -379,5 +409,4 @@ class FOQElasticaExtension extends Extension
$container->setAlias('foq_elastica.manager', sprintf('foq_elastica.manager.%s', $defaultManagerService));
}
}
}

View file

@ -48,6 +48,16 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
$this->options = array_merge($this->options, $options);
}
/**
* Returns the object class that is used for conversion.
*
* @return string
*/
public function getObjectClass()
{
return $this->objectClass;
}
/**
* Transforms an array of elastica objects into an array of
* model objects fetched from the doctrine repository

View file

@ -5,6 +5,7 @@ namespace FOQ\ElasticaBundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use FOQ\ElasticaBundle\DependencyInjection\Compiler\AddProviderPass;
use FOQ\ElasticaBundle\DependencyInjection\Compiler\TransformerPass;
class FOQElasticaBundle extends Bundle
{
@ -13,5 +14,6 @@ class FOQElasticaBundle extends Bundle
parent::build($container);
$container->addCompilerPass(new AddProviderPass());
$container->addCompilerPass(new TransformerPass());
}
}

View file

@ -303,6 +303,25 @@ You can even get paginated results!
/** var Pagerfanta\Pagerfanta */
$userPaginator = $finder->findPaginated('bob');
##### Index wide finder
You can also define a finder that will work on the entire index. Adjust your index
configuration as per below:
foq_elastica:
indexes:
website:
client: default
finder:
You can now use the index wide finder service `foq_elastica.finder.website`:
/** var FOQ\ElasticaBundle\Finder\MappedFinder */
$finder = $container->get('foq_elastica.finder.website');
// Returns a mixed array of any objects mapped
$results = $finder->find('bob');
### Realtime, selective index update
If you use the Doctrine integration, you can let ElasticaBundle update the indexes automatically

View file

@ -11,6 +11,10 @@
<parameter key="foq_elastica.logger.class">FOQ\ElasticaBundle\Logger\ElasticaLogger</parameter>
<parameter key="foq_elastica.data_collector.class">FOQ\ElasticaBundle\DataCollector\ElasticaDataCollector</parameter>
<parameter key="foq_elastica.manager.class">FOQ\ElasticaBundle\Manager\RepositoryManager</parameter>
<<<<<<< HEAD
=======
<parameter key="foq_elastica.elastica_to_model_transformer.collection.class">FOQ\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection</parameter>
>>>>>>> Added finder capability for an index
</parameters>
@ -63,6 +67,10 @@
<argument /> <!-- options -->
</service>
<service id="foq_elastica.elastica_to_model_transformer.collection.prototype" class="%foq_elastica.elastica_to_model_transformer.collection.class%" public="true" abstract="true">
<argument type="collection" /> <!-- transformers -->
<argument type="collection" /> <!-- options -->
</service>
</services>
</container>

View file

@ -0,0 +1,92 @@
<?php
namespace FOQ\ElasticaBundle\Tests\Transformer;
use FOQ\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection;
class ElasticaToModelTransformerCollectionTest extends \PHPUnit_Framework_TestCase
{
/**
* @var \FOQ\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection
*/
protected $collection;
protected $transformers = array();
protected function collectionSetup()
{
$transformer1 = $this->getMock('FOQ\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface');
$transformer1->expects($this->any())
->method('getObjectClass')
->will($this->returnValue('FOQ\ElasticaBundle\Tests\Transformer\POPO'));
$transformer2 = $this->getMock('FOQ\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface');
$transformer2->expects($this->any())
->method('getObjectClass')
->will($this->returnValue('FOQ\ElasticaBundle\Tests\Transformer\POPO2'));
$this->collection = new ElasticaToModelTransformerCollection($this->transformers = array(
'type1' => $transformer1,
'type2' => $transformer2,
), array());
}
public function testGetObjectClass()
{
$this->collectionSetup();
$objectClasses = $this->collection->getObjectClass();
$this->assertEquals(array(
'type1' => 'FOQ\ElasticaBundle\Tests\Transformer\POPO',
'type2' => 'FOQ\ElasticaBundle\Tests\Transformer\POPO2'
), $objectClasses);
}
public function testTransformDelegatesToTransformers()
{
$this->collectionSetup();
$document1 = new \Elastica_Document(123, array('data' => 'lots of data'), 'type1');
$document2 = new \Elastica_Document(124, array('data' => 'not so much data'), 'type2');
$result1 = new POPO(123, 'lots of data');
$result2 = new POPO2(124, 'not so much data');
$this->transformers['type1']->expects($this->once())
->method('transform')
->with(array($document1))
->will($this->returnValue(array($result1)));
$this->transformers['type2']->expects($this->once())
->method('transform')
->with(array($document2))
->will($this->returnValue(array($result2)));
$results = $this->collection->transform(array($document1, $document2));
$this->assertEquals(array(
$result1,
$result2,
), $results);
}
}
class POPO
{
public $id;
public $data;
public function __construct($id, $data)
{
$this->data = $data;
$this->id = $id;
}
public function getId()
{
return $this->id;
}
}
class POPO2 extends POPO
{
}

View file

@ -0,0 +1,65 @@
<?php
namespace FOQ\ElasticaBundle\Transformer;
/**
* Holds a collection of transformers for an index wide transformation.
*
* @author Tim Nagel <tim@nagel.com.au>
*/
class ElasticaToModelTransformerCollection implements ElasticaToModelTransformerInterface
{
protected $transformers = array();
protected $options = array(
'identifier' => 'id'
);
public function __construct(array $transformers, array $options)
{
$this->transformers = $transformers;
$this->options = array_merge($this->options, $options);
}
public function getObjectClass()
{
return array_map(function ($transformer) {
return $transformer->getObjectClass();
}, $this->transformers);
}
public function transform(array $elasticaObjects)
{
$sorted = array();
$order = array();
foreach ($elasticaObjects as $object) {
$sorted[$object->getType()][] = $object;
$order[] = sprintf('%s-%s', $object->getType(), $object->getId());
}
$transformed = array();
foreach ($sorted AS $type => $objects) {
$transformed = array_merge($transformed, $this->transformers[$type]->transform($objects));
}
$positions = array_flip($order);
$identifierGetter = 'get' . ucfirst($this->options['identifier']);
$classMap = $this->getTypeToClassMap();
usort($transformed, function($a, $b) use ($positions, $identifierGetter, $classMap)
{
$aType = array_search(get_class($a), $classMap);
$bType = array_search(get_class($b), $classMap);
return $positions["{$aType}-{$a->$identifierGetter()}"] > $positions["{$bType}-{$b->$identifierGetter()}"];
});
return $transformed;
}
protected function getTypeToClassMap()
{
return array_map(function ($transformer) {
return $transformer->getObjectClass();
}, $this->transformers);
}
}

View file

@ -15,4 +15,11 @@ interface ElasticaToModelTransformerInterface
* @return array of model objects
**/
function transform(array $elasticaObjects);
/**
* Returns the object class used by the transformer.
*
* @return string
*/
function getObjectClass();
}