diff --git a/.travis.yml b/.travis.yml index ad1f366..8f6a9d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,4 +6,4 @@ php: before_script: - echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - - composer install --dev + - composer install --dev --prefer-source diff --git a/Client.php b/Client.php index 8430b36..ea0c572 100644 --- a/Client.php +++ b/Client.php @@ -4,6 +4,7 @@ namespace FOS\ElasticaBundle; use Elastica\Client as ElasticaClient; use Elastica\Request; +use FOS\ElasticaBundle\Logger\ElasticaLogger; /** * @author Gordon Franke @@ -15,9 +16,18 @@ class Client extends ElasticaClient $start = microtime(true); $response = parent::request($path, $method, $data, $query); - if (null !== $this->_logger) { + if (null !== $this->_logger and $this->_logger instanceof ElasticaLogger) { $time = microtime(true) - $start; - $this->_logger->logQuery($path, $method, $data, $time); + + $connection = $this->getLastRequest()->getConnection(); + + $connection_array = array( + 'host' => $connection->getHost(), + 'port' => $connection->getPort(), + 'transport' => $connection->getTransport(), + ); + + $this->_logger->logQuery($path, $method, $data, $time, $connection_array); } return $response; diff --git a/Command/PopulateCommand.php b/Command/PopulateCommand.php index 58e5e9f..7297523 100755 --- a/Command/PopulateCommand.php +++ b/Command/PopulateCommand.php @@ -4,11 +4,9 @@ namespace FOS\ElasticaBundle\Command; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Component\Console\Helper\DialogHelper; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Output\Output; use FOS\ElasticaBundle\IndexManager; use FOS\ElasticaBundle\Provider\ProviderRegistry; use FOS\ElasticaBundle\Resetter; diff --git a/Command/ResetCommand.php b/Command/ResetCommand.php index b318827..06cfe48 100755 --- a/Command/ResetCommand.php +++ b/Command/ResetCommand.php @@ -3,11 +3,9 @@ namespace FOS\ElasticaBundle\Command; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Output\Output; use FOS\ElasticaBundle\IndexManager; use FOS\ElasticaBundle\Resetter; @@ -62,7 +60,7 @@ class ResetCommand extends ContainerAwareCommand if (null !== $type) { $output->writeln(sprintf('Resetting %s/%s', $index, $type)); - $this->resetter->resetIndex($index, $type); + $this->resetter->resetIndexType($index, $type); } else { $indexes = null === $index ? array_keys($this->indexManager->getAllIndexes()) diff --git a/Command/SearchCommand.php b/Command/SearchCommand.php index 62e3340..ec7cfb7 100644 --- a/Command/SearchCommand.php +++ b/Command/SearchCommand.php @@ -7,7 +7,6 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Output\Output; use Elastica\Query; use Elastica\Result; diff --git a/Configuration/Search.php b/Configuration/Search.php index ded65a5..cee10ab 100644 --- a/Configuration/Search.php +++ b/Configuration/Search.php @@ -11,8 +11,6 @@ namespace FOS\ElasticaBundle\Configuration; */ class Search { - /** @var string */ public $repositoryClass; - } diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 50d4b89..fbcd486 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -76,8 +76,9 @@ class Configuration implements ConfigurationInterface return array( 'servers' => array( array( - 'host' => $v['host'], - 'port' => $v['port'], + 'host' => $v['host'], + 'port' => $v['port'], + 'logger' => isset($v['logger']) ? $v['logger'] : null ) ) ); @@ -89,7 +90,8 @@ class Configuration implements ConfigurationInterface return array( 'servers' => array( array( - 'url' => $v['url'], + 'url' => $v['url'], + 'logger' => isset($v['logger']) ? $v['logger'] : null ) ) ); @@ -102,6 +104,11 @@ class Configuration implements ConfigurationInterface ->scalarNode('url')->end() ->scalarNode('host')->end() ->scalarNode('port')->end() + ->scalarNode('logger') + ->defaultValue('fos_elastica.logger') + ->treatNullLike('fos_elastica.logger') + ->treatTrueLike('fos_elastica.logger') + ->end() ->end() ->end() ->end() @@ -293,6 +300,7 @@ class Configuration implements ConfigurationInterface ->append($this->getBoostNode()) ->append($this->getRoutingNode()) ->append($this->getParentNode()) + ->append($this->getAllNode()) ->end() ; @@ -333,18 +341,13 @@ class Configuration implements ConfigurationInterface ->useAttributeAsKey('name') ->prototype('array') ->children() - ->scalarNode('match')->isRequired()->end() + ->scalarNode('match')->end() + ->scalarNode('unmatch')->end() ->scalarNode('match_mapping_type')->end() - ->arrayNode('mapping') - ->isRequired() - ->children() - ->scalarNode('type')->end() - ->scalarNode('index')->end() - ->arrayNode('fields') - ->children() - ->end() - ->end() - ->end() + ->scalarNode('path_match')->end() + ->scalarNode('path_unmatch')->end() + ->scalarNode('match_pattern')->end() + ->append($this->getDynamicTemplateMapping()) ->end() ->end() ; @@ -352,6 +355,21 @@ 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 @@ -369,6 +387,7 @@ class Configuration implements ConfigurationInterface ->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('index_name')->end() ->booleanNode('omit_norms')->end() @@ -434,12 +453,51 @@ class Configuration implements ConfigurationInterface } 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 @@ -574,4 +632,21 @@ class Configuration implements ConfigurationInterface return $node; } + + /** + * Returns the array node used for "_all" + */ + protected function getAllNode() + { + $builder = new TreeBuilder(); + $node = $builder->root('_all'); + + $node + ->children() + ->scalarNode('enabled')->defaultValue(true)->end() + ->end() + ; + + return $node; + } } diff --git a/DependencyInjection/FOSElasticaExtension.php b/DependencyInjection/FOSElasticaExtension.php index 733a039..53c91f1 100644 --- a/DependencyInjection/FOSElasticaExtension.php +++ b/DependencyInjection/FOSElasticaExtension.php @@ -16,6 +16,7 @@ class FOSElasticaExtension extends Extension protected $indexConfigs = array(); protected $typeFields = array(); protected $loadedDrivers = array(); + protected $serializerConfig = array(); public function load(array $configs, ContainerBuilder $container) { @@ -40,8 +41,8 @@ class FOSElasticaExtension extends Extension } $clientIdsByName = $this->loadClients($config['clients'], $container); - $serializerConfig = isset($config['serializer']) ? $config['serializer'] : null; - $indexIdsByName = $this->loadIndexes($config['indexes'], $container, $clientIdsByName, $config['default_client'], $serializerConfig); + $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); @@ -73,7 +74,10 @@ class FOSElasticaExtension extends Extension foreach ($clients as $name => $clientConfig) { $clientId = sprintf('fos_elastica.client.%s', $name); $clientDef = new Definition('%fos_elastica.client.class%', array($clientConfig)); - $clientDef->addMethodCall('setLogger', array(new Reference('fos_elastica.logger'))); + $logger = $clientConfig['servers'][0]['logger']; + if (false !== $logger) { + $clientDef->addMethodCall('setLogger', array(new Reference($logger))); + } $clientDef->addTag('fos_elastica.client'); $container->setDefinition($clientId, $clientDef); @@ -91,10 +95,11 @@ class FOSElasticaExtension extends Extension * @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, $serializerConfig) + protected function loadIndexes(array $indexes, ContainerBuilder $container, array $clientIdsByName, $defaultClientName) { $indexIds = array(); foreach ($indexes as $name => $index) { @@ -129,7 +134,7 @@ class FOSElasticaExtension extends Extension if (!empty($index['settings'])) { $this->indexConfigs[$name]['config']['settings'] = $index['settings']; } - $this->loadTypes(isset($index['types']) ? $index['types'] : array(), $container, $name, $indexId, $typePrototypeConfig, $serializerConfig); + $this->loadTypes(isset($index['types']) ? $index['types'] : array(), $container, $name, $indexId, $typePrototypeConfig); } return $indexIds; @@ -170,8 +175,9 @@ class FOSElasticaExtension extends Extension * @param $indexName * @param $indexId * @param array $typePrototypeConfig + * @param $serializerConfig */ - protected function loadTypes(array $types, ContainerBuilder $container, $indexName, $indexId, array $typePrototypeConfig, $serializerConfig) + protected function loadTypes(array $types, ContainerBuilder $container, $indexName, $indexId, array $typePrototypeConfig) { foreach ($types as $name => $type) { $type = self::deepArrayUnion($typePrototypeConfig, $type); @@ -180,12 +186,12 @@ class FOSElasticaExtension extends Extension $typeDef = new Definition('%fos_elastica.type.class%', $typeDefArgs); $typeDef->setFactoryService($indexId); $typeDef->setFactoryMethod('getType'); - if ($serializerConfig) { - $callbackDef = new Definition($serializerConfig['callback_class']); + if ($this->serializerConfig) { + $callbackDef = new Definition($this->serializerConfig['callback_class']); $callbackId = sprintf('%s.%s.serializer.callback', $indexId, $name); $typeDef->addMethodCall('setSerializer', array(array(new Reference($callbackId), 'serialize'))); - $callbackDef->addMethodCall('setSerializer', array(new Reference($serializerConfig['serializer']))); + $callbackDef->addMethodCall('setSerializer', array(new Reference($this->serializerConfig['serializer']))); if (isset($type['serializer']['groups'])) { $callbackDef->addMethodCall('setGroups', array($type['serializer']['groups'])); } @@ -198,6 +204,11 @@ class FOSElasticaExtension extends Extension $typeDef->addMethodCall('setSerializer', array(array(new Reference($callbackId), 'serialize'))); } $container->setDefinition($typeId, $typeDef); + + $this->indexConfigs[$indexName]['config']['mappings'][$name] = array( + "_source" => array("enabled" => true), // Add a default setting for empty mapping settings + ); + if (isset($type['_id'])) { $this->indexConfigs[$indexName]['config']['mappings'][$name]['_id'] = $type['_id']; } @@ -210,7 +221,7 @@ class FOSElasticaExtension extends Extension if (isset($type['_routing'])) { $this->indexConfigs[$indexName]['config']['mappings'][$name]['_routing'] = $type['_routing']; } - if (isset($type['mappings'])) { + if (isset($type['mappings']) && !empty($type['mappings'])) { $this->indexConfigs[$indexName]['config']['mappings'][$name]['properties'] = $type['mappings']; $typeName = sprintf('%s/%s', $indexName, $name); $this->typeFields[$typeName] = $type['mappings']; @@ -232,6 +243,9 @@ class FOSElasticaExtension extends Extension if (isset($type['index'])) { $this->indexConfigs[$indexName]['config']['mappings'][$name]['index'] = $type['index']; } + if (isset($type['_all'])) { + $this->indexConfigs[$indexName]['config']['mappings'][$name]['_all'] = $type['_all']; + } if (!empty($type['dynamic_templates'])) { $this->indexConfigs[$indexName]['config']['mappings'][$name]['dynamic_templates'] = array(); foreach ($type['dynamic_templates'] as $templateName => $templateData) { @@ -324,8 +338,14 @@ class FOSElasticaExtension extends Extension 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'); + } + $serviceId = sprintf('fos_elastica.model_to_elastica_transformer.%s.%s', $indexName, $typeName); - $serviceDef = new DefinitionDecorator('fos_elastica.model_to_elastica_transformer'); + $serviceDef = new DefinitionDecorator($abstractId); $serviceDef->replaceArgument(0, array( 'identifier' => $typeConfig['identifier'] )); @@ -336,12 +356,26 @@ class FOSElasticaExtension extends Extension protected function loadObjectPersister(array $typeConfig, Definition $typeDef, ContainerBuilder $container, $indexName, $typeName, $transformerId) { + $arguments = array( + $typeDef, + new Reference($transformerId), + $typeConfig['model'], + ); + + if ($this->serializerConfig) { + $abstractId = 'fos_elastica.object_serializer_persister'; + $callbackId = sprintf('%s.%s.serializer.callback', $this->indexConfigs[$indexName]['index'], $typeName); + $arguments[] = array(new Reference($callbackId), 'serialize'); + } else { + $abstractId = 'fos_elastica.object_persister'; + $arguments[] = $this->typeFields[sprintf('%s/%s', $indexName, $typeName)]; + } $serviceId = sprintf('fos_elastica.object_persister.%s.%s', $indexName, $typeName); - $serviceDef = new DefinitionDecorator('fos_elastica.object_persister'); - $serviceDef->replaceArgument(0, $typeDef); - $serviceDef->replaceArgument(1, new Reference($transformerId)); - $serviceDef->replaceArgument(2, $typeConfig['model']); - $serviceDef->replaceArgument(3, $this->typeFields[sprintf('%s/%s', $indexName, $typeName)]); + $serviceDef = new DefinitionDecorator($abstractId); + foreach ($arguments as $i => $argument) { + $serviceDef->replaceArgument($i, $argument); + } + $container->setDefinition($serviceId, $serviceDef); return $serviceId; diff --git a/Doctrine/RepositoryManager.php b/Doctrine/RepositoryManager.php index 6ba6bf5..f8867eb 100644 --- a/Doctrine/RepositoryManager.php +++ b/Doctrine/RepositoryManager.php @@ -40,5 +40,4 @@ class RepositoryManager extends BaseManager return parent::getRepository($realEntityName); } - } diff --git a/Finder/TransformedFinder.php b/Finder/TransformedFinder.php index 24f680f..4c8aa98 100644 --- a/Finder/TransformedFinder.php +++ b/Finder/TransformedFinder.php @@ -97,6 +97,7 @@ class TransformedFinder implements PaginatedFinderInterface public function createPaginatorAdapter($query) { $query = Query::create($query); + return new TransformedPaginatorAdapter($this->searchable, $query, $this->transformer); } } diff --git a/Logger/ElasticaLogger.php b/Logger/ElasticaLogger.php index 1705d06..7aacac5 100644 --- a/Logger/ElasticaLogger.php +++ b/Logger/ElasticaLogger.php @@ -38,15 +38,17 @@ class ElasticaLogger implements LoggerInterface * @param string $method Rest method to use (GET, POST, DELETE, PUT) * @param array $data arguments * @param float $time execution time + * @param array $connection host, port and transport of the query */ - public function logQuery($path, $method, $data, $time) + public function logQuery($path, $method, $data, $time, $connection = array()) { if ($this->debug) { $this->queries[] = array( 'path' => $path, 'method' => $method, 'data' => $data, - 'executionMS' => $time + 'executionMS' => $time, + 'connection' => $connection ); } @@ -145,6 +147,6 @@ class ElasticaLogger implements LoggerInterface */ public function log($level, $message, array $context = array()) { - return $this->logger->log($message, $context); + return $this->logger->log($level, $message, $context); } } diff --git a/Manager/RepositoryManager.php b/Manager/RepositoryManager.php index 6459c19..3cf8e96 100644 --- a/Manager/RepositoryManager.php +++ b/Manager/RepositoryManager.php @@ -5,6 +5,7 @@ namespace FOS\ElasticaBundle\Manager; use Doctrine\Common\Annotations\Reader; use FOS\ElasticaBundle\Finder\FinderInterface; use RuntimeException; + /** * @author Richard Miller * @@ -70,11 +71,10 @@ class RepositoryManager implements RepositoryManagerInterface private function createRepository($entityName) { - $repositoryName = $this->getRepositoryName($entityName); - if (!class_exists($repositoryName)) { + if (!class_exists($repositoryName = $this->getRepositoryName($entityName))) { throw new RuntimeException(sprintf('%s repository for %s does not exist', $repositoryName, $entityName)); } + return new $repositoryName($this->entities[$entityName]['finder']); } - } diff --git a/Manager/RepositoryManagerInterface.php b/Manager/RepositoryManagerInterface.php index c831d35..1008371 100644 --- a/Manager/RepositoryManagerInterface.php +++ b/Manager/RepositoryManagerInterface.php @@ -32,5 +32,4 @@ interface RepositoryManagerInterface * @param string $entityName */ public function getRepository($entityName); - } diff --git a/Paginator/FantaPaginatorAdapter.php b/Paginator/FantaPaginatorAdapter.php index a2f8c0e..2ad6983 100644 --- a/Paginator/FantaPaginatorAdapter.php +++ b/Paginator/FantaPaginatorAdapter.php @@ -3,14 +3,13 @@ namespace FOS\ElasticaBundle\Paginator; use Pagerfanta\Adapter\AdapterInterface; -use FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface; class FantaPaginatorAdapter implements AdapterInterface { private $adapter; /** - * @param PaginatorAdapterInterface $adapter + * @param \FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface $adapter */ public function __construct(PaginatorAdapterInterface $adapter) { @@ -42,7 +41,7 @@ class FantaPaginatorAdapter implements AdapterInterface } /** - * Returns an slice of the results. + * Returns a slice of the results. * * @param integer $offset The offset. * @param integer $length The length. diff --git a/Paginator/PaginatorAdapterInterface.php b/Paginator/PaginatorAdapterInterface.php index 9182973..25786a0 100644 --- a/Paginator/PaginatorAdapterInterface.php +++ b/Paginator/PaginatorAdapterInterface.php @@ -2,8 +2,6 @@ namespace FOS\ElasticaBundle\Paginator; -use FOS\ElasticaBundle\Paginator\PartialResultsInterface; - interface PaginatorAdapterInterface { /** diff --git a/Paginator/RawPaginatorAdapter.php b/Paginator/RawPaginatorAdapter.php index 125cd35..d4b5357 100644 --- a/Paginator/RawPaginatorAdapter.php +++ b/Paginator/RawPaginatorAdapter.php @@ -5,9 +5,6 @@ namespace FOS\ElasticaBundle\Paginator; use Elastica\SearchableInterface; use Elastica\Query; use Elastica\ResultSet; -use FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface; -use FOS\ElasticaBundle\Paginator\RawPartialResults; -use FOS\ElasticaBundle\Paginator\PartialResultsInterface; use InvalidArgumentException; /** @@ -52,6 +49,7 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface * * @param $offset * @param $itemCountPerPage + * @throws \InvalidArgumentException * @return ResultSet */ protected function getElasticaResults($offset, $itemCountPerPage) diff --git a/Paginator/RawPartialResults.php b/Paginator/RawPartialResults.php index 270e4cd..a4afb00 100644 --- a/Paginator/RawPartialResults.php +++ b/Paginator/RawPartialResults.php @@ -2,7 +2,6 @@ namespace FOS\ElasticaBundle\Paginator; -use FOS\ElasticaBundle\Paginator\PartialResultsInterface; use Elastica\ResultSet; use Elastica\Result; diff --git a/Paginator/TransformedPaginatorAdapter.php b/Paginator/TransformedPaginatorAdapter.php index a668636..7bc038a 100644 --- a/Paginator/TransformedPaginatorAdapter.php +++ b/Paginator/TransformedPaginatorAdapter.php @@ -3,7 +3,6 @@ namespace FOS\ElasticaBundle\Paginator; use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface; -use FOS\ElasticaBundle\Paginator\TransformedPartialResults; use Elastica\SearchableInterface; use Elastica\Query; diff --git a/Paginator/TransformedPartialResults.php b/Paginator/TransformedPartialResults.php index f7f125a..13d716c 100644 --- a/Paginator/TransformedPartialResults.php +++ b/Paginator/TransformedPartialResults.php @@ -3,7 +3,6 @@ namespace FOS\ElasticaBundle\Paginator; use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface; -use FOS\ElasticaBundle\Paginator\RawPartialResults; use Elastica\ResultSet; /** diff --git a/Persister/ObjectSerializerPersister.php b/Persister/ObjectSerializerPersister.php new file mode 100644 index 0000000..1a15656 --- /dev/null +++ b/Persister/ObjectSerializerPersister.php @@ -0,0 +1,42 @@ + + */ +class ObjectSerializerPersister extends ObjectPersister +{ + protected $serializer; + + public function __construct(Type $type, ModelToElasticaTransformerInterface $transformer, $objectClass, $serializer) + { + parent::__construct($type, $transformer, $objectClass, array()); + $this->serializer = $serializer; + } + + /** + * Transforms an object to an elastica document + * with just the identifier set + * + * @param object $object + * @return Document the elastica document + */ + public function transformToElasticaDocument($object) + { + $document = $this->transformer->transform($object, array()); + + $data = call_user_func($this->serializer, $object); + $document->setData($data); + + return $document; + } +} diff --git a/Provider/AbstractProvider.php b/Provider/AbstractProvider.php index daa57ae..06883a3 100644 --- a/Provider/AbstractProvider.php +++ b/Provider/AbstractProvider.php @@ -3,7 +3,6 @@ namespace FOS\ElasticaBundle\Provider; use FOS\ElasticaBundle\Persister\ObjectPersisterInterface; -use FOS\ElasticaBundle\Provider\ProviderInterface; abstract class AbstractProvider implements ProviderInterface { diff --git a/Provider/ProviderRegistry.php b/Provider/ProviderRegistry.php index ed5b499..2142223 100644 --- a/Provider/ProviderRegistry.php +++ b/Provider/ProviderRegistry.php @@ -2,8 +2,6 @@ namespace FOS\ElasticaBundle\Provider; -use Symfony\Component\DependencyInjection\ContainerBuilder; - use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; diff --git a/README.md b/README.md index 9f77a1b..b3a4c5d 100644 --- a/README.md +++ b/README.md @@ -182,17 +182,19 @@ per type. content: ~ _parent: { type: "post", property: "post", identifier: "id" } -The parent filed declaration has the following values: +The parent field declaration has the following values: * `type`: The parent type. * `property`: The property in the child entity where to look for the parent entity. It may be ignored if is equal to the parent type. - * `identifier`: The property in the parent entity which have the parent identifier. Defaults to `id`. + * `identifier`: The property in the parent entity which has the parent identifier. Defaults to `id`. Note that to create a document with a parent, you need to call `setParent` on the document rather than setting a _parent field. If you do this wrong, you will see a `RoutingMissingException` as elasticsearch does not know where to store a document that should have a parent but does not specify it. ### Declaring `nested` or `object` +Note that object can autodetect properties + fos_elastica: clients: default: { host: localhost, port: 9200 } @@ -213,6 +215,12 @@ If you do this wrong, you will see a `RoutingMissingException` as elasticsearch properties: date: { boost: 5 } content: ~ + user: + type: "object" + approver: + type: "object" + properties: + date: { boost: 5 } #### Doctrine ORM and `object` mappings @@ -230,7 +238,7 @@ It applies the configured mappings to the types. This command needs providers to insert new documents in the elasticsearch types. There are 2 ways to create providers. If your elasticsearch type matches a Doctrine repository or a Propel query, go for the persistence automatic provider. -Or, for complete flexibility, go for manual provider. +Or, for complete flexibility, go for a manual provider. #### Persistence automatic provider @@ -495,7 +503,7 @@ If you use multiple drivers then you can choose which one is aliased to `fos_ela using the `default_manager` parameter: fos_elastica: - default_manager: mongodb #defauults to orm + default_manager: mongodb #defaults to orm clients: default: { host: localhost, port: 9200 } #-- diff --git a/Resources/config/config.xml b/Resources/config/config.xml index ad6b13b..1fb2c1d 100644 --- a/Resources/config/config.xml +++ b/Resources/config/config.xml @@ -49,6 +49,13 @@ + + + + + + + @@ -56,6 +63,13 @@ + + + + + + + diff --git a/Resources/views/Collector/elastica.html.twig b/Resources/views/Collector/elastica.html.twig index a665e2d..0eb50a6 100644 --- a/Resources/views/Collector/elastica.html.twig +++ b/Resources/views/Collector/elastica.html.twig @@ -19,13 +19,13 @@ {% endblock %} {% block menu %} - - - Elastica - - {{ collector.querycount }} + + + Elastica + + {{ collector.querycount }} + - {% endblock %} {% block panel %} @@ -41,18 +41,52 @@

{% else %}
    - {% for query in collector.queries %} + {% for key, query in collector.queries %}
  • Path: {{ query.path }}
    - Method: {{ query.method }} + Method: {{ query.method }} ({{ query.connection.transport }} on {{ query.connection.host }}:{{ query.connection.port }})
    {{ query.data|json_encode }}
    Time: {{ '%0.2f'|format(query.executionMS * 1000) }} ms + + {% if query.connection.transport in ['Http', 'Https'] %}{# cURL support only HTTP #} + + + + - + Display cURL query + + + + {% endif %}
  • {% endfor %}
+ + {% endif %} {% endblock %} diff --git a/Serializer/Callback.php b/Serializer/Callback.php index 50307e0..9fe7064 100644 --- a/Serializer/Callback.php +++ b/Serializer/Callback.php @@ -8,9 +8,7 @@ use JMS\Serializer\SerializerInterface; class Callback { protected $serializer; - protected $groups; - protected $version; public function setSerializer($serializer) diff --git a/Tests/ClientTest.php b/Tests/ClientTest.php new file mode 100644 index 0000000..c8509cf --- /dev/null +++ b/Tests/ClientTest.php @@ -0,0 +1,42 @@ +getMock('Elastica\Connection'); + $connection->expects($this->any())->method('getTransportObject')->will($this->returnValue($transport)); + $connection->expects($this->any())->method('toArray')->will($this->returnValue(array())); + + $logger = $this->getMock('FOS\ElasticaBundle\Logger\ElasticaLogger'); + $logger + ->expects($this->once()) + ->method('logQuery') + ->with( + 'foo', + Request::GET, + $this->isType('array'), + $this->isType('float'), + $this->isType('array') + ); + + $client = $this->getMockBuilder('FOS\ElasticaBundle\Client') + ->setMethods(array('getConnection')) + ->getMock(); + + $client->expects($this->any())->method('getConnection')->will($this->returnValue($connection)); + + $client->setLogger($logger); + + $response = $client->request('foo'); + + $this->assertInstanceOf('Elastica\Response', $response); + } +} diff --git a/Tests/Command/ResetCommandTest.php b/Tests/Command/ResetCommandTest.php new file mode 100644 index 0000000..b6548aa --- /dev/null +++ b/Tests/Command/ResetCommandTest.php @@ -0,0 +1,91 @@ +resetter = $this->getMockBuilder('\FOS\ElasticaBundle\Resetter') + ->disableOriginalConstructor() + ->setMethods(array('resetIndex', 'resetIndexType')) + ->getMock(); + + $container->set('fos_elastica.resetter', $this->resetter); + + $this->indexManager = $this->getMockBuilder('\FOS\ElasticaBundle\IndexManager') + ->disableOriginalConstructor() + ->setMethods(array('getAllIndexes')) + ->getMock(); + + $container->set('fos_elastica.index_manager', $this->indexManager); + + $this->command = new ResetCommand(); + $this->command->setContainer($container); + } + + public function testResetAllIndexes() + { + $this->indexManager->expects($this->any()) + ->method('getAllIndexes') + ->will($this->returnValue(array('index1' => true, 'index2' => true))); + + $this->resetter->expects($this->at(0)) + ->method('resetIndex') + ->with($this->equalTo('index1')); + + $this->resetter->expects($this->at(1)) + ->method('resetIndex') + ->with($this->equalTo('index2')); + + $this->command->run( + new ArrayInput(array()), + new NullOutput() + ); + } + + public function testResetIndex() + { + $this->indexManager->expects($this->never()) + ->method('getAllIndexes'); + + $this->resetter->expects($this->at(0)) + ->method('resetIndex') + ->with($this->equalTo('index1')); + + $this->command->run( + new ArrayInput(array('--index' => 'index1')), + new NullOutput() + ); + } + + public function testResetIndexType() + { + $this->indexManager->expects($this->never()) + ->method('getAllIndexes'); + + $this->resetter->expects($this->never()) + ->method('resetIndex'); + + $this->resetter->expects($this->at(0)) + ->method('resetIndexType') + ->with($this->equalTo('index1'), $this->equalTo('type1')); + + $this->command->run( + new ArrayInput(array('--index' => 'index1', '--type' => 'type1')), + new NullOutput() + ); + } +} \ No newline at end of file diff --git a/Tests/DataCollector/ElasticaDataCollectorTest.php b/Tests/DataCollector/ElasticaDataCollectorTest.php index 758e1c2..aac1fb4 100644 --- a/Tests/DataCollector/ElasticaDataCollectorTest.php +++ b/Tests/DataCollector/ElasticaDataCollectorTest.php @@ -9,7 +9,6 @@ use FOS\ElasticaBundle\DataCollector\ElasticaDataCollector; */ class ElasticaDataCollectorTest extends \PHPUnit_Framework_TestCase { - public function testCorrectAmountOfQueries() { /** @var $requestMock \PHPUnit_Framework_MockObject_MockObject|\Symfony\Component\HttpFoundation\Request */ @@ -93,5 +92,4 @@ class ElasticaDataCollectorTest extends \PHPUnit_Framework_TestCase $elasticaDataCollector->collect($requestMock, $responseMock); $this->assertEquals(30, $elasticaDataCollector->getTime()); } - } diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index ead9977..35b2af3 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -48,6 +48,22 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $dynamicTemplates['match_mapping_type']); $this->assertNull($dynamicTemplates['match_mapping_type']->getDefaultValue()); + $this->assertArrayHasKey('unmatch', $dynamicTemplates); + $this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $dynamicTemplates['unmatch']); + $this->assertNull($dynamicTemplates['unmatch']->getDefaultValue()); + + $this->assertArrayHasKey('path_match', $dynamicTemplates); + $this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $dynamicTemplates['path_match']); + $this->assertNull($dynamicTemplates['path_match']->getDefaultValue()); + + $this->assertArrayHasKey('path_unmatch', $dynamicTemplates); + $this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $dynamicTemplates['path_unmatch']); + $this->assertNull($dynamicTemplates['path_unmatch']->getDefaultValue()); + + $this->assertArrayHasKey('match_pattern', $dynamicTemplates); + $this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $dynamicTemplates['match_pattern']); + $this->assertNull($dynamicTemplates['match_pattern']->getDefaultValue()); + $this->assertArrayHasKey('mapping', $dynamicTemplates); $this->assertInstanceOf('Symfony\Component\Config\Definition\ArrayNode', $dynamicTemplates['mapping']); } @@ -63,13 +79,10 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase $this->assertArrayHasKey('type', $mapping); $this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $mapping['type']); - $this->assertNull($mapping['type']->getDefaultValue()); + $this->assertSame('string', $mapping['type']->getDefaultValue()); $this->assertArrayHasKey('index', $mapping); $this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $mapping['index']); $this->assertNull($mapping['index']->getDefaultValue()); - - $this->assertArrayHasKey('fields', $mapping); - $this->assertInstanceOf('Symfony\Component\Config\Definition\ArrayNode', $mapping['fields']); } } diff --git a/Tests/Doctrine/RepositoryManagerTest.php b/Tests/Doctrine/RepositoryManagerTest.php index 2863127..ce7b14b 100644 --- a/Tests/Doctrine/RepositoryManagerTest.php +++ b/Tests/Doctrine/RepositoryManagerTest.php @@ -156,5 +156,4 @@ class RepositoryManagerTest extends \PHPUnit_Framework_TestCase $repository = $manager->getRepository($shortEntityName); $this->assertInstanceOf('FOS\ElasticaBundle\Repository', $repository); } - } diff --git a/Tests/FOSElasticaBundleTest.php b/Tests/FOSElasticaBundleTest.php new file mode 100644 index 0000000..2bfc7f9 --- /dev/null +++ b/Tests/FOSElasticaBundleTest.php @@ -0,0 +1,39 @@ +getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); + + $container + ->expects($this->at(0)) + ->method('addCompilerPass') + ->with($this->isInstanceOf($passes[0][0]), $passes[0][1]); + + $container + ->expects($this->at(1)) + ->method('addCompilerPass') + ->with($this->isInstanceOf($passes[1][0])); + + $bundle = new FOSElasticaBundle(); + + $bundle->build($container); + } +} diff --git a/Tests/HybridResultTest.php b/Tests/HybridResultTest.php new file mode 100644 index 0000000..cb382d1 --- /dev/null +++ b/Tests/HybridResultTest.php @@ -0,0 +1,19 @@ +assertSame($result, $hybridResult->getResult()); + $this->assertNull($hybridResult->getTransformed()); + } +} diff --git a/Tests/Logger/ElasticaLoggerTest.php b/Tests/Logger/ElasticaLoggerTest.php index 3cf6d2d..30ce30c 100644 --- a/Tests/Logger/ElasticaLoggerTest.php +++ b/Tests/Logger/ElasticaLoggerTest.php @@ -9,6 +9,39 @@ use FOS\ElasticaBundle\Logger\ElasticaLogger; */ class ElasticaLoggerTest extends \PHPUnit_Framework_TestCase { + /** + * @return \PHPUnit_Framework_MockObject_MockObject|\Symfony\Component\HttpKernel\Log\LoggerInterface + */ + private function getMockLogger() + { + return $this->getMockBuilder('Symfony\Component\HttpKernel\Log\LoggerInterface') + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * @param string $level + * @param string $message + * @param array $context + * @return ElasticaLogger + */ + private function getMockLoggerForLevelMessageAndContext($level, $message, $context) + { + $loggerMock = $this->getMockBuilder('Symfony\Component\HttpKernel\Log\LoggerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $loggerMock->expects($this->once()) + ->method($level) + ->with( + $this->equalTo($message), + $this->equalTo($context) + ); + + $elasticaLogger = new ElasticaLogger($loggerMock); + + return $elasticaLogger; + } public function testGetZeroIfNoQueriesAdded() { @@ -36,15 +69,17 @@ class ElasticaLoggerTest extends \PHPUnit_Framework_TestCase $method = 'testMethod'; $data = array('data'); $time = 12; + $connection = array('host' => 'localhost', 'port' => '8999', 'transport' => 'https'); $expected = array( 'path' => $path, 'method' => $method, 'data' => $data, - 'executionMS' => $time + 'executionMS' => $time, + 'connection' => $connection, ); - $elasticaLogger->logQuery($path, $method, $data, $time); + $elasticaLogger->logQuery($path, $method, $data, $time, $connection); $returnedQueries = $elasticaLogger->getQueries(); $this->assertEquals($expected, $returnedQueries[0]); } @@ -63,10 +98,7 @@ class ElasticaLoggerTest extends \PHPUnit_Framework_TestCase public function testQueryIsLogged() { - /** @var $loggerMock \PHPUnit_Framework_MockObject_MockObject|\Symfony\Component\HttpKernel\Log\LoggerInterface */ - $loggerMock = $this->getMockBuilder('Symfony\Component\HttpKernel\Log\LoggerInterface') - ->disableOriginalConstructor() - ->getMock(); + $loggerMock = $this->getMockLogger(); $elasticaLogger = new ElasticaLogger($loggerMock); @@ -87,4 +119,54 @@ class ElasticaLoggerTest extends \PHPUnit_Framework_TestCase $elasticaLogger->logQuery($path, $method, $data, $time); } + /** + * @return array + */ + public function logLevels() + { + return array( + array('emergency'), + array('alert'), + array('critical'), + array('error'), + array('warning'), + array('notice'), + array('info'), + array('debug'), + ); + } + + /** + * @dataProvider logLevels + */ + public function testMessagesCanBeLoggedAtSpecificLogLevels($level) + { + $message = 'foo'; + $context = array('data'); + + $loggerMock = $this->getMockLoggerForLevelMessageAndContext($level, $message, $context); + + call_user_func(array($loggerMock, $level), $message, $context); + } + + public function testMessagesCanBeLoggedToArbitraryLevels() + { + $loggerMock = $this->getMockLogger(); + + $level = 'info'; + $message = 'foo'; + $context = array('data'); + + $loggerMock->expects($this->once()) + ->method('log') + ->with( + $level, + $message, + $context + ); + + $elasticaLogger = new ElasticaLogger($loggerMock); + + $elasticaLogger->log($level, $message, $context); + } } diff --git a/Tests/Manager/RepositoryManagerTest.php b/Tests/Manager/RepositoryManagerTest.php index 8cdf1b4..8849035 100644 --- a/Tests/Manager/RepositoryManagerTest.php +++ b/Tests/Manager/RepositoryManagerTest.php @@ -97,5 +97,4 @@ class RepositoryManagerTest extends \PHPUnit_Framework_TestCase $manager->addEntity($entityName, $finderMock, 'FOS\ElasticaBundle\Tests\MissingRepository'); $manager->getRepository('Missing Entity'); } - } diff --git a/Tests/Persister/ObjectSerializerPersisterTest.php b/Tests/Persister/ObjectSerializerPersisterTest.php new file mode 100644 index 0000000..aae3a64 --- /dev/null +++ b/Tests/Persister/ObjectSerializerPersisterTest.php @@ -0,0 +1,131 @@ +id; + } + + public function getName() + { + return $this->name; + } +} + +class ObjectSerializerPersisterTest extends \PHPUnit_Framework_TestCase +{ + public function setUp() + { + if (!class_exists('Elastica\Type')) { + $this->markTestSkipped('The Elastica library classes are not available'); + } + } + + public function testThatCanReplaceObject() + { + $transformer = $this->getTransformer(); + + /** @var $typeMock \PHPUnit_Framework_MockObject_MockObject|\Elastica\Type */ + $typeMock = $this->getMockBuilder('Elastica\Type') + ->disableOriginalConstructor() + ->getMock(); + $typeMock->expects($this->once()) + ->method('deleteById') + ->with($this->equalTo(123)); + $typeMock->expects($this->once()) + ->method('addDocument'); + + $serializerMock = $this->getMockBuilder('FOS\ElasticaBundle\Serializer\Callback')->getMock(); + $serializerMock->expects($this->once())->method('serialize'); + + $objectPersister = new ObjectSerializerPersister($typeMock, $transformer, 'SomeClass', array($serializerMock, 'serialize')); + $objectPersister->replaceOne(new POPO()); + } + + public function testThatCanInsertObject() + { + $transformer = $this->getTransformer(); + + /** @var $typeMock \PHPUnit_Framework_MockObject_MockObject|\Elastica\Type */ + $typeMock = $this->getMockBuilder('Elastica\Type') + ->disableOriginalConstructor() + ->getMock(); + $typeMock->expects($this->never()) + ->method('deleteById'); + $typeMock->expects($this->once()) + ->method('addDocument'); + + $serializerMock = $this->getMockBuilder('FOS\ElasticaBundle\Serializer\Callback')->getMock(); + $serializerMock->expects($this->once())->method('serialize'); + + $objectPersister = new ObjectSerializerPersister($typeMock, $transformer, 'SomeClass', array($serializerMock, 'serialize')); + $objectPersister->insertOne(new POPO()); + } + + public function testThatCanDeleteObject() + { + $transformer = $this->getTransformer(); + + /** @var $typeMock \PHPUnit_Framework_MockObject_MockObject|\Elastica\Type */ + $typeMock = $this->getMockBuilder('Elastica\Type') + ->disableOriginalConstructor() + ->getMock(); + $typeMock->expects($this->once()) + ->method('deleteById'); + $typeMock->expects($this->never()) + ->method('addDocument'); + + $serializerMock = $this->getMockBuilder('FOS\ElasticaBundle\Serializer\Callback')->getMock(); + $serializerMock->expects($this->once())->method('serialize'); + + $objectPersister = new ObjectSerializerPersister($typeMock, $transformer, 'SomeClass', array($serializerMock, 'serialize')); + $objectPersister->deleteOne(new POPO()); + } + + public function testThatCanInsertManyObjects() + { + $transformer = $this->getTransformer(); + + /** @var $typeMock \PHPUnit_Framework_MockObject_MockObject|\Elastica\Type */ + $typeMock = $this->getMockBuilder('Elastica\Type') + ->disableOriginalConstructor() + ->getMock(); + $typeMock->expects($this->never()) + ->method('deleteById'); + $typeMock->expects($this->never()) + ->method('addObject'); + $typeMock->expects($this->never()) + ->method('addObjects'); + $typeMock->expects($this->once()) + ->method('addDocuments'); + + $serializerMock = $this->getMockBuilder('FOS\ElasticaBundle\Serializer\Callback')->getMock(); + $serializerMock->expects($this->exactly(2))->method('serialize'); + + $objectPersister = new ObjectSerializerPersister($typeMock, $transformer, 'SomeClass', array($serializerMock, 'serialize')); + $objectPersister->insertMany(array(new POPO(), new POPO())); + } + + /** + * @return ModelToElasticaIdentifierTransformer + */ + private function getTransformer() + { + $transformer = new ModelToElasticaIdentifierTransformer(); + $transformer->setPropertyAccessor(PropertyAccess::getPropertyAccessor()); + + return $transformer; + } +} diff --git a/Tests/RepositoryTest.php b/Tests/RepositoryTest.php index ded8009..c4d4efc 100644 --- a/Tests/RepositoryTest.php +++ b/Tests/RepositoryTest.php @@ -9,7 +9,6 @@ use FOS\ElasticaBundle\Repository; */ class RepositoryTest extends \PHPUnit_Framework_TestCase { - public function testThatFindCallsFindOnFinder() { $testQuery = 'Test Query'; @@ -59,6 +58,22 @@ class RepositoryTest extends \PHPUnit_Framework_TestCase $repository->findPaginated($testQuery); } + public function testThatCreatePaginatorCreatesAPaginatorViaFinder() + { + $testQuery = 'Test Query'; + + /** @var $typeMock \PHPUnit_Framework_MockObject_MockObject|\FOS\ElasticaBundle\Finder\TransformedFinder */ + $finderMock = $this->getMockBuilder('FOS\ElasticaBundle\Finder\TransformedFinder') + ->disableOriginalConstructor() + ->getMock(); + $finderMock->expects($this->once()) + ->method('createPaginatorAdapter') + ->with($this->equalTo($testQuery)); + + $repository = new Repository($finderMock); + $repository->createPaginatorAdapter($testQuery); + } + public function testThatFindHybridCallsFindHybridOnFinder() { $testQuery = 'Test Query'; @@ -75,5 +90,4 @@ class RepositoryTest extends \PHPUnit_Framework_TestCase $repository = new Repository($finderMock); $repository->findHybrid($testQuery, $testLimit); } - } diff --git a/Tests/Transformer/ElasticaToModelTransformerCollectionTest.php b/Tests/Transformer/ElasticaToModelTransformerCollectionTest.php index 9b1d782..eb4d8e4 100644 --- a/Tests/Transformer/ElasticaToModelTransformerCollectionTest.php +++ b/Tests/Transformer/ElasticaToModelTransformerCollectionTest.php @@ -3,6 +3,7 @@ namespace FOS\ElasticaBundle\Tests\Transformer; use Elastica\Document; +use Elastica\Result; use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection; class ElasticaToModelTransformerCollectionTest extends \PHPUnit_Framework_TestCase @@ -98,6 +99,57 @@ class ElasticaToModelTransformerCollectionTest extends \PHPUnit_Framework_TestCa $result2, ), $results); } + + public function testGetIdentifierFieldReturnsAMapOfIdentifiers() + { + $collection = new ElasticaToModelTransformerCollection(array()); + $identifiers = $collection->getIdentifierField(); + $this->assertInternalType('array', $identifiers); + $this->assertEmpty($identifiers); + + $this->collectionSetup(); + $identifiers = $this->collection->getIdentifierField(); + $this->assertInternalType('array', $identifiers); + $this->assertEquals(array('type1' => 'id', 'type2' => 'id'), $identifiers); + } + + public function elasticaResults() + { + $result = new Result(array('_id' => 123, '_type' => 'type1')); + $transformedObject = new POPO(123, array()); + + return array( + array( + $result, $transformedObject + ) + ); + } + + /** + * @dataProvider elasticaResults + */ + public function testHybridTransformDecoratesResultsWithHybridResultObjects($result, $transformedObject) + { + $transformer = $this->getMock('FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface'); + $transformer->expects($this->any())->method('getIdentifierField')->will($this->returnValue('id')); + + $transformer + ->expects($this->any()) + ->method('transform') + ->will($this->returnValue(array($transformedObject))); + + $collection = new ElasticaToModelTransformerCollection(array('type1' => $transformer)); + + $hybridResults = $collection->hybridTransform(array($result)); + + $this->assertInternalType('array', $hybridResults); + $this->assertNotEmpty($hybridResults); + $this->assertContainsOnlyInstancesOf('FOS\ElasticaBundle\HybridResult', $hybridResults); + + $hybridResult = array_pop($hybridResults); + $this->assertEquals($result, $hybridResult->getResult()); + $this->assertEquals($transformedObject, $hybridResult->getTransformed()); + } } class POPO @@ -119,5 +171,4 @@ class POPO class POPO2 extends POPO { - } diff --git a/Tests/Transformer/ModelToElasticaAutoTransformerTest.php b/Tests/Transformer/ModelToElasticaAutoTransformerTest.php index 9990fa2..646d1a4 100644 --- a/Tests/Transformer/ModelToElasticaAutoTransformerTest.php +++ b/Tests/Transformer/ModelToElasticaAutoTransformerTest.php @@ -102,6 +102,11 @@ class POPO ); } + public function getObj() + { + return array('foo' => 'foo', 'bar' => 'foo', 'id' => 1); + } + public function getUpper() { return (object) array('id' => 'parent', 'name' => 'a random name'); @@ -118,7 +123,6 @@ class ModelToElasticaAutoTransformerTest extends \PHPUnit_Framework_TestCase public function setUp() { if (!class_exists('Elastica\Document')) { - ; $this->markTestSkipped('The Elastica library classes are not available'); } } @@ -273,6 +277,25 @@ class ModelToElasticaAutoTransformerTest extends \PHPUnit_Framework_TestCase ), $data['sub']); } + public function testObjectDoesNotRequireProperties() + { + $transformer = $this->getTransformer(); + $document = $transformer->transform(new POPO(), array( + 'obj' => array( + 'type' => 'object' + ) + )); + $data = $document->getData(); + + $this->assertTrue(array_key_exists('obj', $data)); + $this->assertInternalType('array', $data['obj']); + $this->assertEquals(array( + 'foo' => 'foo', + 'bar' => 'foo', + 'id' => 1 + ), $data['obj']); + } + public function testParentMapping() { $transformer = $this->getTransformer(); diff --git a/Tests/Transformer/ModelToElasticaIdentifierTransformerTest.php b/Tests/Transformer/ModelToElasticaIdentifierTransformerTest.php new file mode 100644 index 0000000..f1a77d4 --- /dev/null +++ b/Tests/Transformer/ModelToElasticaIdentifierTransformerTest.php @@ -0,0 +1,65 @@ +id; + } + + public function getName() + { + return $this->name; + } +} + +class ModelToElasticaIdentifierTransformerTest extends \PHPUnit_Framework_TestCase +{ + public function setUp() + { + if (!class_exists('Elastica\Document')) { + $this->markTestSkipped('The Elastica library classes are not available'); + } + } + + public function testGetDocumentWithIdentifierOnly() + { + $transformer = $this->getTransformer(); + $document = $transformer->transform(new POPO(), array()); + $data = $document->getData(); + + $this->assertInstanceOf('Elastica\Document', $document); + $this->assertEquals(123, $document->getId()); + $this->assertCount(0, $data); + } + + public function testGetDocumentWithIdentifierOnlyWithFields() + { + $transformer = $this->getTransformer(); + $document = $transformer->transform(new POPO(), array('name' => array())); + $data = $document->getData(); + + $this->assertInstanceOf('Elastica\Document', $document); + $this->assertEquals(123, $document->getId()); + $this->assertCount(0, $data); + } + + /** + * @return ModelToElasticaIdentifierTransformer + */ + private function getTransformer() + { + $transformer = new ModelToElasticaIdentifierTransformer(); + $transformer->setPropertyAccessor(PropertyAccess::getPropertyAccessor()); + + return $transformer; + } +} diff --git a/Transformer/ElasticaToModelTransformerCollection.php b/Transformer/ElasticaToModelTransformerCollection.php index 6264959..a261e81 100644 --- a/Transformer/ElasticaToModelTransformerCollection.php +++ b/Transformer/ElasticaToModelTransformerCollection.php @@ -84,11 +84,4 @@ class ElasticaToModelTransformerCollection implements ElasticaToModelTransformer return $result; } - - protected function getTypeToClassMap() - { - return array_map(function (ElasticaToModelTransformerInterface $transformer) { - return $transformer->getObjectClass(); - }, $this->transformers); - } } diff --git a/Transformer/ModelToElasticaAutoTransformer.php b/Transformer/ModelToElasticaAutoTransformer.php index 6e01907..3107d0a 100644 --- a/Transformer/ModelToElasticaAutoTransformer.php +++ b/Transformer/ModelToElasticaAutoTransformer.php @@ -71,11 +71,11 @@ class ModelToElasticaAutoTransformer implements ModelToElasticaTransformerInterf $value = $this->propertyAccessor->getValue($object, $key); - if (isset($mapping['type']) && in_array($mapping['type'], array('nested', 'object'))) { + if (isset($mapping['type']) && in_array($mapping['type'], array('nested', 'object')) && isset($mapping['properties'])) { /* $value is a nested document or object. Transform $value into * an array of documents, respective the mapped properties. */ - $document->add($key, $this->transformNested($value, $mapping['properties'])); + $document->set($key, $this->transformNested($value, $mapping['properties'])); continue; } @@ -89,7 +89,7 @@ class ModelToElasticaAutoTransformer implements ModelToElasticaTransformerInterf continue; } - $document->add($key, $this->normalizeValue($value)); + $document->set($key, $this->normalizeValue($value)); } return $document; @@ -149,5 +149,4 @@ class ModelToElasticaAutoTransformer implements ModelToElasticaTransformerInterf return $value; } - } diff --git a/Transformer/ModelToElasticaIdentifierTransformer.php b/Transformer/ModelToElasticaIdentifierTransformer.php new file mode 100644 index 0000000..654850f --- /dev/null +++ b/Transformer/ModelToElasticaIdentifierTransformer.php @@ -0,0 +1,26 @@ +propertyAccessor->getValue($object, $this->options['identifier']); + return new Document($identifier); + } +} diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md new file mode 100644 index 0000000..a9a99a4 --- /dev/null +++ b/UPGRADE-3.0.md @@ -0,0 +1,32 @@ +UPGRADE FROM 2.1 to 3.0 +======================= + +### Serialization + + * you can now define a Serializer service and callback for indexing. All providers and listeners will use it. + + ```yml + serializer: + callback_class: FOS\ElasticaBundle\Serializer\Callback + serializer: serializer + ``` + +### Mapping + + * you do not have to setup any mapping anymore if you use a Serializer, properties are no more indexed only if + they are mapped. So this kind of configuration became valid: + + ```yml + serializer: + callback_class: FOS\ElasticaBundle\Serializer\Callback + serializer: serializer + indexes: + acme: + client: default + types: + Article: + persistence: + driver: orm + model: Acme\Bundle\CoreBundle\Entity\Article + provider: ~ + ```