From a79fa0242e834449ff075285764f878c3e2f5321 Mon Sep 17 00:00:00 2001 From: Tim Nagel Date: Sun, 25 May 2014 20:08:01 +1000 Subject: [PATCH 1/4] Simplified Configuration.php --- DependencyInjection/Configuration.php | 238 +----------------- DependencyInjection/FOSElasticaExtension.php | 4 +- .../DependencyInjection/ConfigurationTest.php | 63 +---- 3 files changed, 15 insertions(+), 290 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 275b23d..0d0d238 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -15,13 +15,15 @@ class Configuration implements ConfigurationInterface */ private $supportedDrivers = array('orm', 'mongodb', 'propel'); - private $configArray = array(); + /** + * If the kernel is running in debug mode. + * + * @var bool + */ private $debug; - public function __construct($configArray, $debug) + public function __construct($debug) { - - $this->configArray = $configArray; $this->debug = $debug; } @@ -104,7 +106,7 @@ class Configuration implements ConfigurationInterface ->scalarNode('port')->end() ->scalarNode('proxy')->end() ->scalarNode('logger') - ->defaultValue(($this->debug) ? 'fos_elastica.logger' : false) + ->defaultValue($this->debug ? 'fos_elastica.logger' : false) ->treatNullLike('fos_elastica.logger') ->treatTrueLike('fos_elastica.logger') ->end() @@ -205,28 +207,10 @@ class Configuration implements ConfigurationInterface $builder = new TreeBuilder(); $node = $builder->root('mappings'); - $nestings = $this->getNestings(); - - $childrenNode = $node + $node ->useAttributeAsKey('name') - ->prototype('array') - ->validate() - ->always() - ->then(function($v) { - foreach (array('fields','properties') as $prop) { - if (isset($v[$prop]) && empty($v[$prop])) { - unset($v[$prop]); - } - } - - return $v; - }) - ->end() - ->treatNullLike(array()) - ->addDefaultsIfNotSet() - ->children(); - - $this->addFieldConfig($childrenNode, $nestings); + ->prototype('variable') + ->treatNullLike(array()); return $node; } @@ -249,7 +233,7 @@ class Configuration implements ConfigurationInterface ->scalarNode('path_match')->end() ->scalarNode('path_unmatch')->end() ->scalarNode('match_pattern')->end() - ->append($this->getDynamicTemplateMapping()) + ->append($this->getMappingsNode()) ->end() ->end() ; @@ -257,206 +241,6 @@ class Configuration implements ConfigurationInterface return $node; } - /** - * @return the array node used for mapping in dynamic templates - */ - protected function getDynamicTemplateMapping() - { - $builder = new TreeBuilder(); - $node = $builder->root('mapping'); - - $nestings = $this->getNestingsForDynamicTemplates(); - - $this->addFieldConfig($node->children(), $nestings); - - return $node; - } - - /** - * @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $node The node to which to attach the field config to - * @param array $nestings the nested mappings for the current field level - */ - protected function addFieldConfig($node, $nestings) - { - $node - ->scalarNode('type')->defaultValue('string')->end() - ->scalarNode('boost')->end() - ->scalarNode('store')->end() - ->scalarNode('index')->end() - ->scalarNode('index_analyzer')->end() - ->scalarNode('search_analyzer')->end() - ->scalarNode('analyzer')->end() - ->scalarNode('term_vector')->end() - ->scalarNode('null_value')->end() - ->booleanNode('include_in_all')->defaultValue(true)->end() - ->booleanNode('enabled')->defaultValue(true)->end() - ->scalarNode('lat_lon')->end() - ->scalarNode('tree')->end() - ->scalarNode('precision')->end() - ->scalarNode('tree_levels')->end() - ->scalarNode('geohash')->end() - ->scalarNode('index_name')->end() - ->booleanNode('omit_norms')->end() - ->scalarNode('index_options')->end() - ->scalarNode('ignore_above')->end() - ->scalarNode('position_offset_gap')->end() - ->arrayNode('_parent') - ->treatNullLike(array()) - ->children() - ->scalarNode('type')->end() - ->scalarNode('identifier')->defaultValue('id')->end() - ->end() - ->end() - ->scalarNode('format')->end() - ->scalarNode('similarity')->end(); - ; - - if (isset($nestings['fields'])) { - $this->addNestedFieldConfig($node, $nestings, 'fields'); - } - - if (isset($nestings['properties'])) { - $node - ->booleanNode('include_in_parent')->end() - ->booleanNode('include_in_root')->end() - ; - $this->addNestedFieldConfig($node, $nestings, 'properties'); - } - } - - /** - * @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $node The node to which to attach the nested config to - * @param array $nestings The nestings for the current field level - * @param string $property the name of the nested property ('fields' or 'properties') - */ - protected function addNestedFieldConfig($node, $nestings, $property) - { - $childrenNode = $node - ->arrayNode($property) - ->useAttributeAsKey('name') - ->prototype('array') - ->validate() - ->always() - ->then(function($v) { - foreach (array('fields','properties') as $prop) { - if (isset($v[$prop]) && empty($v[$prop])) { - unset($v[$prop]); - } - } - - return $v; - }) - ->end() - ->treatNullLike(array()) - ->addDefaultsIfNotSet() - ->children(); - - $this->addFieldConfig($childrenNode, $nestings[$property]); - - $childrenNode - ->end() - ->end() - ->end() - ; - } - - /** - * @return array The unique nested mappings for all types - */ - protected function getNestings() - { - if (!isset($this->configArray[0]['indexes'])) { - return array(); - } - - $nestings = array(); - foreach ($this->configArray[0]['indexes'] as $index) { - if (empty($index['types'])) { - continue; - } - - foreach ($index['types'] as $type) { - if (empty($type['mappings'])) { - continue; - } - - $nestings = array_merge_recursive($nestings, $this->getNestingsForType($type['mappings'], $nestings)); - } - } - return $nestings; - } - - /** - * @return array The unique nested mappings for all dynamic templates - */ - protected function getNestingsForDynamicTemplates() - { - if (!isset($this->configArray[0]['indexes'])) { - return array(); - } - - $nestings = array(); - foreach ($this->configArray[0]['indexes'] as $index) { - if (empty($index['types'])) { - continue; - } - - foreach ($index['types'] as $type) { - if (empty($type['dynamic_templates'])) { - continue; - } - - foreach ($type['dynamic_templates'] as $definition) { - $field = $definition['mapping']; - - if (isset($field['fields'])) { - $this->addPropertyNesting($field, $nestings, 'fields'); - } else if (isset($field['properties'])) { - $this->addPropertyNesting($field, $nestings, 'properties'); - } - } - - } - } - return $nestings; - } - - /** - * @param array $mappings The mappings for the current type - * @return array The nested mappings defined for this type - */ - protected function getNestingsForType(array $mappings = null) - { - if ($mappings === null) { - return array(); - } - - $nestings = array(); - - foreach ($mappings as $field) { - if (isset($field['fields'])) { - $this->addPropertyNesting($field, $nestings, 'fields'); - } else if (isset($field['properties'])) { - $this->addPropertyNesting($field, $nestings, 'properties'); - } - } - - return $nestings; - } - - /** - * @param array $field The field mapping definition - * @param array $nestings The nestings array - * @param string $property The nested property name ('fields' or 'properties') - */ - protected function addPropertyNesting($field, &$nestings, $property) - { - if (!isset($nestings[$property])) { - $nestings[$property] = array(); - } - $nestings[$property] = array_merge_recursive($nestings[$property], $this->getNestingsForType($field[$property])); - } - /** * Returns the array node used for "_id". */ diff --git a/DependencyInjection/FOSElasticaExtension.php b/DependencyInjection/FOSElasticaExtension.php index f58cd5b..806c273 100644 --- a/DependencyInjection/FOSElasticaExtension.php +++ b/DependencyInjection/FOSElasticaExtension.php @@ -22,7 +22,7 @@ class FOSElasticaExtension extends Extension public function load(array $configs, ContainerBuilder $container) { $configuration = $this->getConfiguration($configs, $container); - $config = $this->processConfiguration($configuration, $configs); + $config = $this->processConfiguration($configuration, $configs); $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); @@ -61,7 +61,7 @@ class FOSElasticaExtension extends Extension public function getConfiguration(array $config, ContainerBuilder $container) { - return new Configuration($config, $container->getParameter('kernel.debug')); + return new Configuration($container->getParameter('kernel.debug')); } /** diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index bddd62e..4a4da67 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -22,7 +22,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase private function getConfigs(array $configArray) { - $configuration = new Configuration($configArray, true); + $configuration = new Configuration(true); return $this->processor->processConfiguration($configuration, array($configArray)); } @@ -118,7 +118,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase public function testTypeConfig() { - $configuration = $this->getConfigs(array( + $this->getConfigs(array( 'clients' => array( 'default' => array('url' => 'http://localhost:9200'), ), @@ -159,65 +159,6 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase ) ) )); - - $this->assertEquals('string', $configuration['indexes']['test']['types']['test']['mappings']['title']['type']); - $this->assertTrue($configuration['indexes']['test']['types']['test']['mappings']['title']['include_in_all']); - } - - public function testEmptyPropertiesIndexIsUnset() - { - $config = array( - 'indexes' => array( - 'test' => array( - 'types' => array( - 'test' => array( - 'mappings' => array( - 'title' => array( - 'type' => 'string', - 'fields' => array( - 'autocomplete' => null - ) - ), - 'content' => null, - 'children' => array( - 'type' => 'object', - 'properties' => array( - 'title' => array( - 'type' => 'string', - 'fields' => array( - 'autocomplete' => null - ) - ), - 'content' => null, - 'tags' => array( - 'properties' => array( - 'tag' => array( - 'type' => 'string', - 'index' => 'not_analyzed' - ) - ) - ) - ) - ), - ) - ) - ) - ) - ) - ); - - $processor = new Processor(); - - $configuration = $processor->processConfiguration(new Configuration(array($config), false), array($config)); - - $mapping = $configuration['indexes']['test']['types']['test']['mappings']; - $this->assertArrayNotHasKey('properties', $mapping['content']); - $this->assertArrayNotHasKey('properties', $mapping['title']); - $this->assertArrayHasKey('properties', $mapping['children']); - $this->assertArrayNotHasKey('properties', $mapping['children']['properties']['title']); - $this->assertArrayNotHasKey('properties', $mapping['children']['properties']['content']); - $this->assertArrayHasKey('properties', $mapping['children']['properties']['tags']); - $this->assertArrayNotHasKey('properties', $mapping['children']['properties']['tags']['properties']['tag']); } public function testClientConfigurationNoUrl() From 53180e281041f7168943f54b6480582b536221ce Mon Sep 17 00:00:00 2001 From: Tim Nagel Date: Sun, 25 May 2014 20:14:51 +1000 Subject: [PATCH 2/4] Bring tidy in line with property renaming --- DependencyInjection/Configuration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 208026b..60b0684 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -243,7 +243,7 @@ class Configuration implements ConfigurationInterface ->scalarNode('path_match')->end() ->scalarNode('path_unmatch')->end() ->scalarNode('match_pattern')->end() - ->append($this->getMappingsNode()) + ->append($this->getPropertiesNode()) ->end() ->end() ; From dad15d0b387cab71ca1fa68dc70f85ad4310ad38 Mon Sep 17 00:00:00 2001 From: Tim Nagel Date: Mon, 21 Apr 2014 09:21:50 +1000 Subject: [PATCH 3/4] Deprecate top level classes --- CHANGELOG-3.0.md | 20 +- Client.php | 38 +-- DynamicIndex.php | 20 +- Elastica/Client.php | 47 ++++ Elastica/Index.php | 28 ++ Index/IndexManager.php | 63 +++++ Index/Resetter.php | 246 ++++++++++++++++++ IndexManager.php | 61 +---- Resetter.php | 240 +---------------- Resources/config/config.xml | 4 +- .../LoggingClientTest.php} | 6 +- Tests/{ => Index}/IndexManagerTest.php | 4 +- Tests/{ => Index}/ResetterTest.php | 4 +- 13 files changed, 425 insertions(+), 356 deletions(-) create mode 100644 Elastica/Client.php create mode 100644 Elastica/Index.php create mode 100644 Index/IndexManager.php create mode 100644 Index/Resetter.php rename Tests/{ClientTest.php => Elastica/LoggingClientTest.php} (86%) rename Tests/{ => Index}/IndexManagerTest.php (95%) rename Tests/{ => Index}/ResetterTest.php (98%) diff --git a/CHANGELOG-3.0.md b/CHANGELOG-3.0.md index 9d74640..095c268 100644 --- a/CHANGELOG-3.0.md +++ b/CHANGELOG-3.0.md @@ -12,7 +12,25 @@ https://github.com/FriendsOfSymfony/FOSElasticaBundle/compare/v3.0.0...v3.0.1 To generate a changelog summary since the last version, run `git log --no-merges --oneline v3.0.0...3.0.x` -* 3.0.0-ALPHA3 (xxxx-xx-xx) +* 3.0.0-ALPHA6 + + * Deprecated FOS\ElasticaBundle\Client in favour of FOS\ElasticaBundle\Elastica\Client + * Deprecated FOS\ElasticaBundle\DynamicIndex in favour of FOS\ElasticaBundle\Elastica\Index + * Deprecated FOS\ElasticaBundle\IndexManager in favour of FOS\ElasticaBundle\Index\IndexManager + * Deprecated FOS\ElasticaBundle\Resetter in favour of FOS\ElasticaBundle\Index\Resetter + +* 3.0.0-ALPHA5 (2014-05-23) + +* Doctrine Provider speed up by disabling persistence logging while populating documents + +* 3.0.0-ALPHA4 (2014-04-10) + + * Indexes are now capable of logging errors with Elastica + * Fixed deferred indexing of deleted documents + * Resetting an index will now create it even if it doesn't exist + * Bulk upserting of documents is now supported when populating + +* 3.0.0-ALPHA3 (2014-04-01) * a9c4c93: Logger is now only enabled in debug mode by default * #463: allowing hot swappable reindexing diff --git a/Client.php b/Client.php index 3eb98fe..f85756d 100644 --- a/Client.php +++ b/Client.php @@ -2,43 +2,11 @@ namespace FOS\ElasticaBundle; -use Elastica\Client as ElasticaClient; -use Elastica\Request; -use FOS\ElasticaBundle\Logger\ElasticaLogger; +use FOS\ElasticaBundle\Elastica\LoggingClient; /** - * @author Gordon Franke + * @deprecated Use \FOS\ElasticaBundle\Elastica\LoggingClient */ -class Client extends ElasticaClient +class Client extends LoggingClient { - /** - * {@inheritdoc} - */ - public function request($path, $method = Request::GET, $data = array(), array $query = array()) - { - $start = microtime(true); - $response = parent::request($path, $method, $data, $query); - - if (null !== $this->_logger and $this->_logger instanceof ElasticaLogger) { - $time = microtime(true) - $start; - - $connection = $this->getLastRequest()->getConnection(); - - $connection_array = array( - 'host' => $connection->getHost(), - 'port' => $connection->getPort(), - 'transport' => $connection->getTransport(), - 'headers' => $connection->getConfig('headers'), - ); - - $this->_logger->logQuery($path, $method, $data, $time, $connection_array, $query); - } - - return $response; - } - - public function getIndex($name) - { - return new DynamicIndex($this, $name); - } } diff --git a/DynamicIndex.php b/DynamicIndex.php index cbec8e9..484a0d6 100644 --- a/DynamicIndex.php +++ b/DynamicIndex.php @@ -2,27 +2,11 @@ namespace FOS\ElasticaBundle; -use Elastica\Index; +use FOS\ElasticaBundle\Elastica\Index; /** - * Elastica index capable of reassigning name dynamically - * - * @author Konstantin Tjuterev + * @deprecated Use \FOS\ElasticaBundle\Elastica\TransformingIndex */ class DynamicIndex extends Index { - /** - * Reassign index name - * - * While it's technically a regular setter for name property, it's specifically named overrideName, but not setName - * since it's used for a very specific case and normally should not be used - * - * @param string $name Index name - * - * @return void - */ - public function overrideName($name) - { - $this->_name = $name; - } } diff --git a/Elastica/Client.php b/Elastica/Client.php new file mode 100644 index 0000000..64a7d3d --- /dev/null +++ b/Elastica/Client.php @@ -0,0 +1,47 @@ + + */ +class Client extends BaseClient +{ + /** + * {@inheritdoc} + */ + public function request($path, $method = Request::GET, $data = array(), array $query = array()) + { + $start = microtime(true); + $response = parent::request($path, $method, $data, $query); + + if ($this->_logger and $this->_logger instanceof ElasticaLogger) { + $time = microtime(true) - $start; + + $connection = $this->getLastRequest()->getConnection(); + + $connection_array = array( + 'host' => $connection->getHost(), + 'port' => $connection->getPort(), + 'transport' => $connection->getTransport(), + 'headers' => $connection->getConfig('headers'), + ); + + $this->_logger->logQuery($path, $method, $data, $time, $connection_array, $query); + } + + return $response; + } + + public function getIndex($name) + { + return new Index($this, $name); + } +} diff --git a/Elastica/Index.php b/Elastica/Index.php new file mode 100644 index 0000000..35d49f9 --- /dev/null +++ b/Elastica/Index.php @@ -0,0 +1,28 @@ + + */ +class Index extends BaseIndex +{ + /** + * Reassign index name + * + * While it's technically a regular setter for name property, it's specifically named overrideName, but not setName + * since it's used for a very specific case and normally should not be used + * + * @param string $name Index name + * + * @return void + */ + public function overrideName($name) + { + $this->_name = $name; + } +} diff --git a/Index/IndexManager.php b/Index/IndexManager.php new file mode 100644 index 0000000..543ccae --- /dev/null +++ b/Index/IndexManager.php @@ -0,0 +1,63 @@ +indexesByName = $indexesByName; + $this->defaultIndexName = $defaultIndex->getName(); + } + + /** + * Gets all registered indexes + * + * @return array + */ + public function getAllIndexes() + { + return $this->indexesByName; + } + + /** + * Gets an index by its name + * + * @param string $name Index to return, or the default index if null + * @return Index + * @throws \InvalidArgumentException if no index exists for the given name + */ + public function getIndex($name = null) + { + if (null === $name) { + $name = $this->defaultIndexName; + } + + if (!isset($this->indexesByName[$name])) { + throw new \InvalidArgumentException(sprintf('The index "%s" does not exist', $name)); + } + + return $this->indexesByName[$name]; + } + + /** + * Gets the default index + * + * @return Index + */ + public function getDefaultIndex() + { + return $this->getIndex($this->defaultIndexName); + } +} diff --git a/Index/Resetter.php b/Index/Resetter.php new file mode 100644 index 0000000..99ae75e --- /dev/null +++ b/Index/Resetter.php @@ -0,0 +1,246 @@ +indexConfigsByName = $indexConfigsByName; + } + + /** + * Deletes and recreates all indexes + */ + public function resetAllIndexes() + { + foreach (array_keys($this->indexConfigsByName) as $name) { + $this->resetIndex($name); + } + } + + /** + * Deletes and recreates the named index + * + * @param string $indexName + * @throws \InvalidArgumentException if no index exists for the given name + */ + public function resetIndex($indexName) + { + $indexConfig = $this->getIndexConfig($indexName); + $esIndex = $indexConfig['index']; + if (isset($indexConfig['use_alias']) && $indexConfig['use_alias']) { + $name = $indexConfig['name_or_alias']; + $name .= uniqid(); + $esIndex->overrideName($name); + $esIndex->create($indexConfig['config']); + + return; + } + + $esIndex->create($indexConfig['config'], true); + } + + /** + * Deletes and recreates a mapping type for the named index + * + * @param string $indexName + * @param string $typeName + * @throws \InvalidArgumentException if no index or type mapping exists for the given names + * @throws ResponseException + */ + public function resetIndexType($indexName, $typeName) + { + $indexConfig = $this->getIndexConfig($indexName); + + if (!isset($indexConfig['config']['properties'][$typeName]['properties'])) { + throw new \InvalidArgumentException(sprintf('The mapping for index "%s" and type "%s" does not exist.', $indexName, $typeName)); + } + + $type = $indexConfig['index']->getType($typeName); + try { + $type->delete(); + } catch (ResponseException $e) { + if (strpos($e->getMessage(), 'TypeMissingException') === false) { + throw $e; + } + } + $mapping = $this->createMapping($indexConfig['config']['properties'][$typeName]); + $type->setMapping($mapping); + } + + /** + * create type mapping object + * + * @param array $indexConfig + * @return Mapping + */ + protected function createMapping($indexConfig) + { + $mapping = Mapping::create($indexConfig['properties']); + + $mappingSpecialFields = array('_uid', '_id', '_source', '_all', '_analyzer', '_boost', '_routing', '_index', '_size', '_timestamp', '_ttl', 'dynamic_templates'); + foreach ($mappingSpecialFields as $specialField) { + if (isset($indexConfig[$specialField])) { + $mapping->setParam($specialField, $indexConfig[$specialField]); + } + } + + if (isset($indexConfig['_parent'])) { + $mapping->setParam('_parent', array('type' => $indexConfig['_parent']['type'])); + } + + return $mapping; + } + + /** + * Gets an index config by its name + * + * @param string $indexName Index name + * + * @param $indexName + * @return array + * @throws \InvalidArgumentException if no index config exists for the given name + */ + protected function getIndexConfig($indexName) + { + if (!isset($this->indexConfigsByName[$indexName])) { + throw new \InvalidArgumentException(sprintf('The configuration for index "%s" does not exist.', $indexName)); + } + + return $this->indexConfigsByName[$indexName]; + } + + public function postPopulate($indexName) + { + $indexConfig = $this->getIndexConfig($indexName); + if (isset($indexConfig['use_alias']) && $indexConfig['use_alias']) { + $this->switchIndexAlias($indexName); + } + } + + /** + * Switches the alias for given index (by key) to the newly populated index + * and deletes the old index + * + * @param string $indexName Index name + * + * @throws \RuntimeException + */ + private function switchIndexAlias($indexName) + { + $indexConfig = $this->getIndexConfig($indexName); + $esIndex = $indexConfig['index']; + $aliasName = $indexConfig['name_or_alias']; + $oldIndexName = false; + $newIndexName = $esIndex->getName(); + + $aliasedIndexes = $this->getAliasedIndexes($esIndex, $aliasName); + + if (count($aliasedIndexes) > 1) { + throw new \RuntimeException( + sprintf( + 'Alias %s is used for multiple indexes: [%s]. + Make sure it\'s either not used or is assigned to one index only', + $aliasName, + join(', ', $aliasedIndexes) + ) + ); + } + + // Change the alias to point to the new index + // Elastica's addAlias can't be used directly, because in current (0.19.x) version it's not atomic + // In 0.20.x it's atomic, but it doesn't return the old index name + $aliasUpdateRequest = array('actions' => array()); + if (count($aliasedIndexes) == 1) { + // if the alias is set - add an action to remove it + $oldIndexName = $aliasedIndexes[0]; + $aliasUpdateRequest['actions'][] = array( + 'remove' => array('index' => $oldIndexName, 'alias' => $aliasName) + ); + } + + // add an action to point the alias to the new index + $aliasUpdateRequest['actions'][] = array( + 'add' => array('index' => $newIndexName, 'alias' => $aliasName) + ); + + try { + $esIndex->getClient()->request('_aliases', 'POST', $aliasUpdateRequest); + } catch (ExceptionInterface $renameAliasException) { + $additionalError = ''; + // if we failed to move the alias, delete the newly built index + try { + $esIndex->delete(); + } catch (ExceptionInterface $deleteNewIndexException) { + $additionalError = sprintf( + 'Tried to delete newly built index %s, but also failed: %s', + $newIndexName, + $deleteNewIndexException->getError() + ); + } + + throw new \RuntimeException( + sprintf( + 'Failed to updated index alias: %s. %s', + $renameAliasException->getMessage(), + $additionalError ?: sprintf('Newly built index %s was deleted', $newIndexName) + ) + ); + } + + // Delete the old index after the alias has been switched + if ($oldIndexName) { + $oldIndex = new Index($esIndex->getClient(), $oldIndexName); + try { + $oldIndex->delete(); + } catch (ExceptionInterface $deleteOldIndexException) { + throw new \RuntimeException( + sprintf( + 'Failed to delete old index %s with message: %s', + $oldIndexName, + $deleteOldIndexException->getMessage() + ) + ); + } + } + } + + /** + * Returns array of indexes which are mapped to given alias + * + * @param Index $esIndex ES Index + * @param string $aliasName Alias name + * + * @return array + */ + private function getAliasedIndexes(Index $esIndex, $aliasName) + { + $aliasesInfo = $esIndex->getClient()->request('_aliases', 'GET')->getData(); + $aliasedIndexes = array(); + + foreach ($aliasesInfo as $indexName => $indexInfo) { + $aliases = array_keys($indexInfo['aliases']); + if (in_array($aliasName, $aliases)) { + $aliasedIndexes[] = $indexName; + } + } + + return $aliasedIndexes; + } +} diff --git a/IndexManager.php b/IndexManager.php index e20a791..e7c74c8 100644 --- a/IndexManager.php +++ b/IndexManager.php @@ -2,62 +2,11 @@ namespace FOS\ElasticaBundle; -use Elastica\Index; +use FOS\ElasticaBundle\Index\IndexManager as BaseIndexManager; -class IndexManager +/** + * @deprecated Use \FOS\ElasticaBundle\Index\IndexManager + */ +class IndexManager extends BaseIndexManager { - protected $indexesByName; - protected $defaultIndexName; - - /** - * Constructor. - * - * @param array $indexesByName - * @param Index $defaultIndex - */ - public function __construct(array $indexesByName, Index $defaultIndex) - { - $this->indexesByName = $indexesByName; - $this->defaultIndexName = $defaultIndex->getName(); - } - - /** - * Gets all registered indexes - * - * @return array - */ - public function getAllIndexes() - { - return $this->indexesByName; - } - - /** - * Gets an index by its name - * - * @param string $name Index to return, or the default index if null - * @return Index - * @throws \InvalidArgumentException if no index exists for the given name - */ - public function getIndex($name = null) - { - if (null === $name) { - $name = $this->defaultIndexName; - } - - if (!isset($this->indexesByName[$name])) { - throw new \InvalidArgumentException(sprintf('The index "%s" does not exist', $name)); - } - - return $this->indexesByName[$name]; - } - - /** - * Gets the default index - * - * @return Index - */ - public function getDefaultIndex() - { - return $this->getIndex($this->defaultIndexName); - } } diff --git a/Resetter.php b/Resetter.php index d614f1b..2067579 100644 --- a/Resetter.php +++ b/Resetter.php @@ -2,245 +2,11 @@ namespace FOS\ElasticaBundle; -use Elastica\Exception\ExceptionInterface; -use Elastica\Index; -use Elastica\Exception\ResponseException; -use Elastica\Type\Mapping; +use FOS\ElasticaBundle\Index\Resetter as BaseResetter; /** - * Deletes and recreates indexes + * @deprecated Use \FOS\ElasticaBundle\Index\Resetter */ -class Resetter +class Resetter extends BaseResetter { - protected $indexConfigsByName; - - /** - * Constructor. - * - * @param array $indexConfigsByName - */ - public function __construct(array $indexConfigsByName) - { - $this->indexConfigsByName = $indexConfigsByName; - } - - /** - * Deletes and recreates all indexes - */ - public function resetAllIndexes() - { - foreach (array_keys($this->indexConfigsByName) as $name) { - $this->resetIndex($name); - } - } - - /** - * Deletes and recreates the named index - * - * @param string $indexName - * @throws \InvalidArgumentException if no index exists for the given name - */ - public function resetIndex($indexName) - { - $indexConfig = $this->getIndexConfig($indexName); - $esIndex = $indexConfig['index']; - if (isset($indexConfig['use_alias']) && $indexConfig['use_alias']) { - $name = $indexConfig['name_or_alias']; - $name .= uniqid(); - $esIndex->overrideName($name); - $esIndex->create($indexConfig['config']); - - return; - } - - $esIndex->create($indexConfig['config'], true); - } - - /** - * Deletes and recreates a mapping type for the named index - * - * @param string $indexName - * @param string $typeName - * @throws \InvalidArgumentException if no index or type mapping exists for the given names - * @throws ResponseException - */ - public function resetIndexType($indexName, $typeName) - { - $indexConfig = $this->getIndexConfig($indexName); - - if (!isset($indexConfig['config']['properties'][$typeName]['properties'])) { - throw new \InvalidArgumentException(sprintf('The mapping for index "%s" and type "%s" does not exist.', $indexName, $typeName)); - } - - $type = $indexConfig['index']->getType($typeName); - try { - $type->delete(); - } catch (ResponseException $e) { - if (strpos($e->getMessage(), 'TypeMissingException') === false) { - throw $e; - } - } - $mapping = $this->createMapping($indexConfig['config']['properties'][$typeName]); - $type->setMapping($mapping); - } - - /** - * create type mapping object - * - * @param array $indexConfig - * @return Mapping - */ - protected function createMapping($indexConfig) - { - $mapping = Mapping::create($indexConfig['properties']); - - $mappingSpecialFields = array('_uid', '_id', '_source', '_all', '_analyzer', '_boost', '_routing', '_index', '_size', '_timestamp', '_ttl', 'dynamic_templates'); - foreach ($mappingSpecialFields as $specialField) { - if (isset($indexConfig[$specialField])) { - $mapping->setParam($specialField, $indexConfig[$specialField]); - } - } - - if (isset($indexConfig['_parent'])) { - $mapping->setParam('_parent', array('type' => $indexConfig['_parent']['type'])); - } - - return $mapping; - } - - /** - * Gets an index config by its name - * - * @param string $indexName Index name - * - * @param $indexName - * @return array - * @throws \InvalidArgumentException if no index config exists for the given name - */ - protected function getIndexConfig($indexName) - { - if (!isset($this->indexConfigsByName[$indexName])) { - throw new \InvalidArgumentException(sprintf('The configuration for index "%s" does not exist.', $indexName)); - } - - return $this->indexConfigsByName[$indexName]; - } - - public function postPopulate($indexName) - { - $indexConfig = $this->getIndexConfig($indexName); - if (isset($indexConfig['use_alias']) && $indexConfig['use_alias']) { - $this->switchIndexAlias($indexName); - } - } - - /** - * Switches the alias for given index (by key) to the newly populated index - * and deletes the old index - * - * @param string $indexName Index name - * - * @throws \RuntimeException - */ - private function switchIndexAlias($indexName) - { - $indexConfig = $this->getIndexConfig($indexName); - $esIndex = $indexConfig['index']; - $aliasName = $indexConfig['name_or_alias']; - $oldIndexName = false; - $newIndexName = $esIndex->getName(); - - $aliasedIndexes = $this->getAliasedIndexes($esIndex, $aliasName); - - if (count($aliasedIndexes) > 1) { - throw new \RuntimeException( - sprintf( - 'Alias %s is used for multiple indexes: [%s]. - Make sure it\'s either not used or is assigned to one index only', - $aliasName, - join(', ', $aliasedIndexes) - ) - ); - } - - // Change the alias to point to the new index - // Elastica's addAlias can't be used directly, because in current (0.19.x) version it's not atomic - // In 0.20.x it's atomic, but it doesn't return the old index name - $aliasUpdateRequest = array('actions' => array()); - if (count($aliasedIndexes) == 1) { - // if the alias is set - add an action to remove it - $oldIndexName = $aliasedIndexes[0]; - $aliasUpdateRequest['actions'][] = array( - 'remove' => array('index' => $oldIndexName, 'alias' => $aliasName) - ); - } - - // add an action to point the alias to the new index - $aliasUpdateRequest['actions'][] = array( - 'add' => array('index' => $newIndexName, 'alias' => $aliasName) - ); - - try { - $esIndex->getClient()->request('_aliases', 'POST', $aliasUpdateRequest); - } catch (ExceptionInterface $renameAliasException) { - $additionalError = ''; - // if we failed to move the alias, delete the newly built index - try { - $esIndex->delete(); - } catch (ExceptionInterface $deleteNewIndexException) { - $additionalError = sprintf( - 'Tried to delete newly built index %s, but also failed: %s', - $newIndexName, - $deleteNewIndexException->getError() - ); - } - - throw new \RuntimeException( - sprintf( - 'Failed to updated index alias: %s. %s', - $renameAliasException->getMessage(), - $additionalError ?: sprintf('Newly built index %s was deleted', $newIndexName) - ) - ); - } - - // Delete the old index after the alias has been switched - if ($oldIndexName) { - $oldIndex = new Index($esIndex->getClient(), $oldIndexName); - try { - $oldIndex->delete(); - } catch (ExceptionInterface $deleteOldIndexException) { - throw new \RuntimeException( - sprintf( - 'Failed to delete old index %s with message: %s', - $oldIndexName, - $deleteOldIndexException->getMessage() - ) - ); - } - } - } - - /** - * Returns array of indexes which are mapped to given alias - * - * @param Index $esIndex ES Index - * @param string $aliasName Alias name - * - * @return array - */ - private function getAliasedIndexes(Index $esIndex, $aliasName) - { - $aliasesInfo = $esIndex->getClient()->request('_aliases', 'GET')->getData(); - $aliasedIndexes = array(); - - foreach ($aliasesInfo as $indexName => $indexInfo) { - $aliases = array_keys($indexInfo['aliases']); - if (in_array($aliasName, $aliases)) { - $aliasedIndexes[] = $indexName; - } - } - - return $aliasedIndexes; - } } diff --git a/Resources/config/config.xml b/Resources/config/config.xml index 7687250..cff2b49 100644 --- a/Resources/config/config.xml +++ b/Resources/config/config.xml @@ -5,8 +5,8 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - FOS\ElasticaBundle\Client - FOS\ElasticaBundle\DynamicIndex + FOS\ElasticaBundle\Elastica\Client + FOS\ElasticaBundle\Elastica\Index Elastica\Type FOS\ElasticaBundle\IndexManager FOS\ElasticaBundle\Resetter diff --git a/Tests/ClientTest.php b/Tests/Elastica/LoggingClientTest.php similarity index 86% rename from Tests/ClientTest.php rename to Tests/Elastica/LoggingClientTest.php index 8a9d91a..b08a2cf 100644 --- a/Tests/ClientTest.php +++ b/Tests/Elastica/LoggingClientTest.php @@ -1,11 +1,11 @@ isType('array') ); - $client = $this->getMockBuilder('FOS\ElasticaBundle\Client') + $client = $this->getMockBuilder('FOS\ElasticaBundle\Elastica\LoggingClient') ->setMethods(array('getConnection')) ->getMock(); diff --git a/Tests/IndexManagerTest.php b/Tests/Index/IndexManagerTest.php similarity index 95% rename from Tests/IndexManagerTest.php rename to Tests/Index/IndexManagerTest.php index 0a8ea37..624f64e 100644 --- a/Tests/IndexManagerTest.php +++ b/Tests/Index/IndexManagerTest.php @@ -1,8 +1,8 @@ Date: Mon, 2 Jun 2014 00:40:03 +1000 Subject: [PATCH 4/4] Configuration rework --- DependencyInjection/Configuration.php | 2 +- DependencyInjection/FOSElasticaExtension.php | 406 ++++++++++--------- Resources/config/config.xml | 84 +--- Resources/config/index.xml | 39 ++ Resources/config/mongodb.xml | 4 +- Resources/config/orm.xml | 12 +- Resources/config/persister.xml | 27 ++ Resources/config/propel.xml | 3 - Resources/config/provider.xml | 18 + Resources/config/transformer.xml | 32 ++ Tests/Integration/MappingTest.php | 17 + 11 files changed, 372 insertions(+), 272 deletions(-) create mode 100644 Resources/config/index.xml create mode 100644 Resources/config/persister.xml create mode 100644 Resources/config/provider.xml create mode 100644 Resources/config/transformer.xml create mode 100644 Tests/Integration/MappingTest.php diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 60b0684..2acc9e5 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -288,7 +288,7 @@ class Configuration implements ConfigurationInterface ->end() ->scalarNode('compress')->end() ->scalarNode('compress_threshold')->end() - ->scalarNode('enabled')->end() + ->scalarNode('enabled')->defaultTrue()->end() ->end() ; diff --git a/DependencyInjection/FOSElasticaExtension.php b/DependencyInjection/FOSElasticaExtension.php index e896af2..c844f3a 100644 --- a/DependencyInjection/FOSElasticaExtension.php +++ b/DependencyInjection/FOSElasticaExtension.php @@ -13,9 +13,28 @@ use InvalidArgumentException; class FOSElasticaExtension extends Extension { - protected $indexConfigs = array(); - protected $typeFields = array(); - protected $loadedDrivers = array(); + /** + * Definition of elastica clients as configured by this extension. + * + * @var array + */ + private $clients = array(); + + /** + * An array of indexes as configured by the extension. + * + * @var array + */ + private $indexConfigs = array(); + + /** + * If we've encountered a type mapped to a specific persistence driver, it will be loaded + * here. + * + * @var array + */ + private $loadedDrivers = array(); + protected $serializerConfig = array(); public function load(array $configs, ContainerBuilder $container) @@ -30,7 +49,9 @@ class FOSElasticaExtension extends Extension return; } - $loader->load('config.xml'); + foreach (array('config', 'index', 'persister', 'provider', 'transformer') as $basename) { + $loader->load(sprintf('%s.xml', $basename)); + } if (empty($config['default_client'])) { $keys = array_keys($config['clients']); @@ -42,22 +63,24 @@ class FOSElasticaExtension extends Extension $config['default_index'] = reset($keys); } - $clientIdsByName = $this->loadClients($config['clients'], $container); $this->serializerConfig = isset($config['serializer']) ? $config['serializer'] : null; - $indexIdsByName = $this->loadIndexes($config['indexes'], $container, $clientIdsByName, $config['default_client']); - $indexRefsByName = array_map(function($id) { - return new Reference($id); - }, $indexIdsByName); - - $this->loadIndexManager($indexRefsByName, $container); - $this->loadResetter($this->indexConfigs, $container); + $this->loadClients($config['clients'], $container); $container->setAlias('fos_elastica.client', sprintf('fos_elastica.client.%s', $config['default_client'])); + + $this->loadIndexes($config['indexes'], $container); $container->setAlias('fos_elastica.index', sprintf('fos_elastica.index.%s', $config['default_index'])); + $this->loadIndexManager($container); + $this->createDefaultManagerAlias($config['default_manager'], $container); } + /** + * @param array $config + * @param ContainerBuilder $container + * @return Configuration|null|\Symfony\Component\Config\Definition\ConfigurationInterface + */ public function getConfiguration(array $config, ContainerBuilder $container) { return new Configuration($container->getParameter('kernel.debug')); @@ -70,24 +93,26 @@ class FOSElasticaExtension extends Extension * @param ContainerBuilder $container A ContainerBuilder instance * @return array */ - protected function loadClients(array $clients, ContainerBuilder $container) + private function loadClients(array $clients, ContainerBuilder $container) { - $clientIds = array(); foreach ($clients as $name => $clientConfig) { $clientId = sprintf('fos_elastica.client.%s', $name); - $clientDef = new Definition('%fos_elastica.client.class%', array($clientConfig)); + + $clientDef = new DefinitionDecorator('fos_elastica.client_prototype'); + $clientDef->replaceArgument(0, $clientConfig); + $logger = $clientConfig['servers'][0]['logger']; if (false !== $logger) { $clientDef->addMethodCall('setLogger', array(new Reference($logger))); } - $clientDef->addTag('fos_elastica.client'); $container->setDefinition($clientId, $clientDef); - $clientIds[$name] = $clientId; + $this->clients[$name] = array( + 'id' => $clientId, + 'reference' => new Reference($clientId) + ); } - - return $clientIds; } /** @@ -95,56 +120,44 @@ class FOSElasticaExtension extends Extension * * @param array $indexes An array of indexes configurations * @param ContainerBuilder $container A ContainerBuilder instance - * @param array $clientIdsByName - * @param $defaultClientName - * @param $serializerConfig * @throws \InvalidArgumentException * @return array */ - protected function loadIndexes(array $indexes, ContainerBuilder $container, array $clientIdsByName, $defaultClientName) + private function loadIndexes(array $indexes, ContainerBuilder $container) { - $indexIds = array(); foreach ($indexes as $name => $index) { - if (isset($index['client'])) { - $clientName = $index['client']; - if (!isset($clientIdsByName[$clientName])) { - throw new InvalidArgumentException(sprintf('The elastica client with name "%s" is not defined', $clientName)); - } - } else { - $clientName = $defaultClientName; - } - - $clientId = $clientIdsByName[$clientName]; $indexId = sprintf('fos_elastica.index.%s', $name); - $indexName = isset($index['index_name']) ? $index['index_name'] : $name; - $indexDefArgs = array($indexName); - $indexDef = new Definition('%fos_elastica.index.class%', $indexDefArgs); - $indexDef->setFactoryService($clientId); - $indexDef->setFactoryMethod('getIndex'); + $indexName = $index['index_name'] ?: $name; + + $indexDef = new DefinitionDecorator('fos_elastica.index_prototype'); + $indexDef->replaceArgument(0, $indexName); + + if ($index['client']) { + $client = $this->getClient($index['client']); + $indexDef->setFactoryService($client); + } + $container->setDefinition($indexId, $indexDef); - $typePrototypeConfig = isset($index['type_prototype']) ? $index['type_prototype'] : array(); - $indexIds[$name] = $indexId; + $reference = new Reference($indexId); + $this->indexConfigs[$name] = array( - 'index' => new Reference($indexId), - 'name_or_alias' => $indexName, 'config' => array( - 'properties' => array() - ) + 'properties' => array(), + 'settings' => $index['settings'] + ), + 'elasticsearch_name' => $indexName, + 'index' => $reference, + 'name' => $name, + 'type_prototype' => isset($index['type_prototype']) ? $index['type_prototype'] : array(), + 'use_alias' => $index['use_alias'], ); + if ($index['finder']) { - $this->loadIndexFinder($container, $name, $indexId); - } - if (!empty($index['settings'])) { - $this->indexConfigs[$name]['config']['settings'] = $index['settings']; - } - if ($index['use_alias']) { - $this->indexConfigs[$name]['use_alias'] = true; + $this->loadIndexFinder($container, $name, $reference); } - $this->loadTypes(isset($index['types']) ? $index['types'] : array(), $container, $name, $indexId, $typePrototypeConfig); + $this->loadTypes((array) $index['types'], $container, $this->indexConfigs[$name]); } - - return $indexIds; } /** @@ -152,10 +165,10 @@ class FOSElasticaExtension extends Extension * * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * @param string $name The index name - * @param string $indexId The index service identifier + * @param Reference $index Reference to the related index * @return string */ - protected function loadIndexFinder(ContainerBuilder $container, $name, $indexId) + private function loadIndexFinder(ContainerBuilder $container, $name, Reference $index) { /* Note: transformer services may conflict with "collection.index", if * an index and type names were "collection" and an index, respectively. @@ -166,34 +179,60 @@ class FOSElasticaExtension extends Extension $finderId = sprintf('fos_elastica.finder.%s', $name); $finderDef = new DefinitionDecorator('fos_elastica.finder'); - $finderDef->replaceArgument(0, new Reference($indexId)); + $finderDef->replaceArgument(0, $index); $finderDef->replaceArgument(1, new Reference($transformerId)); $container->setDefinition($finderId, $finderDef); - - return $finderId; } /** * Loads the configured types. * - * @param array $types An array of types configurations - * @param ContainerBuilder $container A ContainerBuilder instance - * @param $indexName - * @param $indexId - * @param array $typePrototypeConfig - * @param $serializerConfig + * @param array $types + * @param ContainerBuilder $container + * @param array $indexConfig */ - protected function loadTypes(array $types, ContainerBuilder $container, $indexName, $indexId, array $typePrototypeConfig) + private function loadTypes(array $types, ContainerBuilder $container, array $indexConfig) { foreach ($types as $name => $type) { - $type = self::deepArrayUnion($typePrototypeConfig, $type); - $typeId = sprintf('%s.%s', $indexId, $name); - $typeDefArgs = array($name); - $typeDef = new Definition('%fos_elastica.type.class%', $typeDefArgs); - $typeDef->setFactoryService($indexId); - $typeDef->setFactoryMethod('getType'); - if ($this->serializerConfig) { + $indexName = $indexConfig['name']; + $type = self::deepArrayUnion($indexConfig['type_prototype'], $type); + + $typeId = sprintf('%s.%s', $indexName, $name); + $typeDef = new DefinitionDecorator('fos_elastica.type_prototype'); + $typeDef->replaceArgument(0, $name); + $typeDef->setFactoryService($indexConfig['reference']); + + if (isset($type['persistence'])) { + $this->loadTypePersistenceIntegration($type['persistence'], $container, $typeDef, $indexName, $name); + } + + foreach (array( + 'index_analyzer', + 'properties', + 'search_analyzer', + '_all', + '_boost', + '_id', + '_parent', + '_routing', + '_source', + '_timestamp', + '_ttl', + ) as $field) { + $this->indexConfigs[$indexName]['config']['properties'][$name][$field] = $type[$field]; + } + + if (!empty($type['dynamic_templates'])) { + $this->indexConfigs[$indexName]['config']['properties'][$name]['dynamic_templates'] = array(); + foreach ($type['dynamic_templates'] as $templateName => $templateData) { + $this->indexConfigs[$indexName]['config']['properties'][$name]['dynamic_templates'][] = array($templateName => $templateData); + } + } + + $container->setDefinition($typeId, $typeDef); + + /*if ($this->serializerConfig) { $callbackDef = new Definition($this->serializerConfig['callback_class']); $callbackId = sprintf('%s.%s.serializer.callback', $indexId, $name); @@ -213,63 +252,7 @@ class FOSElasticaExtension extends Extension $container->setDefinition($callbackId, $callbackDef); $typeDef->addMethodCall('setSerializer', array(array(new Reference($callbackId), 'serialize'))); - } - $container->setDefinition($typeId, $typeDef); - - $this->indexConfigs[$indexName]['config']['properties'][$name] = array( - "_source" => array("enabled" => true), // Add a default setting for empty mapping settings - ); - - if (isset($type['_id'])) { - $this->indexConfigs[$indexName]['config']['properties'][$name]['_id'] = $type['_id']; - } - if (isset($type['_source'])) { - $this->indexConfigs[$indexName]['config']['properties'][$name]['_source'] = $type['_source']; - } - if (isset($type['_boost'])) { - $this->indexConfigs[$indexName]['config']['properties'][$name]['_boost'] = $type['_boost']; - } - if (isset($type['_routing'])) { - $this->indexConfigs[$indexName]['config']['properties'][$name]['_routing'] = $type['_routing']; - } - if (isset($type['properties']) && !empty($type['properties'])) { - $this->cleanUpProperties($type['properties']); - $this->indexConfigs[$indexName]['config']['properties'][$name]['properties'] = $type['properties']; - $typeName = sprintf('%s/%s', $indexName, $name); - $this->typeFields[$typeName] = $type['properties']; - } - if (isset($type['_parent'])) { - $this->indexConfigs[$indexName]['config']['properties'][$name]['_parent'] = array('type' => $type['_parent']['type']); - $typeName = sprintf('%s/%s', $indexName, $name); - $this->typeFields[$typeName]['_parent'] = $type['_parent']; - } - if (isset($type['persistence'])) { - $this->loadTypePersistenceIntegration($type['persistence'], $container, $typeDef, $indexName, $name); - } - if (isset($type['index_analyzer'])) { - $this->indexConfigs[$indexName]['config']['properties'][$name]['index_analyzer'] = $type['index_analyzer']; - } - if (isset($type['search_analyzer'])) { - $this->indexConfigs[$indexName]['config']['properties'][$name]['search_analyzer'] = $type['search_analyzer']; - } - if (isset($type['index'])) { - $this->indexConfigs[$indexName]['config']['properties'][$name]['index'] = $type['index']; - } - if (isset($type['_all'])) { - $this->indexConfigs[$indexName]['config']['properties'][$name]['_all'] = $type['_all']; - } - if (isset($type['_timestamp'])) { - $this->indexConfigs[$indexName]['config']['properties'][$name]['_timestamp'] = $type['_timestamp']; - } - if (isset($type['_ttl'])) { - $this->indexConfigs[$indexName]['config']['properties'][$name]['_ttl'] = $type['_ttl']; - } - if (!empty($type['dynamic_templates'])) { - $this->indexConfigs[$indexName]['config']['properties'][$name]['dynamic_templates'] = array(); - foreach ($type['dynamic_templates'] as $templateName => $templateData) { - $this->indexConfigs[$indexName]['config']['properties'][$name]['dynamic_templates'][] = array($templateName => $templateData); - } - } + }*/ } } @@ -281,7 +264,7 @@ class FOSElasticaExtension extends Extension * * @return array The merged array */ - static protected function deepArrayUnion($array1, $array2) + private static function deepArrayUnion($array1, $array2) { foreach ($array2 as $key => $value) { if (is_array($value) && isset($array1[$key]) && is_array($array1[$key])) { @@ -303,30 +286,40 @@ class FOSElasticaExtension extends Extension * @param $indexName * @param $typeName */ - protected function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Definition $typeDef, $indexName, $typeName) + private function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Definition $typeDef, $indexName, $typeName) { $this->loadDriver($container, $typeConfig['driver']); $elasticaToModelTransformerId = $this->loadElasticaToModelTransformer($typeConfig, $container, $indexName, $typeName); $modelToElasticaTransformerId = $this->loadModelToElasticaTransformer($typeConfig, $container, $indexName, $typeName); - $objectPersisterId = $this->loadObjectPersister($typeConfig, $typeDef, $container, $indexName, $typeName, $modelToElasticaTransformerId); + $objectPersisterId = $this->loadObjectPersister($typeConfig, $typeDef, $container, $indexName, $typeName, $modelToElasticaTransformerId); if (isset($typeConfig['provider'])) { - $this->loadTypeProvider($typeConfig, $container, $objectPersisterId, $typeDef, $indexName, $typeName); + $this->loadTypeProvider($typeConfig, $container, $objectPersisterId, $indexName, $typeName); } if (isset($typeConfig['finder'])) { $this->loadTypeFinder($typeConfig, $container, $elasticaToModelTransformerId, $typeDef, $indexName, $typeName); } if (isset($typeConfig['listener'])) { - $this->loadTypeListener($typeConfig, $container, $objectPersisterId, $typeDef, $indexName, $typeName); + $this->loadTypeListener($typeConfig, $container, $objectPersisterId, $indexName, $typeName); } } - protected function loadElasticaToModelTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName) + /** + * Creates and loads an ElasticaToModelTransformer. + * + * @param array $typeConfig + * @param ContainerBuilder $container + * @param string $indexName + * @param string $typeName + * @return string + */ + private function loadElasticaToModelTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName) { if (isset($typeConfig['elastica_to_model_transformer']['service'])) { return $typeConfig['elastica_to_model_transformer']['service']; } + /* Note: transformer services may conflict with "prototype.driver", if * the index and type names were "prototype" and a driver, respectively. */ @@ -339,28 +332,32 @@ class FOSElasticaExtension extends Extension $argPos = ('propel' === $typeConfig['driver']) ? 0 : 1; $serviceDef->replaceArgument($argPos, $typeConfig['model']); - $serviceDef->replaceArgument($argPos + 1, array( - 'hydrate' => $typeConfig['elastica_to_model_transformer']['hydrate'], - 'identifier' => $typeConfig['identifier'], - 'ignore_missing' => $typeConfig['elastica_to_model_transformer']['ignore_missing'], - 'query_builder_method' => $typeConfig['elastica_to_model_transformer']['query_builder_method'] - )); + $serviceDef->replaceArgument($argPos + 1, array_merge($typeConfig['elastica_to_model_transformer'], array( + 'identifier' => $typeConfig['identifier'], + ))); $container->setDefinition($serviceId, $serviceDef); return $serviceId; } - protected function loadModelToElasticaTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName) + /** + * Creates and loads a ModelToElasticaTransformer for an index/type. + * + * @param array $typeConfig + * @param ContainerBuilder $container + * @param string $indexName + * @param string $typeName + * @return string + */ + private function loadModelToElasticaTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName) { if (isset($typeConfig['model_to_elastica_transformer']['service'])) { return $typeConfig['model_to_elastica_transformer']['service']; } - if ($this->serializerConfig) { - $abstractId = sprintf('fos_elastica.model_to_elastica_identifier_transformer'); - } else { - $abstractId = sprintf('fos_elastica.model_to_elastica_transformer'); - } + $abstractId = $this->serializerConfig ? + 'fos_elastica.model_to_elastica_identifier_transformer' : + 'fos_elastica.model_to_elastica_transformer'; $serviceId = sprintf('fos_elastica.model_to_elastica_transformer.%s.%s', $indexName, $typeName); $serviceDef = new DefinitionDecorator($abstractId); @@ -372,7 +369,18 @@ class FOSElasticaExtension extends Extension return $serviceId; } - protected function loadObjectPersister(array $typeConfig, Definition $typeDef, ContainerBuilder $container, $indexName, $typeName, $transformerId) + /** + * Creates and loads an object persister for a type. + * + * @param array $typeConfig + * @param Definition $typeDef + * @param ContainerBuilder $container + * @param string $indexName + * @param string $typeName + * @param string $transformerId + * @return string + */ + private function loadObjectPersister(array $typeConfig, Definition $typeDef, ContainerBuilder $container, $indexName, $typeName, $transformerId) { $arguments = array( $typeDef, @@ -386,8 +394,9 @@ class FOSElasticaExtension extends Extension $arguments[] = array(new Reference($callbackId), 'serialize'); } else { $abstractId = 'fos_elastica.object_persister'; - $arguments[] = $this->typeFields[sprintf('%s/%s', $indexName, $typeName)]; + $arguments[] = $this->indexConfigs[$indexName]['config']['properties'][$typeName]['properties']; } + $serviceId = sprintf('fos_elastica.object_persister.%s.%s', $indexName, $typeName); $serviceDef = new DefinitionDecorator($abstractId); foreach ($arguments as $i => $argument) { @@ -399,11 +408,22 @@ class FOSElasticaExtension extends Extension return $serviceId; } - protected function loadTypeProvider(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $typeDef, $indexName, $typeName) + /** + * Loads a provider for a type. + * + * @param array $typeConfig + * @param ContainerBuilder $container + * @param string $objectPersisterId + * @param string $indexName + * @param string $typeName + * @return string + */ + private function loadTypeProvider(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName) { if (isset($typeConfig['provider']['service'])) { return $typeConfig['provider']['service']; } + /* Note: provider services may conflict with "prototype.driver", if the * index and type names were "prototype" and a driver, respectively. */ @@ -414,16 +434,28 @@ class FOSElasticaExtension extends Extension $providerDef->replaceArgument(1, $typeConfig['model']); // Propel provider can simply ignore Doctrine-specific options $providerDef->replaceArgument(2, array_diff_key($typeConfig['provider'], array('service' => 1))); + $container->setDefinition($providerId, $providerDef); return $providerId; } - protected function loadTypeListener(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $typeDef, $indexName, $typeName) + /** + * Loads doctrine listeners to handle indexing of new or updated objects. + * + * @param array $typeConfig + * @param ContainerBuilder $container + * @param string $objectPersisterId + * @param string $indexName + * @param string $typeName + * @return string + */ + private function loadTypeListener(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName) { if (isset($typeConfig['listener']['service'])) { return $typeConfig['listener']['service']; } + /* Note: listener services may conflict with "prototype.driver", if the * index and type names were "prototype" and a driver, respectively. */ @@ -438,10 +470,6 @@ class FOSElasticaExtension extends Extension $listenerDef->replaceArgument(4, new Reference($typeConfig['listener']['logger'])); } - switch ($typeConfig['driver']) { - case 'orm': $listenerDef->addTag('doctrine.event_subscriber'); break; - case 'mongodb': $listenerDef->addTag('doctrine_mongodb.odm.event_subscriber'); break; - } if (isset($typeConfig['listener']['is_indexable_callback'])) { $callback = $typeConfig['listener']['is_indexable_callback']; @@ -493,7 +521,18 @@ class FOSElasticaExtension extends Extension return $events; } - protected function loadTypeFinder(array $typeConfig, ContainerBuilder $container, $elasticaToModelId, $typeDef, $indexName, $typeName) + /** + * Loads a Type specific Finder. + * + * @param array $typeConfig + * @param ContainerBuilder $container + * @param string $elasticaToModelId + * @param Definition $typeDef + * @param string $indexName + * @param string $typeName + * @return string + */ + private function loadTypeFinder(array $typeConfig, ContainerBuilder $container, $elasticaToModelId, Definition $typeDef, $indexName, $typeName) { if (isset($typeConfig['finder']['service'])) { $finderId = $typeConfig['finder']['service']; @@ -519,39 +558,39 @@ class FOSElasticaExtension extends Extension /** * Loads the index manager * - * @param array $indexRefsByName * @param ContainerBuilder $container **/ - protected function loadIndexManager(array $indexRefsByName, ContainerBuilder $container) + private function loadIndexManager(ContainerBuilder $container) { $managerDef = $container->getDefinition('fos_elastica.index_manager'); - $managerDef->replaceArgument(0, $indexRefsByName); + $managerDef->replaceArgument(0, array_keys($this->clients)); $managerDef->replaceArgument(1, new Reference('fos_elastica.index')); } /** - * Loads the resetter + * Makes sure a specific driver has been loaded. * - * @param array $indexConfigs - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param ContainerBuilder $container + * @param string $driver */ - protected function loadResetter(array $indexConfigs, ContainerBuilder $container) - { - $resetterDef = $container->getDefinition('fos_elastica.resetter'); - $resetterDef->replaceArgument(0, $indexConfigs); - } - - protected function loadDriver(ContainerBuilder $container, $driver) + private function loadDriver(ContainerBuilder $container, $driver) { if (in_array($driver, $this->loadedDrivers)) { return; } + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load($driver.'.xml'); $this->loadedDrivers[] = $driver; } - protected function createDefaultManagerAlias($defaultManager, ContainerBuilder $container) + /** + * Creates a default manager alias for defined default manager or the first loaded driver. + * + * @param string $defaultManager + * @param ContainerBuilder $container + */ + private function createDefaultManagerAlias($defaultManager, ContainerBuilder $container) { if (0 == count($this->loadedDrivers)) { return; @@ -568,18 +607,19 @@ class FOSElasticaExtension extends Extension $container->setAlias('fos_elastica.manager', sprintf('fos_elastica.manager.%s', $defaultManagerService)); } - protected function cleanUpProperties(&$properties) + /** + * Returns a reference to a client given its configured name. + * + * @param string $clientName + * @return Reference + * @throws \InvalidArgumentException + */ + private function getClient($clientName) { - foreach ($properties as &$fieldProperties) { - if (empty($fieldProperties['fields'])) { - unset($fieldProperties['fields']); - } else { - $this->cleanUpProperties($fieldProperties['fields']); - } - - if (!empty($fieldProperties['properties'])) { - $this->cleanUpProperties($fieldProperties['properties']); - } + if (!array_key_exists($clientName, $this->clients)) { + throw new InvalidArgumentException(sprintf('The elastica client with name "%s" is not defined', $clientName)); } + + return $this->clients[$clientName]['reference']; } } diff --git a/Resources/config/config.xml b/Resources/config/config.xml index cff2b49..c1ac0f9 100644 --- a/Resources/config/config.xml +++ b/Resources/config/config.xml @@ -1,100 +1,34 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - FOS\ElasticaBundle\Elastica\Client - FOS\ElasticaBundle\Elastica\Index - Elastica\Type - FOS\ElasticaBundle\IndexManager - FOS\ElasticaBundle\Resetter - FOS\ElasticaBundle\Finder\TransformedFinder + FOS\ElasticaBundle\Elastica\LoggingClient FOS\ElasticaBundle\Logger\ElasticaLogger FOS\ElasticaBundle\DataCollector\ElasticaDataCollector - FOS\ElasticaBundle\Manager\RepositoryManager - FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection - FOS\ElasticaBundle\Provider\ProviderRegistry Symfony\Component\PropertyAccess\PropertyAccessor - FOS\ElasticaBundle\Persister\ObjectPersister - FOS\ElasticaBundle\Persister\ObjectSerializerPersister - FOS\ElasticaBundle\Transformer\ModelToElasticaAutoTransformer - FOS\ElasticaBundle\Transformer\ModelToElasticaIdentifierTransformer + + + + + %kernel.debug% + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Resources/config/index.xml b/Resources/config/index.xml new file mode 100644 index 0000000..85f5744 --- /dev/null +++ b/Resources/config/index.xml @@ -0,0 +1,39 @@ + + + + + + FOS\ElasticaBundle\Elastica\Index + Elastica\Type + FOS\ElasticaBundle\Index\IndexManager + FOS\ElasticaBundle\Index\Resetter + FOS\ElasticaBundle\Finder\TransformedFinder + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/config/mongodb.xml b/Resources/config/mongodb.xml index 0af7aa1..0c6b2af 100644 --- a/Resources/config/mongodb.xml +++ b/Resources/config/mongodb.xml @@ -5,7 +5,6 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - @@ -18,6 +17,7 @@ + @@ -33,7 +33,5 @@ - - diff --git a/Resources/config/orm.xml b/Resources/config/orm.xml index 5bd16e5..bf67688 100644 --- a/Resources/config/orm.xml +++ b/Resources/config/orm.xml @@ -1,11 +1,10 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - @@ -19,6 +18,7 @@ + @@ -31,10 +31,8 @@ - - + + - - diff --git a/Resources/config/persister.xml b/Resources/config/persister.xml new file mode 100644 index 0000000..8bd4dca --- /dev/null +++ b/Resources/config/persister.xml @@ -0,0 +1,27 @@ + + + + + + FOS\ElasticaBundle\Persister\ObjectPersister + FOS\ElasticaBundle\Persister\ObjectSerializerPersister + + + + + + + + + + + + + + + + + + diff --git a/Resources/config/propel.xml b/Resources/config/propel.xml index 7a7d93e..4ccc867 100644 --- a/Resources/config/propel.xml +++ b/Resources/config/propel.xml @@ -4,7 +4,6 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - @@ -22,7 +21,5 @@ - - diff --git a/Resources/config/provider.xml b/Resources/config/provider.xml new file mode 100644 index 0000000..0732d54 --- /dev/null +++ b/Resources/config/provider.xml @@ -0,0 +1,18 @@ + + + + + + FOS\ElasticaBundle\Provider\ProviderRegistry + + + + + + + + + + diff --git a/Resources/config/transformer.xml b/Resources/config/transformer.xml new file mode 100644 index 0000000..e3abbbc --- /dev/null +++ b/Resources/config/transformer.xml @@ -0,0 +1,32 @@ + + + + + + FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection + FOS\ElasticaBundle\Transformer\ModelToElasticaAutoTransformer + FOS\ElasticaBundle\Transformer\ModelToElasticaIdentifierTransformer + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/Integration/MappingTest.php b/Tests/Integration/MappingTest.php new file mode 100644 index 0000000..be134ed --- /dev/null +++ b/Tests/Integration/MappingTest.php @@ -0,0 +1,17 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + + +namespace FOS\ElasticaBundle\Tests\Integration; + + +class MappingTest { + +} \ No newline at end of file