Compare commits
2 commits
Author | SHA1 | Date | |
---|---|---|---|
5a45392687 | |||
7062ce1701 |
|
@ -1,61 +0,0 @@
|
|||
CHANGELOG for 3.1.x
|
||||
===================
|
||||
|
||||
This changelog references the relevant changes (bug and security fixes) done
|
||||
in 3.1 versions.
|
||||
|
||||
To get the diff for a specific change, go to
|
||||
https://github.com/FriendsOfSymfony/FOSElasticaBundle/commit/XXX where XXX is
|
||||
the commit hash. To get the diff between two versions, go to
|
||||
https://github.com/FriendsOfSymfony/FOSElasticaBundle/compare/v3.0.4...v3.1.0
|
||||
|
||||
* 3.1.3 (2015-04-02)
|
||||
|
||||
* Fix Symfony 2.3 compatibility
|
||||
|
||||
* 3.1.2 (2015-03-27)
|
||||
|
||||
* Fix the previous release
|
||||
|
||||
* 3.1.1 (2015-03-27)
|
||||
|
||||
* Fix PopulateCommand trying to set formats for ProgressBar in Symfony < 2.5
|
||||
* Fix Provider implementations that depend on a batch size from going into
|
||||
infinite loops
|
||||
|
||||
* 3.1.0 (2015-03-18)
|
||||
|
||||
* 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.
|
||||
* 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. #729
|
||||
* BC BREAK: Added methods for retrieving aggregations when paginating results.
|
||||
The `PaginationAdapterInterface` has a new method, `getAggregations`. #726
|
||||
* 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
|
||||
* Fixed a case where ProgressCommand would always ignore errors regardless of
|
||||
--ignore-errors being passed or not.
|
||||
* Added a `SliceFetcher` abstraction for Doctrine providers that get more
|
||||
information about the previous slice allowing for optimising queries during
|
||||
population. #725
|
||||
* New events `PRE_INDEX_POPULATE`, `POST_INDEX_POPULATE`, `PRE_TYPE_POPULATE` and
|
||||
`POST_TYPE_POPULATE` allow for monitoring when an index is about to be or has
|
||||
just been populated. #744
|
||||
* New events `PRE_INDEX_RESET`, `POST_INDEX_RESET`, `PRE_TYPE_RESET` and
|
||||
`POST_TYPE_RESET` are run before and after operations that will reset an
|
||||
index. #744
|
||||
* Added indexable callback support for the __invoke method of a service. #823
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Command;
|
||||
|
||||
use FOS\ElasticaBundle\Event\IndexPopulateEvent;
|
||||
use FOS\ElasticaBundle\Event\TypePopulateEvent;
|
||||
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
|
||||
use Symfony\Component\Console\Helper\DialogHelper;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
@ -12,28 +10,18 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||
use FOS\ElasticaBundle\IndexManager;
|
||||
use FOS\ElasticaBundle\Provider\ProviderRegistry;
|
||||
use FOS\ElasticaBundle\Resetter;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use FOS\ElasticaBundle\Provider\ProviderInterface;
|
||||
|
||||
/**
|
||||
* Populate the search index.
|
||||
*/
|
||||
class PopulateCommand extends ContainerAwareCommand
|
||||
{
|
||||
/**
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
/**
|
||||
* @var IndexManager
|
||||
*/
|
||||
private $indexManager;
|
||||
|
||||
/**
|
||||
* @var ProgressClosureBuilder
|
||||
*/
|
||||
private $progressClosureBuilder;
|
||||
|
||||
/**
|
||||
* @var ProviderRegistry
|
||||
*/
|
||||
|
@ -58,46 +46,31 @@ class PopulateCommand extends ContainerAwareCommand
|
|||
->addOption('sleep', null, InputOption::VALUE_REQUIRED, 'Sleep time between persisting iterations (microseconds)', 0)
|
||||
->addOption('batch-size', null, InputOption::VALUE_REQUIRED, 'Index packet size (overrides provider config option)')
|
||||
->addOption('ignore-errors', null, InputOption::VALUE_NONE, 'Do not stop on errors')
|
||||
->addOption('no-overwrite-format', null, InputOption::VALUE_NONE, 'Prevent this command from overwriting ProgressBar\'s formats')
|
||||
->setDescription('Populates search indexes from providers')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see Symfony\Component\Console\Command\Command::initialize()
|
||||
*/
|
||||
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->resetter = $this->getContainer()->get('fos_elastica.resetter');
|
||||
$this->progressClosureBuilder = new ProgressClosureBuilder();
|
||||
|
||||
if (!$input->getOption('no-overwrite-format') && class_exists('Symfony\\Component\\Console\\Helper\\ProgressBar')) {
|
||||
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%");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see Symfony\Component\Console\Command\Command::execute()
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$index = $input->getOption('index');
|
||||
$type = $input->getOption('type');
|
||||
$reset = !$input->getOption('no-reset');
|
||||
$options = array(
|
||||
'ignore_errors' => $input->getOption('ignore-errors'),
|
||||
'offset' => $input->getOption('offset'),
|
||||
'sleep' => $input->getOption('sleep')
|
||||
);
|
||||
if ($input->getOption('batch-size')) {
|
||||
$options['batch_size'] = (int) $input->getOption('batch-size');
|
||||
}
|
||||
$index = $input->getOption('index');
|
||||
$type = $input->getOption('type');
|
||||
$reset = !$input->getOption('no-reset');
|
||||
$options = $input->getOptions();
|
||||
|
||||
$options['ignore-errors'] = $input->hasOption('ignore-errors');
|
||||
|
||||
if ($input->isInteractive() && $reset && $input->getOption('offset')) {
|
||||
/** @var DialogHelper $dialog */
|
||||
|
@ -136,22 +109,25 @@ class PopulateCommand extends ContainerAwareCommand
|
|||
*/
|
||||
private function populateIndex(OutputInterface $output, $index, $reset, $options)
|
||||
{
|
||||
$event = new IndexPopulateEvent($index, $reset, $options);
|
||||
$this->dispatcher->dispatch(IndexPopulateEvent::PRE_INDEX_POPULATE, $event);
|
||||
|
||||
if ($event->isReset()) {
|
||||
if ($reset) {
|
||||
$output->writeln(sprintf('<info>Resetting</info> <comment>%s</comment>', $index));
|
||||
$this->resetter->resetIndex($index, true);
|
||||
}
|
||||
|
||||
$types = array_keys($this->providerRegistry->getIndexProviders($index));
|
||||
foreach ($types as $type) {
|
||||
$this->populateIndexType($output, $index, $type, false, $event->getOptions());
|
||||
/** @var $providers ProviderInterface[] */
|
||||
$providers = $this->providerRegistry->getIndexProviders($index);
|
||||
|
||||
foreach ($providers as $type => $provider) {
|
||||
$loggerClosure = function ($message) use ($output, $index, $type) {
|
||||
$output->writeln(sprintf('<info>Populating</info> %s/%s, %s', $index, $type, $message));
|
||||
};
|
||||
|
||||
$provider->populate($loggerClosure, $options);
|
||||
}
|
||||
|
||||
$this->dispatcher->dispatch(IndexPopulateEvent::POST_INDEX_POPULATE, $event);
|
||||
|
||||
$this->refreshIndex($output, $index);
|
||||
$output->writeln(sprintf('<info>Refreshing</info> <comment>%s</comment>', $index));
|
||||
$this->resetter->postPopulate($index);
|
||||
$this->indexManager->getIndex($index)->refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,35 +141,17 @@ class PopulateCommand extends ContainerAwareCommand
|
|||
*/
|
||||
private function populateIndexType(OutputInterface $output, $index, $type, $reset, $options)
|
||||
{
|
||||
$event = new TypePopulateEvent($index, $type, $reset, $options);
|
||||
$this->dispatcher->dispatch(TypePopulateEvent::PRE_TYPE_POPULATE, $event);
|
||||
|
||||
if ($event->isReset()) {
|
||||
if ($reset) {
|
||||
$output->writeln(sprintf('<info>Resetting</info> <comment>%s/%s</comment>', $index, $type));
|
||||
$this->resetter->resetIndexType($index, $type);
|
||||
}
|
||||
|
||||
$loggerClosure = function ($message) use ($output, $index, $type) {
|
||||
$output->writeln(sprintf('<info>Populating</info> %s/%s, %s', $index, $type, $message));
|
||||
};
|
||||
|
||||
$provider = $this->providerRegistry->getProvider($index, $type);
|
||||
$loggerClosure = $this->progressClosureBuilder->build($output, 'Populating', $index, $type);
|
||||
$provider->populate($loggerClosure, $event->getOptions());
|
||||
|
||||
$this->dispatcher->dispatch(TypePopulateEvent::POST_TYPE_POPULATE, $event);
|
||||
|
||||
$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);
|
||||
}
|
||||
$provider->populate($loggerClosure, $options);
|
||||
|
||||
$output->writeln(sprintf('<info>Refreshing</info> <comment>%s</comment>', $index));
|
||||
$this->indexManager->getIndex($index)->refresh();
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Tim Nagel <tim@nagel.com.au>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ProgressClosureBuilder
|
||||
{
|
||||
/**
|
||||
* Builds a loggerClosure to be called from inside the Provider to update the command
|
||||
* line.
|
||||
*
|
||||
* @param OutputInterface $output
|
||||
* @param string $action
|
||||
* @param string $index
|
||||
* @param string $type
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public function build(OutputInterface $output, $action, $index, $type)
|
||||
{
|
||||
if (!class_exists('Symfony\Component\Console\Helper\ProgressBar') ||
|
||||
!is_callable(array('Symfony\Component\Console\Helper\ProgressBar', 'getProgress'))) {
|
||||
return $this->buildLegacy($output, $action, $index, $type);
|
||||
}
|
||||
|
||||
$progress = null;
|
||||
|
||||
return function ($increment, $totalObjects, $message = null) use (&$progress, $output, $action, $index, $type) {
|
||||
if (null === $progress) {
|
||||
$progress = new ProgressBar($output, $totalObjects);
|
||||
$progress->start();
|
||||
}
|
||||
|
||||
if (null !== $message) {
|
||||
$progress->clear();
|
||||
$output->writeln(sprintf('<info>%s</info> <error>%s</error>', $action, $message));
|
||||
$progress->display();
|
||||
}
|
||||
|
||||
$progress->setMessage(sprintf('<info>%s</info> <comment>%s/%s</comment>', $action, $index, $type));
|
||||
$progress->advance($increment);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a legacy closure that outputs lines for each step. Used in cases
|
||||
* where the ProgressBar component doesnt exist or does not have the correct
|
||||
* methods to support what we need.
|
||||
*
|
||||
* @param OutputInterface $output
|
||||
* @param string $action
|
||||
* @param string $index
|
||||
* @param string $type
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
private function buildLegacy(OutputInterface $output, $action, $index, $type)
|
||||
{
|
||||
$lastStep = null;
|
||||
$current = 0;
|
||||
|
||||
return function ($increment, $totalObjects, $message = null) use ($output, $action, $index, $type, &$lastStep, &$current) {
|
||||
if ($current + $increment > $totalObjects) {
|
||||
$increment = $totalObjects - $current;
|
||||
}
|
||||
|
||||
if (null !== $message) {
|
||||
$output->writeln(sprintf('<info>%s</info> <error>%s</error>', $action, $message));
|
||||
}
|
||||
|
||||
$currentTime = microtime(true);
|
||||
$timeDifference = $currentTime - $lastStep;
|
||||
$objectsPerSecond = $lastStep ? ($increment / $timeDifference) : $increment;
|
||||
$lastStep = $currentTime;
|
||||
$current += $increment;
|
||||
$percent = 100 * $current / $totalObjects;
|
||||
|
||||
$output->writeln(sprintf(
|
||||
'<info>%s</info> <comment>%s/%s</comment> %0.1f%% (%d/%d), %d objects/s (RAM: current=%uMo peak=%uMo)',
|
||||
$action,
|
||||
$index,
|
||||
$type,
|
||||
$percent,
|
||||
$current,
|
||||
$totalObjects,
|
||||
$objectsPerSecond,
|
||||
round(memory_get_usage() / (1024 * 1024)),
|
||||
round(memory_get_peak_usage() / (1024 * 1024))
|
||||
));
|
||||
};
|
||||
}
|
||||
}
|
|
@ -35,22 +35,6 @@ 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
|
||||
*/
|
||||
|
@ -77,14 +61,6 @@ class TypeConfig
|
|||
null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|null
|
||||
*/
|
||||
public function getNumericDetection()
|
||||
{
|
||||
return $this->getConfig('numeric_detection');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
|
|
@ -250,10 +250,7 @@ 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())
|
||||
|
|
|
@ -246,9 +246,6 @@ 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] :
|
||||
|
@ -482,30 +479,19 @@ class FOSElasticaExtension extends Extension
|
|||
$listenerId = sprintf('fos_elastica.listener.%s.%s', $indexName, $typeName);
|
||||
$listenerDef = new DefinitionDecorator($abstractListenerId);
|
||||
$listenerDef->replaceArgument(0, new Reference($objectPersisterId));
|
||||
$listenerDef->replaceArgument(2, array(
|
||||
$listenerDef->replaceArgument(1, $this->getDoctrineEvents($typeConfig));
|
||||
$listenerDef->replaceArgument(3, array(
|
||||
'identifier' => $typeConfig['identifier'],
|
||||
'indexName' => $indexName,
|
||||
'typeName' => $typeName,
|
||||
));
|
||||
$listenerDef->replaceArgument(3, $typeConfig['listener']['logger'] ?
|
||||
new Reference($typeConfig['listener']['logger']) :
|
||||
null
|
||||
);
|
||||
|
||||
$tagName = null;
|
||||
switch ($typeConfig['driver']) {
|
||||
case 'orm':
|
||||
$tagName = 'doctrine.event_listener';
|
||||
break;
|
||||
case 'mongodb':
|
||||
$tagName = 'doctrine_mongodb.odm.event_listener';
|
||||
break;
|
||||
if ($typeConfig['listener']['logger']) {
|
||||
$listenerDef->replaceArgument(4, new Reference($typeConfig['listener']['logger']));
|
||||
}
|
||||
|
||||
if (null !== $tagName) {
|
||||
foreach ($this->getDoctrineEvents($typeConfig) as $event) {
|
||||
$listenerDef->addTag($tagName, array('event' => $event));
|
||||
}
|
||||
switch ($typeConfig['driver']) {
|
||||
case 'orm': $listenerDef->addTag('doctrine.event_subscriber'); break;
|
||||
case 'mongodb': $listenerDef->addTag('doctrine_mongodb.odm.event_subscriber'); break;
|
||||
}
|
||||
|
||||
$container->setDefinition($listenerId, $listenerDef);
|
||||
|
@ -527,6 +513,7 @@ class FOSElasticaExtension extends Extension
|
|||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException(sprintf('Cannot determine events for driver "%s"', $typeConfig['driver']));
|
||||
break;
|
||||
}
|
||||
|
||||
$events = array();
|
||||
|
|
|
@ -2,22 +2,20 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Doctrine;
|
||||
|
||||
use Doctrine\Common\Persistence\ManagerRegistry;
|
||||
use FOS\ElasticaBundle\HybridResult;
|
||||
use FOS\ElasticaBundle\Transformer\AbstractElasticaToModelTransformer as BaseTransformer;
|
||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
||||
use FOS\ElasticaBundle\Transformer\HighlightableModelInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
/**
|
||||
* Maps Elastica documents with Doctrine objects
|
||||
* This mapper assumes an exact match between
|
||||
* elastica documents ids and doctrine object ids.
|
||||
*/
|
||||
abstract class AbstractElasticaToModelTransformer extends BaseTransformer
|
||||
abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||
{
|
||||
/**
|
||||
* Manager registry.
|
||||
*
|
||||
* @var ManagerRegistry
|
||||
*/
|
||||
protected $registry = null;
|
||||
|
||||
|
@ -40,14 +38,21 @@ abstract class AbstractElasticaToModelTransformer extends BaseTransformer
|
|||
'query_builder_method' => 'createQueryBuilder',
|
||||
);
|
||||
|
||||
/**
|
||||
* PropertyAccessor instance.
|
||||
*
|
||||
* @var PropertyAccessorInterface
|
||||
*/
|
||||
protected $propertyAccessor;
|
||||
|
||||
/**
|
||||
* Instantiates a new Mapper.
|
||||
*
|
||||
* @param ManagerRegistry $registry
|
||||
* @param object $registry
|
||||
* @param string $objectClass
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(ManagerRegistry $registry, $objectClass, array $options = array())
|
||||
public function __construct($registry, $objectClass, array $options = array())
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->objectClass = $objectClass;
|
||||
|
@ -64,6 +69,16 @@ abstract class AbstractElasticaToModelTransformer extends BaseTransformer
|
|||
return $this->objectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PropertyAccessor.
|
||||
*
|
||||
* @param PropertyAccessorInterface $propertyAccessor
|
||||
*/
|
||||
public function setPropertyAccessor(PropertyAccessorInterface $propertyAccessor)
|
||||
{
|
||||
$this->propertyAccessor = $propertyAccessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an array of elastica objects into an array of
|
||||
* model objects fetched from the doctrine repository.
|
||||
|
@ -96,7 +111,10 @@ abstract class AbstractElasticaToModelTransformer extends BaseTransformer
|
|||
// sort objects in the order of ids
|
||||
$idPos = array_flip($ids);
|
||||
$identifier = $this->options['identifier'];
|
||||
usort($objects, $this->getSortingClosure($idPos, $identifier));
|
||||
$propertyAccessor = $this->propertyAccessor;
|
||||
usort($objects, function ($a, $b) use ($idPos, $identifier, $propertyAccessor) {
|
||||
return $idPos[$propertyAccessor->getValue($a, $identifier)] > $idPos[$propertyAccessor->getValue($b, $identifier)];
|
||||
});
|
||||
|
||||
return $objects;
|
||||
}
|
||||
|
@ -120,7 +138,7 @@ abstract class AbstractElasticaToModelTransformer extends BaseTransformer
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIdentifierField()
|
||||
{
|
||||
|
|
|
@ -7,18 +7,9 @@ use Elastica\Exception\Bulk\ResponseException as BulkResponseException;
|
|||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use FOS\ElasticaBundle\Provider\AbstractProvider as BaseAbstractProvider;
|
||||
use FOS\ElasticaBundle\Provider\IndexableInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
abstract class AbstractProvider extends BaseAbstractProvider
|
||||
{
|
||||
/**
|
||||
* @var SliceFetcherInterface
|
||||
*/
|
||||
private $sliceFetcher;
|
||||
|
||||
/**
|
||||
* @var ManagerRegistry
|
||||
*/
|
||||
protected $managerRegistry;
|
||||
|
||||
/**
|
||||
|
@ -27,22 +18,94 @@ abstract class AbstractProvider extends BaseAbstractProvider
|
|||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param IndexableInterface $indexable
|
||||
* @param string $objectClass
|
||||
* @param array $baseOptions
|
||||
* @param array $options
|
||||
* @param ManagerRegistry $managerRegistry
|
||||
* @param SliceFetcherInterface $sliceFetcher
|
||||
*/
|
||||
public function __construct(
|
||||
ObjectPersisterInterface $objectPersister,
|
||||
IndexableInterface $indexable,
|
||||
$objectClass,
|
||||
array $baseOptions,
|
||||
ManagerRegistry $managerRegistry,
|
||||
SliceFetcherInterface $sliceFetcher = null
|
||||
array $options,
|
||||
ManagerRegistry $managerRegistry
|
||||
) {
|
||||
parent::__construct($objectPersister, $indexable, $objectClass, $baseOptions);
|
||||
parent::__construct($objectPersister, $indexable, $objectClass, array_merge(array(
|
||||
'clear_object_manager' => true,
|
||||
'debug_logging' => false,
|
||||
'ignore_errors' => false,
|
||||
'query_builder_method' => 'createQueryBuilder',
|
||||
), $options));
|
||||
|
||||
$this->managerRegistry = $managerRegistry;
|
||||
$this->sliceFetcher = $sliceFetcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Provider\ProviderInterface::populate()
|
||||
*/
|
||||
public function populate(\Closure $loggerClosure = null, array $options = array())
|
||||
{
|
||||
if (!$this->options['debug_logging']) {
|
||||
$logger = $this->disableLogging();
|
||||
}
|
||||
|
||||
$queryBuilder = $this->createQueryBuilder();
|
||||
$nbObjects = $this->countObjects($queryBuilder);
|
||||
$offset = isset($options['offset']) ? intval($options['offset']) : 0;
|
||||
$sleep = isset($options['sleep']) ? intval($options['sleep']) : 0;
|
||||
$batchSize = isset($options['batch-size']) ? intval($options['batch-size']) : $this->options['batch_size'];
|
||||
$ignoreErrors = isset($options['ignore-errors']) ? $options['ignore-errors'] : $this->options['ignore_errors'];
|
||||
$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('<info>Entire batch was filtered away, skipping...</info>');
|
||||
}
|
||||
|
||||
if ($this->options['clear_object_manager']) {
|
||||
$manager->clear();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$ignoreErrors) {
|
||||
$this->objectPersister->insertMany($objects);
|
||||
} else {
|
||||
try {
|
||||
$this->objectPersister->insertMany($objects);
|
||||
} catch (BulkResponseException $e) {
|
||||
if ($loggerClosure) {
|
||||
$loggerClosure(sprintf('<error>%s</error>', $e->getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->options['clear_object_manager']) {
|
||||
$manager->clear();
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->options['debug_logging']) {
|
||||
$this->enableLogging($logger);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,13 +118,20 @@ abstract class AbstractProvider extends BaseAbstractProvider
|
|||
abstract protected function countObjects($queryBuilder);
|
||||
|
||||
/**
|
||||
* Creates the query builder, which will be used to fetch objects to index.
|
||||
* Disables logging and returns the logger that was previously set.
|
||||
*
|
||||
* @param string $method
|
||||
*
|
||||
* @return object
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function createQueryBuilder($method);
|
||||
abstract protected function disableLogging();
|
||||
|
||||
/**
|
||||
* Reenables the logger with the previously returned logger from disableLogging();.
|
||||
*
|
||||
* @param mixed $logger
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function enableLogging($logger);
|
||||
|
||||
/**
|
||||
* Fetches a slice of objects using the query builder.
|
||||
|
@ -75,96 +145,9 @@ abstract class AbstractProvider extends BaseAbstractProvider
|
|||
abstract protected function fetchSlice($queryBuilder, $limit, $offset);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function doPopulate($options, \Closure $loggerClosure = null)
|
||||
{
|
||||
$manager = $this->managerRegistry->getManagerForClass($this->objectClass);
|
||||
|
||||
$queryBuilder = $this->createQueryBuilder($options['query_builder_method']);
|
||||
$nbObjects = $this->countObjects($queryBuilder);
|
||||
$offset = $options['offset'];
|
||||
|
||||
$objects = array();
|
||||
for (; $offset < $nbObjects; $offset += $options['batch_size']) {
|
||||
try {
|
||||
$objects = $this->getSlice($queryBuilder, $options['batch_size'], $offset, $objects);
|
||||
$objects = $this->filterObjects($options, $objects);
|
||||
|
||||
if (!empty($objects)) {
|
||||
$this->objectPersister->insertMany($objects);
|
||||
}
|
||||
} catch (BulkResponseException $e) {
|
||||
if (!$options['ignore_errors']) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (null !== $loggerClosure) {
|
||||
$loggerClosure(
|
||||
$options['batch_size'],
|
||||
$nbObjects,
|
||||
sprintf('<error>%s</error>', $e->getMessage())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['clear_object_manager']) {
|
||||
$manager->clear();
|
||||
}
|
||||
|
||||
usleep($options['sleep']);
|
||||
|
||||
if (null !== $loggerClosure) {
|
||||
$loggerClosure($options['batch_size'], $nbObjects);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function configureOptions()
|
||||
{
|
||||
parent::configureOptions();
|
||||
|
||||
$this->resolver->setDefaults(array(
|
||||
'clear_object_manager' => true,
|
||||
'debug_logging' => false,
|
||||
'ignore_errors' => false,
|
||||
'offset' => 0,
|
||||
'query_builder_method' => 'createQueryBuilder',
|
||||
'sleep' => 0
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* If this Provider has a SliceFetcher defined, we use it instead of falling back to
|
||||
* the fetchSlice methods defined in the ORM/MongoDB subclasses.
|
||||
* Creates the query builder, which will be used to fetch objects to index.
|
||||
*
|
||||
* @param $queryBuilder
|
||||
* @param int $limit
|
||||
* @param int $offset
|
||||
* @param array $lastSlice
|
||||
*
|
||||
* @return array
|
||||
* @return object
|
||||
*/
|
||||
private function getSlice($queryBuilder, $limit, $offset, $lastSlice)
|
||||
{
|
||||
if (!$this->sliceFetcher) {
|
||||
return $this->fetchSlice($queryBuilder, $limit, $offset);
|
||||
}
|
||||
|
||||
$manager = $this->managerRegistry->getManagerForClass($this->objectClass);
|
||||
$identifierFieldNames = $manager
|
||||
->getClassMetadata($this->objectClass)
|
||||
->getIdentifierFieldNames();
|
||||
|
||||
return $this->sliceFetcher->fetch(
|
||||
$queryBuilder,
|
||||
$limit,
|
||||
$offset,
|
||||
$lastSlice,
|
||||
$identifierFieldNames
|
||||
);
|
||||
}
|
||||
abstract protected function createQueryBuilder();
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Doctrine;
|
||||
|
||||
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersister;
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersister;
|
||||
use FOS\ElasticaBundle\Provider\IndexableInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
|
@ -14,40 +14,37 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
|||
* Automatically update ElasticSearch based on changes to the Doctrine source
|
||||
* data. One listener is generated for each Doctrine entity / ElasticSearch type.
|
||||
*/
|
||||
class Listener
|
||||
class Listener implements EventSubscriber
|
||||
{
|
||||
/**
|
||||
* Object persister.
|
||||
*
|
||||
* @var ObjectPersisterInterface
|
||||
* @var ObjectPersister
|
||||
*/
|
||||
protected $objectPersister;
|
||||
|
||||
/**
|
||||
* Configuration for the listener.
|
||||
* List of subscribed events.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $events;
|
||||
|
||||
/**
|
||||
* Configuration for the listener.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* Objects scheduled for insertion.
|
||||
*
|
||||
* @var array
|
||||
* Objects scheduled for insertion and replacement.
|
||||
*/
|
||||
public $scheduledForInsertion = array();
|
||||
|
||||
/**
|
||||
* Objects scheduled to be updated or removed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $scheduledForUpdate = array();
|
||||
|
||||
/**
|
||||
* IDs of objects scheduled for removal.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $scheduledForDeletion = array();
|
||||
|
||||
|
@ -59,7 +56,7 @@ class Listener
|
|||
protected $propertyAccessor;
|
||||
|
||||
/**
|
||||
* @var IndexableInterface
|
||||
* @var \FOS\ElasticaBundle\Provider\IndexableInterface
|
||||
*/
|
||||
private $indexable;
|
||||
|
||||
|
@ -67,50 +64,73 @@ class Listener
|
|||
* Constructor.
|
||||
*
|
||||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param array $events
|
||||
* @param IndexableInterface $indexable
|
||||
* @param array $config
|
||||
* @param LoggerInterface $logger
|
||||
* @param null $logger
|
||||
*/
|
||||
public function __construct(
|
||||
ObjectPersisterInterface $objectPersister,
|
||||
array $events,
|
||||
IndexableInterface $indexable,
|
||||
array $config = array(),
|
||||
LoggerInterface $logger = null
|
||||
$logger = null
|
||||
) {
|
||||
$this->config = array_merge(array(
|
||||
'identifier' => 'id',
|
||||
), $config);
|
||||
$this->events = $events;
|
||||
$this->indexable = $indexable;
|
||||
$this->objectPersister = $objectPersister;
|
||||
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
|
||||
|
||||
if ($logger && $this->objectPersister instanceof ObjectPersister) {
|
||||
if ($logger) {
|
||||
$this->objectPersister->setLogger($logger);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for new objects that should be indexed.
|
||||
*
|
||||
* @param LifecycleEventArgs $eventArgs
|
||||
* @see Doctrine\Common\EventSubscriber::getSubscribedEvents()
|
||||
*/
|
||||
public function postPersist(LifecycleEventArgs $eventArgs)
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
$entity = $eventArgs->getObject();
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides unified method for retrieving a doctrine object from an EventArgs instance.
|
||||
*
|
||||
* @param EventArgs $eventArgs
|
||||
*
|
||||
* @return object Entity | Document
|
||||
*
|
||||
* @throws \RuntimeException if no valid getter is found.
|
||||
*/
|
||||
private function getDoctrineObject(EventArgs $eventArgs)
|
||||
{
|
||||
if (method_exists($eventArgs, 'getObject')) {
|
||||
return $eventArgs->getObject();
|
||||
} elseif (method_exists($eventArgs, 'getEntity')) {
|
||||
return $eventArgs->getEntity();
|
||||
} elseif (method_exists($eventArgs, 'getDocument')) {
|
||||
return $eventArgs->getDocument();
|
||||
}
|
||||
|
||||
throw new \RuntimeException('Unable to retrieve object from EventArgs.');
|
||||
}
|
||||
|
||||
public function postPersist(EventArgs $eventArgs)
|
||||
{
|
||||
$entity = $this->getDoctrineObject($eventArgs);
|
||||
|
||||
if ($this->objectPersister->handlesObject($entity) && $this->isObjectIndexable($entity)) {
|
||||
$this->scheduledForInsertion[] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for objects being updated that should be indexed or removed from the index.
|
||||
*
|
||||
* @param LifecycleEventArgs $eventArgs
|
||||
*/
|
||||
public function postUpdate(LifecycleEventArgs $eventArgs)
|
||||
public function postUpdate(EventArgs $eventArgs)
|
||||
{
|
||||
$entity = $eventArgs->getObject();
|
||||
$entity = $this->getDoctrineObject($eventArgs);
|
||||
|
||||
if ($this->objectPersister->handlesObject($entity)) {
|
||||
if ($this->isObjectIndexable($entity)) {
|
||||
|
@ -125,12 +145,10 @@ class Listener
|
|||
/**
|
||||
* Delete objects preRemove instead of postRemove so that we have access to the id. Because this is called
|
||||
* preRemove, first check that the entity is managed by Doctrine.
|
||||
*
|
||||
* @param LifecycleEventArgs $eventArgs
|
||||
*/
|
||||
public function preRemove(LifecycleEventArgs $eventArgs)
|
||||
public function preRemove(EventArgs $eventArgs)
|
||||
{
|
||||
$entity = $eventArgs->getObject();
|
||||
$entity = $this->getDoctrineObject($eventArgs);
|
||||
|
||||
if ($this->objectPersister->handlesObject($entity)) {
|
||||
$this->scheduleForDeletion($entity);
|
||||
|
@ -163,10 +181,6 @@ class Listener
|
|||
* data in the event of a crash during flush.
|
||||
*
|
||||
* This method is only called in legacy configurations of the listener.
|
||||
*
|
||||
* @deprecated This method should only be called in applications that depend
|
||||
* on the behaviour that entities are indexed regardless of if a
|
||||
* flush is successful.
|
||||
*/
|
||||
public function preFlush()
|
||||
{
|
||||
|
@ -187,7 +201,7 @@ class Listener
|
|||
*
|
||||
* @param object $object
|
||||
*/
|
||||
private function scheduleForDeletion($object)
|
||||
protected function scheduleForDeletion($object)
|
||||
{
|
||||
if ($identifierValue = $this->propertyAccessor->getValue($object, $this->config['identifier'])) {
|
||||
$this->scheduledForDeletion[] = $identifierValue;
|
||||
|
|
|
@ -44,7 +44,7 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
|
||||
*/
|
||||
protected function countObjects($queryBuilder)
|
||||
{
|
||||
|
@ -58,7 +58,7 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::fetchSlice()
|
||||
*/
|
||||
protected function fetchSlice($queryBuilder, $limit, $offset)
|
||||
{
|
||||
|
@ -67,21 +67,21 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
return $queryBuilder
|
||||
->skip($offset)
|
||||
->limit($limit)
|
||||
->skip($offset)
|
||||
->getQuery()
|
||||
->execute()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::createQueryBuilder()
|
||||
*/
|
||||
protected function createQueryBuilder($method)
|
||||
protected function createQueryBuilder()
|
||||
{
|
||||
return $this->managerRegistry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getRepository($this->objectClass)
|
||||
->{$method}();
|
||||
->{$this->options['query_builder_method']}();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Doctrine\MongoDB;
|
||||
|
||||
use Doctrine\ODM\MongoDB\Query\Builder;
|
||||
use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
|
||||
use FOS\ElasticaBundle\Doctrine\SliceFetcherInterface;
|
||||
|
||||
/**
|
||||
* Fetches a slice of objects.
|
||||
*
|
||||
* @author Thomas Prelot <tprelot@gmail.com>
|
||||
*/
|
||||
class SliceFetcher implements SliceFetcherInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch($queryBuilder, $limit, $offset, array $previousSlice, array $identifierFieldNames)
|
||||
{
|
||||
if (!$queryBuilder instanceof Builder) {
|
||||
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ODM\MongoDB\Query\Builder');
|
||||
}
|
||||
|
||||
$lastObject = array_pop($previousSlice);
|
||||
|
||||
if ($lastObject) {
|
||||
$queryBuilder
|
||||
->field('_id')->gt($lastObject->getId())
|
||||
->skip(0)
|
||||
;
|
||||
} else {
|
||||
$queryBuilder->skip($offset);
|
||||
}
|
||||
|
||||
return $queryBuilder
|
||||
->limit($limit)
|
||||
->sort(array('_id' => 'asc'))
|
||||
->getQuery()
|
||||
->execute()
|
||||
->toArray()
|
||||
;
|
||||
}
|
||||
}
|
|
@ -46,7 +46,7 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
|
||||
*/
|
||||
protected function countObjects($queryBuilder)
|
||||
{
|
||||
|
@ -69,9 +69,7 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* This method should remain in sync with SliceFetcher::fetch until it is deprecated and removed.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::fetchSlice()
|
||||
*/
|
||||
protected function fetchSlice($queryBuilder, $limit, $offset)
|
||||
{
|
||||
|
@ -80,7 +78,7 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
/*
|
||||
* An orderBy DQL part is required to avoid fetching the same row twice.
|
||||
* An orderBy DQL part is required to avoid feching the same row twice.
|
||||
* @see http://stackoverflow.com/questions/6314879/does-limit-offset-length-require-order-by-for-pagination
|
||||
* @see http://www.postgresql.org/docs/current/static/queries-limit.html
|
||||
* @see http://www.sqlite.org/lang_select.html#orderby
|
||||
|
@ -105,14 +103,14 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::createQueryBuilder()
|
||||
*/
|
||||
protected function createQueryBuilder($method)
|
||||
protected function createQueryBuilder()
|
||||
{
|
||||
return $this->managerRegistry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getRepository($this->objectClass)
|
||||
// ORM query builders require an alias argument
|
||||
->{$method}(static::ENTITY_ALIAS);
|
||||
->{$this->options['query_builder_method']}(static::ENTITY_ALIAS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Doctrine\ORM;
|
||||
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
|
||||
use FOS\ElasticaBundle\Doctrine\SliceFetcherInterface;
|
||||
|
||||
/**
|
||||
* Fetches a slice of objects.
|
||||
*
|
||||
* @author Thomas Prelot <tprelot@gmail.com>
|
||||
*/
|
||||
class SliceFetcher implements SliceFetcherInterface
|
||||
{
|
||||
/**
|
||||
* This method should remain in sync with Provider::fetchSlice until that method is deprecated and
|
||||
* removed.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch($queryBuilder, $limit, $offset, array $previousSlice, array $identifierFieldNames)
|
||||
{
|
||||
if (!$queryBuilder instanceof QueryBuilder) {
|
||||
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
|
||||
}
|
||||
|
||||
/*
|
||||
* An orderBy DQL part is required to avoid feching the same row twice.
|
||||
* @see http://stackoverflow.com/questions/6314879/does-limit-offset-length-require-order-by-for-pagination
|
||||
* @see http://www.postgresql.org/docs/current/static/queries-limit.html
|
||||
* @see http://www.sqlite.org/lang_select.html#orderby
|
||||
*/
|
||||
$orderBy = $queryBuilder->getDQLPart('orderBy');
|
||||
if (empty($orderBy)) {
|
||||
$rootAliases = $queryBuilder->getRootAliases();
|
||||
|
||||
foreach ($identifierFieldNames as $fieldName) {
|
||||
$queryBuilder->addOrderBy($rootAliases[0].'.'.$fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
return $queryBuilder
|
||||
->setFirstResult($offset)
|
||||
->setMaxResults($limit)
|
||||
->getQuery()
|
||||
->getResult()
|
||||
;
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Doctrine;
|
||||
|
||||
/**
|
||||
* Fetches a slice of objects.
|
||||
*
|
||||
* @author Thomas Prelot <tprelot@gmail.com>
|
||||
*/
|
||||
interface SliceFetcherInterface
|
||||
{
|
||||
/**
|
||||
* Fetches a slice of objects using the query builder.
|
||||
*
|
||||
* @param object $queryBuilder
|
||||
* @param integer $limit
|
||||
* @param integer $offset
|
||||
* @param array $previousSlice
|
||||
* @param array $identifierFieldNames
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetch($queryBuilder, $limit, $offset, array $previousSlice, array $identifierFieldNames);
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Tim Nagel <tim@nagel.com.au>
|
||||
*
|
||||
* 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 IndexEvent extends Event
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $index;
|
||||
|
||||
/**
|
||||
* @param string $index
|
||||
*/
|
||||
public function __construct($index)
|
||||
{
|
||||
$this->index = $index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIndex()
|
||||
{
|
||||
return $this->index;
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) FriendsOfSymfony <https://github.com/FriendsOfSymfony/FOSElasticaBundle/graphs/contributors>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Event;
|
||||
|
||||
/**
|
||||
* Index Populate Event.
|
||||
*
|
||||
* @author Oleg Andreyev <oleg.andreyev@intexsys.lv>
|
||||
*/
|
||||
class IndexPopulateEvent extends IndexEvent
|
||||
{
|
||||
const PRE_INDEX_POPULATE = 'elastica.index.index_pre_populate';
|
||||
const POST_INDEX_POPULATE = 'elastica.index.index_post_populate';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $reset;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* @param string $index
|
||||
* @param boolean $reset
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($index, $reset, $options)
|
||||
{
|
||||
parent::__construct($index);
|
||||
|
||||
$this->reset = $reset;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function isReset()
|
||||
{
|
||||
return $this->reset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $reset
|
||||
*/
|
||||
public function setReset($reset)
|
||||
{
|
||||
$this->reset = $reset;
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Infinite Networks Pty Ltd <http://www.infinite.net.au>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Event;
|
||||
|
||||
/**
|
||||
* Index ResetEvent.
|
||||
*
|
||||
* @author Oleg Andreyev <oleg.andreyev@intexsys.lv>
|
||||
*/
|
||||
class IndexResetEvent extends IndexEvent
|
||||
{
|
||||
const PRE_INDEX_RESET = 'elastica.index.pre_reset';
|
||||
const POST_INDEX_RESET = 'elastica.index.post_reset';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $force;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $populating;
|
||||
|
||||
/**
|
||||
* @param string $index
|
||||
* @param bool $populating
|
||||
* @param bool $force
|
||||
*/
|
||||
public function __construct($index, $populating, $force)
|
||||
{
|
||||
parent::__construct($index);
|
||||
|
||||
$this->force = $force;
|
||||
$this->populating = $populating;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function isForce()
|
||||
{
|
||||
return $this->force;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function isPopulating()
|
||||
{
|
||||
return $this->populating;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $force
|
||||
*/
|
||||
public function setForce($force)
|
||||
{
|
||||
$this->force = $force;
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Infinite Networks Pty Ltd <http://www.infinite.net.au>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) FriendsOfSymfony <https://github.com/FriendsOfSymfony/FOSElasticaBundle/graphs/contributors>
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Type Populate Event.
|
||||
*
|
||||
* @author Oleg Andreyev <oleg.andreyev@intexsys.lv>
|
||||
*/
|
||||
class TypePopulateEvent extends IndexPopulateEvent
|
||||
{
|
||||
const PRE_TYPE_POPULATE = 'elastica.index.type_pre_populate';
|
||||
const POST_TYPE_POPULATE = 'elastica.index.type_post_populate';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @param string $index
|
||||
* @param string $type
|
||||
* @param bool $reset
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($index, $type, $reset, $options)
|
||||
{
|
||||
parent::__construct($index, $reset, $options);
|
||||
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Infinite Networks Pty Ltd <http://www.infinite.net.au>
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Type ResetEvent.
|
||||
*
|
||||
* @author Oleg Andreyev <oleg.andreyev@intexsys.lv>
|
||||
*/
|
||||
class TypeResetEvent extends IndexEvent
|
||||
{
|
||||
const PRE_TYPE_RESET = 'elastica.index.type_pre_reset';
|
||||
const POST_TYPE_RESET = 'elastica.index.type_post_reset';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @param string $index
|
||||
* @param string $type
|
||||
*/
|
||||
public function __construct($index, $type)
|
||||
{
|
||||
parent::__construct($index);
|
||||
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,6 @@ class AliasIsIndexException extends \Exception
|
|||
{
|
||||
public function __construct($indexName)
|
||||
{
|
||||
parent::__construct(sprintf('Expected %s to be an alias but it is an index.', $indexName));
|
||||
parent::__construct(sprintf('Expected alias %s instead of index', $indexName));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,12 +28,7 @@ class AliasProcessor
|
|||
*/
|
||||
public function setRootName(IndexConfig $indexConfig, Index $index)
|
||||
{
|
||||
$index->overrideName(
|
||||
sprintf('%s_%s',
|
||||
$indexConfig->getElasticSearchName(),
|
||||
date('Y-m-d-His')
|
||||
)
|
||||
);
|
||||
$index->overrideName(sprintf('%s_%s', $indexConfig->getElasticSearchName(), uniqid()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,47 +49,37 @@ class AliasProcessor
|
|||
$client = $index->getClient();
|
||||
|
||||
$aliasName = $indexConfig->getElasticSearchName();
|
||||
$oldIndexName = null;
|
||||
$oldIndexName = false;
|
||||
$newIndexName = $index->getName();
|
||||
|
||||
try {
|
||||
$oldIndexName = $this->getAliasedIndex($client, $aliasName);
|
||||
$aliasedIndexes = $this->getAliasedIndexes($client, $aliasName);
|
||||
} catch (AliasIsIndexException $e) {
|
||||
if (!$force) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->deleteIndex($client, $aliasName);
|
||||
$aliasedIndexes = array();
|
||||
}
|
||||
|
||||
try {
|
||||
$aliasUpdateRequest = $this->buildAliasUpdateRequest($oldIndexName, $aliasName, $newIndexName);
|
||||
$client->request('_aliases', 'POST', $aliasUpdateRequest);
|
||||
} catch (ExceptionInterface $e) {
|
||||
$this->cleanupRenameFailure($client, $newIndexName, $e);
|
||||
if (count($aliasedIndexes) > 1) {
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
'Alias %s is used for multiple indexes: [%s].
|
||||
Make sure it\'s either not used or is assigned to one index only',
|
||||
$aliasName,
|
||||
implode(', ', $aliasedIndexes)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Delete the old index after the alias has been switched
|
||||
if (null !== $oldIndexName) {
|
||||
$this->deleteIndex($client, $oldIndexName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an ElasticSearch request to rename or create an alias.
|
||||
*
|
||||
* @param string|null $aliasedIndex
|
||||
* @param string $aliasName
|
||||
* @param string $newIndexName
|
||||
* @return array
|
||||
*/
|
||||
private function buildAliasUpdateRequest($aliasedIndex, $aliasName, $newIndexName)
|
||||
{
|
||||
$aliasUpdateRequest = array('actions' => array());
|
||||
if (null !== $aliasedIndex) {
|
||||
if (count($aliasedIndexes) === 1) {
|
||||
// if the alias is set - add an action to remove it
|
||||
$oldIndexName = $aliasedIndexes[0];
|
||||
$aliasUpdateRequest['actions'][] = array(
|
||||
'remove' => array('index' => $aliasedIndex, 'alias' => $aliasName),
|
||||
'remove' => array('index' => $oldIndexName, 'alias' => $aliasName),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -103,68 +88,58 @@ class AliasProcessor
|
|||
'add' => array('index' => $newIndexName, 'alias' => $aliasName),
|
||||
);
|
||||
|
||||
return $aliasUpdateRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up an index when we encounter a failure to rename the alias.
|
||||
*
|
||||
* @param Client $client
|
||||
* @param string $indexName
|
||||
* @param \Exception $renameAliasException
|
||||
*/
|
||||
private function cleanupRenameFailure(Client $client, $indexName, \Exception $renameAliasException)
|
||||
{
|
||||
$additionalError = '';
|
||||
try {
|
||||
$this->deleteIndex($client, $indexName);
|
||||
} catch (ExceptionInterface $deleteNewIndexException) {
|
||||
$additionalError = sprintf(
|
||||
'Tried to delete newly built index %s, but also failed: %s',
|
||||
$indexName,
|
||||
$deleteNewIndexException->getMessage()
|
||||
$client->request('_aliases', 'POST', $aliasUpdateRequest);
|
||||
} catch (ExceptionInterface $renameAliasException) {
|
||||
$additionalError = '';
|
||||
// if we failed to move the alias, delete the newly built index
|
||||
try {
|
||||
$index->delete();
|
||||
} catch (ExceptionInterface $deleteNewIndexException) {
|
||||
$additionalError = sprintf(
|
||||
'Tried to delete newly built index %s, but also failed: %s',
|
||||
$newIndexName,
|
||||
$deleteNewIndexException->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
'Failed to updated index alias: %s. %s',
|
||||
$renameAliasException->getMessage(),
|
||||
$additionalError ?: sprintf('Newly built index %s was deleted', $newIndexName)
|
||||
), 0, $renameAliasException
|
||||
);
|
||||
}
|
||||
|
||||
throw new \RuntimeException(sprintf(
|
||||
'Failed to updated index alias: %s. %s',
|
||||
$renameAliasException->getMessage(),
|
||||
$additionalError ?: sprintf('Newly built index %s was deleted', $indexName)
|
||||
), 0, $renameAliasException);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an index.
|
||||
*
|
||||
* @param Client $client
|
||||
* @param string $indexName Index name to delete
|
||||
*/
|
||||
private function deleteIndex(Client $client, $indexName)
|
||||
{
|
||||
try {
|
||||
$path = sprintf("%s", $indexName);
|
||||
$client->request($path, Request::DELETE);
|
||||
} catch (ExceptionInterface $deleteOldIndexException) {
|
||||
throw new \RuntimeException(sprintf(
|
||||
'Failed to delete index %s with message: %s',
|
||||
$indexName,
|
||||
$deleteOldIndexException->getMessage()
|
||||
), 0, $deleteOldIndexException);
|
||||
// Delete the old index after the alias has been switched
|
||||
if ($oldIndexName) {
|
||||
$oldIndex = new Index($client, $oldIndexName);
|
||||
try {
|
||||
$oldIndex->delete();
|
||||
} catch (ExceptionInterface $deleteOldIndexException) {
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
'Failed to delete old index %s with message: %s',
|
||||
$oldIndexName,
|
||||
$deleteOldIndexException->getMessage()
|
||||
), 0, $deleteOldIndexException
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of a single index that an alias points to or throws
|
||||
* an exception if there is more than one.
|
||||
* Returns array of indexes which are mapped to given alias.
|
||||
*
|
||||
* @param Client $client
|
||||
* @param string $aliasName Alias name
|
||||
*
|
||||
* @return string|null
|
||||
* @return array
|
||||
*
|
||||
* @throws AliasIsIndexException
|
||||
*/
|
||||
private function getAliasedIndex(Client $client, $aliasName)
|
||||
private function getAliasedIndexes(Client $client, $aliasName)
|
||||
{
|
||||
$aliasesInfo = $client->request('_aliases', 'GET')->getData();
|
||||
$aliasedIndexes = array();
|
||||
|
@ -183,15 +158,18 @@ class AliasProcessor
|
|||
}
|
||||
}
|
||||
|
||||
if (count($aliasedIndexes) > 1) {
|
||||
throw new \RuntimeException(sprintf(
|
||||
'Alias %s is used for multiple indexes: [%s]. Make sure it\'s'.
|
||||
'either not used or is assigned to one index only',
|
||||
$aliasName,
|
||||
implode(', ', $aliasedIndexes)
|
||||
));
|
||||
}
|
||||
return $aliasedIndexes;
|
||||
}
|
||||
|
||||
return array_shift($aliasedIndexes);
|
||||
/**
|
||||
* Delete an index.
|
||||
*
|
||||
* @param Client $client
|
||||
* @param string $indexName Index name to delete
|
||||
*/
|
||||
private function deleteIndex(Client $client, $indexName)
|
||||
{
|
||||
$path = sprintf("%s", $indexName);
|
||||
$client->request($path, Request::DELETE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,6 @@ use FOS\ElasticaBundle\Elastica\Index;
|
|||
|
||||
class IndexManager
|
||||
{
|
||||
/**
|
||||
* @var Index
|
||||
*/
|
||||
private $defaultIndex;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
|
|
@ -38,13 +38,13 @@ class MappingBuilder
|
|||
}
|
||||
|
||||
$mapping = array();
|
||||
if (!empty($typeMappings)) {
|
||||
if ($typeMappings) {
|
||||
$mapping['mappings'] = $typeMappings;
|
||||
}
|
||||
// 'warmers' => $indexConfig->getWarmers(),
|
||||
|
||||
$settings = $indexConfig->getSettings();
|
||||
if (!empty($settings)) {
|
||||
if ($settings) {
|
||||
$mapping['settings'] = $settings;
|
||||
}
|
||||
|
||||
|
@ -60,19 +60,13 @@ class MappingBuilder
|
|||
*/
|
||||
public function buildTypeMapping(TypeConfig $typeConfig)
|
||||
{
|
||||
$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();
|
||||
}
|
||||
$mapping = array_merge($typeConfig->getMapping(), array(
|
||||
// 'date_detection' => true,
|
||||
// 'dynamic_date_formats' => array()
|
||||
// 'dynamic_templates' => $typeConfig->getDynamicTemplates(),
|
||||
// 'numeric_detection' => false,
|
||||
// 'properties' => array(),
|
||||
));
|
||||
|
||||
if ($typeConfig->getIndexAnalyzer()) {
|
||||
$mapping['index_analyzer'] = $typeConfig->getIndexAnalyzer();
|
||||
|
@ -95,7 +89,7 @@ class MappingBuilder
|
|||
$mapping['_meta']['model'] = $typeConfig->getModel();
|
||||
}
|
||||
|
||||
if (empty($mapping)) {
|
||||
if (!$mapping) {
|
||||
// Empty mapping, we want it encoded as a {} instead of a []
|
||||
$mapping = new \stdClass();
|
||||
}
|
||||
|
@ -112,8 +106,6 @@ class MappingBuilder
|
|||
private function fixProperties(&$properties)
|
||||
{
|
||||
foreach ($properties as $name => &$property) {
|
||||
unset($property['property_path']);
|
||||
|
||||
if (!isset($property['type'])) {
|
||||
$property['type'] = 'string';
|
||||
}
|
||||
|
|
|
@ -6,9 +6,6 @@ use Elastica\Index;
|
|||
use Elastica\Exception\ResponseException;
|
||||
use Elastica\Type\Mapping;
|
||||
use FOS\ElasticaBundle\Configuration\ConfigManager;
|
||||
use FOS\ElasticaBundle\Event\IndexResetEvent;
|
||||
use FOS\ElasticaBundle\Event\TypeResetEvent;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* Deletes and recreates indexes.
|
||||
|
@ -21,15 +18,10 @@ class Resetter
|
|||
private $aliasProcessor;
|
||||
|
||||
/***
|
||||
* @var ConfigManager
|
||||
* @var \FOS\ElasticaBundle\Configuration\Manager
|
||||
*/
|
||||
private $configManager;
|
||||
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
/**
|
||||
* @var IndexManager
|
||||
*/
|
||||
|
@ -40,32 +32,16 @@ class Resetter
|
|||
*/
|
||||
private $mappingBuilder;
|
||||
|
||||
/**
|
||||
* @param ConfigManager $configManager
|
||||
* @param IndexManager $indexManager
|
||||
* @param AliasProcessor $aliasProcessor
|
||||
* @param MappingBuilder $mappingBuilder
|
||||
* @param EventDispatcherInterface $eventDispatcher
|
||||
*/
|
||||
public function __construct(
|
||||
ConfigManager $configManager,
|
||||
IndexManager $indexManager,
|
||||
AliasProcessor $aliasProcessor,
|
||||
MappingBuilder $mappingBuilder,
|
||||
EventDispatcherInterface $eventDispatcher
|
||||
) {
|
||||
public function __construct(ConfigManager $configManager, IndexManager $indexManager, AliasProcessor $aliasProcessor, MappingBuilder $mappingBuilder)
|
||||
{
|
||||
$this->aliasProcessor = $aliasProcessor;
|
||||
$this->configManager = $configManager;
|
||||
$this->dispatcher = $eventDispatcher;
|
||||
$this->indexManager = $indexManager;
|
||||
$this->mappingBuilder = $mappingBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes and recreates all indexes.
|
||||
*
|
||||
* @param bool $populating
|
||||
* @param bool $force
|
||||
*/
|
||||
public function resetAllIndexes($populating = false, $force = false)
|
||||
{
|
||||
|
@ -89,9 +65,6 @@ class Resetter
|
|||
$indexConfig = $this->configManager->getIndexConfiguration($indexName);
|
||||
$index = $this->indexManager->getIndex($indexName);
|
||||
|
||||
$event = new IndexResetEvent($indexName, $populating, $force);
|
||||
$this->dispatcher->dispatch(IndexResetEvent::PRE_INDEX_RESET, $event);
|
||||
|
||||
if ($indexConfig->isUseAlias()) {
|
||||
$this->aliasProcessor->setRootName($indexConfig, $index);
|
||||
}
|
||||
|
@ -102,8 +75,6 @@ class Resetter
|
|||
if (!$populating and $indexConfig->isUseAlias()) {
|
||||
$this->aliasProcessor->switchIndexAlias($indexConfig, $index, $force);
|
||||
}
|
||||
|
||||
$this->dispatcher->dispatch(IndexResetEvent::POST_INDEX_RESET, $event);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,9 +91,6 @@ class Resetter
|
|||
$typeConfig = $this->configManager->getTypeConfiguration($indexName, $typeName);
|
||||
$type = $this->indexManager->getIndex($indexName)->getType($typeName);
|
||||
|
||||
$event = new TypeResetEvent($indexName, $typeName);
|
||||
$this->dispatcher->dispatch(TypeResetEvent::PRE_TYPE_RESET, $event);
|
||||
|
||||
try {
|
||||
$type->delete();
|
||||
} catch (ResponseException $e) {
|
||||
|
@ -137,8 +105,6 @@ class Resetter
|
|||
}
|
||||
|
||||
$type->setMapping($mapping);
|
||||
|
||||
$this->dispatcher->dispatch(TypeResetEvent::POST_TYPE_RESET, $event);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,6 +20,8 @@ class FantaPaginatorAdapter implements AdapterInterface
|
|||
* Returns the number of results.
|
||||
*
|
||||
* @return integer The number of results.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getNbResults()
|
||||
{
|
||||
|
@ -30,24 +32,14 @@ class FantaPaginatorAdapter implements AdapterInterface
|
|||
* Returns Facets.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getFacets()
|
||||
{
|
||||
return $this->adapter->getFacets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Aggregations.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getAggregations()
|
||||
{
|
||||
return $this->adapter->getAggregations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a slice of the results.
|
||||
*
|
||||
|
@ -55,6 +47,8 @@ class FantaPaginatorAdapter implements AdapterInterface
|
|||
* @param integer $length The length.
|
||||
*
|
||||
* @return array|\Traversable The slice.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getSlice($offset, $length)
|
||||
{
|
||||
|
|
|
@ -8,6 +8,8 @@ interface PaginatorAdapterInterface
|
|||
* Returns the number of results.
|
||||
*
|
||||
* @return integer The number of results.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getTotalHits();
|
||||
|
||||
|
@ -18,6 +20,8 @@ interface PaginatorAdapterInterface
|
|||
* @param integer $length The length.
|
||||
*
|
||||
* @return PartialResultsInterface
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getResults($offset, $length);
|
||||
|
||||
|
@ -27,11 +31,4 @@ interface PaginatorAdapterInterface
|
|||
* @return mixed
|
||||
*/
|
||||
public function getFacets();
|
||||
|
||||
/**
|
||||
* Returns Aggregations.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAggregations();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ interface PartialResultsInterface
|
|||
* Returns the paginated results.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function toArray();
|
||||
|
||||
|
@ -15,6 +17,8 @@ interface PartialResultsInterface
|
|||
* Returns the number of results.
|
||||
*
|
||||
* @return integer The number of results.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getTotalHits();
|
||||
|
||||
|
@ -24,11 +28,4 @@ interface PartialResultsInterface
|
|||
* @return array
|
||||
*/
|
||||
public function getFacets();
|
||||
|
||||
/**
|
||||
* Returns the aggregations.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAggregations();
|
||||
}
|
||||
|
|
|
@ -37,11 +37,6 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
*/
|
||||
private $facets;
|
||||
|
||||
/**
|
||||
* @var array for the aggregations
|
||||
*/
|
||||
private $aggregations;
|
||||
|
||||
/**
|
||||
* @see PaginatorAdapterInterface::__construct
|
||||
*
|
||||
|
@ -74,7 +69,7 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
? (integer) $this->query->getParam('size')
|
||||
: null;
|
||||
|
||||
if (null !== $size && $size < $offset + $itemCountPerPage) {
|
||||
if ($size && $size < $offset + $itemCountPerPage) {
|
||||
$itemCountPerPage = $size - $offset;
|
||||
}
|
||||
|
||||
|
@ -89,7 +84,6 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
$resultSet = $this->searchable->search($query, $this->options);
|
||||
$this->totalHits = $resultSet->getTotalHits();
|
||||
$this->facets = $resultSet->getFacets();
|
||||
$this->aggregations = $resultSet->getAggregations();
|
||||
|
||||
return $resultSet;
|
||||
}
|
||||
|
@ -110,21 +104,15 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
/**
|
||||
* Returns the number of results.
|
||||
*
|
||||
* If genuineTotal is provided as true, total hits is returned from the
|
||||
* hits.total value from the search results instead of just returning
|
||||
* the requested size.
|
||||
*
|
||||
* @param boolean $genuineTotal
|
||||
*
|
||||
* @return integer The number of results.
|
||||
*/
|
||||
public function getTotalHits($genuineTotal = false)
|
||||
public function getTotalHits()
|
||||
{
|
||||
if (! isset($this->totalHits)) {
|
||||
$this->totalHits = $this->searchable->count($this->query);
|
||||
$this->totalHits = $this->searchable->search($this->query)->getTotalHits();
|
||||
}
|
||||
|
||||
return $this->query->hasParam('size') && !$genuineTotal
|
||||
return $this->query->hasParam('size')
|
||||
? min($this->totalHits, (integer) $this->query->getParam('size'))
|
||||
: $this->totalHits;
|
||||
}
|
||||
|
@ -143,20 +131,6 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
return $this->facets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Aggregations.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAggregations()
|
||||
{
|
||||
if (!isset($this->aggregations)) {
|
||||
$this->aggregations = $this->searchable->search($this->query)->getAggregations();
|
||||
}
|
||||
|
||||
return $this->aggregations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Query.
|
||||
*
|
||||
|
|
|
@ -49,16 +49,4 @@ class RawPartialResults implements PartialResultsInterface
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getAggregations()
|
||||
{
|
||||
if ($this->resultSet->hasAggregations()) {
|
||||
return $this->resultSet->getAggregations();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,9 +42,6 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
return $object instanceof $this->objectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LoggerInterface $logger
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
|
|
|
@ -10,15 +10,6 @@ namespace FOS\ElasticaBundle\Persister;
|
|||
*/
|
||||
interface ObjectPersisterInterface
|
||||
{
|
||||
/**
|
||||
* Checks if this persister can handle the given object or not.
|
||||
*
|
||||
* @param mixed $object
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function handlesObject($object);
|
||||
|
||||
/**
|
||||
* Insert one object into the type
|
||||
* The object will be transformed to an elastica document.
|
||||
|
|
|
@ -18,16 +18,12 @@ class ObjectSerializerPersister extends ObjectPersister
|
|||
protected $serializer;
|
||||
|
||||
/**
|
||||
* @param Type $type
|
||||
* @param ModelToElasticaTransformerInterface $transformer
|
||||
* @param string $objectClass
|
||||
* @param callable $serializer
|
||||
*/
|
||||
public function __construct(Type $type, ModelToElasticaTransformerInterface $transformer, $objectClass, $serializer)
|
||||
{
|
||||
parent::__construct($type, $transformer, $objectClass, array());
|
||||
|
||||
$this->serializer = $serializer;
|
||||
$this->serializer = $serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
namespace FOS\ElasticaBundle\Propel;
|
||||
|
||||
use FOS\ElasticaBundle\HybridResult;
|
||||
use FOS\ElasticaBundle\Transformer\AbstractElasticaToModelTransformer;
|
||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
|
@ -15,7 +14,7 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
|||
*
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
*/
|
||||
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
||||
class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||
{
|
||||
/**
|
||||
* Propel model class to map to Elastica documents.
|
||||
|
@ -34,6 +33,13 @@ class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
|||
'identifier' => 'id',
|
||||
);
|
||||
|
||||
/**
|
||||
* PropertyAccessor instance.
|
||||
*
|
||||
* @var PropertyAccessorInterface
|
||||
*/
|
||||
protected $propertyAccessor;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
@ -46,6 +52,16 @@ class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
|||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PropertyAccessor instance.
|
||||
*
|
||||
* @param PropertyAccessorInterface $propertyAccessor
|
||||
*/
|
||||
public function setPropertyAccessor(PropertyAccessorInterface $propertyAccessor)
|
||||
{
|
||||
$this->propertyAccessor = $propertyAccessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an array of Elastica document into an array of Propel entities
|
||||
* fetched from the database.
|
||||
|
@ -66,7 +82,11 @@ class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
|||
// Sort objects in the order of their IDs
|
||||
$idPos = array_flip($ids);
|
||||
$identifier = $this->options['identifier'];
|
||||
$sortCallback = $this->getSortingClosure($idPos, $identifier);
|
||||
$propertyAccessor = $this->propertyAccessor;
|
||||
|
||||
$sortCallback = function ($a, $b) use ($idPos, $identifier, $propertyAccessor) {
|
||||
return $idPos[$propertyAccessor->getValue($a, $identifier)] > $idPos[$propertyAccessor->getValue($b, $identifier)];
|
||||
};
|
||||
|
||||
if (is_object($objects)) {
|
||||
$objects->uasort($sortCallback);
|
||||
|
@ -85,7 +105,7 @@ class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
|||
$objects = $this->transform($elasticaObjects);
|
||||
|
||||
$result = array();
|
||||
for ($i = 0, $j = count($elasticaObjects); $i < $j; $i++) {
|
||||
for ($i = 0; $i < count($elasticaObjects); $i++) {
|
||||
$result[] = new HybridResult($elasticaObjects[$i], $objects[$i]);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,62 +12,46 @@ use FOS\ElasticaBundle\Provider\AbstractProvider;
|
|||
class Provider extends AbstractProvider
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see FOS\ElasticaBundle\Provider\ProviderInterface::populate()
|
||||
*/
|
||||
public function doPopulate($options, \Closure $loggerClosure = null)
|
||||
public function populate(\Closure $loggerClosure = null, array $options = array())
|
||||
{
|
||||
$queryClass = $this->objectClass.'Query';
|
||||
$nbObjects = $queryClass::create()->count();
|
||||
$offset = isset($options['offset']) ? intval($options['offset']) : 0;
|
||||
$sleep = isset($options['sleep']) ? intval($options['sleep']) : 0;
|
||||
$batchSize = isset($options['batch-size']) ? intval($options['batch-size']) : $this->options['batch_size'];
|
||||
|
||||
$offset = $options['offset'];
|
||||
for (; $offset < $nbObjects; $offset += $batchSize) {
|
||||
if ($loggerClosure) {
|
||||
$stepStartTime = microtime(true);
|
||||
}
|
||||
|
||||
for (; $offset < $nbObjects; $offset += $options['batch_size']) {
|
||||
$objects = $queryClass::create()
|
||||
->limit($options['batch_size'])
|
||||
->limit($batchSize)
|
||||
->offset($offset)
|
||||
->find()
|
||||
->getArrayCopy();
|
||||
$objects = $this->filterObjects($options, $objects);
|
||||
if (!empty($objects)) {
|
||||
$this->objectPersister->insertMany($objects);
|
||||
if ($loggerClosure) {
|
||||
$stepNbObjects = count($objects);
|
||||
}
|
||||
$objects = array_filter($objects, array($this, 'isObjectIndexable'));
|
||||
if (!$objects) {
|
||||
$loggerClosure('<info>Entire batch was filtered away, skipping...</info>');
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
usleep($options['sleep']);
|
||||
$this->objectPersister->insertMany($objects);
|
||||
|
||||
usleep($sleep);
|
||||
|
||||
if ($loggerClosure) {
|
||||
$loggerClosure($options['batch_size'], $nbObjects);
|
||||
$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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function configureOptions()
|
||||
{
|
||||
parent::configureOptions();
|
||||
|
||||
$this->resolver->setDefaults(array(
|
||||
'clear_object_manager' => true,
|
||||
'debug_logging' => false,
|
||||
'ignore_errors' => false,
|
||||
'offset' => 0,
|
||||
'query_builder_method' => null,
|
||||
'sleep' => 0
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function disableLogging()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function enableLogging($logger)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
namespace FOS\ElasticaBundle\Provider;
|
||||
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* AbstractProvider.
|
||||
|
@ -11,9 +10,9 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
|||
abstract class AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
* @var ObjectPersisterInterface
|
||||
*/
|
||||
protected $baseOptions;
|
||||
protected $objectPersister;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
@ -21,17 +20,12 @@ abstract class AbstractProvider implements ProviderInterface
|
|||
protected $objectClass;
|
||||
|
||||
/**
|
||||
* @var ObjectPersisterInterface
|
||||
* @var array
|
||||
*/
|
||||
protected $objectPersister;
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* @var OptionsResolver
|
||||
*/
|
||||
protected $resolver;
|
||||
|
||||
/**
|
||||
* @var IndexableInterface
|
||||
* @var Indexable
|
||||
*/
|
||||
private $indexable;
|
||||
|
||||
|
@ -41,117 +35,26 @@ abstract class AbstractProvider implements ProviderInterface
|
|||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param IndexableInterface $indexable
|
||||
* @param string $objectClass
|
||||
* @param array $baseOptions
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(
|
||||
ObjectPersisterInterface $objectPersister,
|
||||
IndexableInterface $indexable,
|
||||
$objectClass,
|
||||
array $baseOptions = array()
|
||||
array $options = array()
|
||||
) {
|
||||
$this->baseOptions = $baseOptions;
|
||||
$this->indexable = $indexable;
|
||||
$this->objectClass = $objectClass;
|
||||
$this->objectPersister = $objectPersister;
|
||||
$this->resolver = new OptionsResolver();
|
||||
$this->configureOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function populate(\Closure $loggerClosure = null, array $options = array())
|
||||
{
|
||||
$options = $this->resolveOptions($options);
|
||||
|
||||
$logger = !$options['debug_logging'] ?
|
||||
$this->disableLogging() :
|
||||
null;
|
||||
|
||||
$this->doPopulate($options, $loggerClosure);
|
||||
|
||||
if (null !== $logger) {
|
||||
$this->enableLogging($logger);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables logging and returns the logger that was previously set.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function disableLogging();
|
||||
|
||||
/**
|
||||
* Perform actual population.
|
||||
*
|
||||
* @param array $options
|
||||
* @param \Closure $loggerClosure
|
||||
*/
|
||||
abstract protected function doPopulate($options, \Closure $loggerClosure = null);
|
||||
|
||||
/**
|
||||
* Reenables the logger with the previously returned logger from disableLogging();.
|
||||
*
|
||||
* @param mixed $logger
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function enableLogging($logger);
|
||||
|
||||
/**
|
||||
* Configures the option resolver.
|
||||
*/
|
||||
protected function configureOptions()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
$this->options = array_merge(array(
|
||||
'batch_size' => 100,
|
||||
'skip_indexable_check' => false,
|
||||
));
|
||||
$this->resolver->setAllowedTypes(array(
|
||||
'batch_size' => 'int'
|
||||
));
|
||||
|
||||
$this->resolver->setRequired(array(
|
||||
'indexName',
|
||||
'typeName',
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filters objects away if they are not indexable.
|
||||
*
|
||||
* @param array $options
|
||||
* @param array $objects
|
||||
* @return array
|
||||
*/
|
||||
protected function filterObjects(array $options, array $objects)
|
||||
{
|
||||
if ($options['skip_indexable_check']) {
|
||||
return $objects;
|
||||
}
|
||||
|
||||
$index = $options['indexName'];
|
||||
$type = $options['typeName'];
|
||||
|
||||
$return = array();
|
||||
foreach ($objects as $object) {
|
||||
if (!$this->indexable->isObjectIndexable($index, $type, $object)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$return[] = $object;
|
||||
}
|
||||
|
||||
return $return;
|
||||
), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given object should be indexed or not.
|
||||
*
|
||||
* @deprecated To be removed in 4.0
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @return bool
|
||||
|
@ -159,8 +62,8 @@ abstract class AbstractProvider implements ProviderInterface
|
|||
protected function isObjectIndexable($object)
|
||||
{
|
||||
return $this->indexable->isObjectIndexable(
|
||||
$this->baseOptions['indexName'],
|
||||
$this->baseOptions['typeName'],
|
||||
$this->options['indexName'],
|
||||
$this->options['typeName'],
|
||||
$object
|
||||
);
|
||||
}
|
||||
|
@ -168,8 +71,6 @@ 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()
|
||||
|
@ -179,17 +80,4 @@ abstract class AbstractProvider implements ProviderInterface
|
|||
|
||||
return sprintf('(RAM : current=%uMo peak=%uMo)', $memory, $memoryMax);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the base options provided by the class with options passed to the populate
|
||||
* method and runs them through the resolver.
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function resolveOptions(array $options)
|
||||
{
|
||||
return $this->resolver->resolve(array_merge($this->baseOptions, $options));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,6 @@ class Indexable implements IndexableInterface
|
|||
|
||||
/**
|
||||
* @param array $callbacks
|
||||
* @param ContainerInterface $container
|
||||
*/
|
||||
public function __construct(array $callbacks, ContainerInterface $container)
|
||||
{
|
||||
|
@ -82,7 +81,7 @@ class Indexable implements IndexableInterface
|
|||
}
|
||||
|
||||
if ($callback instanceof Expression) {
|
||||
return (bool) $this->getExpressionLanguage()->evaluate($callback, array(
|
||||
return $this->getExpressionLanguage()->evaluate($callback, array(
|
||||
'object' => $object,
|
||||
$this->getExpressionVar($object) => $object,
|
||||
));
|
||||
|
@ -113,48 +112,39 @@ class Indexable implements IndexableInterface
|
|||
return $callback;
|
||||
}
|
||||
|
||||
if (is_array($callback) && !is_object($callback[0])) {
|
||||
return $this->processArrayToCallback($type, $callback);
|
||||
if (is_array($callback)) {
|
||||
list($class, $method) = $callback + array(null, null);
|
||||
|
||||
if (is_object($class)) {
|
||||
$class = get_class($class);
|
||||
}
|
||||
|
||||
if (strpos($class, '@') === 0) {
|
||||
$service = $this->container->get(substr($class, 1));
|
||||
|
||||
return array($service, $method);
|
||||
}
|
||||
|
||||
if ($class && $method) {
|
||||
throw new \InvalidArgumentException(sprintf('Callback for type "%s", "%s::%s()", is not callable.', $type, $class, $method));
|
||||
}
|
||||
}
|
||||
|
||||
if (is_string($callback)) {
|
||||
return $this->buildExpressionCallback($type, $object, $callback);
|
||||
if (is_string($callback) && $expression = $this->getExpressionLanguage()) {
|
||||
$callback = new Expression($callback);
|
||||
|
||||
try {
|
||||
$expression->compile($callback, array('object', $this->getExpressionVar($object)));
|
||||
|
||||
return $callback;
|
||||
} catch (SyntaxError $e) {
|
||||
throw new \InvalidArgumentException(sprintf('Callback for type "%s" is an invalid expression', $type), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('Callback for type "%s" is not a valid callback.', $type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a string expression into an Expression.
|
||||
*
|
||||
* @param string $type
|
||||
* @param mixed $object
|
||||
* @param string $callback
|
||||
*
|
||||
* @return Expression
|
||||
*/
|
||||
private function buildExpressionCallback($type, $object, $callback)
|
||||
{
|
||||
$expression = $this->getExpressionLanguage();
|
||||
if (!$expression) {
|
||||
throw new \RuntimeException('Unable to process an expression without the ExpressionLanguage component.');
|
||||
}
|
||||
|
||||
try {
|
||||
$callback = new Expression($callback);
|
||||
$expression->compile($callback, array(
|
||||
'object', $this->getExpressionVar($object)
|
||||
));
|
||||
|
||||
return $callback;
|
||||
} catch (SyntaxError $e) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Callback for type "%s" is an invalid expression',
|
||||
$type
|
||||
), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreives a cached callback, or creates a new callback if one is not found.
|
||||
*
|
||||
|
@ -173,13 +163,15 @@ class Indexable implements IndexableInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the ExpressionLanguage class if it is available.
|
||||
*
|
||||
* @return ExpressionLanguage|null
|
||||
* @return bool|ExpressionLanguage
|
||||
*/
|
||||
private function getExpressionLanguage()
|
||||
{
|
||||
if (null === $this->expressionLanguage && class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
||||
if (null === $this->expressionLanguage) {
|
||||
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->expressionLanguage = new ExpressionLanguage();
|
||||
}
|
||||
|
||||
|
@ -187,54 +179,14 @@ class Indexable implements IndexableInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the variable name to be used to access the object when using the ExpressionLanguage
|
||||
* component to parse and evaluate an expression.
|
||||
*
|
||||
* @param mixed $object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getExpressionVar($object = null)
|
||||
{
|
||||
if (!is_object($object)) {
|
||||
return 'object';
|
||||
}
|
||||
|
||||
$ref = new \ReflectionClass($object);
|
||||
|
||||
return strtolower($ref->getShortName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an array into a callback. Replaces the first element with a service if
|
||||
* it begins with an @.
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $callback
|
||||
* @return array
|
||||
*/
|
||||
private function processArrayToCallback($type, array $callback)
|
||||
{
|
||||
list($class, $method) = $callback + array(null, '__invoke');
|
||||
|
||||
if (strpos($class, '@') === 0) {
|
||||
$service = $this->container->get(substr($class, 1));
|
||||
$callback = array($service, $method);
|
||||
|
||||
if (!is_callable($callback)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Method "%s" on service "%s" is not callable.',
|
||||
$method,
|
||||
substr($class, 1)
|
||||
));
|
||||
}
|
||||
|
||||
return $callback;
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Unable to parse callback array for type "%s"',
|
||||
$type
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,11 +12,6 @@ interface ProviderInterface
|
|||
/**
|
||||
* Persists all domain objects to ElasticSearch for this provider.
|
||||
*
|
||||
* The closure can expect 2 or 3 arguments:
|
||||
* * The step size
|
||||
* * The total number of objects
|
||||
* * A message to output in error conditions (not normally provided)
|
||||
*
|
||||
* @param \Closure $loggerClosure
|
||||
* @param array $options
|
||||
*
|
||||
|
|
|
@ -59,7 +59,7 @@ class ProviderRegistry implements ContainerAwareInterface
|
|||
*
|
||||
* @param string $index
|
||||
*
|
||||
* @return ProviderInterface[]
|
||||
* @return array of ProviderInterface instances
|
||||
*
|
||||
* @throws \InvalidArgumentException if no providers were registered for the index
|
||||
*/
|
||||
|
|
|
@ -11,16 +11,15 @@ Symfony2. Features include:
|
|||
> **Note** Propel support is limited and contributions fixing issues are welcome!
|
||||
|
||||
[![Build Status](https://secure.travis-ci.org/FriendsOfSymfony/FOSElasticaBundle.png?branch=master)](http://travis-ci.org/FriendsOfSymfony/FOSElasticaBundle) [![Total Downloads](https://poser.pugx.org/FriendsOfSymfony/elastica-bundle/downloads.png)](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle) [![Latest Stable Version](https://poser.pugx.org/FriendsOfSymfony/elastica-bundle/v/stable.png)](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle) [![Latest Unstable Version](https://poser.pugx.org/friendsofsymfony/elastica-bundle/v/unstable.svg)](https://packagist.org/packages/friendsofsymfony/elastica-bundle)
|
||||
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/FriendsOfSymfony/FOSElasticaBundle/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/FriendsOfSymfony/FOSElasticaBundle/?branch=master)
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Documentation for FOSElasticaBundle is in `Resources/doc/index.md`
|
||||
|
||||
[Read the documentation for 3.1.x](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/Resources/doc/index.md)
|
||||
[Read the documentation for 3.0.x (master)](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/Resources/doc/index.md)
|
||||
|
||||
[Read the documentation for 3.0.x](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/3.0.x/Resources/doc/index.md)
|
||||
[Read the documentation for 2.1.x](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/2.1.x/README.md)
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
<argument type="service" id="fos_elastica.index_manager" />
|
||||
<argument type="service" id="fos_elastica.alias_processor" />
|
||||
<argument type="service" id="fos_elastica.mapping_builder" />
|
||||
<argument type="service" id="event_dispatcher"/>
|
||||
</service>
|
||||
|
||||
<!-- Abstract definition for all finders. -->
|
||||
|
|
|
@ -5,28 +5,25 @@
|
|||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="fos_elastica.slice_fetcher.mongodb.class">FOS\ElasticaBundle\Doctrine\MongoDB\SliceFetcher</parameter>
|
||||
<parameter key="fos_elastica.provider.prototype.mongodb.class">FOS\ElasticaBundle\Doctrine\MongoDB\Provider</parameter>
|
||||
<parameter key="fos_elastica.listener.prototype.mongodb.class">FOS\ElasticaBundle\Doctrine\Listener</parameter>
|
||||
<parameter key="fos_elastica.elastica_to_model_transformer.prototype.mongodb.class">FOS\ElasticaBundle\Doctrine\MongoDB\ElasticaToModelTransformer</parameter>
|
||||
<parameter key="fos_elastica.manager.mongodb.class">FOS\ElasticaBundle\Doctrine\RepositoryManager</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="fos_elastica.slice_fetcher.mongodb" class="%fos_elastica.slice_fetcher.mongodb.class%">
|
||||
</service>
|
||||
|
||||
<services>
|
||||
<service id="fos_elastica.provider.prototype.mongodb" class="%fos_elastica.provider.prototype.mongodb.class%" public="true" abstract="true">
|
||||
<argument /> <!-- object persister -->
|
||||
<argument type="service" id="fos_elastica.indexable" />
|
||||
<argument /> <!-- model -->
|
||||
<argument type="collection" /> <!-- options -->
|
||||
<argument type="service" id="doctrine_mongodb" /> <!-- manager registry -->
|
||||
<argument type="service" id="fos_elastica.slice_fetcher.mongodb" /> <!-- slice fetcher -->
|
||||
<argument type="service" id="doctrine_mongodb" />
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.listener.prototype.mongodb" class="%fos_elastica.listener.prototype.mongodb.class%" public="false" abstract="true">
|
||||
<argument /> <!-- object persister -->
|
||||
<argument type="collection" /> <!-- events -->
|
||||
<argument type="service" id="fos_elastica.indexable" />
|
||||
<argument type="collection" /> <!-- configuration -->
|
||||
<argument>null</argument> <!-- logger -->
|
||||
|
|
|
@ -4,29 +4,25 @@
|
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="fos_elastica.slice_fetcher.orm.class">FOS\ElasticaBundle\Doctrine\ORM\SliceFetcher</parameter>
|
||||
<parameter key="fos_elastica.provider.prototype.orm.class">FOS\ElasticaBundle\Doctrine\ORM\Provider</parameter>
|
||||
<parameter key="fos_elastica.listener.prototype.orm.class">FOS\ElasticaBundle\Doctrine\Listener</parameter>
|
||||
<parameter key="fos_elastica.elastica_to_model_transformer.prototype.orm.class">FOS\ElasticaBundle\Doctrine\ORM\ElasticaToModelTransformer</parameter>
|
||||
<parameter key="fos_elastica.manager.orm.class">FOS\ElasticaBundle\Doctrine\RepositoryManager</parameter>
|
||||
</parameters>
|
||||
<parameters>
|
||||
<parameter key="fos_elastica.provider.prototype.orm.class">FOS\ElasticaBundle\Doctrine\ORM\Provider</parameter>
|
||||
<parameter key="fos_elastica.listener.prototype.orm.class">FOS\ElasticaBundle\Doctrine\Listener</parameter>
|
||||
<parameter key="fos_elastica.elastica_to_model_transformer.prototype.orm.class">FOS\ElasticaBundle\Doctrine\ORM\ElasticaToModelTransformer</parameter>
|
||||
<parameter key="fos_elastica.manager.orm.class">FOS\ElasticaBundle\Doctrine\RepositoryManager</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="fos_elastica.slice_fetcher.orm" class="%fos_elastica.slice_fetcher.orm.class%">
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.provider.prototype.orm" class="%fos_elastica.provider.prototype.orm.class%" public="true" abstract="true">
|
||||
<argument /> <!-- object persister -->
|
||||
<argument type="service" id="fos_elastica.indexable" />
|
||||
<argument /> <!-- model -->
|
||||
<argument type="collection" /> <!-- options -->
|
||||
<argument type="service" id="doctrine" /> <!-- manager registry -->
|
||||
<argument type="service" id="fos_elastica.slice_fetcher.orm" /> <!-- slice fetcher -->
|
||||
<argument type="service" id="doctrine" />
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.listener.prototype.orm" class="%fos_elastica.listener.prototype.orm.class%" public="false" abstract="true">
|
||||
<argument /> <!-- object persister -->
|
||||
<argument type="collection" /> <!-- events -->
|
||||
<argument type="service" id="fos_elastica.indexable" />
|
||||
<argument type="collection" /> <!-- configuration -->
|
||||
<argument>null</argument> <!-- logger -->
|
||||
|
|
|
@ -12,8 +12,7 @@
|
|||
|
||||
<services>
|
||||
<service id="fos_elastica.model_to_elastica_transformer" class="%fos_elastica.model_to_elastica_transformer.class%" public="false" abstract="true">
|
||||
<argument type="collection" /> <!-- options -->
|
||||
<argument type="service" id="event_dispatcher" /> <!-- options -->
|
||||
<argument type="collection" /> <!-- options -->
|
||||
<call method="setPropertyAccessor">
|
||||
<argument type="service" id="fos_elastica.property_accessor" />
|
||||
</call>
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
##### 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',
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
|
@ -4,7 +4,7 @@ As well as the default repository you can create a custom repository for an enti
|
|||
methods for particular searches. These need to extend `FOS\ElasticaBundle\Repository` to have
|
||||
access to the finder:
|
||||
|
||||
```php
|
||||
```
|
||||
<?php
|
||||
|
||||
namespace Acme\ElasticaBundle\SearchRepository;
|
||||
|
@ -23,41 +23,37 @@ class UserRepository extends Repository
|
|||
|
||||
To use the custom repository specify it in the mapping for the entity:
|
||||
|
||||
```yaml
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
indexes:
|
||||
website:
|
||||
client: default
|
||||
types:
|
||||
user:
|
||||
mappings:
|
||||
# your mappings
|
||||
persistence:
|
||||
driver: orm
|
||||
model: Application\UserBundle\Entity\User
|
||||
provider: ~
|
||||
finder: ~
|
||||
repository: Acme\ElasticaBundle\SearchRepository\UserRepository
|
||||
```
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
indexes:
|
||||
website:
|
||||
client: default
|
||||
types:
|
||||
user:
|
||||
mappings:
|
||||
# your mappings
|
||||
persistence:
|
||||
driver: orm
|
||||
model: Application\UserBundle\Entity\User
|
||||
provider: ~
|
||||
finder: ~
|
||||
repository: Acme\ElasticaBundle\SearchRepository\UserRepository
|
||||
|
||||
Then the custom queries will be available when using the repository returned from the manager:
|
||||
|
||||
```php
|
||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
||||
$repositoryManager = $container->get('fos_elastica.manager');
|
||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
||||
$repositoryManager = $container->get('fos_elastica.manager');
|
||||
|
||||
/** var FOS\ElasticaBundle\Repository */
|
||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
||||
/** var FOS\ElasticaBundle\Repository */
|
||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
||||
|
||||
/** var array of Acme\UserBundle\Entity\User */
|
||||
$users = $repository->findWithCustomQuery('bob');
|
||||
```
|
||||
/** var array of Acme\UserBundle\Entity\User */
|
||||
$users = $repository->findWithCustomQuery('bob');
|
||||
|
||||
Alternatively you can specify the custom repository using an annotation in the entity:
|
||||
|
||||
```php
|
||||
```
|
||||
<?php
|
||||
|
||||
namespace Application\UserBundle\Entity;
|
||||
|
@ -73,4 +69,4 @@ class User
|
|||
//---
|
||||
|
||||
}
|
||||
```
|
||||
```
|
|
@ -13,7 +13,6 @@ 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)
|
||||
|
|
|
@ -1,50 +1,40 @@
|
|||
Step 1: Setting up the bundle
|
||||
=============================
|
||||
|
||||
A: Download the Bundle
|
||||
----------------------
|
||||
A) Install FOSElasticaBundle
|
||||
----------------------------
|
||||
|
||||
Open a command console, enter your project directory and execute the
|
||||
following command to download the latest stable version of this bundle:
|
||||
FOSElasticaBundle is installed using [Composer](https://getcomposer.org).
|
||||
|
||||
```bash
|
||||
$ composer require friendsofsymfony/elastica-bundle
|
||||
$ php composer.phar 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](https://www.elastic.co/downloads/elasticsearch).
|
||||
Instructions for installing and deploying Elasticsearch may be found
|
||||
[here](http://www.elasticsearch.org/guide/reference/setup/installation/).
|
||||
|
||||
Step 2: Enable the Bundle
|
||||
-------------------------
|
||||
|
||||
Then, enable the bundle by adding the following line in the `app/AppKernel.php`
|
||||
file of your project:
|
||||
B) Enable FOSElasticaBundle
|
||||
---------------------------
|
||||
|
||||
Enable FOSElasticaBundle in your AppKernel:
|
||||
|
||||
```php
|
||||
<?php
|
||||
// app/AppKernel.php
|
||||
|
||||
// ...
|
||||
class AppKernel extends Kernel
|
||||
public function registerBundles()
|
||||
{
|
||||
public function registerBundles()
|
||||
{
|
||||
$bundles = array(
|
||||
// ...
|
||||
new FOS\ElasticaBundle\FOSElasticaBundle(),
|
||||
);
|
||||
|
||||
$bundles = array(
|
||||
// ...
|
||||
}
|
||||
new FOS\ElasticaBundle\FOSElasticaBundle(),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
C: Basic Bundle Configuration
|
||||
C) Basic Bundle Configuration
|
||||
-----------------------------
|
||||
|
||||
The basic minimal configuration for FOSElasticaBundle is one client with one Elasticsearch
|
||||
|
@ -81,7 +71,7 @@ In this case, the service `fos_elastica.index.app` will relate to an ElasticSear
|
|||
that varies depending on your kernel's environment. For example, in dev it will relate to
|
||||
`app_dev`.
|
||||
|
||||
D: Defining index types
|
||||
D) Defining index types
|
||||
-----------------------
|
||||
|
||||
By default, FOSElasticaBundle requires each type that is to be indexed to be mapped.
|
||||
|
@ -135,7 +125,7 @@ Below is an example for the Doctrine ORM.
|
|||
There are a significant number of options available for types, that can be
|
||||
[found here](types.md)
|
||||
|
||||
E: Populating the Elasticsearch index
|
||||
E) Populating the Elasticsearch index
|
||||
-------------------------------------
|
||||
|
||||
When using the providers and listeners that come with the bundle, any new or modified
|
||||
|
@ -150,7 +140,7 @@ $ php app/console fos:elastica:populate
|
|||
The command will also create all indexes and types defined if they do not already exist
|
||||
on the Elasticsearch server.
|
||||
|
||||
F: Usage
|
||||
F) Usage
|
||||
--------
|
||||
|
||||
Usage documentation for the bundle is available [here](usage.md)
|
||||
|
|
|
@ -1,34 +1,6 @@
|
|||
Type configuration
|
||||
==================
|
||||
|
||||
Custom Property Paths
|
||||
---------------------
|
||||
|
||||
Since FOSElasticaBundle 3.1.0, it is now possible to define custom property paths
|
||||
to be used for data retrieval from the underlying model.
|
||||
|
||||
```yaml
|
||||
user:
|
||||
mappings:
|
||||
username:
|
||||
property_path: indexableUsername
|
||||
firstName:
|
||||
property_path: names[first]
|
||||
```
|
||||
|
||||
This feature uses the Symfony PropertyAccessor component and supports all features
|
||||
that the component supports.
|
||||
|
||||
The above example would retrieve an indexed field `username` from the property
|
||||
`User->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
|
||||
-----------------------------------------------
|
||||
|
||||
|
@ -201,18 +173,13 @@ index enabled users.
|
|||
The callback option supports multiple approaches:
|
||||
|
||||
* A method on the object itself provided as a string. `enabled` will call
|
||||
`Object->enabled()`. Note that this does not support chaining methods with dot notation
|
||||
like property paths. To achieve something similar use the ExpressionLanguage option
|
||||
below.
|
||||
`Object->enabled()`
|
||||
* An array of a service id and a method which will be called with the object as the first
|
||||
and only argument. `[ @my_custom_service, 'userIndexable' ]` will call the userIndexable
|
||||
method on a service defined as my_custom_service.
|
||||
* An array of a class and a static method to call on that class which will be called with
|
||||
the object as the only argument. `[ 'Acme\DemoBundle\IndexableChecker', 'isIndexable' ]`
|
||||
will call Acme\DemoBundle\IndexableChecker::isIndexable($object)
|
||||
* A single element array with a service id can be used if the service has an __invoke
|
||||
method. Such an invoke method must accept a single parameter for the object to be indexed.
|
||||
`[ @my_custom_invokable_service ]`
|
||||
* If you have the ExpressionLanguage component installed, A valid ExpressionLanguage
|
||||
expression provided as a string. The object being indexed will be supplied as `object`
|
||||
in the expression. `object.isEnabled() or object.shouldBeIndexedAnyway()`. For more
|
||||
|
|
|
@ -26,9 +26,7 @@ $userPaginator = $finder->findPaginated('bob');
|
|||
$countOfResults = $userPaginator->getNbResults();
|
||||
|
||||
// Option 3b. KnpPaginator resultset
|
||||
$paginator = $this->get('knp_paginator');
|
||||
$results = $finder->createPaginatorAdapter('bob');
|
||||
$pagination = $paginator->paginate($results, $page, 10);
|
||||
|
||||
```
|
||||
|
||||
Faceted Searching
|
||||
|
@ -162,7 +160,7 @@ fos_elastica:
|
|||
site:
|
||||
settings:
|
||||
index:
|
||||
analysis:
|
||||
analysis:
|
||||
analyzer:
|
||||
my_analyzer:
|
||||
type: snowball
|
||||
|
|
|
@ -8,7 +8,7 @@ use JMS\Serializer\SerializerInterface;
|
|||
class Callback
|
||||
{
|
||||
protected $serializer;
|
||||
protected $groups = array();
|
||||
protected $groups;
|
||||
protected $version;
|
||||
|
||||
public function setSerializer($serializer)
|
||||
|
@ -23,8 +23,10 @@ class Callback
|
|||
{
|
||||
$this->groups = $groups;
|
||||
|
||||
if (!empty($this->groups) && !$this->serializer instanceof SerializerInterface) {
|
||||
throw new \RuntimeException('Setting serialization groups requires using "JMS\Serializer\Serializer".');
|
||||
if ($this->groups) {
|
||||
if (!$this->serializer instanceof SerializerInterface) {
|
||||
throw new \RuntimeException('Setting serialization groups requires using "JMS\Serializer\Serializer".');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,8 +34,10 @@ class Callback
|
|||
{
|
||||
$this->version = $version;
|
||||
|
||||
if ($this->version && !$this->serializer instanceof SerializerInterface) {
|
||||
throw new \RuntimeException('Setting serialization version requires using "JMS\Serializer\Serializer".');
|
||||
if ($this->version) {
|
||||
if (!$this->serializer instanceof SerializerInterface) {
|
||||
throw new \RuntimeException('Setting serialization version requires using "JMS\Serializer\Serializer".');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +45,7 @@ class Callback
|
|||
{
|
||||
$context = $this->serializer instanceof SerializerInterface ? SerializationContext::create()->enableMaxDepthChecks() : array();
|
||||
|
||||
if (!empty($this->groups)) {
|
||||
if ($this->groups) {
|
||||
$context->setGroups($this->groups);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,10 +32,6 @@ class PaginateElasticaQuerySubscriber implements EventSubscriberInterface
|
|||
if (null != $facets) {
|
||||
$event->setCustomPaginationParameter('facets', $facets);
|
||||
}
|
||||
$aggregations = $results->getAggregations();
|
||||
if (null != $aggregations) {
|
||||
$event->setCustomPaginationParameter('aggregations', $aggregations);
|
||||
}
|
||||
|
||||
$event->stopPropagation();
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ use Symfony\Component\DependencyInjection\Container;
|
|||
|
||||
class ResetCommandTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $command;
|
||||
private $resetter;
|
||||
|
||||
private $indexManager;
|
||||
|
||||
public function setup()
|
||||
|
|
|
@ -16,7 +16,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
||||
$indexable = $this->getMockIndexable('index', 'type', $entity, true);
|
||||
|
||||
$listener = $this->createListener($persister, $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener = $this->createListener($persister, array(), $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener->postPersist($eventArgs);
|
||||
|
||||
$this->assertEquals($entity, current($listener->scheduledForInsertion));
|
||||
|
@ -35,7 +35,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
||||
$indexable = $this->getMockIndexable('index', 'type', $entity, false);
|
||||
|
||||
$listener = $this->createListener($persister, $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener = $this->createListener($persister, array(), $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener->postPersist($eventArgs);
|
||||
|
||||
$this->assertEmpty($listener->scheduledForInsertion);
|
||||
|
@ -55,7 +55,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
||||
$indexable = $this->getMockIndexable('index', 'type', $entity, true);
|
||||
|
||||
$listener = $this->createListener($persister, $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener = $this->createListener($persister, array(), $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener->postUpdate($eventArgs);
|
||||
|
||||
$this->assertEquals($entity, current($listener->scheduledForUpdate));
|
||||
|
@ -89,7 +89,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
->with($entity, 'id')
|
||||
->will($this->returnValue($entity->getId()));
|
||||
|
||||
$listener = $this->createListener($persister, $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener = $this->createListener($persister, array(), $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener->postUpdate($eventArgs);
|
||||
|
||||
$this->assertEmpty($listener->scheduledForUpdate);
|
||||
|
@ -124,7 +124,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
->with($entity, 'id')
|
||||
->will($this->returnValue($entity->getId()));
|
||||
|
||||
$listener = $this->createListener($persister, $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener = $this->createListener($persister, array(), $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener->preRemove($eventArgs);
|
||||
|
||||
$this->assertEquals($entity->getId(), current($listener->scheduledForDeletion));
|
||||
|
@ -157,7 +157,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
->with($entity, 'identifier')
|
||||
->will($this->returnValue($entity->getId()));
|
||||
|
||||
$listener = $this->createListener($persister, $indexable, array('identifier' => 'identifier', 'indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener = $this->createListener($persister, array(), $indexable, array('identifier' => 'identifier', 'indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener->preRemove($eventArgs);
|
||||
|
||||
$this->assertEquals($entity->identifier, current($listener->scheduledForDeletion));
|
||||
|
@ -272,7 +272,6 @@ namespace FOS\ElasticaBundle\Tests\Doctrine\Listener;
|
|||
class Entity
|
||||
{
|
||||
private $id;
|
||||
public $identifier;
|
||||
|
||||
/**
|
||||
* @param integer $id
|
||||
|
|
|
@ -13,7 +13,6 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
private $options;
|
||||
private $managerRegistry;
|
||||
private $indexable;
|
||||
private $sliceFetcher;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
|
@ -29,8 +28,6 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
->method('getManagerForClass')
|
||||
->with($this->objectClass)
|
||||
->will($this->returnValue($this->objectManager));
|
||||
|
||||
$this->sliceFetcher = $this->getMockSliceFetcher();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,53 +41,6 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
$queryBuilder = new \stdClass();
|
||||
|
||||
$provider->expects($this->once())
|
||||
->method('createQueryBuilder')
|
||||
->will($this->returnValue($queryBuilder));
|
||||
|
||||
$provider->expects($this->once())
|
||||
->method('countObjects')
|
||||
->with($queryBuilder)
|
||||
->will($this->returnValue($nbObjects));
|
||||
|
||||
$this->indexable->expects($this->any())
|
||||
->method('isObjectIndexable')
|
||||
->with('index', 'type', $this->anything())
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$previousSlice = array();
|
||||
|
||||
foreach ($objectsByIteration as $i => $objects) {
|
||||
$offset = $objects[0] - 1;
|
||||
|
||||
$this->sliceFetcher->expects($this->at($i))
|
||||
->method('fetch')
|
||||
->with($queryBuilder, $batchSize, $offset, $previousSlice, array('id'))
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->objectManager->expects($this->at($i))
|
||||
->method('clear');
|
||||
|
||||
$previousSlice = $objects;
|
||||
}
|
||||
|
||||
$this->objectPersister->expects($this->exactly(count($objectsByIteration)))
|
||||
->method('insertMany');
|
||||
|
||||
$provider->populate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providePopulateIterations
|
||||
*/
|
||||
public function testPopulateIterationsWithoutSliceFetcher($nbObjects, $objectsByIteration, $batchSize)
|
||||
{
|
||||
$this->options['batch_size'] = $batchSize;
|
||||
|
||||
$provider = $this->getMockAbstractProvider(false);
|
||||
|
||||
$queryBuilder = new \stdClass();
|
||||
|
||||
$provider->expects($this->once())
|
||||
->method('createQueryBuilder')
|
||||
->will($this->returnValue($queryBuilder));
|
||||
|
@ -153,8 +103,8 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
->method('countObjects')
|
||||
->will($this->returnValue($nbObjects));
|
||||
|
||||
$this->sliceFetcher->expects($this->any())
|
||||
->method('fetch')
|
||||
$provider->expects($this->any())
|
||||
->method('fetchSlice')
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->indexable->expects($this->any())
|
||||
|
@ -173,14 +123,14 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
$nbObjects = 1;
|
||||
$objects = array(1);
|
||||
|
||||
$provider = $this->getMockAbstractProvider(true);
|
||||
$provider = $this->getMockAbstractProvider();
|
||||
|
||||
$provider->expects($this->any())
|
||||
->method('countObjects')
|
||||
->will($this->returnValue($nbObjects));
|
||||
|
||||
$this->sliceFetcher->expects($this->any())
|
||||
->method('fetch')
|
||||
$provider->expects($this->any())
|
||||
->method('fetchSlice')
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->indexable->expects($this->any())
|
||||
|
@ -205,8 +155,8 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
->method('countObjects')
|
||||
->will($this->returnValue($nbObjects));
|
||||
|
||||
$this->sliceFetcher->expects($this->any())
|
||||
->method('fetch')
|
||||
$provider->expects($this->any())
|
||||
->method('fetchSlice')
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->indexable->expects($this->any())
|
||||
|
@ -237,8 +187,8 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
->method('countObjects')
|
||||
->will($this->returnValue($nbObjects));
|
||||
|
||||
$this->sliceFetcher->expects($this->any())
|
||||
->method('fetch')
|
||||
$provider->expects($this->any())
|
||||
->method('fetchSlice')
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->indexable->expects($this->any())
|
||||
|
@ -252,7 +202,7 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
$this->setExpectedException('Elastica\Exception\Bulk\ResponseException');
|
||||
|
||||
$provider->populate(null, array('ignore_errors' => false));
|
||||
$provider->populate(null, array('ignore-errors' => false));
|
||||
}
|
||||
|
||||
public function testPopulateRunsIndexCallable()
|
||||
|
@ -264,9 +214,8 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
$provider->expects($this->any())
|
||||
->method('countObjects')
|
||||
->will($this->returnValue($nbObjects));
|
||||
|
||||
$this->sliceFetcher->expects($this->any())
|
||||
->method('fetch')
|
||||
$provider->expects($this->any())
|
||||
->method('fetchSlice')
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->indexable->expects($this->at(0))
|
||||
|
@ -280,17 +229,15 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
$this->objectPersister->expects($this->once())
|
||||
->method('insertMany')
|
||||
->with(array(2));
|
||||
->with(array(1 => 2));
|
||||
|
||||
$provider->populate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $setSliceFetcher Whether or not to set the slice fetcher.
|
||||
*
|
||||
* @return \FOS\ElasticaBundle\Doctrine\AbstractProvider|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private function getMockAbstractProvider($setSliceFetcher = true)
|
||||
private function getMockAbstractProvider()
|
||||
{
|
||||
return $this->getMockForAbstractClass('FOS\ElasticaBundle\Doctrine\AbstractProvider', array(
|
||||
$this->objectPersister,
|
||||
|
@ -298,7 +245,6 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
$this->objectClass,
|
||||
$this->options,
|
||||
$this->managerRegistry,
|
||||
$setSliceFetcher ? $this->sliceFetcher : null
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -325,17 +271,7 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
private function getMockObjectManager()
|
||||
{
|
||||
$mock = $this->getMock(__NAMESPACE__.'\ObjectManager');
|
||||
|
||||
$mock->expects($this->any())
|
||||
->method('getClassMetadata')
|
||||
->will($this->returnSelf());
|
||||
|
||||
$mock->expects($this->any())
|
||||
->method('getIdentifierFieldNames')
|
||||
->will($this->returnValue(array('id')));
|
||||
|
||||
return $mock;
|
||||
return $this->getMock(__NAMESPACE__.'\ObjectManager');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -353,14 +289,6 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
return $this->getMock('FOS\ElasticaBundle\Provider\IndexableInterface');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \FOS\ElasticaBundle\Doctrine\SliceFetcherInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private function getMockSliceFetcher()
|
||||
{
|
||||
return $this->getMock('FOS\ElasticaBundle\Doctrine\SliceFetcherInterface');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -370,6 +298,4 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
interface ObjectManager
|
||||
{
|
||||
public function clear();
|
||||
public function getClassMetadata();
|
||||
public function getIdentifierFieldNames();
|
||||
}
|
||||
|
|
|
@ -53,9 +53,6 @@ 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']);
|
||||
|
@ -113,7 +110,6 @@ class MappingToElasticaTest extends WebTestCase
|
|||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string $type
|
||||
*
|
||||
* @return \Elastica\Type
|
||||
*/
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Tim Nagel <tim@nagel.com.au>
|
||||
*
|
||||
* 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');
|
||||
}
|
||||
}
|
|
@ -13,10 +13,8 @@ namespace FOS\ElasticaBundle\Tests\Functional;
|
|||
|
||||
class TypeObj
|
||||
{
|
||||
public $id = 5;
|
||||
public $coll;
|
||||
public $field1;
|
||||
public $field2;
|
||||
|
||||
public function isIndexable()
|
||||
{
|
||||
|
|
|
@ -51,8 +51,6 @@ 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_*"
|
||||
|
@ -63,7 +61,6 @@ fos_elastica:
|
|||
mapping:
|
||||
analyzer: english
|
||||
type: string
|
||||
numeric_detection: true
|
||||
properties:
|
||||
field1: ~
|
||||
field2:
|
||||
|
|
|
@ -13,12 +13,12 @@ namespace FOS\ElasticaBundle\Tests\Functional\app\ORM;
|
|||
|
||||
class IndexableService
|
||||
{
|
||||
public function isIndexable()
|
||||
public function isIndexable($object)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function isntIndexable()
|
||||
public static function isntIndexable($object)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -65,18 +65,6 @@ 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:
|
||||
|
|
|
@ -28,7 +28,7 @@ fos_elastica:
|
|||
serializer: ~
|
||||
indexes:
|
||||
index:
|
||||
index_name: foselastica_ser_test_%kernel.environment%
|
||||
index_name: foselastica_test_%kernel.environment%
|
||||
types:
|
||||
type:
|
||||
properties:
|
||||
|
|
|
@ -1,223 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Tim Nagel <tim@nagel.com.au>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\Index;
|
||||
|
||||
use Elastica\Exception\ResponseException;
|
||||
use Elastica\Request;
|
||||
use Elastica\Response;
|
||||
use FOS\ElasticaBundle\Configuration\IndexConfig;
|
||||
use FOS\ElasticaBundle\Index\AliasProcessor;
|
||||
|
||||
class AliasProcessorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var AliasProcessor
|
||||
*/
|
||||
private $processor;
|
||||
|
||||
/**
|
||||
* @dataProvider getSetRootNameData
|
||||
* @param string $name
|
||||
* @param array $configArray
|
||||
* @param string $resultStartsWith
|
||||
*/
|
||||
public function testSetRootName($name, $configArray, $resultStartsWith)
|
||||
{
|
||||
$indexConfig = new IndexConfig($name, array(), $configArray);
|
||||
$index = $this->getMockBuilder('FOS\\ElasticaBundle\\Elastica\\Index')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$index->expects($this->once())
|
||||
->method('overrideName')
|
||||
->with($this->stringStartsWith($resultStartsWith));
|
||||
|
||||
$this->processor->setRootName($indexConfig, $index);
|
||||
}
|
||||
|
||||
public function testSwitchAliasNoAliasSet()
|
||||
{
|
||||
$indexConfig = new IndexConfig('name', array(), array());
|
||||
list($index, $client) = $this->getMockedIndex('unique_name');
|
||||
|
||||
$client->expects($this->at(0))
|
||||
->method('request')
|
||||
->with('_aliases', 'GET')
|
||||
->willReturn(new Response(array()));
|
||||
$client->expects($this->at(1))
|
||||
->method('request')
|
||||
->with('_aliases', 'POST', array('actions' => array(
|
||||
array('add' => array('index' => 'unique_name', 'alias' => 'name'))
|
||||
)));
|
||||
|
||||
$this->processor->switchIndexAlias($indexConfig, $index, false);
|
||||
}
|
||||
|
||||
public function testSwitchAliasExistingAliasSet()
|
||||
{
|
||||
$indexConfig = new IndexConfig('name', array(), array());
|
||||
list($index, $client) = $this->getMockedIndex('unique_name');
|
||||
|
||||
$client->expects($this->at(0))
|
||||
->method('request')
|
||||
->with('_aliases', 'GET')
|
||||
->willReturn(new Response(array(
|
||||
'old_unique_name' => array('aliases' => array('name'))
|
||||
)));
|
||||
$client->expects($this->at(1))
|
||||
->method('request')
|
||||
->with('_aliases', 'POST', array('actions' => array(
|
||||
array('remove' => array('index' => 'old_unique_name', 'alias' => 'name')),
|
||||
array('add' => array('index' => 'unique_name', 'alias' => 'name'))
|
||||
)));
|
||||
|
||||
$this->processor->switchIndexAlias($indexConfig, $index, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testSwitchAliasThrowsWhenMoreThanOneExists()
|
||||
{
|
||||
$indexConfig = new IndexConfig('name', array(), array());
|
||||
list($index, $client) = $this->getMockedIndex('unique_name');
|
||||
|
||||
$client->expects($this->at(0))
|
||||
->method('request')
|
||||
->with('_aliases', 'GET')
|
||||
->willReturn(new Response(array(
|
||||
'old_unique_name' => array('aliases' => array('name')),
|
||||
'another_old_unique_name' => array('aliases' => array('name'))
|
||||
)));
|
||||
|
||||
$this->processor->switchIndexAlias($indexConfig, $index, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \FOS\ElasticaBundle\Exception\AliasIsIndexException
|
||||
*/
|
||||
public function testSwitchAliasThrowsWhenAliasIsAnIndex()
|
||||
{
|
||||
$indexConfig = new IndexConfig('name', array(), array());
|
||||
list($index, $client) = $this->getMockedIndex('unique_name');
|
||||
|
||||
$client->expects($this->at(0))
|
||||
->method('request')
|
||||
->with('_aliases', 'GET')
|
||||
->willReturn(new Response(array(
|
||||
'name' => array(),
|
||||
)));
|
||||
|
||||
$this->processor->switchIndexAlias($indexConfig, $index, false);
|
||||
}
|
||||
|
||||
public function testSwitchAliasDeletesIndexCollisionIfForced()
|
||||
{
|
||||
$indexConfig = new IndexConfig('name', array(), array());
|
||||
list($index, $client) = $this->getMockedIndex('unique_name');
|
||||
|
||||
$client->expects($this->at(0))
|
||||
->method('request')
|
||||
->with('_aliases', 'GET')
|
||||
->willReturn(new Response(array(
|
||||
'name' => array(),
|
||||
)));
|
||||
$client->expects($this->at(1))
|
||||
->method('request')
|
||||
->with('name', 'DELETE');
|
||||
|
||||
$this->processor->switchIndexAlias($indexConfig, $index, true);
|
||||
}
|
||||
|
||||
public function testSwitchAliasDeletesOldIndex()
|
||||
{
|
||||
$indexConfig = new IndexConfig('name', array(), array());
|
||||
list($index, $client) = $this->getMockedIndex('unique_name');
|
||||
|
||||
$client->expects($this->at(0))
|
||||
->method('request')
|
||||
->with('_aliases', 'GET')
|
||||
->willReturn(new Response(array(
|
||||
'old_unique_name' => array('aliases' => array('name')),
|
||||
)));
|
||||
$client->expects($this->at(1))
|
||||
->method('request')
|
||||
->with('_aliases', 'POST', array('actions' => array(
|
||||
array('remove' => array('index' => 'old_unique_name', 'alias' => 'name')),
|
||||
array('add' => array('index' => 'unique_name', 'alias' => 'name'))
|
||||
)));
|
||||
$client->expects($this->at(2))
|
||||
->method('request')
|
||||
->with('old_unique_name', 'DELETE');
|
||||
|
||||
$this->processor->switchIndexAlias($indexConfig, $index, true);
|
||||
}
|
||||
|
||||
public function testSwitchAliasCleansUpOnRenameFailure()
|
||||
{
|
||||
$indexConfig = new IndexConfig('name', array(), array());
|
||||
list($index, $client) = $this->getMockedIndex('unique_name');
|
||||
|
||||
$client->expects($this->at(0))
|
||||
->method('request')
|
||||
->with('_aliases', 'GET')
|
||||
->willReturn(new Response(array(
|
||||
'old_unique_name' => array('aliases' => array('name')),
|
||||
)));
|
||||
$client->expects($this->at(1))
|
||||
->method('request')
|
||||
->with('_aliases', 'POST', array('actions' => array(
|
||||
array('remove' => array('index' => 'old_unique_name', 'alias' => 'name')),
|
||||
array('add' => array('index' => 'unique_name', 'alias' => 'name'))
|
||||
)))
|
||||
->will($this->throwException(new ResponseException(new Request(''), new Response(''))));
|
||||
$client->expects($this->at(2))
|
||||
->method('request')
|
||||
->with('unique_name', 'DELETE');
|
||||
// Not an annotation: we do not want a RuntimeException until now.
|
||||
$this->setExpectedException('RuntimeException');
|
||||
|
||||
$this->processor->switchIndexAlias($indexConfig, $index, true);
|
||||
}
|
||||
|
||||
public function getSetRootNameData()
|
||||
{
|
||||
return array(
|
||||
array('name', array(), 'name_'),
|
||||
array('name', array('elasticSearchName' => 'notname'), 'notname_')
|
||||
);
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->processor = new AliasProcessor();
|
||||
}
|
||||
|
||||
private function getMockedIndex($name)
|
||||
{
|
||||
$index = $this->getMockBuilder('FOS\\ElasticaBundle\\Elastica\\Index')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$client = $this->getMockBuilder('Elastica\\Client')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$index->expects($this->any())
|
||||
->method('getClient')
|
||||
->willReturn($client);
|
||||
|
||||
$index->expects($this->any())
|
||||
->method('getName')
|
||||
->willReturn($name);
|
||||
|
||||
return array($index, $client);
|
||||
}
|
||||
}
|
|
@ -5,14 +5,8 @@ namespace FOS\ElasticaBundle\Tests\Index;
|
|||
use Elastica\Exception\ResponseException;
|
||||
use Elastica\Request;
|
||||
use Elastica\Response;
|
||||
use Elastica\Type;
|
||||
use Elastica\Type\Mapping;
|
||||
use FOS\ElasticaBundle\Configuration\IndexConfig;
|
||||
use FOS\ElasticaBundle\Configuration\TypeConfig;
|
||||
use FOS\ElasticaBundle\Elastica\Index;
|
||||
use FOS\ElasticaBundle\Event\IndexResetEvent;
|
||||
use FOS\ElasticaBundle\Event\TypeResetEvent;
|
||||
use FOS\ElasticaBundle\Index\AliasProcessor;
|
||||
use FOS\ElasticaBundle\Index\Resetter;
|
||||
|
||||
class ResetterTest extends \PHPUnit_Framework_TestCase
|
||||
|
@ -22,253 +16,227 @@ class ResetterTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
private $resetter;
|
||||
|
||||
private $aliasProcessor;
|
||||
private $configManager;
|
||||
private $dispatcher;
|
||||
private $elasticaClient;
|
||||
private $indexManager;
|
||||
private $aliasProcessor;
|
||||
private $mappingBuilder;
|
||||
|
||||
public function testResetAllIndexes()
|
||||
public function setUp()
|
||||
{
|
||||
$indexName = 'index1';
|
||||
$indexConfig = new IndexConfig($indexName, array(), array());
|
||||
$this->mockIndex($indexName, $indexConfig);
|
||||
|
||||
$this->configManager->expects($this->once())
|
||||
->method('getIndexNames')
|
||||
->will($this->returnValue(array($indexName)));
|
||||
|
||||
$this->dispatcherExpects(array(
|
||||
array(IndexResetEvent::PRE_INDEX_RESET, $this->isInstanceOf('FOS\\ElasticaBundle\\Event\\IndexResetEvent')),
|
||||
array(IndexResetEvent::POST_INDEX_RESET, $this->isInstanceOf('FOS\\ElasticaBundle\\Event\\IndexResetEvent'))
|
||||
));
|
||||
|
||||
$this->elasticaClient->expects($this->exactly(2))
|
||||
->method('request')
|
||||
->withConsecutive(
|
||||
array('index1/', 'DELETE'),
|
||||
array('index1/', 'PUT', array(), array())
|
||||
);
|
||||
|
||||
$this->resetter->resetAllIndexes();
|
||||
}
|
||||
|
||||
public function testResetIndex()
|
||||
{
|
||||
$indexConfig = new IndexConfig('index1', array(), array());
|
||||
$this->mockIndex('index1', $indexConfig);
|
||||
|
||||
$this->dispatcherExpects(array(
|
||||
array(IndexResetEvent::PRE_INDEX_RESET, $this->isInstanceOf('FOS\\ElasticaBundle\\Event\\IndexResetEvent')),
|
||||
array(IndexResetEvent::POST_INDEX_RESET, $this->isInstanceOf('FOS\\ElasticaBundle\\Event\\IndexResetEvent'))
|
||||
));
|
||||
|
||||
$this->elasticaClient->expects($this->exactly(2))
|
||||
->method('request')
|
||||
->withConsecutive(
|
||||
array('index1/', 'DELETE'),
|
||||
array('index1/', 'PUT', array(), array())
|
||||
);
|
||||
|
||||
$this->resetter->resetIndex('index1');
|
||||
}
|
||||
|
||||
public function testResetIndexWithDifferentName()
|
||||
{
|
||||
$indexConfig = new IndexConfig('index1', array(), array(
|
||||
'elasticSearchName' => 'notIndex1'
|
||||
));
|
||||
$this->mockIndex('index1', $indexConfig);
|
||||
$this->dispatcherExpects(array(
|
||||
array(IndexResetEvent::PRE_INDEX_RESET, $this->isInstanceOf('FOS\\ElasticaBundle\\Event\\IndexResetEvent')),
|
||||
array(IndexResetEvent::POST_INDEX_RESET, $this->isInstanceOf('FOS\\ElasticaBundle\\Event\\IndexResetEvent'))
|
||||
));
|
||||
|
||||
$this->elasticaClient->expects($this->exactly(2))
|
||||
->method('request')
|
||||
->withConsecutive(
|
||||
array('index1/', 'DELETE'),
|
||||
array('index1/', 'PUT', array(), array())
|
||||
);
|
||||
|
||||
$this->resetter->resetIndex('index1');
|
||||
}
|
||||
|
||||
public function testResetIndexWithDifferentNameAndAlias()
|
||||
{
|
||||
$indexConfig = new IndexConfig('index1', array(), array(
|
||||
'elasticSearchName' => 'notIndex1',
|
||||
'useAlias' => true
|
||||
));
|
||||
$index = $this->mockIndex('index1', $indexConfig);
|
||||
$this->dispatcherExpects(array(
|
||||
array(IndexResetEvent::PRE_INDEX_RESET, $this->isInstanceOf('FOS\\ElasticaBundle\\Event\\IndexResetEvent')),
|
||||
array(IndexResetEvent::POST_INDEX_RESET, $this->isInstanceOf('FOS\\ElasticaBundle\\Event\\IndexResetEvent'))
|
||||
));
|
||||
|
||||
$this->aliasProcessor->expects($this->once())
|
||||
->method('switchIndexAlias')
|
||||
->with($indexConfig, $index, false);
|
||||
|
||||
$this->elasticaClient->expects($this->exactly(2))
|
||||
->method('request')
|
||||
->withConsecutive(
|
||||
array('index1/', 'DELETE'),
|
||||
array('index1/', 'PUT', array(), array())
|
||||
);
|
||||
|
||||
$this->resetter->resetIndex('index1');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testFailureWhenMissingIndexDoesntDispatch()
|
||||
{
|
||||
$this->configManager->expects($this->once())
|
||||
->method('getIndexConfiguration')
|
||||
->with('nonExistant')
|
||||
->will($this->throwException(new \InvalidArgumentException));
|
||||
|
||||
$this->indexManager->expects($this->never())
|
||||
->method('getIndex');
|
||||
|
||||
$this->resetter->resetIndex('nonExistant');
|
||||
}
|
||||
|
||||
public function testResetType()
|
||||
{
|
||||
$typeConfig = new TypeConfig('type', array(), array());
|
||||
$this->mockType('type', 'index', $typeConfig);
|
||||
|
||||
$this->dispatcherExpects(array(
|
||||
array(TypeResetEvent::PRE_TYPE_RESET, $this->isInstanceOf('FOS\\ElasticaBundle\\Event\\TypeResetEvent')),
|
||||
array(TypeResetEvent::POST_TYPE_RESET, $this->isInstanceOf('FOS\\ElasticaBundle\\Event\\TypeResetEvent'))
|
||||
));
|
||||
|
||||
$this->elasticaClient->expects($this->exactly(2))
|
||||
->method('request')
|
||||
->withConsecutive(
|
||||
array('index/type/', 'DELETE'),
|
||||
array('index/type/_mapping', 'PUT', array('type' => array()), array())
|
||||
);
|
||||
|
||||
$this->resetter->resetIndexType('index', 'type');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testNonExistantResetType()
|
||||
{
|
||||
$this->configManager->expects($this->once())
|
||||
->method('getTypeConfiguration')
|
||||
->with('index', 'nonExistant')
|
||||
->will($this->throwException(new \InvalidArgumentException));
|
||||
|
||||
$this->indexManager->expects($this->never())
|
||||
->method('getIndex');
|
||||
|
||||
$this->resetter->resetIndexType('index', 'nonExistant');
|
||||
}
|
||||
|
||||
public function testPostPopulateWithoutAlias()
|
||||
{
|
||||
$this->mockIndex('index', new IndexConfig('index', array(), array()));
|
||||
|
||||
$this->indexManager->expects($this->never())
|
||||
->method('getIndex');
|
||||
$this->aliasProcessor->expects($this->never())
|
||||
->method('switchIndexAlias');
|
||||
|
||||
$this->resetter->postPopulate('index');
|
||||
}
|
||||
|
||||
public function testPostPopulate()
|
||||
{
|
||||
$indexConfig = new IndexConfig('index', array(), array( 'useAlias' => true));
|
||||
$index = $this->mockIndex('index', $indexConfig);
|
||||
|
||||
$this->aliasProcessor->expects($this->once())
|
||||
->method('switchIndexAlias')
|
||||
->with($indexConfig, $index);
|
||||
|
||||
$this->resetter->postPopulate('index');
|
||||
}
|
||||
|
||||
private function dispatcherExpects(array $events)
|
||||
{
|
||||
$expectation = $this->dispatcher->expects($this->exactly(count($events)))
|
||||
->method('dispatch');
|
||||
|
||||
call_user_func_array(array($expectation, 'withConsecutive'), $events);
|
||||
}
|
||||
|
||||
private function mockIndex($indexName, IndexConfig $config, $mapping = array())
|
||||
{
|
||||
$this->configManager->expects($this->atLeast(1))
|
||||
->method('getIndexConfiguration')
|
||||
->with($indexName)
|
||||
->will($this->returnValue($config));
|
||||
$index = new Index($this->elasticaClient, $indexName);
|
||||
$this->indexManager->expects($this->any())
|
||||
->method('getIndex')
|
||||
->with($indexName)
|
||||
->willReturn($index);
|
||||
$this->mappingBuilder->expects($this->any())
|
||||
->method('buildIndexMapping')
|
||||
->with($config)
|
||||
->willReturn($mapping);
|
||||
|
||||
return $index;
|
||||
}
|
||||
|
||||
private function mockType($typeName, $indexName, TypeConfig $config, $mapping = array())
|
||||
{
|
||||
$this->configManager->expects($this->atLeast(1))
|
||||
->method('getTypeConfiguration')
|
||||
->with($indexName, $typeName)
|
||||
->will($this->returnValue($config));
|
||||
$index = new Index($this->elasticaClient, $indexName);
|
||||
$this->indexManager->expects($this->once())
|
||||
->method('getIndex')
|
||||
->with($indexName)
|
||||
->willReturn($index);
|
||||
$this->mappingBuilder->expects($this->once())
|
||||
->method('buildTypeMapping')
|
||||
->with($config)
|
||||
->willReturn($mapping);
|
||||
|
||||
return $index;
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->aliasProcessor = $this->getMockBuilder('FOS\\ElasticaBundle\\Index\\AliasProcessor')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->markTestIncomplete('To be rewritten');
|
||||
$this->configManager = $this->getMockBuilder('FOS\\ElasticaBundle\\Configuration\\ConfigManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->dispatcher = $this->getMockBuilder('Symfony\\Component\\EventDispatcher\\EventDispatcherInterface')
|
||||
->getMock();
|
||||
$this->elasticaClient = $this->getMockBuilder('Elastica\\Client')
|
||||
$this->indexManager = $this->getMockBuilder('FOS\\ElasticaBundle\\Index\\IndexManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->indexManager = $this->getMockBuilder('FOS\\ElasticaBundle\\Index\\IndexManager')
|
||||
$this->aliasProcessor = $this->getMockBuilder('FOS\\ElasticaBundle\\Index\\AliasProcessor')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->mappingBuilder = $this->getMockBuilder('FOS\\ElasticaBundle\\Index\\MappingBuilder')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->resetter = new Resetter(
|
||||
$this->configManager,
|
||||
$this->indexManager,
|
||||
$this->aliasProcessor,
|
||||
$this->mappingBuilder,
|
||||
$this->dispatcher
|
||||
);
|
||||
$this->resetter = new Resetter($this->configManager, $this->indexManager, $this->aliasProcessor, $this->mappingBuilder);
|
||||
|
||||
/*$this->indexConfigsByName = array(
|
||||
'foo' => array(
|
||||
'index' => $this->getMockElasticaIndex(),
|
||||
'config' => array(
|
||||
'properties' => array(
|
||||
'a' => array(
|
||||
'dynamic_templates' => array(),
|
||||
'properties' => array(),
|
||||
),
|
||||
'b' => array('properties' => array()),
|
||||
),
|
||||
),
|
||||
),
|
||||
'bar' => array(
|
||||
'index' => $this->getMockElasticaIndex(),
|
||||
'config' => array(
|
||||
'properties' => array(
|
||||
'a' => array('properties' => array()),
|
||||
'b' => array('properties' => array()),
|
||||
),
|
||||
),
|
||||
),
|
||||
'parent' => array(
|
||||
'index' => $this->getMockElasticaIndex(),
|
||||
'config' => array(
|
||||
'properties' => array(
|
||||
'a' => array(
|
||||
'properties' => array(
|
||||
'field_2' => array()
|
||||
),
|
||||
'_parent' => array(
|
||||
'type' => 'b',
|
||||
'property' => 'b',
|
||||
'identifier' => 'id'
|
||||
),
|
||||
),
|
||||
'b' => array('properties' => array()),
|
||||
),
|
||||
),
|
||||
),
|
||||
);*/
|
||||
}
|
||||
|
||||
public function testResetAllIndexes()
|
||||
{
|
||||
$this->configManager->expects($this->once())
|
||||
->method('getIndexNames')
|
||||
->will($this->returnValue(array('index1')));
|
||||
|
||||
$this->configManager->expects($this->once())
|
||||
->method('getIndexConfiguration')
|
||||
->with('index1')
|
||||
->will($this->returnValue(new IndexConfig('index1', array(), array())));
|
||||
|
||||
$this->indexManager->expects($this->once())
|
||||
->method('getIndex')
|
||||
->with('index1')
|
||||
->will($this->returnValue());
|
||||
|
||||
/*$this->indexConfigsByName['foo']['index']->expects($this->once())
|
||||
->method('create')
|
||||
->with($this->indexConfigsByName['foo']['config'], true);
|
||||
|
||||
$this->indexConfigsByName['bar']['index']->expects($this->once())
|
||||
->method('create')
|
||||
->with($this->indexConfigsByName['bar']['config'], true);
|
||||
|
||||
$resetter = new Resetter($this->indexConfigsByName);*/
|
||||
$this->resetter->resetAllIndexes();
|
||||
}
|
||||
|
||||
public function testResetIndex()
|
||||
{
|
||||
$this->indexConfigsByName['foo']['index']->expects($this->once())
|
||||
->method('create')
|
||||
->with($this->indexConfigsByName['foo']['config'], true);
|
||||
|
||||
$this->indexConfigsByName['bar']['index']->expects($this->never())
|
||||
->method('create');
|
||||
|
||||
$resetter = new Resetter($this->indexConfigsByName);
|
||||
$resetter->resetIndex('foo');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testResetIndexShouldThrowExceptionForInvalidIndex()
|
||||
{
|
||||
$resetter = new Resetter($this->indexConfigsByName);
|
||||
$resetter->resetIndex('baz');
|
||||
}
|
||||
|
||||
public function testResetIndexType()
|
||||
{
|
||||
$type = $this->getMockElasticaType();
|
||||
|
||||
$this->indexConfigsByName['foo']['index']->expects($this->once())
|
||||
->method('getType')
|
||||
->with('a')
|
||||
->will($this->returnValue($type));
|
||||
|
||||
$type->expects($this->once())
|
||||
->method('delete');
|
||||
|
||||
$mapping = Mapping::create($this->indexConfigsByName['foo']['config']['properties']['a']['properties']);
|
||||
$mapping->setParam('dynamic_templates', $this->indexConfigsByName['foo']['config']['properties']['a']['dynamic_templates']);
|
||||
$type->expects($this->once())
|
||||
->method('setMapping')
|
||||
->with($mapping);
|
||||
|
||||
$resetter = new Resetter($this->indexConfigsByName);
|
||||
$resetter->resetIndexType('foo', 'a');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testResetIndexTypeShouldThrowExceptionForInvalidIndex()
|
||||
{
|
||||
$resetter = new Resetter($this->indexConfigsByName);
|
||||
$resetter->resetIndexType('baz', 'a');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testResetIndexTypeShouldThrowExceptionForInvalidType()
|
||||
{
|
||||
$resetter = new Resetter($this->indexConfigsByName);
|
||||
$resetter->resetIndexType('foo', 'c');
|
||||
}
|
||||
|
||||
public function testResetIndexTypeIgnoreTypeMissingException()
|
||||
{
|
||||
$type = $this->getMockElasticaType();
|
||||
|
||||
$this->indexConfigsByName['foo']['index']->expects($this->once())
|
||||
->method('getType')
|
||||
->with('a')
|
||||
->will($this->returnValue($type));
|
||||
|
||||
$type->expects($this->once())
|
||||
->method('delete')
|
||||
->will($this->throwException(new ResponseException(
|
||||
new Request(''),
|
||||
new Response(array('error' => 'TypeMissingException[[de_20131022] type[bla] missing]', 'status' => 404)))
|
||||
));
|
||||
|
||||
$mapping = Mapping::create($this->indexConfigsByName['foo']['config']['properties']['a']['properties']);
|
||||
$mapping->setParam('dynamic_templates', $this->indexConfigsByName['foo']['config']['properties']['a']['dynamic_templates']);
|
||||
$type->expects($this->once())
|
||||
->method('setMapping')
|
||||
->with($mapping);
|
||||
|
||||
$resetter = new Resetter($this->indexConfigsByName);
|
||||
$resetter->resetIndexType('foo', 'a');
|
||||
}
|
||||
|
||||
public function testIndexMappingForParent()
|
||||
{
|
||||
$type = $this->getMockElasticaType();
|
||||
|
||||
$this->indexConfigsByName['parent']['index']->expects($this->once())
|
||||
->method('getType')
|
||||
->with('a')
|
||||
->will($this->returnValue($type));
|
||||
|
||||
$type->expects($this->once())
|
||||
->method('delete');
|
||||
|
||||
$mapping = Mapping::create($this->indexConfigsByName['parent']['config']['properties']['a']['properties']);
|
||||
$mapping->setParam('_parent', array('type' => 'b'));
|
||||
$type->expects($this->once())
|
||||
->method('setMapping')
|
||||
->with($mapping);
|
||||
|
||||
$resetter = new Resetter($this->indexConfigsByName);
|
||||
$resetter->resetIndexType('parent', 'a');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Elastica\Index
|
||||
*/
|
||||
private function getMockElasticaIndex()
|
||||
{
|
||||
return $this->getMockBuilder('Elastica\Index')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Elastica\Type
|
||||
*/
|
||||
private function getMockElasticaType()
|
||||
{
|
||||
return $this->getMockBuilder('Elastica\Type')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -203,7 +203,7 @@ class ObjectPersisterTest extends \PHPUnit_Framework_TestCase
|
|||
private function getTransformer()
|
||||
{
|
||||
$transformer = new ModelToElasticaAutoTransformer();
|
||||
$transformer->setPropertyAccessor(PropertyAccess::createPropertyAccessor());
|
||||
$transformer->setPropertyAccessor(PropertyAccess::getPropertyAccessor());
|
||||
|
||||
return $transformer;
|
||||
}
|
||||
|
|
|
@ -112,7 +112,7 @@ class ObjectSerializerPersisterTest extends \PHPUnit_Framework_TestCase
|
|||
private function getTransformer()
|
||||
{
|
||||
$transformer = new ModelToElasticaIdentifierTransformer();
|
||||
$transformer->setPropertyAccessor(PropertyAccess::createPropertyAccessor());
|
||||
$transformer->setPropertyAccessor(PropertyAccess::getPropertyAccessor());
|
||||
|
||||
return $transformer;
|
||||
}
|
||||
|
|
|
@ -55,7 +55,6 @@ class IndexableTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
return array(
|
||||
array('nonexistentEntityMethod'),
|
||||
array(array('@indexableService', 'internalMethod')),
|
||||
array(array(new IndexableDecider(), 'internalMethod')),
|
||||
array(42),
|
||||
array('entity.getIsIndexable() && nonexistentEntityFunction()'),
|
||||
|
@ -68,13 +67,10 @@ class IndexableTest extends \PHPUnit_Framework_TestCase
|
|||
array('isIndexable', false),
|
||||
array(array(new IndexableDecider(), 'isIndexable'), true),
|
||||
array(array('@indexableService', 'isIndexable'), true),
|
||||
array(array('@indexableService'), true),
|
||||
array(function (Entity $entity) { return $entity->maybeIndex(); }, true),
|
||||
array('entity.maybeIndex()', true),
|
||||
array('!object.isIndexable() && entity.property == "abc"', true),
|
||||
array('entity.property != "abc"', false),
|
||||
array('["array", "values"]', true),
|
||||
array('[]', false)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -115,9 +111,4 @@ class IndexableDecider
|
|||
protected function internalMethod()
|
||||
{
|
||||
}
|
||||
|
||||
public function __invoke($object)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,10 +57,9 @@ class RepositoryTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
/**
|
||||
* @param string $testQuery
|
||||
* @param mixed $testLimit
|
||||
* @param int $testLimit
|
||||
* @param string $method
|
||||
*
|
||||
* @return \FOS\ElasticaBundle\Finder\TransformedFinder
|
||||
* @return \FOS\ElasticaBundle\Finder\TransformedFinder|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private function getFinderMock($testQuery, $testLimit = null, $method = 'find')
|
||||
{
|
||||
|
|
|
@ -37,7 +37,7 @@ class ElasticaToModelTransformerCollectionTest extends \PHPUnit_Framework_TestCa
|
|||
$this->collection = new ElasticaToModelTransformerCollection($this->transformers = array(
|
||||
'type1' => $transformer1,
|
||||
'type2' => $transformer2,
|
||||
));
|
||||
), array());
|
||||
}
|
||||
|
||||
public function testGetObjectClass()
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Tests\Transformer\ModelToElasticaAutoTransformer;
|
||||
|
||||
use FOS\ElasticaBundle\Event\TransformEvent;
|
||||
use FOS\ElasticaBundle\Transformer\ModelToElasticaAutoTransformer;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
|
||||
|
@ -126,35 +125,6 @@ class POPO
|
|||
|
||||
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();
|
||||
|
@ -271,11 +241,11 @@ class ModelToElasticaAutoTransformerTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
$transformer = $this->getTransformer();
|
||||
$document = $transformer->transform(new POPO(), array(
|
||||
'sub' => array(
|
||||
'type' => 'nested',
|
||||
'properties' => array('foo' => array()),
|
||||
),
|
||||
));
|
||||
'sub' => array(
|
||||
'type' => 'nested',
|
||||
'properties' => array('foo' => '~'),
|
||||
),
|
||||
));
|
||||
$data = $document->getData();
|
||||
|
||||
$this->assertTrue(array_key_exists('sub', $data));
|
||||
|
@ -321,7 +291,7 @@ class ModelToElasticaAutoTransformerTest extends \PHPUnit_Framework_TestCase
|
|||
'foo' => 'foo',
|
||||
'bar' => 'foo',
|
||||
'id' => 1,
|
||||
), $data['obj']);
|
||||
), $data['obj']);
|
||||
}
|
||||
|
||||
public function testObjectsMappingOfAtLeastOneAutoMappedObjectAndAtLeastOneManuallyMappedObject()
|
||||
|
@ -410,14 +380,12 @@ class ModelToElasticaAutoTransformerTest extends \PHPUnit_Framework_TestCase
|
|||
}
|
||||
|
||||
/**
|
||||
* @param null|\Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*
|
||||
* @return ModelToElasticaAutoTransformer
|
||||
*/
|
||||
private function getTransformer($dispatcher = null)
|
||||
private function getTransformer()
|
||||
{
|
||||
$transformer = new ModelToElasticaAutoTransformer(array(), $dispatcher);
|
||||
$transformer->setPropertyAccessor(PropertyAccess::createPropertyAccessor());
|
||||
$transformer = new ModelToElasticaAutoTransformer();
|
||||
$transformer->setPropertyAccessor(PropertyAccess::getPropertyAccessor());
|
||||
|
||||
return $transformer;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ class ModelToElasticaIdentifierTransformerTest extends \PHPUnit_Framework_TestCa
|
|||
private function getTransformer()
|
||||
{
|
||||
$transformer = new ModelToElasticaIdentifierTransformer();
|
||||
$transformer->setPropertyAccessor(PropertyAccess::createPropertyAccessor());
|
||||
$transformer->setPropertyAccessor(PropertyAccess::getPropertyAccessor());
|
||||
|
||||
return $transformer;
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) FriendsOfSymfony <https://github.com/FriendsOfSymfony/FOSElasticaBundle/graphs/contributors>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Transformer;
|
||||
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||
{
|
||||
/**
|
||||
* PropertyAccessor instance.
|
||||
*
|
||||
* @var PropertyAccessorInterface
|
||||
*/
|
||||
protected $propertyAccessor;
|
||||
|
||||
/**
|
||||
* Set the PropertyAccessor instance.
|
||||
*
|
||||
* @param PropertyAccessorInterface $propertyAccessor
|
||||
*/
|
||||
public function setPropertyAccessor(PropertyAccessorInterface $propertyAccessor)
|
||||
{
|
||||
$this->propertyAccessor = $propertyAccessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sorting closure to be used with usort() to put retrieved objects
|
||||
* back in the order that they were returned by ElasticSearch.
|
||||
*
|
||||
* @param array $idPos
|
||||
* @param string $identifierPath
|
||||
* @return callable
|
||||
*/
|
||||
protected function getSortingClosure(array $idPos, $identifierPath)
|
||||
{
|
||||
$propertyAccessor = $this->propertyAccessor;
|
||||
|
||||
return function ($a, $b) use ($idPos, $identifierPath, $propertyAccessor) {
|
||||
return $idPos[$propertyAccessor->getValue($a, $identifierPath)] > $idPos[$propertyAccessor->getValue($b, $identifierPath)];
|
||||
};
|
||||
}
|
||||
}
|
|
@ -81,7 +81,7 @@ class ElasticaToModelTransformerCollection implements ElasticaToModelTransformer
|
|||
$objects = $this->transform($elasticaObjects);
|
||||
|
||||
$result = array();
|
||||
for ($i = 0, $j = count($elasticaObjects); $i < $j; $i++) {
|
||||
for ($i = 0; $i < count($elasticaObjects); $i++) {
|
||||
$result[] = new HybridResult($elasticaObjects[$i], $objects[$i]);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,17 +3,10 @@
|
|||
namespace FOS\ElasticaBundle\Transformer;
|
||||
|
||||
/**
|
||||
* Indicates that the model should have elastica highlights injected.
|
||||
* Maps Elastica documents with model objects.
|
||||
*/
|
||||
interface HighlightableModelInterface
|
||||
{
|
||||
/**
|
||||
* Returns a unique identifier for the model.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getId();
|
||||
|
||||
/**
|
||||
* Set ElasticSearch highlight data.
|
||||
*
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Transformer;
|
||||
|
||||
use FOS\ElasticaBundle\Event\TransformEvent;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
use Elastica\Document;
|
||||
|
||||
|
@ -14,11 +12,6 @@ use Elastica\Document;
|
|||
*/
|
||||
class ModelToElasticaAutoTransformer implements ModelToElasticaTransformerInterface
|
||||
{
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
protected $dispatcher;
|
||||
|
||||
/**
|
||||
* Optional parameters.
|
||||
*
|
||||
|
@ -38,13 +31,11 @@ class ModelToElasticaAutoTransformer implements ModelToElasticaTransformerInterf
|
|||
/**
|
||||
* Instanciates a new Mapper.
|
||||
*
|
||||
* @param array $options
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(array $options = array(), EventDispatcherInterface $dispatcher = null)
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
$this->options = array_merge($this->options, $options);
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,24 +66,16 @@ 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;
|
||||
}
|
||||
|
||||
$path = isset($mapping['property_path']) ?
|
||||
$mapping['property_path'] :
|
||||
$key;
|
||||
if (false === $path) {
|
||||
continue;
|
||||
}
|
||||
$value = $this->propertyAccessor->getValue($object, $path);
|
||||
$value = $this->propertyAccessor->getValue($object, $key);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -103,20 +86,12 @@ 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,24 +21,32 @@
|
|||
"psr/log": "~1.0"
|
||||
},
|
||||
"require-dev":{
|
||||
"doctrine/orm": "~2.4",
|
||||
"doctrine/doctrine-bundle": "~1.2",
|
||||
"doctrine/orm": "~2.2",
|
||||
"doctrine/doctrine-bundle": "~1.2@beta",
|
||||
"jms/serializer-bundle": "@stable",
|
||||
"phpunit/phpunit": "~4.1",
|
||||
"propel/propel1": "1.6.*",
|
||||
"pagerfanta/pagerfanta": "~1.0",
|
||||
"pagerfanta/pagerfanta": "1.0.*@dev",
|
||||
"knplabs/knp-components": "~1.2",
|
||||
"knplabs/knp-paginator-bundle": "~2.4",
|
||||
"symfony/browser-kit" : "~2.3",
|
||||
"symfony/expression-language" : "~2.4",
|
||||
"symfony/twig-bundle": "~2.3"
|
||||
},
|
||||
"suggest": {
|
||||
"doctrine/orm": "~2.2",
|
||||
"doctrine/mongodb-odm": "1.0.*@dev",
|
||||
"propel/propel1": "1.6.*",
|
||||
"pagerfanta/pagerfanta": "1.0.*@dev",
|
||||
"knplabs/knp-components": "~1.2",
|
||||
"symfony/expression-language" : "~2.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "FOS\\ElasticaBundle\\": "" }
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2.x-dev"
|
||||
"dev-master": "3.1.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue