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;
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 896604c..82f674b 100644
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -71,13 +71,14 @@ class Configuration
->performNoDeepMerging()
->children()
->scalarNode('client')->end()
+ ->scalarNode('finder')->end()
->arrayNode('type_prototype')
->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 +151,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()
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 c9c1a1e..812aa43 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
@@ -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
@@ -361,3 +380,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.
+
+```
+FOQ\ElasticaBundle\Logger\ElasticaLogger
FOQ\ElasticaBundle\DataCollector\ElasticaDataCollector
FOQ\ElasticaBundle\Manager\RepositoryManager
+ FOQ\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection
-
@@ -63,6 +63,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();
}