diff --git a/.travis.yml b/.travis.yml
index a83f9b9..fbb22d1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,9 @@
language: php
+cache:
+ directories:
+ - $HOME/.composer/cache
+
php:
- 5.3
- 5.4
@@ -21,7 +25,8 @@ before_script:
- sh -c 'if [ "$SYMFONY_VERSION" != "" ]; then composer require --dev --no-update symfony/symfony=$SYMFONY_VERSION; fi;'
- composer install --dev --prefer-source
-script: vendor/bin/phpunit --coverage-clover=coverage.clover
+script:
+ - vendor/bin/phpunit --coverage-clover=coverage.clover
services:
- elasticsearch
diff --git a/CHANGELOG-3.0.md b/CHANGELOG-3.0.md
index 2596a29..973269d 100644
--- a/CHANGELOG-3.0.md
+++ b/CHANGELOG-3.0.md
@@ -12,6 +12,24 @@ 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.8 (2014-01-31)
+
+ * Fixed handling of empty indexes #760
+ * Added support for `connectionStrategy` Elastica configuration #732
+ * Allow Elastica 1.4
+
+* 3.0.7 (2015-01-21)
+
+ * Fixed the indexing of parent/child relations, broken since 3.0 #774
+ * Fixed multi_field properties not being normalised #769
+
+* 3.0.6 (2015-01-04)
+
+ * Removed unused public image asset for the web development toolbar #742
+ * Fixed is_indexable_callback BC code to support array notation #761
+ * Fixed debug_logger for type providers #724
+ * Clean the OM if we filter away the entire batch #737
+
* 3.0.0-ALPHA6
* Moved `is_indexable_callback` from the listener properties to a type property called
diff --git a/CHANGELOG-3.1.md b/CHANGELOG-3.1.md
index 0ce44ad..19bec1f 100644
--- a/CHANGELOG-3.1.md
+++ b/CHANGELOG-3.1.md
@@ -14,6 +14,20 @@ https://github.com/FriendsOfSymfony/FOSElasticaBundle/compare/v3.0.4...v3.1.0
* BC BREAK: `Doctrine\Listener#scheduleForDeletion` access changed to private.
* BC BREAK: `ObjectPersisterInterface` gains the method `handlesObject` that
returns a boolean value if it will handle a given object or not.
- * Removed `Doctrine\Listener#getSubscribedEvents`. The container
+ * BC BREAK: Removed `Doctrine\Listener#getSubscribedEvents`. The container
configuration now configures tags with the methods to call to avoid loading
- this class on every request where doctrine is active.
+ this class on every request where doctrine is active. #729
+ * Added ability to configure `date_detection`, `numeric_detection` and
+ `dynamic_date_formats` for types. #753
+ * New event `POST_TRANSFORM` which allows developers to add custom properties to
+ Elastica Documents for indexing.
+ * When available, the `fos:elastica:populate` command will now use the
+ ProgressBar helper instead of outputting strings. You can use verbosity
+ controls on the command to output additional information like memory
+ usage, runtime and estimated time.
+ * Added new option `property_path` to a type property definition to allow
+ customisation of the property path used to retrieve data from objects.
+ Setting `property_path` to `false` will configure the Transformer to ignore
+ that property while transforming. Combined with the above POST_TRANSFORM event
+ developers can now create calculated dynamic properties on Elastica documents
+ for indexing. #794
diff --git a/Command/PopulateCommand.php b/Command/PopulateCommand.php
index 89d2ebc..8b6c0f2 100644
--- a/Command/PopulateCommand.php
+++ b/Command/PopulateCommand.php
@@ -2,22 +2,30 @@
namespace FOS\ElasticaBundle\Command;
+use FOS\ElasticaBundle\Event\IndexPopulateEvent;
use FOS\ElasticaBundle\Event\PopulateEvent;
+use FOS\ElasticaBundle\Event\TypePopulateEvent;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Helper\DialogHelper;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-use FOS\ElasticaBundle\Index\IndexManager;
+use FOS\ElasticaBundle\IndexManager;
use FOS\ElasticaBundle\Provider\ProviderRegistry;
+use FOS\ElasticaBundle\Resetter;
use FOS\ElasticaBundle\Provider\ProviderInterface;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\Console\Helper\ProgressBar;
/**
* Populate the search index
*/
class PopulateCommand extends ContainerAwareCommand
{
+ /**
+ * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+ */
+ private $dispatcher;
+
/**
* @var IndexManager
*/
@@ -29,9 +37,9 @@ class PopulateCommand extends ContainerAwareCommand
private $providerRegistry;
/**
- * @var EventDispatcherInterface
+ * @var Resetter
*/
- private $eventDispatcher;
+ private $resetter;
/**
* @see Symfony\Component\Console\Command\Command::configure()
@@ -56,9 +64,10 @@ class PopulateCommand extends ContainerAwareCommand
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
+ $this->dispatcher = $this->getContainer()->get('event_dispatcher');
$this->indexManager = $this->getContainer()->get('fos_elastica.index_manager');
$this->providerRegistry = $this->getContainer()->get('fos_elastica.provider_registry');
- $this->eventDispatcher = $this->getContainer()->get('event_dispatcher');
+ $this->resetter = $this->getContainer()->get('fos_elastica.resetter');
}
/**
@@ -100,6 +109,86 @@ class PopulateCommand extends ContainerAwareCommand
}
}
+ /**
+ * @param ProviderInterface $provider
+ * @param OutputInterface $output
+ * @param string $index
+ * @param string $type
+ * @param array $options
+ */
+ private function doPopulateType(ProviderInterface $provider, OutputInterface $output, $index, $type, $options)
+ {
+ $event = new TypePopulateEvent($index, $type, $options);
+ $this->dispatcher->dispatch(TypePopulateEvent::PRE_TYPE_POPULATE, $event);
+
+ $loggerClosure = $this->getLoggerClosure($output, $index, $type);
+ $provider->populate($loggerClosure, $event->getOptions());
+
+ $this->dispatcher->dispatch(TypePopulateEvent::POST_TYPE_POPULATE, $event);
+ }
+
+ /**
+ * Builds a loggerClosure to be called from inside the Provider to update the command
+ * line.
+ *
+ * @param OutputInterface $output
+ * @param string $index
+ * @param string $type
+ * @return callable
+ */
+ private function getLoggerClosure(OutputInterface $output, $index, $type)
+ {
+ if (!class_exists('Symfony\Component\Console\Helper\ProgressBar')) {
+ $lastStep = null;
+ $current = 0;
+
+ return function ($increment, $totalObjects) use ($output, $index, $type, &$lastStep, &$current) {
+ if ($current + $increment > $totalObjects) {
+ $increment = $totalObjects - $current;
+ }
+
+ $currentTime = microtime(true);
+ $timeDifference = $currentTime - $lastStep;
+ $objectsPerSecond = $lastStep ? ($increment / $timeDifference) : $increment;
+ $lastStep = $currentTime;
+ $current += $increment;
+ $percent = 100 * $current / $totalObjects;
+
+ $output->writeln(sprintf(
+ 'Populating %s/%s %0.1f%% (%d/%d), %d objects/s (RAM: current=%uMo peak=%uMo)',
+ $index,
+ $type,
+ $percent,
+ $current,
+ $totalObjects,
+ $objectsPerSecond,
+ round(memory_get_usage() / (1024 * 1024)),
+ round(memory_get_peak_usage() / (1024 * 1024))
+ ));
+ };
+ }
+
+ ProgressBar::setFormatDefinition('normal', " %current%/%max% [%bar%] %percent:3s%%\n%message%");
+ ProgressBar::setFormatDefinition('verbose', " %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%\n%message%");
+ ProgressBar::setFormatDefinition('very_verbose', " %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%\n%message%");
+ ProgressBar::setFormatDefinition('debug', " %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%\n%message%");
+ $progress = null;
+
+ return function ($increment, $totalObjects) use (&$progress, $output, $index, $type) {
+ if (null === $progress) {
+ $progress = new ProgressBar($output, $totalObjects);
+ $progress->start();
+ }
+
+ $progress->setMessage(sprintf('Populating %s/%s', $index, $type));
+ $progress->advance($increment);
+
+ if ($progress->getProgressPercent() >= 1.0) {
+ $progress->finish();
+ }
+ };
+ }
+
/**
* Recreates an index, populates its types, and refreshes the index.
*
@@ -110,13 +199,23 @@ class PopulateCommand extends ContainerAwareCommand
*/
private function populateIndex(OutputInterface $output, $index, $reset, $options)
{
- /** @var $providers ProviderInterface[] */
+ $event = new IndexPopulateEvent($index, $reset, $options);
+ $this->dispatcher->dispatch(IndexPopulateEvent::PRE_INDEX_POPULATE, $event);
+
+ if ($event->isReset()) {
+ $output->writeln(sprintf('Resetting %s', $index));
+ $this->resetter->resetIndex($index, true);
+ }
+
$providers = $this->providerRegistry->getIndexProviders($index);
- $this->populate($output, $providers, $index, null, $reset, $options);
+ foreach ($providers as $type => $provider) {
+ $this->doPopulateType($provider, $output, $index, $type, $event->getOptions());
+ }
- $output->writeln(sprintf('Refreshing %s', $index));
- $this->indexManager->getIndex($index)->refresh();
+ $this->dispatcher->dispatch(IndexPopulateEvent::POST_INDEX_POPULATE, $event);
+
+ $this->refreshIndex($output, $index);
}
/**
@@ -130,48 +229,31 @@ class PopulateCommand extends ContainerAwareCommand
*/
private function populateIndexType(OutputInterface $output, $index, $type, $reset, $options)
{
- $provider = $this->providerRegistry->getProvider($index, $type);
+ if ($reset) {
+ $output->writeln(sprintf('Resetting %s/%s', $index, $type));
+ $this->resetter->resetIndexType($index, $type);
+ }
- $this->populate($output, array($type => $provider), $index, $type, $reset, $options);
+ $provider = $this->providerRegistry->getProvider($index, $type);
+ $this->doPopulateType($provider, $output, $index, $type, $options);
+
+ $this->refreshIndex($output, $index, false);
+ }
+
+ /**
+ * Refreshes an index.
+ *
+ * @param OutputInterface $output
+ * @param string $index
+ * @param bool $postPopulate
+ */
+ private function refreshIndex(OutputInterface $output, $index, $postPopulate = true)
+ {
+ if ($postPopulate) {
+ $this->resetter->postPopulate($index);
+ }
$output->writeln(sprintf('Refreshing %s', $index));
$this->indexManager->getIndex($index)->refresh();
}
-
- /**
- * @param OutputInterface $output
- * @param ProviderInterface[] $providers
- * @param string $index
- * @param string $type
- * @param boolean $reset
- * @param array $options
- */
- private function populate(OutputInterface $output, array $providers, $index, $type, $reset, $options)
- {
- if ($reset) {
- if ($type) {
- $output->writeln(sprintf('Resetting %s/%s', $index, $type));
- } else {
- $output->writeln(sprintf('Resetting %s', $index));
- }
- }
-
- $this->eventDispatcher->dispatch(PopulateEvent::PRE_INDEX_POPULATE, new PopulateEvent($index, $type, $reset, $options));
-
- foreach ($providers as $providerType => $provider) {
- $event = new PopulateEvent($index, $providerType, $reset, $options);
-
- $this->eventDispatcher->dispatch(PopulateEvent::PRE_TYPE_POPULATE, $event);
-
- $loggerClosure = function($message) use ($output, $index, $providerType) {
- $output->writeln(sprintf('Populating %s/%s, %s', $index, $providerType, $message));
- };
-
- $provider->populate($loggerClosure, $options);
-
- $this->eventDispatcher->dispatch(PopulateEvent::POST_TYPE_POPULATE, $event);
- }
-
- $this->eventDispatcher->dispatch(PopulateEvent::POST_INDEX_POPULATE, new PopulateEvent($index, $type, $reset, $options));
- }
}
diff --git a/Configuration/Source/ContainerSource.php b/Configuration/Source/ContainerSource.php
index 8d094c7..abcdf1b 100644
--- a/Configuration/Source/ContainerSource.php
+++ b/Configuration/Source/ContainerSource.php
@@ -40,16 +40,7 @@ class ContainerSource implements SourceInterface
{
$indexes = array();
foreach ($this->configArray as $config) {
- $types = array();
- foreach ($config['types'] as $typeConfig) {
- $types[$typeConfig['name']] = new TypeConfig(
- $typeConfig['name'],
- $typeConfig['mapping'],
- $typeConfig['config']
- );
- // TODO: handle prototypes..
- }
-
+ $types = $this->getTypes($config);
$index = new IndexConfig($config['name'], $types, array(
'elasticSearchName' => $config['elasticsearch_name'],
'settings' => $config['settings'],
@@ -61,4 +52,28 @@ class ContainerSource implements SourceInterface
return $indexes;
}
+
+ /**
+ * Builds TypeConfig objects for each type.
+ *
+ * @param array $config
+ * @return array
+ */
+ protected function getTypes($config)
+ {
+ $types = array();
+
+ if (isset($config['types'])) {
+ foreach ($config['types'] as $typeConfig) {
+ $types[$typeConfig['name']] = new TypeConfig(
+ $typeConfig['name'],
+ $typeConfig['mapping'],
+ $typeConfig['config']
+ );
+ // TODO: handle prototypes..
+ }
+ }
+
+ return $types;
+ }
}
diff --git a/Configuration/TypeConfig.php b/Configuration/TypeConfig.php
index fc9041d..a46cd34 100644
--- a/Configuration/TypeConfig.php
+++ b/Configuration/TypeConfig.php
@@ -35,6 +35,22 @@ class TypeConfig
$this->name = $name;
}
+ /**
+ * @return bool|null
+ */
+ public function getDateDetection()
+ {
+ return $this->getConfig('date_detection');
+ }
+
+ /**
+ * @return array
+ */
+ public function getDynamicDateFormats()
+ {
+ return $this->getConfig('dynamic_date_formats');
+ }
+
/**
* @return string|null
*/
@@ -61,6 +77,14 @@ class TypeConfig
null;
}
+ /**
+ * @return bool|null
+ */
+ public function getNumericDetection()
+ {
+ return $this->getConfig('numeric_detection');
+ }
+
/**
* @return string
*/
diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
index b204f0c..3b3844e 100644
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -84,6 +84,16 @@ class Configuration implements ConfigurationInterface
return $v;
})
->end()
+ // Elastica names its properties with camel case, support both
+ ->beforeNormalization()
+ ->ifTrue(function ($v) { return isset($v['connection_strategy']); })
+ ->then(function ($v) {
+ $v['connectionStrategy'] = $v['connection_strategy'];
+ unset($v['connection_strategy']);
+
+ return $v;
+ })
+ ->end()
// If there is no connections array key defined, assume a single connection.
->beforeNormalization()
->ifTrue(function ($v) { return is_array($v) && !array_key_exists('connections', $v); })
@@ -124,6 +134,7 @@ class Configuration implements ConfigurationInterface
->end()
->scalarNode('timeout')->end()
->scalarNode('headers')->end()
+ ->scalarNode('connectionStrategy')->defaultValue('Simple')->end()
->end()
->end()
->end()
@@ -199,7 +210,17 @@ class Configuration implements ConfigurationInterface
isset($v['persistence']['listener']['is_indexable_callback']);
})
->then(function ($v) {
- $v['indexable_callback'] = $v['persistence']['listener']['is_indexable_callback'];
+ $callback = $v['persistence']['listener']['is_indexable_callback'];
+
+ if (is_array($callback)) {
+ list($class) = $callback + array(null);
+
+ if ($class[0] !== '@' && is_string($class) && !class_exists($class)) {
+ $callback[0] = '@'.$class;
+ }
+ }
+
+ $v['indexable_callback'] = $callback;
unset($v['persistence']['listener']['is_indexable_callback']);
return $v;
@@ -225,7 +246,10 @@ class Configuration implements ConfigurationInterface
})
->end()
->children()
+ ->booleanNode('date_detection')->end()
+ ->arrayNode('dynamic_date_formats')->prototype('scalar')->end()->end()
->scalarNode('index_analyzer')->end()
+ ->booleanNode('numeric_detection')->end()
->scalarNode('search_analyzer')->end()
->variableNode('indexable_callback')->end()
->append($this->getPersistenceNode())
diff --git a/DependencyInjection/FOSElasticaExtension.php b/DependencyInjection/FOSElasticaExtension.php
index 705da12..1e446f7 100644
--- a/DependencyInjection/FOSElasticaExtension.php
+++ b/DependencyInjection/FOSElasticaExtension.php
@@ -241,6 +241,9 @@ class FOSElasticaExtension extends Extension
'serializer',
'index_analyzer',
'search_analyzer',
+ 'date_detection',
+ 'dynamic_date_formats',
+ 'numeric_detection',
) as $field) {
$typeConfig['config'][$field] = array_key_exists($field, $type) ?
$type[$field] :
@@ -393,7 +396,12 @@ class FOSElasticaExtension extends Extension
$arguments[] = array(new Reference($callbackId), 'serialize');
} else {
$abstractId = 'fos_elastica.object_persister';
- $arguments[] = $this->indexConfigs[$indexName]['types'][$typeName]['mapping']['properties'];
+ $mapping = $this->indexConfigs[$indexName]['types'][$typeName]['mapping'];
+ $argument = $mapping['properties'];
+ if(isset($mapping['_parent'])){
+ $argument['_parent'] = $mapping['_parent'];
+ }
+ $arguments[] = $argument;
}
$serviceId = sprintf('fos_elastica.object_persister.%s.%s', $indexName, $typeName);
diff --git a/Doctrine/AbstractProvider.php b/Doctrine/AbstractProvider.php
index 92be6ce..80d0716 100644
--- a/Doctrine/AbstractProvider.php
+++ b/Doctrine/AbstractProvider.php
@@ -39,7 +39,7 @@ abstract class AbstractProvider extends BaseAbstractProvider
}
/**
- * @see FOS\ElasticaBundle\Provider\ProviderInterface::populate()
+ * {@inheritDoc}
*/
public function populate(\Closure $loggerClosure = null, array $options = array())
{
@@ -56,34 +56,19 @@ abstract class AbstractProvider extends BaseAbstractProvider
$manager = $this->managerRegistry->getManagerForClass($this->objectClass);
for (; $offset < $nbObjects; $offset += $batchSize) {
- if ($loggerClosure) {
- $stepStartTime = microtime(true);
- }
$objects = $this->fetchSlice($queryBuilder, $batchSize, $offset);
- if ($loggerClosure) {
- $stepNbObjects = count($objects);
- }
$objects = array_filter($objects, array($this, 'isObjectIndexable'));
- if (!$objects) {
- if ($loggerClosure) {
- $loggerClosure('Entire batch was filtered away, skipping...');
- }
- if ($this->options['clear_object_manager']) {
- $manager->clear();
- }
-
- continue;
- }
-
- if (!$ignoreErrors) {
- $this->objectPersister->insertMany($objects);
- } else {
- try {
+ if ($objects) {
+ if (!$ignoreErrors) {
$this->objectPersister->insertMany($objects);
- } catch(BulkResponseException $e) {
- if ($loggerClosure) {
- $loggerClosure(sprintf('%s',$e->getMessage()));
+ } else {
+ try {
+ $this->objectPersister->insertMany($objects);
+ } catch(BulkResponseException $e) {
+ if ($loggerClosure) {
+ $loggerClosure(sprintf('%s',$e->getMessage()));
+ }
}
}
}
@@ -95,11 +80,7 @@ abstract class AbstractProvider extends BaseAbstractProvider
usleep($sleep);
if ($loggerClosure) {
- $stepCount = $stepNbObjects + $offset;
- $percentComplete = 100 * $stepCount / $nbObjects;
- $timeDifference = microtime(true) - $stepStartTime;
- $objectsPerSecond = $timeDifference ? ($stepNbObjects / $timeDifference) : $stepNbObjects;
- $loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s %s', $percentComplete, $stepCount, $nbObjects, $objectsPerSecond, $this->getMemoryUsage()));
+ $loggerClosure($batchSize, $nbObjects);
}
}
diff --git a/Event/PopulateEvent.php b/Event/IndexPopulateEvent.php
similarity index 74%
rename from Event/PopulateEvent.php
rename to Event/IndexPopulateEvent.php
index 305c393..5074f05 100644
--- a/Event/PopulateEvent.php
+++ b/Event/IndexPopulateEvent.php
@@ -17,24 +17,16 @@ use Symfony\Component\EventDispatcher\Event;
*
* @author Oleg Andreyev
*/
-class PopulateEvent extends Event
+class IndexPopulateEvent extends Event
{
const PRE_INDEX_POPULATE = 'elastica.index.index_pre_populate';
const POST_INDEX_POPULATE = 'elastica.index.index_post_populate';
- const PRE_TYPE_POPULATE = 'elastica.index.type_pre_populate';
- const POST_TYPE_POPULATE = 'elastica.index.type_post_populate';
-
/**
* @var string
*/
private $index;
- /**
- * @var string
- */
- private $type;
-
/**
* @var bool
*/
@@ -47,14 +39,12 @@ class PopulateEvent extends Event
/**
* @param string $index
- * @param string|null $type
* @param boolean $reset
* @param array $options
*/
- public function __construct($index, $type, $reset, $options)
+ public function __construct($index, $reset, $options)
{
$this->index = $index;
- $this->type = $type;
$this->reset = $reset;
$this->options = $options;
}
@@ -67,14 +57,6 @@ class PopulateEvent extends Event
return $this->index;
}
- /**
- * @return string
- */
- public function getType()
- {
- return $this->type;
- }
-
/**
* @return boolean
*/
diff --git a/Event/TransformEvent.php b/Event/TransformEvent.php
new file mode 100644
index 0000000..4f6871f
--- /dev/null
+++ b/Event/TransformEvent.php
@@ -0,0 +1,78 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace FOS\ElasticaBundle\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+
+class TransformEvent extends Event
+{
+ const POST_TRANSFORM = 'fos_elastica.post_transform';
+
+ /**
+ * @var mixed
+ */
+ private $document;
+
+ /**
+ * @var array
+ */
+ private $fields;
+
+ /**
+ * @var mixed
+ */
+ private $object;
+
+ /**
+ * @param mixed $document
+ * @param array $fields
+ * @param mixed $object
+ */
+ public function __construct($document, array $fields, $object)
+ {
+ $this->document = $document;
+ $this->fields = $fields;
+ $this->object = $object;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getDocument()
+ {
+ return $this->document;
+ }
+
+ /**
+ * @return array
+ */
+ public function getFields()
+ {
+ return $this->fields;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getObject()
+ {
+ return $this->object;
+ }
+
+ /**
+ * @param mixed $document
+ */
+ public function setDocument($document)
+ {
+ $this->document = $document;
+ }
+}
diff --git a/Event/TypePopulateEvent.php b/Event/TypePopulateEvent.php
new file mode 100644
index 0000000..9239c15
--- /dev/null
+++ b/Event/TypePopulateEvent.php
@@ -0,0 +1,80 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace FOS\ElasticaBundle\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+
+/**
+ * Populate Event
+ *
+ * @author Oleg Andreyev
+ */
+class TypePopulateEvent extends Event
+{
+ const PRE_TYPE_POPULATE = 'elastica.index.type_pre_populate';
+ const POST_TYPE_POPULATE = 'elastica.index.type_post_populate';
+
+ /**
+ * @var string
+ */
+ private $index;
+
+ /**
+ * @var string
+ */
+ private $type;
+
+ /**
+ * @var array
+ */
+ private $options;
+
+ /**
+ * @param string $index
+ * @param string $type
+ * @param array $options
+ */
+ public function __construct($index, $type, $options)
+ {
+ $this->index = $index;
+ $this->type = $type;
+ $this->options = $options;
+ }
+
+ /**
+ * @return string
+ */
+ public function getIndex()
+ {
+ return $this->index;
+ }
+
+ /**
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * @return array
+ */
+ public function getOptions()
+ {
+ return $this->options;
+ }
+
+ public function setOptions($options)
+ {
+ $this->options = $options;
+ }
+}
diff --git a/Index/MappingBuilder.php b/Index/MappingBuilder.php
index 21ae871..92beaf7 100644
--- a/Index/MappingBuilder.php
+++ b/Index/MappingBuilder.php
@@ -58,13 +58,19 @@ class MappingBuilder
*/
public function buildTypeMapping(TypeConfig $typeConfig)
{
- $mapping = array_merge($typeConfig->getMapping(), array(
- // 'date_detection' => true,
- // 'dynamic_date_formats' => array()
- // 'dynamic_templates' => $typeConfig->getDynamicTemplates(),
- // 'numeric_detection' => false,
- // 'properties' => array(),
- ));
+ $mapping = $typeConfig->getMapping();
+
+ if (null !== $typeConfig->getDynamicDateFormats()) {
+ $mapping['dynamic_date_formats'] = $typeConfig->getDynamicDateFormats();
+ }
+
+ if (null !== $typeConfig->getDateDetection()) {
+ $mapping['date_detection'] = $typeConfig->getDateDetection();
+ }
+
+ if (null !== $typeConfig->getNumericDetection()) {
+ $mapping['numeric_detection'] = $typeConfig->getNumericDetection();
+ }
if ($typeConfig->getIndexAnalyzer()) {
$mapping['index_analyzer'] = $typeConfig->getIndexAnalyzer();
@@ -104,9 +110,14 @@ class MappingBuilder
private function fixProperties(&$properties)
{
foreach ($properties as $name => &$property) {
+ unset($property['property_path']);
+
if (!isset($property['type'])) {
$property['type'] = 'string';
}
+ if ($property['type'] == 'multi_field' && isset($property['fields'])) {
+ $this->fixProperties($property['fields']);
+ }
if (isset($property['properties'])) {
$this->fixProperties($property['properties']);
}
diff --git a/Index/Resetter.php b/Index/Resetter.php
index 996bfdf..d2b3cea 100644
--- a/Index/Resetter.php
+++ b/Index/Resetter.php
@@ -62,6 +62,9 @@ class Resetter
/**
* Deletes and recreates all indexes
+ *
+ * @param bool $populating
+ * @param bool $force
*/
public function resetAllIndexes($populating = false, $force = false)
{
diff --git a/Propel/Provider.php b/Propel/Provider.php
index 38f7a61..a3af1bd 100644
--- a/Propel/Provider.php
+++ b/Propel/Provider.php
@@ -12,7 +12,7 @@ use FOS\ElasticaBundle\Provider\AbstractProvider;
class Provider extends AbstractProvider
{
/**
- * @see FOS\ElasticaBundle\Provider\ProviderInterface::populate()
+ * {@inheritDoc}
*/
public function populate(\Closure $loggerClosure = null, array $options = array())
{
@@ -23,34 +23,21 @@ class Provider extends AbstractProvider
$batchSize = isset($options['batch-size']) ? intval($options['batch-size']) : $this->options['batch_size'];
for (; $offset < $nbObjects; $offset += $batchSize) {
- if ($loggerClosure) {
- $stepStartTime = microtime(true);
- }
-
$objects = $queryClass::create()
->limit($batchSize)
->offset($offset)
->find()
->getArrayCopy();
- if ($loggerClosure) {
- $stepNbObjects = count($objects);
- }
+
$objects = array_filter($objects, array($this, 'isObjectIndexable'));
- if (!$objects) {
- $loggerClosure('Entire batch was filtered away, skipping...');
-
- continue;
+ if ($objects) {
+ $this->objectPersister->insertMany($objects);
}
- $this->objectPersister->insertMany($objects);
-
usleep($sleep);
if ($loggerClosure) {
- $stepCount = $stepNbObjects + $offset;
- $percentComplete = 100 * $stepCount / $nbObjects;
- $objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
- $loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s %s', $percentComplete, $stepCount, $nbObjects, $objectsPerSecond, $this->getMemoryUsage()));
+ $loggerClosure($batchSize, $nbObjects);
}
}
}
diff --git a/Provider/AbstractProvider.php b/Provider/AbstractProvider.php
index 82ea914..842518d 100644
--- a/Provider/AbstractProvider.php
+++ b/Provider/AbstractProvider.php
@@ -70,6 +70,7 @@ abstract class AbstractProvider implements ProviderInterface
/**
* Get string with RAM usage information (current and peak)
*
+ * @deprecated To be removed in 4.0
* @return string
*/
protected function getMemoryUsage()
diff --git a/Provider/ProviderRegistry.php b/Provider/ProviderRegistry.php
index 2142223..389d972 100644
--- a/Provider/ProviderRegistry.php
+++ b/Provider/ProviderRegistry.php
@@ -58,7 +58,7 @@ class ProviderRegistry implements ContainerAwareInterface
* Providers will be indexed by "type" strings in the returned array.
*
* @param string $index
- * @return array of ProviderInterface instances
+ * @return ProviderInterface[]
* @throws \InvalidArgumentException if no providers were registered for the index
*/
public function getIndexProviders($index)
diff --git a/Resources/config/mongodb.xml b/Resources/config/mongodb.xml
index 8e15533..703c1d2 100644
--- a/Resources/config/mongodb.xml
+++ b/Resources/config/mongodb.xml
@@ -23,7 +23,6 @@
-
diff --git a/Resources/config/transformer.xml b/Resources/config/transformer.xml
index 4ce5062..0957152 100644
--- a/Resources/config/transformer.xml
+++ b/Resources/config/transformer.xml
@@ -12,7 +12,8 @@
-
+
+
diff --git a/Resources/doc/cookbook/custom-properties.md b/Resources/doc/cookbook/custom-properties.md
new file mode 100644
index 0000000..1d7687e
--- /dev/null
+++ b/Resources/doc/cookbook/custom-properties.md
@@ -0,0 +1,33 @@
+##### Custom Properties
+
+Since FOSElasticaBundle 3.1.0, we now dispatch an event for each transformation of an
+object into an Elastica document which allows you to set custom properties on the Elastica
+document for indexing.
+
+Set up an event listener or subscriber for
+`FOS\ElasticaBundle\Event\TransformEvent::POST_TRANSFORM` to be able to inject your own
+parameters.
+
+```php
+class CustomPropertyListener implements EventSubscriberInterface
+{
+ private $anotherService;
+
+ // ...
+
+ public function addCustomProperty(TransformEvent $event)
+ {
+ $document = $event->getDocument();
+ $custom = $this->anotherService->calculateCustom($event->getObject());
+
+ $document->set('custom', $custom);
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array(
+ TransformEvent::POST_TRANSFORM => 'addCustomProperty',
+ );
+ }
+}
+```
diff --git a/Resources/doc/cookbook/manual-provider.md b/Resources/doc/cookbook/manual-provider.md
index f4365da..ed5568e 100644
--- a/Resources/doc/cookbook/manual-provider.md
+++ b/Resources/doc/cookbook/manual-provider.md
@@ -8,7 +8,7 @@ index and type for which the service will provide.
# app/config/config.yml
services:
acme.search_provider.user:
- class: Acme\UserBundle\Search\UserProvider
+ class: Acme\UserBundle\Provider\UserProvider
arguments:
- @fos_elastica.index.website.user
tags:
diff --git a/Resources/doc/cookbook/multiple-connections.md b/Resources/doc/cookbook/multiple-connections.md
index 7b5226c..9544359 100644
--- a/Resources/doc/cookbook/multiple-connections.md
+++ b/Resources/doc/cookbook/multiple-connections.md
@@ -11,6 +11,11 @@ fos_elastica:
connections:
- url: http://es1.example.net:9200
- url: http://es2.example.net:9200
+ connection_strategy: RoundRobin
```
+Elastica allows for definition of different connection strategies and by default
+supports `RoundRobin` and `Simple`. You can see definitions for these strategies
+in the `Elastica\Connection\Strategy` namespace.
+
For more information on Elastica clustering see http://elastica.io/getting-started/installation.html#section-connect-cluster
diff --git a/Resources/doc/cookbook/suppress-server-errors.md b/Resources/doc/cookbook/suppress-server-errors.md
index e4e371e..72c7b38 100644
--- a/Resources/doc/cookbook/suppress-server-errors.md
+++ b/Resources/doc/cookbook/suppress-server-errors.md
@@ -23,7 +23,7 @@ namespace Acme\ElasticaBundle;
use Elastica\Exception\ExceptionInterface;
use Elastica\Request;
use Elastica\Response;
-use FOS\ElasticaBundle\Client as BaseClient;
+use FOS\ElasticaBundle\Elastica\Client as BaseClient;
class Client extends BaseClient
{
diff --git a/Resources/doc/index.md b/Resources/doc/index.md
index 349723b..c856798 100644
--- a/Resources/doc/index.md
+++ b/Resources/doc/index.md
@@ -13,6 +13,7 @@ Cookbook Entries
----------------
* [Aliased Indexes](cookbook/aliased-indexes.md)
+* [Custom Indexed Properties](cookbook/custom-properties.md)
* [Custom Repositories](cookbook/custom-repositories.md)
* [HTTP Headers for Elastica](cookbook/elastica-client-http-headers.md)
* Performance - [Logging](cookbook/logging.md)
diff --git a/Resources/doc/setup.md b/Resources/doc/setup.md
index 16e427f..ea3f769 100644
--- a/Resources/doc/setup.md
+++ b/Resources/doc/setup.md
@@ -1,40 +1,50 @@
Step 1: Setting up the bundle
=============================
-A) Install FOSElasticaBundle
-----------------------------
+A: Download the Bundle
+----------------------
-FOSElasticaBundle is installed using [Composer](https://getcomposer.org).
+Open a command console, enter your project directory and execute the
+following command to download the latest stable version of this bundle:
```bash
-$ php composer.phar require friendsofsymfony/elastica-bundle
+$ composer require friendsofsymfony/elastica-bundle "~3.0"
```
+This command requires you to have Composer installed globally, as explained
+in the [installation chapter](https://getcomposer.org/doc/00-intro.md)
+of the Composer documentation.
+
### Elasticsearch
-Instructions for installing and deploying Elasticsearch may be found
-[here](http://www.elasticsearch.org/guide/reference/setup/installation/).
+Instructions for installing and deploying Elasticsearch may be found [here](http://www.elasticsearch.org/guide/reference/setup/installation/).
+Step 2: Enable the Bundle
+-------------------------
-B) Enable FOSElasticaBundle
----------------------------
-
-Enable FOSElasticaBundle in your AppKernel:
+Then, enable the bundle by adding the following line in the `app/AppKernel.php`
+file of your project:
```php
indexableUsername`, and the indexed field `firstName` would be populated from a
+key `first` from an array on `User->names`.
+
+Setting the property path to `false` will disable transformation of that value. In this
+case the mapping will be created but no value will be populated while indexing. You can
+populate this value by listening to the `POST_TRANSFORM` event emitted by this bundle.
+See [cookbook/custom-properties.md](cookbook/custom-properties.md) for more information
+about this event.
+
Handling missing results with FOSElasticaBundle
-----------------------------------------------
diff --git a/Resources/doc/usage.md b/Resources/doc/usage.md
index 37514b3..588532c 100644
--- a/Resources/doc/usage.md
+++ b/Resources/doc/usage.md
@@ -160,7 +160,7 @@ fos_elastica:
site:
settings:
index:
- analysis:
+ analysis:
analyzer:
my_analyzer:
type: snowball
diff --git a/Resources/public/images/elastica.png b/Resources/public/images/elastica.png
deleted file mode 100644
index dbde014..0000000
Binary files a/Resources/public/images/elastica.png and /dev/null differ
diff --git a/Resources/views/Collector/elastica.html.twig b/Resources/views/Collector/elastica.html.twig
index e6d7072..82a3dcf 100644
--- a/Resources/views/Collector/elastica.html.twig
+++ b/Resources/views/Collector/elastica.html.twig
@@ -23,7 +23,7 @@
{% block menu %}
-
+
Elastica
{{ collector.querycount }}
diff --git a/Tests/DependencyInjection/FOSElasticaExtensionTest.php b/Tests/DependencyInjection/FOSElasticaExtensionTest.php
new file mode 100644
index 0000000..feb520f
--- /dev/null
+++ b/Tests/DependencyInjection/FOSElasticaExtensionTest.php
@@ -0,0 +1,32 @@
+setParameter('kernel.debug', true);
+
+ $extension = new FOSElasticaExtension;
+
+ $extension->load($config, $containerBuilder);
+
+ $this->assertTrue($containerBuilder->hasDefinition('fos_elastica.object_persister.test_index.child_field'));
+
+ $persisterCallDefinition = $containerBuilder->getDefinition('fos_elastica.object_persister.test_index.child_field');
+
+ $arguments = $persisterCallDefinition->getArguments();
+ $arguments = $arguments['index_3'];
+
+ $this->assertArrayHasKey('_parent', $arguments);
+ $this->assertEquals('parent_field', $arguments['_parent']['type']);
+ }
+}
diff --git a/Tests/DependencyInjection/fixtures/config.yml b/Tests/DependencyInjection/fixtures/config.yml
new file mode 100644
index 0000000..5528d18
--- /dev/null
+++ b/Tests/DependencyInjection/fixtures/config.yml
@@ -0,0 +1,21 @@
+fos_elastica:
+ clients:
+ default:
+ url: http://localhost:9200
+ indexes:
+ test_index:
+ client: default
+ types:
+ parent_field:
+ mappings:
+ text: ~
+ persistence:
+ driver: orm
+ model: foo_model
+ child_field:
+ mappings:
+ text: ~
+ persistence:
+ driver: orm
+ model: foo_model
+ _parent: { type: "parent_field", property: "parent" }
diff --git a/Tests/Functional/ClientTest.php b/Tests/Functional/ClientTest.php
new file mode 100644
index 0000000..8a6357a
--- /dev/null
+++ b/Tests/Functional/ClientTest.php
@@ -0,0 +1,48 @@
+
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace FOS\ElasticaBundle\Tests\Functional;
+
+use Symfony\Bundle\FrameworkBundle\Client;
+
+/**
+ * @group functional
+ */
+class ClientTest extends WebTestCase
+{
+ public function testContainerSource()
+ {
+ $client = $this->createClient(array('test_case' => 'Basic'));
+
+ $es = $client->getContainer()->get('fos_elastica.client.default');
+ $this->assertInstanceOf('Elastica\\Connection\\Strategy\\RoundRobin', $es->getConnectionStrategy());
+
+ $es = $client->getContainer()->get('fos_elastica.client.second_server');
+ $this->assertInstanceOf('Elastica\\Connection\\Strategy\\RoundRobin', $es->getConnectionStrategy());
+
+ $es = $client->getContainer()->get('fos_elastica.client.third');
+ $this->assertInstanceOf('Elastica\\Connection\\Strategy\\Simple', $es->getConnectionStrategy());
+ }
+
+ protected function setUp()
+ {
+ parent::setUp();
+
+ $this->deleteTmpDir('Basic');
+ }
+
+ protected function tearDown()
+ {
+ parent::tearDown();
+
+ $this->deleteTmpDir('Basic');
+ }
+}
diff --git a/Tests/Functional/MappingToElasticaTest.php b/Tests/Functional/MappingToElasticaTest.php
index f42df61..197dcca 100644
--- a/Tests/Functional/MappingToElasticaTest.php
+++ b/Tests/Functional/MappingToElasticaTest.php
@@ -32,6 +32,10 @@ class MappingToElasticaTest extends WebTestCase
$this->assertTrue($mapping['type']['properties']['field1']['store']);
$this->assertArrayNotHasKey('store', $mapping['type']['properties']['field2']);
+ $type = $this->getType($client, 'type');
+ $mapping = $type->getMapping();
+ $this->assertEquals('parent', $mapping['type']['_parent']['type']);
+
$parent = $this->getType($client, 'parent');
$mapping = $parent->getMapping();
@@ -49,6 +53,9 @@ class MappingToElasticaTest extends WebTestCase
$mapping = $type->getMapping();
$this->assertNotEmpty($mapping, 'Mapping was populated');
+ $this->assertFalse($mapping['type']['date_detection']);
+ $this->assertTrue($mapping['type']['numeric_detection']);
+ $this->assertEquals(array('yyyy-MM-dd'), $mapping['type']['dynamic_date_formats']);
$this->assertArrayHasKey('store', $mapping['type']['properties']['field1']);
$this->assertTrue($mapping['type']['properties']['field1']['store']);
$this->assertArrayNotHasKey('store', $mapping['type']['properties']['field2']);
@@ -105,6 +112,7 @@ class MappingToElasticaTest extends WebTestCase
/**
* @param Client $client
+ * @param string $type
* @return \Elastica\Type
*/
private function getType(Client $client, $type = 'type')
diff --git a/Tests/Functional/PropertyPathTest.php b/Tests/Functional/PropertyPathTest.php
new file mode 100644
index 0000000..860cb86
--- /dev/null
+++ b/Tests/Functional/PropertyPathTest.php
@@ -0,0 +1,54 @@
+
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace FOS\ElasticaBundle\Tests\Functional;
+
+use Elastica\Query\Match;
+
+/**
+ * @group functional
+ */
+class PropertyPathTest extends WebTestCase
+{
+ public function testContainerSource()
+ {
+ $client = $this->createClient(array('test_case' => 'ORM'));
+ /** @var \FOS\ElasticaBundle\Persister\ObjectPersister $persister */
+ $persister = $client->getContainer()->get('fos_elastica.object_persister.index.property_paths_type');
+ $obj = new TypeObj();
+ $obj->coll = 'Hello';
+ $persister->insertOne($obj);
+
+ /** @var \Elastica\Index $elClient */
+ $index = $client->getContainer()->get('fos_elastica.index.index');
+ $index->flush(true);
+
+ $query = new Match();
+ $query->setField('something', 'Hello');
+ $search = $index->createSearch($query);
+
+ $this->assertEquals(1, $search->count());
+ }
+
+ protected function setUp()
+ {
+ parent::setUp();
+
+ $this->deleteTmpDir('Basic');
+ }
+
+ protected function tearDown()
+ {
+ parent::tearDown();
+
+ $this->deleteTmpDir('Basic');
+ }
+}
diff --git a/Tests/Functional/TypeObj.php b/Tests/Functional/TypeObj.php
index 46e5968..39e9fe9 100644
--- a/Tests/Functional/TypeObj.php
+++ b/Tests/Functional/TypeObj.php
@@ -13,8 +13,10 @@ namespace FOS\ElasticaBundle\Tests\Functional;
class TypeObj
{
+ public $id = 5;
public $coll;
public $field1;
+ public $field2;
public function isIndexable()
{
diff --git a/Tests/Functional/app/Basic/config.yml b/Tests/Functional/app/Basic/config.yml
index 3c3d369..8ed88eb 100644
--- a/Tests/Functional/app/Basic/config.yml
+++ b/Tests/Functional/app/Basic/config.yml
@@ -15,7 +15,12 @@ fos_elastica:
- url: http://localhost:9200
- host: localhost
port: 9200
+ connectionStrategy: RoundRobin
second_server:
+ connections:
+ - url: http://localhost:9200
+ connection_strategy: RoundRobin
+ third:
url: http://localhost:9200
indexes:
index:
@@ -46,6 +51,8 @@ fos_elastica:
index_analyzer: my_analyzer
type:
search_analyzer: my_analyzer
+ date_detection: false
+ dynamic_date_formats: [ 'yyyy-MM-dd' ]
dynamic_templates:
- dates:
match: "date_*"
@@ -56,6 +63,7 @@ fos_elastica:
mapping:
analyzer: english
type: string
+ numeric_detection: true
properties:
field1: ~
field2:
@@ -71,6 +79,11 @@ fos_elastica:
properties:
date: { boost: 5 }
content: ~
+ multiple:
+ type: "multi_field"
+ properties:
+ name: ~
+ position: ~
user:
type: "object"
approver:
@@ -87,3 +100,4 @@ fos_elastica:
identifier: "id"
null_mappings:
mappings: ~
+ empty_index: ~
diff --git a/Tests/Functional/app/ORM/config.yml b/Tests/Functional/app/ORM/config.yml
index 98c9221..d2ff931 100644
--- a/Tests/Functional/app/ORM/config.yml
+++ b/Tests/Functional/app/ORM/config.yml
@@ -65,6 +65,18 @@ fos_elastica:
provider: ~
listener:
is_indexable_callback: [ 'FOS\ElasticaBundle\Tests\Functional\app\ORM\IndexableService', 'isntIndexable' ]
+ property_paths_type:
+ persistence:
+ driver: orm
+ model: FOS\ElasticaBundle\Tests\Functional\TypeObj
+ provider: ~
+ properties:
+ field1:
+ property_path: field2
+ something:
+ property_path: coll
+ dynamic:
+ property_path: false
second_index:
index_name: foselastica_orm_test_second_%kernel.environment%
types:
diff --git a/Tests/Functional/app/Serializer/config.yml b/Tests/Functional/app/Serializer/config.yml
index 9bea4ba..de7caec 100644
--- a/Tests/Functional/app/Serializer/config.yml
+++ b/Tests/Functional/app/Serializer/config.yml
@@ -28,7 +28,7 @@ fos_elastica:
serializer: ~
indexes:
index:
- index_name: foselastica_test_%kernel.environment%
+ index_name: foselastica_ser_test_%kernel.environment%
types:
type:
properties:
diff --git a/Tests/Transformer/ModelToElasticaAutoTransformerTest.php b/Tests/Transformer/ModelToElasticaAutoTransformerTest.php
index 1fa6a8e..1dbf5fd 100644
--- a/Tests/Transformer/ModelToElasticaAutoTransformerTest.php
+++ b/Tests/Transformer/ModelToElasticaAutoTransformerTest.php
@@ -2,6 +2,7 @@
namespace FOS\ElasticaBundle\Tests\Transformer\ModelToElasticaAutoTransformer;
+use FOS\ElasticaBundle\Event\TransformEvent;
use FOS\ElasticaBundle\Transformer\ModelToElasticaAutoTransformer;
use Symfony\Component\PropertyAccess\PropertyAccess;
@@ -132,6 +133,35 @@ class ModelToElasticaAutoTransformerTest extends \PHPUnit_Framework_TestCase
}
}
+ public function testTransformerDispatches()
+ {
+ $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')
+ ->getMock();
+ $dispatcher->expects($this->once())
+ ->method('dispatch')
+ ->with(
+ TransformEvent::POST_TRANSFORM,
+ $this->isInstanceOf('FOS\ElasticaBundle\Event\TransformEvent')
+ );
+
+ $transformer = $this->getTransformer($dispatcher);
+ $transformer->transform(new POPO(), array());
+ }
+
+ public function testPropertyPath()
+ {
+ $transformer = $this->getTransformer();
+
+ $document = $transformer->transform(new POPO(), array('name' => array('property_path' => false)));
+ $this->assertInstanceOf('Elastica\Document', $document);
+ $this->assertFalse($document->has('name'));
+
+ $document = $transformer->transform(new POPO(), array('realName' => array('property_path' => 'name')));
+ $this->assertInstanceOf('Elastica\Document', $document);
+ $this->assertTrue($document->has('realName'));
+ $this->assertEquals('someName', $document->get('realName'));
+ }
+
public function testThatCanTransformObject()
{
$transformer = $this->getTransformer();
@@ -250,7 +280,7 @@ class ModelToElasticaAutoTransformerTest extends \PHPUnit_Framework_TestCase
$document = $transformer->transform(new POPO(), array(
'sub' => array(
'type' => 'nested',
- 'properties' => array('foo' => '~')
+ 'properties' => array('foo' => array())
)
));
$data = $document->getData();
@@ -295,8 +325,8 @@ class ModelToElasticaAutoTransformerTest extends \PHPUnit_Framework_TestCase
$this->assertTrue(array_key_exists('obj', $data));
$this->assertInternalType('array', $data['obj']);
$this->assertEquals(array(
- 'foo' => 'foo',
- 'bar' => 'foo',
+ 'foo' => 'foo',
+ 'bar' => 'foo',
'id' => 1
), $data['obj']);
}
@@ -387,11 +417,12 @@ class ModelToElasticaAutoTransformerTest extends \PHPUnit_Framework_TestCase
}
/**
+ * @param null|\Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
* @return ModelToElasticaAutoTransformer
*/
- private function getTransformer()
+ private function getTransformer($dispatcher = null)
{
- $transformer = new ModelToElasticaAutoTransformer();
+ $transformer = new ModelToElasticaAutoTransformer(array(), $dispatcher);
$transformer->setPropertyAccessor(PropertyAccess::getPropertyAccessor());
return $transformer;
diff --git a/Transformer/ModelToElasticaAutoTransformer.php b/Transformer/ModelToElasticaAutoTransformer.php
index 106db15..6a9fbca 100644
--- a/Transformer/ModelToElasticaAutoTransformer.php
+++ b/Transformer/ModelToElasticaAutoTransformer.php
@@ -2,6 +2,8 @@
namespace FOS\ElasticaBundle\Transformer;
+use FOS\ElasticaBundle\Event\TransformEvent;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Elastica\Document;
@@ -12,6 +14,11 @@ use Elastica\Document;
*/
class ModelToElasticaAutoTransformer implements ModelToElasticaTransformerInterface
{
+ /**
+ * @var EventDispatcherInterface
+ */
+ protected $dispatcher;
+
/**
* Optional parameters
*
@@ -32,10 +39,12 @@ class ModelToElasticaAutoTransformer implements ModelToElasticaTransformerInterf
* Instanciates a new Mapper
*
* @param array $options
+ * @param EventDispatcherInterface $dispatcher
*/
- public function __construct(array $options = array())
+ public function __construct(array $options = array(), EventDispatcherInterface $dispatcher = null)
{
$this->options = array_merge($this->options, $options);
+ $this->dispatcher = $dispatcher;
}
/**
@@ -66,16 +75,24 @@ class ModelToElasticaAutoTransformer implements ModelToElasticaTransformerInterf
$property = (null !== $mapping['property'])?$mapping['property']:$mapping['type'];
$value = $this->propertyAccessor->getValue($object, $property);
$document->setParent($this->propertyAccessor->getValue($value, $mapping['identifier']));
+
continue;
}
- $value = $this->propertyAccessor->getValue($object, $key);
+ $path = isset($mapping['property_path']) ?
+ $mapping['property_path'] :
+ $key;
+ if (false === $path) {
+ continue;
+ }
+ $value = $this->propertyAccessor->getValue($object, $path);
if (isset($mapping['type']) && in_array($mapping['type'], array('nested', 'object')) && isset($mapping['properties']) && !empty($mapping['properties'])) {
/* $value is a nested document or object. Transform $value into
* an array of documents, respective the mapped properties.
*/
$document->set($key, $this->transformNested($value, $mapping['properties']));
+
continue;
}
@@ -86,12 +103,20 @@ class ModelToElasticaAutoTransformer implements ModelToElasticaTransformerInterf
} else {
$document->addFileContent($key, $value);
}
+
continue;
}
$document->set($key, $this->normalizeValue($value));
}
+ if ($this->dispatcher) {
+ $event = new TransformEvent($document, $fields, $object);
+ $this->dispatcher->dispatch(TransformEvent::POST_TRANSFORM, $event);
+
+ $document = $event->getDocument();
+ }
+
return $document;
}
diff --git a/composer.json b/composer.json
index 833621a..917e839 100644
--- a/composer.json
+++ b/composer.json
@@ -17,7 +17,7 @@
"symfony/console": "~2.1",
"symfony/form": "~2.1",
"symfony/property-access": "~2.2",
- "ruflin/elastica": ">=0.90.10.0, <1.4-dev",
+ "ruflin/elastica": ">=0.90.10.0, <1.5-dev",
"psr/log": "~1.0"
},
"require-dev":{