From 4e48bbd1822d8b9ab36668b061fc33cd9f56cb3c Mon Sep 17 00:00:00 2001 From: Richard Miller Date: Fri, 16 Dec 2011 07:38:45 -0800 Subject: [PATCH 1/7] Merge pull request #46 from nurikabe/master Allow variable "fields" configuration node for attachment mapping. --- DependencyInjection/Configuration.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 301f5a9..42f49b7 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -204,6 +204,21 @@ class Configuration ->scalarNode('index')->end() ->scalarNode('analyzer')->end() ->scalarNode('term_vector')->end() + ->arrayNode('fields') + ->useAttributeAsKey('name') + ->prototype('array') + ->treatNullLike(array()) + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('type')->defaultValue('string')->end() + ->scalarNode('boost')->end() + ->scalarNode('store')->end() + ->scalarNode('index')->end() + ->scalarNode('analyzer')->end() + ->scalarNode('term_vector')->end() + ->end() + ->end() + ->end() ->end() ->end() ; From 8d0f38c44e896d7c5560cceb7215f5fb30930c49 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 28 Dec 2011 16:35:00 -0500 Subject: [PATCH 2/7] Revert "Hide exceptions if elasticsearch is not running. This needs some fixing and cleaning up by someone smarter than myself." This reverts commit eb1900265fb3f077cf104c0944d8832a8d48012e and fixes #45. --- Client.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Client.php b/Client.php index 2d7ea17..c868399 100644 --- a/Client.php +++ b/Client.php @@ -20,14 +20,7 @@ class Client extends Elastica_Client public function request($path, $method, $data = array()) { $start = microtime(true); - //this is ghetto, but i couldnt figure out another way of making our site continue to behave normally even if ES was not running. - //Any improvements on this welcome. Perhaps another parameter that allows you to control if you want to ignore exceptions about ES not running - try { - $response = parent::request($path, $method, $data); - } catch(\Exception $e) { - //again, ghetto, but couldnt figure out how to return a default empty Elastica_Response - return new \Elastica_Response('{"took":0,"timed_out":false,"hits":{"total":0,"max_score":0,"hits":[]}}'); - } + $response = parent::request($path, $method, $data); if (null !== $this->logger) { $time = microtime(true) - $start; From 64dd1d780b30e57e364461ef49906662f0b4832e Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 28 Dec 2011 17:08:47 -0500 Subject: [PATCH 3/7] [#45] Add example of exception suppression to README --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index 6f9645c..0ef4644 100644 --- a/README.md +++ b/README.md @@ -319,3 +319,36 @@ Any setting can be specified when declaring a type. For example, to enable a cus blog: mappings: title: { boost: 8, analyzer: my_analyzer } + +### Overriding the Client class to suppress exceptions + +By default, exceptions from the Elastica client library will propogate through +the bundle's Client class. For instance, if the elasticsearch server is offline, +issuing a request will result in an `Elastica_Exception_Client` being thrown. +Depending on your needs, it may be desirable to suppress these exceptions and +allow searches to fail silently. + +One way to achieve this is to override the `foq_elastica.client.class` service +container parameter with a custom class. In the following example, we override +the `Client::request()` method and return the equivalent of an empty search +response if an exception occurred. + +``` + Date: Wed, 28 Dec 2011 17:51:07 -0500 Subject: [PATCH 4/7] Ensure persistence.driver option exists before validating The driver option is not required by the configuration, so we should check for it the validation functions. If type_prototype is being used, there is a legitimate case where type definitions would not specify a driver. Likewise, the type_prototype may not specify the driver. If we wish to validate that a driver has been specified for a type, we'll have to do that in the extension class after loading the configuration and merging in prototypes. --- DependencyInjection/Configuration.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 896604c..d44ebef 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -75,9 +75,9 @@ class Configuration ->children() ->arrayNode('persistence') ->validate() - ->ifTrue(function($v) { return 'propel' === $v['driver'] && isset($v['listener']); }) + ->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['listener']); }) ->thenInvalid('Propel doesn\'t support listeners') - ->ifTrue(function($v) { return 'propel' === $v['driver'] && isset($v['repository']); }) + ->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['repository']); }) ->thenInvalid('Propel doesn\'t support the "repository" parameter') ->end() ->children() @@ -150,9 +150,9 @@ class Configuration ->children() ->arrayNode('persistence') ->validate() - ->ifTrue(function($v) { return 'propel' === $v['driver'] && isset($v['listener']); }) + ->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['listener']); }) ->thenInvalid('Propel doesn\'t support listeners') - ->ifTrue(function($v) { return 'propel' === $v['driver'] && isset($v['repository']); }) + ->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['repository']); }) ->thenInvalid('Propel doesn\'t support the "repository" parameter') ->end() ->children() From 5aab521b42e52199b9bac978bdba37990c95dfeb Mon Sep 17 00:00:00 2001 From: daFish Date: Thu, 29 Dec 2011 16:34:03 +0100 Subject: [PATCH 5/7] Changed path to custom provider example. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ec16cd..3021672 100644 --- a/README.md +++ b/README.md @@ -253,7 +253,7 @@ Its class must implement `FOQ\ElasticaBundle\Provider\ProviderInterface`. } } -You will find a more complete implementation example in `src/FOQ/ElasticaBundle/Provider/Doctrine/ORM/Provider.php`. +You will find a more complete implementation example in `src/FOQ/ElasticaBundle/Doctrine/AbstractProvider.php`. ### Search From 9c4ef3d8bd12cfff18a866ad4614882a959859c5 Mon Sep 17 00:00:00 2001 From: Tim Nagel Date: Tue, 3 Jan 2012 21:20:38 +1100 Subject: [PATCH 6/7] Added finder capability for an index --- .../Compiler/TransformerPass.php | 48 ++++++++++ DependencyInjection/Configuration.php | 1 + DependencyInjection/FOQElasticaExtension.php | 33 ++++++- .../AbstractElasticaToModelTransformer.php | 10 ++ FOQElasticaBundle.php | 2 + README.md | 19 ++++ Resources/config/config.xml | 8 ++ ...asticaToModelTransformerCollectionTest.php | 92 +++++++++++++++++++ .../ElasticaToModelTransformerCollection.php | 65 +++++++++++++ .../ElasticaToModelTransformerInterface.php | 7 ++ 10 files changed, 283 insertions(+), 2 deletions(-) create mode 100644 DependencyInjection/Compiler/TransformerPass.php create mode 100644 Tests/Transformer/ElasticaToModelTransformerCollectionTest.php create mode 100644 Transformer/ElasticaToModelTransformerCollection.php diff --git a/DependencyInjection/Compiler/TransformerPass.php b/DependencyInjection/Compiler/TransformerPass.php new file mode 100644 index 0000000..ea7bc85 --- /dev/null +++ b/DependencyInjection/Compiler/TransformerPass.php @@ -0,0 +1,48 @@ + + */ +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); + } + } +} \ No newline at end of file diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index d44ebef..82f674b 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -71,6 +71,7 @@ class Configuration ->performNoDeepMerging() ->children() ->scalarNode('client')->end() + ->scalarNode('finder')->end() ->arrayNode('type_prototype') ->children() ->arrayNode('persistence') diff --git a/DependencyInjection/FOQElasticaExtension.php b/DependencyInjection/FOQElasticaExtension.php index 23a286b..60029a8 100644 --- a/DependencyInjection/FOQElasticaExtension.php +++ b/DependencyInjection/FOQElasticaExtension.php @@ -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)); } - -} +} \ No newline at end of file diff --git a/Doctrine/AbstractElasticaToModelTransformer.php b/Doctrine/AbstractElasticaToModelTransformer.php index cff6e3a..13f7312 100644 --- a/Doctrine/AbstractElasticaToModelTransformer.php +++ b/Doctrine/AbstractElasticaToModelTransformer.php @@ -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 diff --git a/FOQElasticaBundle.php b/FOQElasticaBundle.php index 806f576..b24069e 100644 --- a/FOQElasticaBundle.php +++ b/FOQElasticaBundle.php @@ -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()); } } diff --git a/README.md b/README.md index 3021672..812aa43 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/Resources/config/config.xml b/Resources/config/config.xml index 990ac6b..48d322a 100644 --- a/Resources/config/config.xml +++ b/Resources/config/config.xml @@ -11,6 +11,10 @@ FOQ\ElasticaBundle\Logger\ElasticaLogger FOQ\ElasticaBundle\DataCollector\ElasticaDataCollector FOQ\ElasticaBundle\Manager\RepositoryManager +<<<<<<< HEAD +======= + FOQ\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection +>>>>>>> Added finder capability for an index @@ -63,6 +67,10 @@ + + + + diff --git a/Tests/Transformer/ElasticaToModelTransformerCollectionTest.php b/Tests/Transformer/ElasticaToModelTransformerCollectionTest.php new file mode 100644 index 0000000..e4beef4 --- /dev/null +++ b/Tests/Transformer/ElasticaToModelTransformerCollectionTest.php @@ -0,0 +1,92 @@ +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 +{ + +} diff --git a/Transformer/ElasticaToModelTransformerCollection.php b/Transformer/ElasticaToModelTransformerCollection.php new file mode 100644 index 0000000..ab736f9 --- /dev/null +++ b/Transformer/ElasticaToModelTransformerCollection.php @@ -0,0 +1,65 @@ + + */ +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); + } +} \ No newline at end of file diff --git a/Transformer/ElasticaToModelTransformerInterface.php b/Transformer/ElasticaToModelTransformerInterface.php index 49b222a..f4b6e43 100644 --- a/Transformer/ElasticaToModelTransformerInterface.php +++ b/Transformer/ElasticaToModelTransformerInterface.php @@ -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(); } From dfe72d8947ea2dfb994ca6f908fc4b132240cbde Mon Sep 17 00:00:00 2001 From: Tim Nagel Date: Wed, 4 Jan 2012 11:16:00 +1100 Subject: [PATCH 7/7] Fixed config.xml --- Resources/config/config.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Resources/config/config.xml b/Resources/config/config.xml index 48d322a..ba9627f 100644 --- a/Resources/config/config.xml +++ b/Resources/config/config.xml @@ -11,13 +11,9 @@ FOQ\ElasticaBundle\Logger\ElasticaLogger FOQ\ElasticaBundle\DataCollector\ElasticaDataCollector FOQ\ElasticaBundle\Manager\RepositoryManager -<<<<<<< HEAD -======= FOQ\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection ->>>>>>> Added finder capability for an index -