Compare commits
186 commits
Author | SHA1 | Date | |
---|---|---|---|
Simon Vieille | 30e351b1d5 | ||
Simon Vieille | e3abbdc700 | ||
4451bd07c6 | |||
1f8a330140 | |||
8f7f24e6d3 | |||
e71ec4ac8a | |||
adf7fb21e3 | |||
1287d9f0df | |||
c5728b5870 | |||
7baf494c56 | |||
ad20382e08 | |||
e933a49d07 | |||
69470d7e20 | |||
b6e01cd332 | |||
35276f469a | |||
ec9f23bd8d | |||
ae4cfd7e04 | |||
49a0c22724 | |||
8d8b04ead8 | |||
a59f2015b4 | |||
447d29ab9c | |||
73093beadb | |||
5181b02933 | |||
ac98549eb5 | |||
9c1c771799 | |||
3bb2f384ba | |||
d4f01e8d2e | |||
72a9dfa267 | |||
9cf0117c71 | |||
bb4618c101 | |||
6a07f7b24e | |||
dd3269d1ef | |||
84e5831a81 | |||
d5a9b7b235 | |||
4af9f442fd | |||
4081c32ca0 | |||
925410a66e | |||
b6c252aac3 | |||
133f71b88a | |||
c013ed9657 | |||
f72c51503a | |||
4e087af50d | |||
559b14b4a5 | |||
2215d07ff8 | |||
89db88c2a0 | |||
345b5d423d | |||
a8f41fa5ef | |||
e796d6179b | |||
dd388e4b25 | |||
81186e40db | |||
14af748840 | |||
19e9abaa53 | |||
5b88baeca6 | |||
b7c7f77383 | |||
181b5a0ac0 | |||
0009c858a7 | |||
f834499a2c | |||
3d69c08b5a | |||
cbb247978a | |||
a830a9b7b6 | |||
7b90c84daa | |||
034b3a9c60 | |||
cf9f7c6be8 | |||
4a564401b4 | |||
ef2671dd36 | |||
47785260a4 | |||
cb7b4c1dca | |||
4c87d24fc1 | |||
f6df88cc67 | |||
7fa14f713c | |||
55bfee22e8 | |||
07995d9b75 | |||
72a981ab51 | |||
1be1cb645c | |||
70fe702ccf | |||
3c87cc16c7 | |||
c5185a0307 | |||
e1d5ef72d2 | |||
797d066286 | |||
72589f8341 | |||
2401b1083c | |||
8e627ee011 | |||
6992beeb47 | |||
7bc88494a1 | |||
30d52bf0f0 | |||
58eed2dc7f | |||
d44525f6f3 | |||
67c0b79505 | |||
6bb2def21e | |||
e5d9c3ddbb | |||
906e2e0749 | |||
81f5f983c0 | |||
030b194c7b | |||
7f28be3c4e | |||
79e263d7a7 | |||
e772ca6450 | |||
55abe132c6 | |||
aef5940578 | |||
b9b0c1b961 | |||
64fa26e3d9 | |||
c4a2858265 | |||
7471c13d75 | |||
c901d60552 | |||
401446e1c4 | |||
1cea135dc5 | |||
32d190f554 | |||
5060fa4d4a | |||
b4c01f3641 | |||
1bf59d5b66 | |||
9188566dfe | |||
d7e9d9b8a6 | |||
a28b9d3069 | |||
c2c87a53e4 | |||
2ce2d7e610 | |||
905265ea0e | |||
ee9f7e5297 | |||
a0f11ff36f | |||
7c90660e02 | |||
7efcdad97c | |||
afbe1e03a1 | |||
303af508b2 | |||
c39b86c6c5 | |||
b6d46dba4a | |||
9f5ce217dc | |||
6ef6092f3f | |||
92aab4bcf6 | |||
e361b7c53b | |||
7fac93ff8b | |||
d731443aa5 | |||
156884527c | |||
5eaff9e61b | |||
3975ed3d5b | |||
d33e064801 | |||
1f7acc563a | |||
eaa32cbf22 | |||
f3e31e613e | |||
97848ca0d0 | |||
88e9f5aac6 | |||
ca57c42489 | |||
2664fec35e | |||
c45dcd955d | |||
7dc2f833c4 | |||
e211f31658 | |||
00df6c586f | |||
921377d5ed | |||
c4ee9fa83e | |||
b09bf3cf10 | |||
1e2da2d84f | |||
2d8903a330 | |||
e6d50c584c | |||
2a7459f327 | |||
c80b4efd3e | |||
1369a01dd7 | |||
901ea49a32 | |||
1b01aef46f | |||
be75b387a5 | |||
445f2f93f6 | |||
e7634d8ba2 | |||
419bf2ccf6 | |||
6f4e389dfd | |||
196aed6630 | |||
7c6fe4eaab | |||
d2de7ba6e8 | |||
197bb3ebad | |||
f9ce1dcd4e | |||
4c4e9ffe36 | |||
7fa7e44bee | |||
67ae044309 | |||
25d56d0a0f | |||
a88e4e38f8 | |||
5cdeac9b45 | |||
71a86cada5 | |||
d0ce82ac2a | |||
cf586a4ef4 | |||
6bea3c2154 | |||
001daeeac0 | |||
c08d86124a | |||
49c5b2ee63 | |||
27385046ca | |||
517d6e679c | |||
229d4cb982 | |||
9d45de8b64 | |||
659468ae3a | |||
001b38cf59 | |||
1ef55b1239 | |||
714502fa1f |
10
.travis.yml
10
.travis.yml
|
@ -1,7 +1,12 @@
|
|||
language: php
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
|
||||
|
@ -11,8 +16,6 @@ matrix:
|
|||
env: SYMFONY_VERSION='2.3.*'
|
||||
- php: 5.5
|
||||
env: SYMFONY_VERSION='2.5.*'
|
||||
- php: 5.5
|
||||
env: SYMFONY_VERSION='dev-master'
|
||||
|
||||
before_script:
|
||||
- /usr/share/elasticsearch/bin/elasticsearch -v
|
||||
|
@ -22,7 +25,8 @@ before_script:
|
|||
- sh -c 'if [ "$SYMFONY_VERSION" != "" ]; then composer require --dev --no-update symfony/symfony=$SYMFONY_VERSION; fi;'
|
||||
- composer install --dev --prefer-source
|
||||
|
||||
script: vendor/bin/phpunit --coverage-clover=coverage.clover
|
||||
script:
|
||||
- vendor/bin/phpunit --coverage-clover=coverage.clover
|
||||
|
||||
services:
|
||||
- elasticsearch
|
||||
|
|
|
@ -12,6 +12,29 @@ https://github.com/FriendsOfSymfony/FOSElasticaBundle/compare/v3.0.0...v3.0.1
|
|||
To generate a changelog summary since the last version, run
|
||||
`git log --no-merges --oneline v3.0.0...3.0.x`
|
||||
|
||||
* 3.0.9 (2015-03-12)
|
||||
|
||||
* Fix a bug in the BC layer of the type configuration for empty configs
|
||||
* Fix the service definition for the Doctrine listener when the logger is not enabled
|
||||
|
||||
* 3.0.8 (2014-01-31)
|
||||
|
||||
* Fixed handling of empty indexes #760
|
||||
* Added support for `connectionStrategy` Elastica configuration #732
|
||||
* Allow Elastica 1.4
|
||||
|
||||
* 3.0.7 (2015-01-21)
|
||||
|
||||
* Fixed the indexing of parent/child relations, broken since 3.0 #774
|
||||
* Fixed multi_field properties not being normalised #769
|
||||
|
||||
* 3.0.6 (2015-01-04)
|
||||
|
||||
* Removed unused public image asset for the web development toolbar #742
|
||||
* Fixed is_indexable_callback BC code to support array notation #761
|
||||
* Fixed debug_logger for type providers #724
|
||||
* Clean the OM if we filter away the entire batch #737
|
||||
|
||||
* 3.0.0-ALPHA6
|
||||
|
||||
* Moved `is_indexable_callback` from the listener properties to a type property called
|
||||
|
|
61
CHANGELOG-3.1.md
Normal file
61
CHANGELOG-3.1.md
Normal file
|
@ -0,0 +1,61 @@
|
|||
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,6 +2,8 @@
|
|||
|
||||
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;
|
||||
|
@ -10,18 +12,28 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||
use FOS\ElasticaBundle\IndexManager;
|
||||
use FOS\ElasticaBundle\Provider\ProviderRegistry;
|
||||
use FOS\ElasticaBundle\Resetter;
|
||||
use FOS\ElasticaBundle\Provider\ProviderInterface;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
|
||||
/**
|
||||
* Populate the search index
|
||||
* 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
|
||||
*/
|
||||
|
@ -46,31 +58,46 @@ 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')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Symfony\Component\Console\Command\Command::initialize()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
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%");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Symfony\Component\Console\Command\Command::execute()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$index = $input->getOption('index');
|
||||
$type = $input->getOption('type');
|
||||
$reset = !$input->getOption('no-reset');
|
||||
$options = $input->getOptions();
|
||||
|
||||
$options['ignore-errors'] = $input->hasOption('ignore-errors');
|
||||
$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');
|
||||
}
|
||||
|
||||
if ($input->isInteractive() && $reset && $input->getOption('offset')) {
|
||||
/** @var DialogHelper $dialog */
|
||||
|
@ -109,25 +136,22 @@ class PopulateCommand extends ContainerAwareCommand
|
|||
*/
|
||||
private function populateIndex(OutputInterface $output, $index, $reset, $options)
|
||||
{
|
||||
if ($reset) {
|
||||
$event = new IndexPopulateEvent($index, $reset, $options);
|
||||
$this->dispatcher->dispatch(IndexPopulateEvent::PRE_INDEX_POPULATE, $event);
|
||||
|
||||
if ($event->isReset()) {
|
||||
$output->writeln(sprintf('<info>Resetting</info> <comment>%s</comment>', $index));
|
||||
$this->resetter->resetIndex($index, true);
|
||||
}
|
||||
|
||||
/** @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);
|
||||
$types = array_keys($this->providerRegistry->getIndexProviders($index));
|
||||
foreach ($types as $type) {
|
||||
$this->populateIndexType($output, $index, $type, false, $event->getOptions());
|
||||
}
|
||||
|
||||
$output->writeln(sprintf('<info>Refreshing</info> <comment>%s</comment>', $index));
|
||||
$this->resetter->postPopulate($index);
|
||||
$this->indexManager->getIndex($index)->refresh();
|
||||
$this->dispatcher->dispatch(IndexPopulateEvent::POST_INDEX_POPULATE, $event);
|
||||
|
||||
$this->refreshIndex($output, $index);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -141,17 +165,35 @@ class PopulateCommand extends ContainerAwareCommand
|
|||
*/
|
||||
private function populateIndexType(OutputInterface $output, $index, $type, $reset, $options)
|
||||
{
|
||||
if ($reset) {
|
||||
$event = new TypePopulateEvent($index, $type, $reset, $options);
|
||||
$this->dispatcher->dispatch(TypePopulateEvent::PRE_TYPE_POPULATE, $event);
|
||||
|
||||
if ($event->isReset()) {
|
||||
$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);
|
||||
$provider->populate($loggerClosure, $options);
|
||||
$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);
|
||||
}
|
||||
|
||||
$output->writeln(sprintf('<info>Refreshing</info> <comment>%s</comment>', $index));
|
||||
$this->indexManager->getIndex($index)->refresh();
|
||||
|
|
103
Command/ProgressClosureBuilder.php
Normal file
103
Command/ProgressClosureBuilder.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?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))
|
||||
));
|
||||
};
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ use FOS\ElasticaBundle\IndexManager;
|
|||
use FOS\ElasticaBundle\Resetter;
|
||||
|
||||
/**
|
||||
* Reset search indexes
|
||||
* Reset search indexes.
|
||||
*/
|
||||
class ResetCommand extends ContainerAwareCommand
|
||||
{
|
||||
|
|
|
@ -11,7 +11,7 @@ use Elastica\Query;
|
|||
use Elastica\Result;
|
||||
|
||||
/**
|
||||
* Searches a type
|
||||
* Searches a type.
|
||||
*/
|
||||
class SearchCommand extends ContainerAwareCommand
|
||||
{
|
||||
|
|
|
@ -53,9 +53,9 @@ class IndexConfig
|
|||
/**
|
||||
* Constructor expects an array as generated by the Container Configuration builder.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $name
|
||||
* @param TypeConfig[] $types
|
||||
* @param array $config
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct($name, array $types, array $config)
|
||||
{
|
||||
|
@ -92,7 +92,9 @@ class IndexConfig
|
|||
|
||||
/**
|
||||
* @param string $typeName
|
||||
*
|
||||
* @return TypeConfig
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getType($typeName)
|
||||
|
|
|
@ -20,6 +20,7 @@ interface ManagerInterface
|
|||
* Returns configuration for an index.
|
||||
*
|
||||
* @param $index
|
||||
*
|
||||
* @return IndexConfig
|
||||
*/
|
||||
public function getIndexConfiguration($index);
|
||||
|
@ -36,6 +37,7 @@ interface ManagerInterface
|
|||
*
|
||||
* @param string $index
|
||||
* @param string $type
|
||||
*
|
||||
* @return TypeConfig
|
||||
*/
|
||||
public function getTypeConfiguration($index, $type);
|
||||
|
|
|
@ -17,9 +17,10 @@ use FOS\ElasticaBundle\Annotation\Search as BaseSearch;
|
|||
* Annotation class for setting search repository.
|
||||
*
|
||||
* @Annotation
|
||||
*
|
||||
* @deprecated Use FOS\ElasticaBundle\Annotation\Search instead
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
class Search extends BaseSearch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,16 +40,7 @@ class ContainerSource implements SourceInterface
|
|||
{
|
||||
$indexes = array();
|
||||
foreach ($this->configArray as $config) {
|
||||
$types = array();
|
||||
foreach ($config['types'] as $typeConfig) {
|
||||
$types[$typeConfig['name']] = new TypeConfig(
|
||||
$typeConfig['name'],
|
||||
$typeConfig['mapping'],
|
||||
$typeConfig['config']
|
||||
);
|
||||
// TODO: handle prototypes..
|
||||
}
|
||||
|
||||
$types = $this->getTypes($config);
|
||||
$index = new IndexConfig($config['name'], $types, array(
|
||||
'elasticSearchName' => $config['elasticsearch_name'],
|
||||
'settings' => $config['settings'],
|
||||
|
@ -61,4 +52,29 @@ class ContainerSource implements SourceInterface
|
|||
|
||||
return $indexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds TypeConfig objects for each type.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTypes($config)
|
||||
{
|
||||
$types = array();
|
||||
|
||||
if (isset($config['types'])) {
|
||||
foreach ($config['types'] as $typeConfig) {
|
||||
$types[$typeConfig['name']] = new TypeConfig(
|
||||
$typeConfig['name'],
|
||||
$typeConfig['mapping'],
|
||||
$typeConfig['config']
|
||||
);
|
||||
// TODO: handle prototypes..
|
||||
}
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,4 +23,4 @@ interface SourceInterface
|
|||
* @return \FOS\ElasticaBundle\Configuration\IndexConfig[]
|
||||
*/
|
||||
public function getConfiguration();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,22 @@ class TypeConfig
|
|||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|null
|
||||
*/
|
||||
public function getDateDetection()
|
||||
{
|
||||
return $this->getConfig('date_detection');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getDynamicDateFormats()
|
||||
{
|
||||
return $this->getConfig('dynamic_date_formats');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
|
@ -61,6 +77,14 @@ class TypeConfig
|
|||
null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|null
|
||||
*/
|
||||
public function getNumericDetection()
|
||||
{
|
||||
return $this->getConfig('numeric_detection');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
|
|
@ -33,4 +33,4 @@ class ConfigSourcePass implements CompilerPassInterface
|
|||
|
||||
$container->getDefinition('fos_elastica.config_manager')->replaceArgument(0, $sources);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ class RegisterProvidersPass implements CompilerPassInterface
|
|||
* Returns whether the class implements ProviderInterface.
|
||||
*
|
||||
* @param string $class
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function isProviderImplementation($class)
|
||||
|
|
|
@ -31,7 +31,7 @@ class TransformerPass implements CompilerPassInterface
|
|||
throw new InvalidArgumentException('The Transformer must have both a type and an index defined.');
|
||||
}
|
||||
|
||||
$transformers[$tag['index']][$tag['type']]= new Reference($id);
|
||||
$transformers[$tag['index']][$tag['type']] = new Reference($id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ class Configuration implements ConfigurationInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds the configuration for the "clients" key
|
||||
* Adds the configuration for the "clients" key.
|
||||
*/
|
||||
private function addClientsSection(ArrayNodeDefinition $rootNode)
|
||||
{
|
||||
|
@ -76,20 +76,30 @@ class Configuration implements ConfigurationInterface
|
|||
->performNoDeepMerging()
|
||||
// BC - Renaming 'servers' node to 'connections'
|
||||
->beforeNormalization()
|
||||
->ifTrue(function($v) { return isset($v['servers']); })
|
||||
->then(function($v) {
|
||||
->ifTrue(function ($v) { return isset($v['servers']); })
|
||||
->then(function ($v) {
|
||||
$v['connections'] = $v['servers'];
|
||||
unset($v['servers']);
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
// Elastica names its properties with camel case, support both
|
||||
->beforeNormalization()
|
||||
->ifTrue(function ($v) { return isset($v['connection_strategy']); })
|
||||
->then(function ($v) {
|
||||
$v['connectionStrategy'] = $v['connection_strategy'];
|
||||
unset($v['connection_strategy']);
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
// If there is no connections array key defined, assume a single connection.
|
||||
->beforeNormalization()
|
||||
->ifTrue(function ($v) { return is_array($v) && !array_key_exists('connections', $v); })
|
||||
->then(function ($v) {
|
||||
return array(
|
||||
'connections' => array($v)
|
||||
'connections' => array($v),
|
||||
);
|
||||
})
|
||||
->end()
|
||||
|
@ -101,8 +111,8 @@ class Configuration implements ConfigurationInterface
|
|||
->children()
|
||||
->scalarNode('url')
|
||||
->validate()
|
||||
->ifTrue(function($url) { return $url && substr($url, -1) !== '/'; })
|
||||
->then(function($url) { return $url.'/'; })
|
||||
->ifTrue(function ($url) { return $url && substr($url, -1) !== '/'; })
|
||||
->then(function ($url) { return $url.'/'; })
|
||||
->end()
|
||||
->end()
|
||||
->scalarNode('host')->end()
|
||||
|
@ -124,6 +134,7 @@ class Configuration implements ConfigurationInterface
|
|||
->end()
|
||||
->scalarNode('timeout')->end()
|
||||
->scalarNode('headers')->end()
|
||||
->scalarNode('connectionStrategy')->defaultValue('Simple')->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
|
@ -132,7 +143,7 @@ class Configuration implements ConfigurationInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds the configuration for the "indexes" key
|
||||
* Adds the configuration for the "indexes" key.
|
||||
*/
|
||||
private function addIndexesSection(ArrayNodeDefinition $rootNode)
|
||||
{
|
||||
|
@ -181,10 +192,14 @@ class Configuration implements ConfigurationInterface
|
|||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->treatNullLike(array())
|
||||
->beforeNormalization()
|
||||
->ifNull()
|
||||
->thenEmptyArray()
|
||||
->end()
|
||||
// BC - Renaming 'mappings' node to 'properties'
|
||||
->beforeNormalization()
|
||||
->ifTrue(function($v) { return array_key_exists('mappings', $v); })
|
||||
->then(function($v) {
|
||||
->ifTrue(function ($v) { return array_key_exists('mappings', $v); })
|
||||
->then(function ($v) {
|
||||
$v['properties'] = $v['mappings'];
|
||||
unset($v['mappings']);
|
||||
|
||||
|
@ -199,7 +214,17 @@ class Configuration implements ConfigurationInterface
|
|||
isset($v['persistence']['listener']['is_indexable_callback']);
|
||||
})
|
||||
->then(function ($v) {
|
||||
$v['indexable_callback'] = $v['persistence']['listener']['is_indexable_callback'];
|
||||
$callback = $v['persistence']['listener']['is_indexable_callback'];
|
||||
|
||||
if (is_array($callback)) {
|
||||
list($class) = $callback + array(null);
|
||||
|
||||
if ($class[0] !== '@' && is_string($class) && !class_exists($class)) {
|
||||
$callback[0] = '@'.$class;
|
||||
}
|
||||
}
|
||||
|
||||
$v['indexable_callback'] = $callback;
|
||||
unset($v['persistence']['listener']['is_indexable_callback']);
|
||||
|
||||
return $v;
|
||||
|
@ -225,7 +250,10 @@ class Configuration implements ConfigurationInterface
|
|||
})
|
||||
->end()
|
||||
->children()
|
||||
->booleanNode('date_detection')->end()
|
||||
->arrayNode('dynamic_date_formats')->prototype('scalar')->end()->end()
|
||||
->scalarNode('index_analyzer')->end()
|
||||
->booleanNode('numeric_detection')->end()
|
||||
->scalarNode('search_analyzer')->end()
|
||||
->variableNode('indexable_callback')->end()
|
||||
->append($this->getPersistenceNode())
|
||||
|
@ -394,7 +422,7 @@ class Configuration implements ConfigurationInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the array node used for "_all"
|
||||
* Returns the array node used for "_all".
|
||||
*/
|
||||
protected function getAllNode()
|
||||
{
|
||||
|
@ -413,7 +441,7 @@ class Configuration implements ConfigurationInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the array node used for "_timestamp"
|
||||
* Returns the array node used for "_timestamp".
|
||||
*/
|
||||
protected function getTimestampNode()
|
||||
{
|
||||
|
@ -434,7 +462,7 @@ class Configuration implements ConfigurationInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the array node used for "_ttl"
|
||||
* Returns the array node used for "_ttl".
|
||||
*/
|
||||
protected function getTtlNode()
|
||||
{
|
||||
|
@ -463,9 +491,9 @@ class Configuration implements ConfigurationInterface
|
|||
|
||||
$node
|
||||
->validate()
|
||||
->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['listener']); })
|
||||
->ifTrue(function ($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['listener']); })
|
||||
->thenInvalid('Propel doesn\'t support listeners')
|
||||
->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['repository']); })
|
||||
->ifTrue(function ($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['repository']); })
|
||||
->thenInvalid('Propel doesn\'t support the "repository" parameter')
|
||||
->end()
|
||||
->children()
|
||||
|
@ -480,9 +508,13 @@ class Configuration implements ConfigurationInterface
|
|||
->scalarNode('identifier')->defaultValue('id')->end()
|
||||
->arrayNode('provider')
|
||||
->children()
|
||||
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
|
||||
->scalarNode('batch_size')->defaultValue(100)->end()
|
||||
->scalarNode('clear_object_manager')->defaultTrue()->end()
|
||||
->scalarNode('debug_logging')
|
||||
->defaultValue($this->debug)
|
||||
->treatNullLike(true)
|
||||
->end()
|
||||
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
|
|
|
@ -80,8 +80,9 @@ class FOSElasticaExtension extends Extension
|
|||
}
|
||||
|
||||
/**
|
||||
* @param array $config
|
||||
* @param array $config
|
||||
* @param ContainerBuilder $container
|
||||
*
|
||||
* @return Configuration
|
||||
*/
|
||||
public function getConfiguration(array $config, ContainerBuilder $container)
|
||||
|
@ -92,8 +93,9 @@ class FOSElasticaExtension extends Extension
|
|||
/**
|
||||
* Loads the configured clients.
|
||||
*
|
||||
* @param array $clients An array of clients configurations
|
||||
* @param array $clients An array of clients configurations
|
||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function loadClients(array $clients, ContainerBuilder $container)
|
||||
|
@ -114,7 +116,7 @@ class FOSElasticaExtension extends Extension
|
|||
|
||||
$this->clients[$name] = array(
|
||||
'id' => $clientId,
|
||||
'reference' => new Reference($clientId)
|
||||
'reference' => new Reference($clientId),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -122,9 +124,11 @@ class FOSElasticaExtension extends Extension
|
|||
/**
|
||||
* Loads the configured indexes.
|
||||
*
|
||||
* @param array $indexes An array of indexes configurations
|
||||
* @param array $indexes An array of indexes configurations
|
||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function loadIndexes(array $indexes, ContainerBuilder $container)
|
||||
|
@ -133,7 +137,7 @@ class FOSElasticaExtension extends Extension
|
|||
|
||||
foreach ($indexes as $name => $index) {
|
||||
$indexId = sprintf('fos_elastica.index.%s', $name);
|
||||
$indexName = isset($index['index_name']) ? $index['index_name']: $name;
|
||||
$indexName = isset($index['index_name']) ? $index['index_name'] : $name;
|
||||
|
||||
$indexDef = new DefinitionDecorator('fos_elastica.index_prototype');
|
||||
$indexDef->replaceArgument(0, $indexName);
|
||||
|
@ -173,8 +177,9 @@ class FOSElasticaExtension extends Extension
|
|||
* Loads the configured index finders.
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
||||
* @param string $name The index name
|
||||
* @param Reference $index Reference to the related index
|
||||
* @param string $name The index name
|
||||
* @param Reference $index Reference to the related index
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function loadIndexFinder(ContainerBuilder $container, $name, Reference $index)
|
||||
|
@ -197,10 +202,10 @@ class FOSElasticaExtension extends Extension
|
|||
/**
|
||||
* Loads the configured types.
|
||||
*
|
||||
* @param array $types
|
||||
* @param array $types
|
||||
* @param ContainerBuilder $container
|
||||
* @param array $indexConfig
|
||||
* @param array $indexableCallbacks
|
||||
* @param array $indexConfig
|
||||
* @param array $indexableCallbacks
|
||||
*/
|
||||
private function loadTypes(array $types, ContainerBuilder $container, array $indexConfig, array &$indexableCallbacks)
|
||||
{
|
||||
|
@ -241,6 +246,9 @@ class FOSElasticaExtension extends Extension
|
|||
'serializer',
|
||||
'index_analyzer',
|
||||
'search_analyzer',
|
||||
'date_detection',
|
||||
'dynamic_date_formats',
|
||||
'numeric_detection',
|
||||
) as $field) {
|
||||
$typeConfig['config'][$field] = array_key_exists($field, $type) ?
|
||||
$type[$field] :
|
||||
|
@ -277,13 +285,13 @@ class FOSElasticaExtension extends Extension
|
|||
}
|
||||
|
||||
/**
|
||||
* Loads the optional provider and finder for a type
|
||||
* Loads the optional provider and finder for a type.
|
||||
*
|
||||
* @param array $typeConfig
|
||||
* @param array $typeConfig
|
||||
* @param ContainerBuilder $container
|
||||
* @param Reference $typeRef
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param Reference $typeRef
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*/
|
||||
private function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Reference $typeRef, $indexName, $typeName)
|
||||
{
|
||||
|
@ -307,10 +315,11 @@ class FOSElasticaExtension extends Extension
|
|||
/**
|
||||
* Creates and loads an ElasticaToModelTransformer.
|
||||
*
|
||||
* @param array $typeConfig
|
||||
* @param array $typeConfig
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function loadElasticaToModelTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
|
||||
|
@ -342,10 +351,11 @@ class FOSElasticaExtension extends Extension
|
|||
/**
|
||||
* Creates and loads a ModelToElasticaTransformer for an index/type.
|
||||
*
|
||||
* @param array $typeConfig
|
||||
* @param array $typeConfig
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function loadModelToElasticaTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
|
||||
|
@ -361,7 +371,7 @@ class FOSElasticaExtension extends Extension
|
|||
$serviceId = sprintf('fos_elastica.model_to_elastica_transformer.%s.%s', $indexName, $typeName);
|
||||
$serviceDef = new DefinitionDecorator($abstractId);
|
||||
$serviceDef->replaceArgument(0, array(
|
||||
'identifier' => $typeConfig['identifier']
|
||||
'identifier' => $typeConfig['identifier'],
|
||||
));
|
||||
$container->setDefinition($serviceId, $serviceDef);
|
||||
|
||||
|
@ -371,12 +381,13 @@ class FOSElasticaExtension extends Extension
|
|||
/**
|
||||
* Creates and loads an object persister for a type.
|
||||
*
|
||||
* @param array $typeConfig
|
||||
* @param Reference $typeRef
|
||||
* @param array $typeConfig
|
||||
* @param Reference $typeRef
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param string $transformerId
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param string $transformerId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function loadObjectPersister(array $typeConfig, Reference $typeRef, ContainerBuilder $container, $indexName, $typeName, $transformerId)
|
||||
|
@ -393,7 +404,12 @@ class FOSElasticaExtension extends Extension
|
|||
$arguments[] = array(new Reference($callbackId), 'serialize');
|
||||
} else {
|
||||
$abstractId = 'fos_elastica.object_persister';
|
||||
$arguments[] = $this->indexConfigs[$indexName]['types'][$typeName]['mapping']['properties'];
|
||||
$mapping = $this->indexConfigs[$indexName]['types'][$typeName]['mapping'];
|
||||
$argument = $mapping['properties'];
|
||||
if (isset($mapping['_parent'])) {
|
||||
$argument['_parent'] = $mapping['_parent'];
|
||||
}
|
||||
$arguments[] = $argument;
|
||||
}
|
||||
|
||||
$serviceId = sprintf('fos_elastica.object_persister.%s.%s', $indexName, $typeName);
|
||||
|
@ -410,11 +426,12 @@ class FOSElasticaExtension extends Extension
|
|||
/**
|
||||
* Loads a provider for a type.
|
||||
*
|
||||
* @param array $typeConfig
|
||||
* @param array $typeConfig
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $objectPersisterId
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param string $objectPersisterId
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function loadTypeProvider(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
|
||||
|
@ -427,7 +444,7 @@ class FOSElasticaExtension extends Extension
|
|||
* index and type names were "prototype" and a driver, respectively.
|
||||
*/
|
||||
$providerId = sprintf('fos_elastica.provider.%s.%s', $indexName, $typeName);
|
||||
$providerDef = new DefinitionDecorator('fos_elastica.provider.prototype.' . $typeConfig['driver']);
|
||||
$providerDef = new DefinitionDecorator('fos_elastica.provider.prototype.'.$typeConfig['driver']);
|
||||
$providerDef->addTag('fos_elastica.provider', array('index' => $indexName, 'type' => $typeName));
|
||||
$providerDef->replaceArgument(0, new Reference($objectPersisterId));
|
||||
$providerDef->replaceArgument(2, $typeConfig['model']);
|
||||
|
@ -444,11 +461,12 @@ class FOSElasticaExtension extends Extension
|
|||
/**
|
||||
* Loads doctrine listeners to handle indexing of new or updated objects.
|
||||
*
|
||||
* @param array $typeConfig
|
||||
* @param array $typeConfig
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $objectPersisterId
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param string $objectPersisterId
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function loadTypeListener(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
|
||||
|
@ -464,19 +482,30 @@ 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(1, $this->getDoctrineEvents($typeConfig));
|
||||
$listenerDef->replaceArgument(3, array(
|
||||
$listenerDef->replaceArgument(2, array(
|
||||
'identifier' => $typeConfig['identifier'],
|
||||
'indexName' => $indexName,
|
||||
'typeName' => $typeName,
|
||||
));
|
||||
if ($typeConfig['listener']['logger']) {
|
||||
$listenerDef->replaceArgument(4, new Reference($typeConfig['listener']['logger']));
|
||||
$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;
|
||||
}
|
||||
|
||||
switch ($typeConfig['driver']) {
|
||||
case 'orm': $listenerDef->addTag('doctrine.event_subscriber'); break;
|
||||
case 'mongodb': $listenerDef->addTag('doctrine_mongodb.odm.event_subscriber'); break;
|
||||
if (null !== $tagName) {
|
||||
foreach ($this->getDoctrineEvents($typeConfig) as $event) {
|
||||
$listenerDef->addTag($tagName, array('event' => $event));
|
||||
}
|
||||
}
|
||||
|
||||
$container->setDefinition($listenerId, $listenerDef);
|
||||
|
@ -485,7 +514,7 @@ class FOSElasticaExtension extends Extension
|
|||
}
|
||||
|
||||
/**
|
||||
* Map Elastica to Doctrine events for the current driver
|
||||
* Map Elastica to Doctrine events for the current driver.
|
||||
*/
|
||||
private function getDoctrineEvents(array $typeConfig)
|
||||
{
|
||||
|
@ -498,7 +527,6 @@ class FOSElasticaExtension extends Extension
|
|||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException(sprintf('Cannot determine events for driver "%s"', $typeConfig['driver']));
|
||||
break;
|
||||
}
|
||||
|
||||
$events = array();
|
||||
|
@ -506,7 +534,7 @@ class FOSElasticaExtension extends Extension
|
|||
'insert' => array(constant($eventsClass.'::postPersist')),
|
||||
'update' => array(constant($eventsClass.'::postUpdate')),
|
||||
'delete' => array(constant($eventsClass.'::preRemove')),
|
||||
'flush' => array($typeConfig['listener']['immediate'] ? constant($eventsClass.'::preFlush') : constant($eventsClass.'::postFlush'))
|
||||
'flush' => array($typeConfig['listener']['immediate'] ? constant($eventsClass.'::preFlush') : constant($eventsClass.'::postFlush')),
|
||||
);
|
||||
|
||||
foreach ($eventMapping as $event => $doctrineEvents) {
|
||||
|
@ -521,12 +549,13 @@ class FOSElasticaExtension extends Extension
|
|||
/**
|
||||
* Loads a Type specific Finder.
|
||||
*
|
||||
* @param array $typeConfig
|
||||
* @param array $typeConfig
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $elasticaToModelId
|
||||
* @param Reference $typeRef
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param string $elasticaToModelId
|
||||
* @param Reference $typeRef
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function loadTypeFinder(array $typeConfig, ContainerBuilder $container, $elasticaToModelId, Reference $typeRef, $indexName, $typeName)
|
||||
|
@ -553,7 +582,7 @@ class FOSElasticaExtension extends Extension
|
|||
}
|
||||
|
||||
/**
|
||||
* Loads the index manager
|
||||
* Loads the index manager.
|
||||
*
|
||||
* @param ContainerBuilder $container
|
||||
**/
|
||||
|
@ -571,7 +600,7 @@ class FOSElasticaExtension extends Extension
|
|||
* Makes sure a specific driver has been loaded.
|
||||
*
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $driver
|
||||
* @param string $driver
|
||||
*/
|
||||
private function loadDriver(ContainerBuilder $container, $driver)
|
||||
{
|
||||
|
@ -587,7 +616,7 @@ class FOSElasticaExtension extends Extension
|
|||
/**
|
||||
* Loads and configures the serializer prototype.
|
||||
*
|
||||
* @param array $config
|
||||
* @param array $config
|
||||
* @param ContainerBuilder $container
|
||||
*/
|
||||
private function loadSerializer($config, ContainerBuilder $container)
|
||||
|
@ -606,7 +635,7 @@ class FOSElasticaExtension extends Extension
|
|||
/**
|
||||
* Creates a default manager alias for defined default manager or the first loaded driver.
|
||||
*
|
||||
* @param string $defaultManager
|
||||
* @param string $defaultManager
|
||||
* @param ContainerBuilder $container
|
||||
*/
|
||||
private function createDefaultManagerAlias($defaultManager, ContainerBuilder $container)
|
||||
|
@ -630,7 +659,9 @@ class FOSElasticaExtension extends Extension
|
|||
* Returns a reference to a client given its configured name.
|
||||
*
|
||||
* @param string $clientName
|
||||
*
|
||||
* @return Reference
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function getClient($clientName)
|
||||
|
|
|
@ -2,32 +2,34 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Doctrine;
|
||||
|
||||
use Doctrine\Common\Persistence\ManagerRegistry;
|
||||
use FOS\ElasticaBundle\HybridResult;
|
||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
||||
use FOS\ElasticaBundle\Transformer\AbstractElasticaToModelTransformer as BaseTransformer;
|
||||
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
|
||||
* elastica documents ids and doctrine object ids.
|
||||
*/
|
||||
abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||
abstract class AbstractElasticaToModelTransformer extends BaseTransformer
|
||||
{
|
||||
/**
|
||||
* Manager registry
|
||||
* Manager registry.
|
||||
*
|
||||
* @var ManagerRegistry
|
||||
*/
|
||||
protected $registry = null;
|
||||
|
||||
/**
|
||||
* Class of the model to map to the elastica documents
|
||||
* Class of the model to map to the elastica documents.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $objectClass = null;
|
||||
|
||||
/**
|
||||
* Optional parameters
|
||||
* Optional parameters.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
|
@ -39,20 +41,13 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
|||
);
|
||||
|
||||
/**
|
||||
* PropertyAccessor instance
|
||||
* Instantiates a new Mapper.
|
||||
*
|
||||
* @var PropertyAccessorInterface
|
||||
*/
|
||||
protected $propertyAccessor;
|
||||
|
||||
/**
|
||||
* Instantiates a new Mapper
|
||||
*
|
||||
* @param object $registry
|
||||
* @param ManagerRegistry $registry
|
||||
* @param string $objectClass
|
||||
* @param array $options
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($registry, $objectClass, array $options = array())
|
||||
public function __construct(ManagerRegistry $registry, $objectClass, array $options = array())
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->objectClass = $objectClass;
|
||||
|
@ -69,22 +64,14 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
|||
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
|
||||
* model objects fetched from the doctrine repository.
|
||||
*
|
||||
* @param array $elasticaObjects of elastica objects
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*
|
||||
* @return array
|
||||
**/
|
||||
public function transform(array $elasticaObjects)
|
||||
|
@ -109,11 +96,7 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
|||
// sort objects in the order of ids
|
||||
$idPos = array_flip($ids);
|
||||
$identifier = $this->options['identifier'];
|
||||
$propertyAccessor = $this->propertyAccessor;
|
||||
usort($objects, function($a, $b) use ($idPos, $identifier, $propertyAccessor)
|
||||
{
|
||||
return $idPos[$propertyAccessor->getValue($a, $identifier)] > $idPos[$propertyAccessor->getValue($b, $identifier)];
|
||||
});
|
||||
usort($objects, $this->getSortingClosure($idPos, $identifier));
|
||||
|
||||
return $objects;
|
||||
}
|
||||
|
@ -137,7 +120,7 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getIdentifierField()
|
||||
{
|
||||
|
@ -145,11 +128,12 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
|||
}
|
||||
|
||||
/**
|
||||
* Fetches objects by theses identifier values
|
||||
* Fetches objects by theses identifier values.
|
||||
*
|
||||
* @param array $identifierValues ids values
|
||||
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
||||
*
|
||||
* @param array $identifierValues ids values
|
||||
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
||||
* @return array of objects or arrays
|
||||
*/
|
||||
protected abstract function findByIdentifiers(array $identifierValues, $hydrate);
|
||||
abstract protected function findByIdentifiers(array $identifierValues, $hydrate);
|
||||
}
|
||||
|
|
|
@ -7,125 +7,61 @@ 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;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param IndexableInterface $indexable
|
||||
* @param string $objectClass
|
||||
* @param array $options
|
||||
* @param ManagerRegistry $managerRegistry
|
||||
* @param IndexableInterface $indexable
|
||||
* @param string $objectClass
|
||||
* @param array $baseOptions
|
||||
* @param ManagerRegistry $managerRegistry
|
||||
* @param SliceFetcherInterface $sliceFetcher
|
||||
*/
|
||||
public function __construct(
|
||||
ObjectPersisterInterface $objectPersister,
|
||||
IndexableInterface $indexable,
|
||||
$objectClass,
|
||||
array $options,
|
||||
ManagerRegistry $managerRegistry
|
||||
array $baseOptions,
|
||||
ManagerRegistry $managerRegistry,
|
||||
SliceFetcherInterface $sliceFetcher = null
|
||||
) {
|
||||
parent::__construct($objectPersister, $indexable, $objectClass, array_merge(array(
|
||||
'clear_object_manager' => true,
|
||||
'debug_logging' => false,
|
||||
'ignore_errors' => false,
|
||||
'query_builder_method' => 'createQueryBuilder',
|
||||
), $options));
|
||||
parent::__construct($objectPersister, $indexable, $objectClass, $baseOptions);
|
||||
|
||||
$this->managerRegistry = $managerRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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>');
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
$this->sliceFetcher = $sliceFetcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts objects that would be indexed using the query builder.
|
||||
*
|
||||
* @param object $queryBuilder
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
protected abstract function countObjects($queryBuilder);
|
||||
abstract protected function countObjects($queryBuilder);
|
||||
|
||||
/**
|
||||
* Disables logging and returns the logger that was previously set.
|
||||
* Creates the query builder, which will be used to fetch objects to index.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected abstract function disableLogging();
|
||||
|
||||
/**
|
||||
* Reenables the logger with the previously returned logger from disableLogging();
|
||||
* @param string $method
|
||||
*
|
||||
* @param mixed $logger
|
||||
* @return mixed
|
||||
* @return object
|
||||
*/
|
||||
protected abstract function enableLogging($logger);
|
||||
abstract protected function createQueryBuilder($method);
|
||||
|
||||
/**
|
||||
* Fetches a slice of objects using the query builder.
|
||||
|
@ -133,14 +69,102 @@ abstract class AbstractProvider extends BaseAbstractProvider
|
|||
* @param object $queryBuilder
|
||||
* @param integer $limit
|
||||
* @param integer $offset
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected abstract function fetchSlice($queryBuilder, $limit, $offset);
|
||||
abstract protected function fetchSlice($queryBuilder, $limit, $offset);
|
||||
|
||||
/**
|
||||
* Creates the query builder, which will be used to fetch objects to index.
|
||||
*
|
||||
* @return object
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected abstract function createQueryBuilder();
|
||||
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.
|
||||
*
|
||||
* @param $queryBuilder
|
||||
* @param int $limit
|
||||
* @param int $offset
|
||||
* @param array $lastSlice
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Doctrine;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersister;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use FOS\ElasticaBundle\Provider\IndexableInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
|
@ -14,49 +14,52 @@ 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 implements EventSubscriber
|
||||
class Listener
|
||||
{
|
||||
/**
|
||||
* Object persister
|
||||
* Object persister.
|
||||
*
|
||||
* @var ObjectPersister
|
||||
* @var ObjectPersisterInterface
|
||||
*/
|
||||
protected $objectPersister;
|
||||
|
||||
/**
|
||||
* List of subscribed events
|
||||
* Configuration for the listener.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $events;
|
||||
|
||||
/**
|
||||
* Configuration for the listener
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* Objects scheduled for insertion and replacement
|
||||
* Objects scheduled for insertion.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $scheduledForInsertion = array();
|
||||
|
||||
/**
|
||||
* Objects scheduled to be updated or removed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $scheduledForUpdate = array();
|
||||
|
||||
/**
|
||||
* IDs of objects scheduled for removal
|
||||
* IDs of objects scheduled for removal.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $scheduledForDeletion = array();
|
||||
|
||||
/**
|
||||
* PropertyAccessor instance
|
||||
* PropertyAccessor instance.
|
||||
*
|
||||
* @var PropertyAccessorInterface
|
||||
*/
|
||||
protected $propertyAccessor;
|
||||
|
||||
/**
|
||||
* @var \FOS\ElasticaBundle\Provider\IndexableInterface
|
||||
* @var IndexableInterface
|
||||
*/
|
||||
private $indexable;
|
||||
|
||||
|
@ -64,71 +67,50 @@ class Listener implements EventSubscriber
|
|||
* Constructor.
|
||||
*
|
||||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param array $events
|
||||
* @param IndexableInterface $indexable
|
||||
* @param array $config
|
||||
* @param null $logger
|
||||
* @param IndexableInterface $indexable
|
||||
* @param array $config
|
||||
* @param LoggerInterface $logger
|
||||
*/
|
||||
public function __construct(
|
||||
ObjectPersisterInterface $objectPersister,
|
||||
array $events,
|
||||
IndexableInterface $indexable,
|
||||
array $config = array(),
|
||||
$logger = null
|
||||
LoggerInterface $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) {
|
||||
if ($logger && $this->objectPersister instanceof ObjectPersister) {
|
||||
$this->objectPersister->setLogger($logger);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Doctrine\Common\EventSubscriber::getSubscribedEvents()
|
||||
*/
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides unified method for retrieving a doctrine object from an EventArgs instance
|
||||
* Looks for new objects that should be indexed.
|
||||
*
|
||||
* @param EventArgs $eventArgs
|
||||
* @return object Entity | Document
|
||||
* @throws \RuntimeException if no valid getter is found.
|
||||
* @param LifecycleEventArgs $eventArgs
|
||||
*/
|
||||
private function getDoctrineObject(EventArgs $eventArgs)
|
||||
public function postPersist(LifecycleEventArgs $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);
|
||||
$entity = $eventArgs->getObject();
|
||||
|
||||
if ($this->objectPersister->handlesObject($entity) && $this->isObjectIndexable($entity)) {
|
||||
$this->scheduledForInsertion[] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
public function postUpdate(EventArgs $eventArgs)
|
||||
/**
|
||||
* Looks for objects being updated that should be indexed or removed from the index.
|
||||
*
|
||||
* @param LifecycleEventArgs $eventArgs
|
||||
*/
|
||||
public function postUpdate(LifecycleEventArgs $eventArgs)
|
||||
{
|
||||
$entity = $this->getDoctrineObject($eventArgs);
|
||||
$entity = $eventArgs->getObject();
|
||||
|
||||
if ($this->objectPersister->handlesObject($entity)) {
|
||||
if ($this->isObjectIndexable($entity)) {
|
||||
|
@ -142,11 +124,13 @@ class Listener implements EventSubscriber
|
|||
|
||||
/**
|
||||
* 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
|
||||
* preRemove, first check that the entity is managed by Doctrine.
|
||||
*
|
||||
* @param LifecycleEventArgs $eventArgs
|
||||
*/
|
||||
public function preRemove(EventArgs $eventArgs)
|
||||
public function preRemove(LifecycleEventArgs $eventArgs)
|
||||
{
|
||||
$entity = $this->getDoctrineObject($eventArgs);
|
||||
$entity = $eventArgs->getObject();
|
||||
|
||||
if ($this->objectPersister->handlesObject($entity)) {
|
||||
$this->scheduleForDeletion($entity);
|
||||
|
@ -155,7 +139,7 @@ class Listener implements EventSubscriber
|
|||
|
||||
/**
|
||||
* Persist scheduled objects to ElasticSearch
|
||||
* After persisting, clear the scheduled queue to prevent multiple data updates when using multiple flush calls
|
||||
* After persisting, clear the scheduled queue to prevent multiple data updates when using multiple flush calls.
|
||||
*/
|
||||
private function persistScheduled()
|
||||
{
|
||||
|
@ -179,6 +163,10 @@ class Listener implements EventSubscriber
|
|||
* 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()
|
||||
{
|
||||
|
@ -199,7 +187,7 @@ class Listener implements EventSubscriber
|
|||
*
|
||||
* @param object $object
|
||||
*/
|
||||
protected function scheduleForDeletion($object)
|
||||
private function scheduleForDeletion($object)
|
||||
{
|
||||
if ($identifierValue = $this->propertyAccessor->getValue($object, $this->config['identifier'])) {
|
||||
$this->scheduledForDeletion[] = $identifierValue;
|
||||
|
@ -210,6 +198,7 @@ class Listener implements EventSubscriber
|
|||
* Checks if the object is indexable or not.
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isObjectIndexable($object)
|
||||
|
|
|
@ -7,15 +7,16 @@ use FOS\ElasticaBundle\Doctrine\AbstractElasticaToModelTransformer;
|
|||
/**
|
||||
* Maps Elastica documents with Doctrine objects
|
||||
* This mapper assumes an exact match between
|
||||
* elastica documents ids and doctrine object ids
|
||||
* elastica documents ids and doctrine object ids.
|
||||
*/
|
||||
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
||||
{
|
||||
/**
|
||||
* Fetch objects for theses identifier values
|
||||
* Fetch objects for theses identifier values.
|
||||
*
|
||||
* @param array $identifierValues ids values
|
||||
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
||||
*
|
||||
* @param array $identifierValues ids values
|
||||
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
||||
* @return array of objects or arrays
|
||||
*/
|
||||
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
||||
|
|
|
@ -27,9 +27,10 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* Reenables the logger with the previously returned logger from disableLogging();
|
||||
* Reenables the logger with the previously returned logger from disableLogging();.
|
||||
*
|
||||
* @param mixed $logger
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function enableLogging($logger)
|
||||
|
@ -43,7 +44,7 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function countObjects($queryBuilder)
|
||||
{
|
||||
|
@ -57,7 +58,7 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::fetchSlice()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function fetchSlice($queryBuilder, $limit, $offset)
|
||||
{
|
||||
|
@ -66,21 +67,21 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
return $queryBuilder
|
||||
->limit($limit)
|
||||
->skip($offset)
|
||||
->limit($limit)
|
||||
->getQuery()
|
||||
->execute()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::createQueryBuilder()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function createQueryBuilder()
|
||||
protected function createQueryBuilder($method)
|
||||
{
|
||||
return $this->managerRegistry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getRepository($this->objectClass)
|
||||
->{$this->options['query_builder_method']}();
|
||||
->{$method}();
|
||||
}
|
||||
}
|
||||
|
|
44
Doctrine/MongoDB/SliceFetcher.php
Normal file
44
Doctrine/MongoDB/SliceFetcher.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?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()
|
||||
;
|
||||
}
|
||||
}
|
|
@ -8,17 +8,18 @@ use Doctrine\ORM\Query;
|
|||
/**
|
||||
* Maps Elastica documents with Doctrine objects
|
||||
* This mapper assumes an exact match between
|
||||
* elastica documents ids and doctrine object ids
|
||||
* elastica documents ids and doctrine object ids.
|
||||
*/
|
||||
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
||||
{
|
||||
const ENTITY_ALIAS = 'o';
|
||||
|
||||
/**
|
||||
* Fetch objects for theses identifier values
|
||||
* Fetch objects for theses identifier values.
|
||||
*
|
||||
* @param array $identifierValues ids values
|
||||
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
||||
*
|
||||
* @param array $identifierValues ids values
|
||||
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
||||
* @return array of objects or arrays
|
||||
*/
|
||||
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
||||
|
@ -36,7 +37,7 @@ class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
|||
}
|
||||
|
||||
/**
|
||||
* Retrieves a query builder to be used for querying by identifiers
|
||||
* Retrieves a query builder to be used for querying by identifiers.
|
||||
*
|
||||
* @return \Doctrine\ORM\QueryBuilder
|
||||
*/
|
||||
|
|
|
@ -29,9 +29,10 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* Reenables the logger with the previously returned logger from disableLogging();
|
||||
* Reenables the logger with the previously returned logger from disableLogging();.
|
||||
*
|
||||
* @param mixed $logger
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function enableLogging($logger)
|
||||
|
@ -45,7 +46,7 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function countObjects($queryBuilder)
|
||||
{
|
||||
|
@ -68,7 +69,9 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::fetchSlice()
|
||||
* This method should remain in sync with SliceFetcher::fetch until it is deprecated and removed.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function fetchSlice($queryBuilder, $limit, $offset)
|
||||
{
|
||||
|
@ -76,8 +79,8 @@ class Provider extends AbstractProvider
|
|||
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
|
||||
}
|
||||
|
||||
/**
|
||||
* An orderBy DQL part is required to avoid feching the same row twice.
|
||||
/*
|
||||
* An orderBy DQL part is required to avoid fetching 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
|
||||
|
@ -102,14 +105,14 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::createQueryBuilder()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function createQueryBuilder()
|
||||
protected function createQueryBuilder($method)
|
||||
{
|
||||
return $this->managerRegistry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getRepository($this->objectClass)
|
||||
// ORM query builders require an alias argument
|
||||
->{$this->options['query_builder_method']}(static::ENTITY_ALIAS);
|
||||
->{$method}(static::ENTITY_ALIAS);
|
||||
}
|
||||
}
|
||||
|
|
50
Doctrine/ORM/SliceFetcher.php
Normal file
50
Doctrine/ORM/SliceFetcher.php
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?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()
|
||||
;
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ class RepositoryManager extends BaseManager
|
|||
}
|
||||
|
||||
/**
|
||||
* Return repository for entity
|
||||
* Return repository for entity.
|
||||
*
|
||||
* Returns custom repository if one specified otherwise
|
||||
* returns a basic repository.
|
||||
|
@ -35,7 +35,7 @@ class RepositoryManager extends BaseManager
|
|||
$realEntityName = $entityName;
|
||||
if (strpos($entityName, ':') !== false) {
|
||||
list($namespaceAlias, $simpleClassName) = explode(':', $entityName);
|
||||
$realEntityName = $this->managerRegistry->getAliasNamespace($namespaceAlias) . '\\' . $simpleClassName;
|
||||
$realEntityName = $this->managerRegistry->getAliasNamespace($namespaceAlias).'\\'.$simpleClassName;
|
||||
}
|
||||
|
||||
return parent::getRepository($realEntityName);
|
||||
|
|
24
Doctrine/SliceFetcherInterface.php
Normal file
24
Doctrine/SliceFetcherInterface.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?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);
|
||||
}
|
|
@ -23,7 +23,7 @@ class Client extends BaseClient
|
|||
private $indexCache = array();
|
||||
|
||||
/**
|
||||
* Symfony's debugging Stopwatch
|
||||
* Symfony's debugging Stopwatch.
|
||||
*
|
||||
* @var Stopwatch|null
|
||||
*/
|
||||
|
@ -32,8 +32,9 @@ class Client extends BaseClient
|
|||
/**
|
||||
* @param string $path
|
||||
* @param string $method
|
||||
* @param array $data
|
||||
* @param array $query
|
||||
* @param array $data
|
||||
* @param array $query
|
||||
*
|
||||
* @return \Elastica\Response
|
||||
*/
|
||||
public function request($path, $method = Request::GET, $data = array(), array $query = array())
|
||||
|
@ -78,9 +79,9 @@ class Client extends BaseClient
|
|||
*
|
||||
* @param string $path
|
||||
* @param string $method
|
||||
* @param array $data
|
||||
* @param array $query
|
||||
* @param int $start
|
||||
* @param array $data
|
||||
* @param array $query
|
||||
* @param int $start
|
||||
*/
|
||||
private function logQuery($path, $method, $data, array $query, $start)
|
||||
{
|
||||
|
|
|
@ -44,14 +44,12 @@ class Index extends BaseIndex
|
|||
}
|
||||
|
||||
/**
|
||||
* Reassign index name
|
||||
* Reassign index name.
|
||||
*
|
||||
* While it's technically a regular setter for name property, it's specifically named overrideName, but not setName
|
||||
* since it's used for a very specific case and normally should not be used
|
||||
*
|
||||
* @param string $name Index name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function overrideName($name)
|
||||
{
|
||||
|
|
38
Event/IndexEvent.php
Normal file
38
Event/IndexEvent.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
70
Event/IndexPopulateEvent.php
Normal file
70
Event/IndexPopulateEvent.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
70
Event/IndexResetEvent.php
Normal file
70
Event/IndexResetEvent.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
78
Event/TransformEvent.php
Normal file
78
Event/TransformEvent.php
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
51
Event/TypePopulateEvent.php
Normal file
51
Event/TypePopulateEvent.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
49
Event/TypeResetEvent.php
Normal file
49
Event/TypeResetEvent.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?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,7 +6,6 @@ class AliasIsIndexException extends \Exception
|
|||
{
|
||||
public function __construct($indexName)
|
||||
{
|
||||
parent::__construct(sprintf('Expected alias %s instead of index', $indexName));
|
||||
parent::__construct(sprintf('Expected %s to be an alias but it is an index.', $indexName));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
|
|||
|
||||
/**
|
||||
* Bundle.
|
||||
*
|
||||
*/
|
||||
class FOSElasticaBundle extends Bundle
|
||||
{
|
||||
|
|
|
@ -5,12 +5,13 @@ namespace FOS\ElasticaBundle\Finder;
|
|||
interface FinderInterface
|
||||
{
|
||||
/**
|
||||
* Searches for query results within a given limit
|
||||
* Searches for query results within a given limit.
|
||||
*
|
||||
* @param mixed $query Can be a string, an array or an \Elastica\Query object
|
||||
* @param int $limit How many results to get
|
||||
* @param mixed $query Can be a string, an array or an \Elastica\Query object
|
||||
* @param int $limit How many results to get
|
||||
* @param array $options
|
||||
*
|
||||
* @return array results
|
||||
*/
|
||||
function find($query, $limit = null, $options = array());
|
||||
public function find($query, $limit = null, $options = array());
|
||||
}
|
||||
|
|
|
@ -9,20 +9,22 @@ use Elastica\Query;
|
|||
interface PaginatedFinderInterface extends FinderInterface
|
||||
{
|
||||
/**
|
||||
* Searches for query results and returns them wrapped in a paginator
|
||||
* Searches for query results and returns them wrapped in a paginator.
|
||||
*
|
||||
* @param mixed $query Can be a string, an array or an \Elastica\Query object
|
||||
* @param mixed $query Can be a string, an array or an \Elastica\Query object
|
||||
* @param array $options
|
||||
*
|
||||
* @return Pagerfanta paginated results
|
||||
*/
|
||||
function findPaginated($query, $options = array());
|
||||
public function findPaginated($query, $options = array());
|
||||
|
||||
/**
|
||||
* Creates a paginator adapter for this query
|
||||
* Creates a paginator adapter for this query.
|
||||
*
|
||||
* @param mixed $query
|
||||
* @param array $options
|
||||
*
|
||||
* @return PaginatorAdapterInterface
|
||||
*/
|
||||
function createPaginatorAdapter($query, $options = array());
|
||||
public function createPaginatorAdapter($query, $options = array());
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use Elastica\SearchableInterface;
|
|||
use Elastica\Query;
|
||||
|
||||
/**
|
||||
* Finds elastica documents and map them to persisted objects
|
||||
* Finds elastica documents and map them to persisted objects.
|
||||
*/
|
||||
class TransformedFinder implements PaginatedFinderInterface
|
||||
{
|
||||
|
@ -25,11 +25,12 @@ class TransformedFinder implements PaginatedFinderInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Search for a query string
|
||||
* Search for a query string.
|
||||
*
|
||||
* @param string $query
|
||||
* @param string $query
|
||||
* @param integer $limit
|
||||
* @param array $options
|
||||
* @param array $options
|
||||
*
|
||||
* @return array of model objects
|
||||
**/
|
||||
public function find($query, $limit = null, $options = array())
|
||||
|
@ -50,8 +51,9 @@ class TransformedFinder implements PaginatedFinderInterface
|
|||
* Find documents similar to one with passed id.
|
||||
*
|
||||
* @param integer $id
|
||||
* @param array $params
|
||||
* @param array $query
|
||||
* @param array $params
|
||||
* @param array $query
|
||||
*
|
||||
* @return array of model objects
|
||||
**/
|
||||
public function moreLikeThis($id, $params = array(), $query = array())
|
||||
|
@ -65,7 +67,8 @@ class TransformedFinder implements PaginatedFinderInterface
|
|||
/**
|
||||
* @param $query
|
||||
* @param null|int $limit
|
||||
* @param array $options
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function search($query, $limit = null, $options = array())
|
||||
|
@ -80,10 +83,11 @@ class TransformedFinder implements PaginatedFinderInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets a paginator wrapping the result of a search
|
||||
* Gets a paginator wrapping the result of a search.
|
||||
*
|
||||
* @param string $query
|
||||
* @param array $options
|
||||
* @param array $options
|
||||
*
|
||||
* @return Pagerfanta
|
||||
*/
|
||||
public function findPaginated($query, $options = array())
|
||||
|
|
|
@ -24,4 +24,4 @@ class HybridResult
|
|||
{
|
||||
return $this->result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,11 +24,16 @@ class AliasProcessor
|
|||
* Sets the randomised root name for an index.
|
||||
*
|
||||
* @param IndexConfig $indexConfig
|
||||
* @param Index $index
|
||||
* @param Index $index
|
||||
*/
|
||||
public function setRootName(IndexConfig $indexConfig, Index $index)
|
||||
{
|
||||
$index->overrideName(sprintf('%s_%s', $indexConfig->getElasticSearchName(), uniqid()));
|
||||
$index->overrideName(
|
||||
sprintf('%s_%s',
|
||||
$indexConfig->getElasticSearchName(),
|
||||
date('Y-m-d-His')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,8 +43,9 @@ class AliasProcessor
|
|||
* $force will delete an index encountered where an alias is expected.
|
||||
*
|
||||
* @param IndexConfig $indexConfig
|
||||
* @param Index $index
|
||||
* @param bool $force
|
||||
* @param Index $index
|
||||
* @param bool $force
|
||||
*
|
||||
* @throws AliasIsIndexException
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
|
@ -48,95 +54,117 @@ class AliasProcessor
|
|||
$client = $index->getClient();
|
||||
|
||||
$aliasName = $indexConfig->getElasticSearchName();
|
||||
$oldIndexName = false;
|
||||
$oldIndexName = null;
|
||||
$newIndexName = $index->getName();
|
||||
|
||||
try {
|
||||
$aliasedIndexes = $this->getAliasedIndexes($client, $aliasName);
|
||||
} catch(AliasIsIndexException $e) {
|
||||
$oldIndexName = $this->getAliasedIndex($client, $aliasName);
|
||||
} catch (AliasIsIndexException $e) {
|
||||
if (!$force) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->deleteIndex($client, $aliasName);
|
||||
$aliasedIndexes = array();
|
||||
}
|
||||
|
||||
if (count($aliasedIndexes) > 1) {
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
'Alias %s is used for multiple indexes: [%s].
|
||||
Make sure it\'s either not used or is assigned to one index only',
|
||||
$aliasName,
|
||||
join(', ', $aliasedIndexes)
|
||||
)
|
||||
);
|
||||
try {
|
||||
$aliasUpdateRequest = $this->buildAliasUpdateRequest($oldIndexName, $aliasName, $newIndexName);
|
||||
$client->request('_aliases', 'POST', $aliasUpdateRequest);
|
||||
} catch (ExceptionInterface $e) {
|
||||
$this->cleanupRenameFailure($client, $newIndexName, $e);
|
||||
}
|
||||
|
||||
// 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 (count($aliasedIndexes) === 1) {
|
||||
if (null !== $aliasedIndex) {
|
||||
// if the alias is set - add an action to remove it
|
||||
$oldIndexName = $aliasedIndexes[0];
|
||||
$aliasUpdateRequest['actions'][] = array(
|
||||
'remove' => array('index' => $oldIndexName, 'alias' => $aliasName)
|
||||
'remove' => array('index' => $aliasedIndex, 'alias' => $aliasName),
|
||||
);
|
||||
}
|
||||
|
||||
// add an action to point the alias to the new index
|
||||
$aliasUpdateRequest['actions'][] = array(
|
||||
'add' => array('index' => $newIndexName, 'alias' => $aliasName)
|
||||
'add' => array('index' => $newIndexName, 'alias' => $aliasName),
|
||||
);
|
||||
|
||||
try {
|
||||
$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()
|
||||
);
|
||||
}
|
||||
return $aliasUpdateRequest;
|
||||
}
|
||||
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
'Failed to updated index alias: %s. %s',
|
||||
$renameAliasException->getMessage(),
|
||||
$additionalError ?: sprintf('Newly built index %s was deleted', $newIndexName)
|
||||
), 0, $renameAliasException
|
||||
/**
|
||||
* 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()
|
||||
);
|
||||
}
|
||||
|
||||
// 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
|
||||
);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of indexes which are mapped to given alias
|
||||
* Returns the name of a single index that an alias points to or throws
|
||||
* an exception if there is more than one.
|
||||
*
|
||||
* @param Client $client
|
||||
* @param string $aliasName Alias name
|
||||
* @return array
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @throws AliasIsIndexException
|
||||
*/
|
||||
private function getAliasedIndexes(Client $client, $aliasName)
|
||||
private function getAliasedIndex(Client $client, $aliasName)
|
||||
{
|
||||
$aliasesInfo = $client->request('_aliases', 'GET')->getData();
|
||||
$aliasedIndexes = array();
|
||||
|
@ -155,18 +183,15 @@ class AliasProcessor
|
|||
}
|
||||
}
|
||||
|
||||
return $aliasedIndexes;
|
||||
}
|
||||
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 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);
|
||||
return array_shift($aliasedIndexes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,11 @@ use FOS\ElasticaBundle\Elastica\Index;
|
|||
|
||||
class IndexManager
|
||||
{
|
||||
/**
|
||||
* @var Index
|
||||
*/
|
||||
private $defaultIndex;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
@ -22,7 +27,7 @@ class IndexManager
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets all registered indexes
|
||||
* Gets all registered indexes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
@ -32,10 +37,12 @@ class IndexManager
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets an index by its name
|
||||
* Gets an index by its name.
|
||||
*
|
||||
* @param string $name Index to return, or the default index if null
|
||||
*
|
||||
* @return Index
|
||||
*
|
||||
* @throws \InvalidArgumentException if no index exists for the given name
|
||||
*/
|
||||
public function getIndex($name = null)
|
||||
|
@ -52,7 +59,7 @@ class IndexManager
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the default index
|
||||
* Gets the default index.
|
||||
*
|
||||
* @return Index
|
||||
*/
|
||||
|
|
|
@ -27,6 +27,7 @@ class MappingBuilder
|
|||
* Builds mappings for an entire index.
|
||||
*
|
||||
* @param IndexConfig $indexConfig
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function buildIndexMapping(IndexConfig $indexConfig)
|
||||
|
@ -37,13 +38,13 @@ class MappingBuilder
|
|||
}
|
||||
|
||||
$mapping = array();
|
||||
if ($typeMappings) {
|
||||
if (!empty($typeMappings)) {
|
||||
$mapping['mappings'] = $typeMappings;
|
||||
}
|
||||
// 'warmers' => $indexConfig->getWarmers(),
|
||||
|
||||
$settings = $indexConfig->getSettings();
|
||||
if ($settings) {
|
||||
if (!empty($settings)) {
|
||||
$mapping['settings'] = $settings;
|
||||
}
|
||||
|
||||
|
@ -54,17 +55,24 @@ class MappingBuilder
|
|||
* Builds mappings for a single type.
|
||||
*
|
||||
* @param TypeConfig $typeConfig
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function buildTypeMapping(TypeConfig $typeConfig)
|
||||
{
|
||||
$mapping = array_merge($typeConfig->getMapping(), array(
|
||||
// 'date_detection' => true,
|
||||
// 'dynamic_date_formats' => array()
|
||||
// 'dynamic_templates' => $typeConfig->getDynamicTemplates(),
|
||||
// 'numeric_detection' => false,
|
||||
// 'properties' => array(),
|
||||
));
|
||||
$mapping = $typeConfig->getMapping();
|
||||
|
||||
if (null !== $typeConfig->getDynamicDateFormats()) {
|
||||
$mapping['dynamic_date_formats'] = $typeConfig->getDynamicDateFormats();
|
||||
}
|
||||
|
||||
if (null !== $typeConfig->getDateDetection()) {
|
||||
$mapping['date_detection'] = $typeConfig->getDateDetection();
|
||||
}
|
||||
|
||||
if (null !== $typeConfig->getNumericDetection()) {
|
||||
$mapping['numeric_detection'] = $typeConfig->getNumericDetection();
|
||||
}
|
||||
|
||||
if ($typeConfig->getIndexAnalyzer()) {
|
||||
$mapping['index_analyzer'] = $typeConfig->getIndexAnalyzer();
|
||||
|
@ -87,9 +95,9 @@ class MappingBuilder
|
|||
$mapping['_meta']['model'] = $typeConfig->getModel();
|
||||
}
|
||||
|
||||
if (!$mapping) {
|
||||
if (empty($mapping)) {
|
||||
// Empty mapping, we want it encoded as a {} instead of a []
|
||||
$mapping = new \stdClass;
|
||||
$mapping = new \stdClass();
|
||||
}
|
||||
|
||||
return $mapping;
|
||||
|
@ -104,9 +112,14 @@ class MappingBuilder
|
|||
private function fixProperties(&$properties)
|
||||
{
|
||||
foreach ($properties as $name => &$property) {
|
||||
unset($property['property_path']);
|
||||
|
||||
if (!isset($property['type'])) {
|
||||
$property['type'] = 'string';
|
||||
}
|
||||
if ($property['type'] == 'multi_field' && isset($property['fields'])) {
|
||||
$this->fixProperties($property['fields']);
|
||||
}
|
||||
if (isset($property['properties'])) {
|
||||
$this->fixProperties($property['properties']);
|
||||
}
|
||||
|
|
|
@ -6,9 +6,12 @@ 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
|
||||
* Deletes and recreates indexes.
|
||||
*/
|
||||
class Resetter
|
||||
{
|
||||
|
@ -18,10 +21,15 @@ class Resetter
|
|||
private $aliasProcessor;
|
||||
|
||||
/***
|
||||
* @var \FOS\ElasticaBundle\Configuration\Manager
|
||||
* @var ConfigManager
|
||||
*/
|
||||
private $configManager;
|
||||
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
/**
|
||||
* @var IndexManager
|
||||
*/
|
||||
|
@ -32,16 +40,32 @@ class Resetter
|
|||
*/
|
||||
private $mappingBuilder;
|
||||
|
||||
public function __construct(ConfigManager $configManager, IndexManager $indexManager, AliasProcessor $aliasProcessor, MappingBuilder $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
|
||||
) {
|
||||
$this->aliasProcessor = $aliasProcessor;
|
||||
$this->configManager = $configManager;
|
||||
$this->dispatcher = $eventDispatcher;
|
||||
$this->indexManager = $indexManager;
|
||||
$this->mappingBuilder = $mappingBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes and recreates all indexes
|
||||
* Deletes and recreates all indexes.
|
||||
*
|
||||
* @param bool $populating
|
||||
* @param bool $force
|
||||
*/
|
||||
public function resetAllIndexes($populating = false, $force = false)
|
||||
{
|
||||
|
@ -55,8 +79,9 @@ class Resetter
|
|||
* with a randomised name for an alias to be set after population.
|
||||
*
|
||||
* @param string $indexName
|
||||
* @param bool $populating
|
||||
* @param bool $force If index exists with same name as alias, remove it
|
||||
* @param bool $populating
|
||||
* @param bool $force If index exists with same name as alias, remove it
|
||||
*
|
||||
* @throws \InvalidArgumentException if no index exists for the given name
|
||||
*/
|
||||
public function resetIndex($indexName, $populating = false, $force = false)
|
||||
|
@ -64,6 +89,9 @@ 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);
|
||||
}
|
||||
|
@ -74,13 +102,16 @@ class Resetter
|
|||
if (!$populating and $indexConfig->isUseAlias()) {
|
||||
$this->aliasProcessor->switchIndexAlias($indexConfig, $index, $force);
|
||||
}
|
||||
|
||||
$this->dispatcher->dispatch(IndexResetEvent::POST_INDEX_RESET, $event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes and recreates a mapping type for the named index
|
||||
* Deletes and recreates a mapping type for the named index.
|
||||
*
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*
|
||||
* @throws \InvalidArgumentException if no index or type mapping exists for the given names
|
||||
* @throws ResponseException
|
||||
*/
|
||||
|
@ -89,6 +120,9 @@ 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) {
|
||||
|
@ -97,12 +131,14 @@ class Resetter
|
|||
}
|
||||
}
|
||||
|
||||
$mapping = new Mapping;
|
||||
$mapping = new Mapping();
|
||||
foreach ($this->mappingBuilder->buildTypeMapping($typeConfig) as $name => $field) {
|
||||
$mapping->setParam($name, $field);
|
||||
}
|
||||
|
||||
$type->setMapping($mapping);
|
||||
|
||||
$this->dispatcher->dispatch(TypeResetEvent::POST_TYPE_RESET, $event);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,13 +25,13 @@ class RepositoryManager implements RepositoryManagerInterface
|
|||
|
||||
public function addEntity($entityName, FinderInterface $finder, $repositoryName = null)
|
||||
{
|
||||
$this->entities[$entityName]= array();
|
||||
$this->entities[$entityName] = array();
|
||||
$this->entities[$entityName]['finder'] = $finder;
|
||||
$this->entities[$entityName]['repositoryName'] = $repositoryName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return repository for entity
|
||||
* Return repository for entity.
|
||||
*
|
||||
* Returns custom repository if one specified otherwise
|
||||
* returns a basic repository.
|
||||
|
@ -63,6 +63,7 @@ class RepositoryManager implements RepositoryManagerInterface
|
|||
if ($annotation) {
|
||||
$this->entities[$entityName]['repositoryName']
|
||||
= $annotation->repositoryClass;
|
||||
|
||||
return $annotation->repositoryClass;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ use FOS\ElasticaBundle\Finder\FinderInterface;
|
|||
*/
|
||||
interface RepositoryManagerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Adds entity name and its finder.
|
||||
* Custom repository class name can also be added.
|
||||
|
@ -24,7 +23,7 @@ interface RepositoryManagerInterface
|
|||
public function addEntity($entityName, FinderInterface $finder, $repositoryName = null);
|
||||
|
||||
/**
|
||||
* Return repository for entity
|
||||
* Return repository for entity.
|
||||
*
|
||||
* Returns custom repository if one specified otherwise
|
||||
* returns a basic repository.
|
||||
|
|
|
@ -20,8 +20,6 @@ class FantaPaginatorAdapter implements AdapterInterface
|
|||
* Returns the number of results.
|
||||
*
|
||||
* @return integer The number of results.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getNbResults()
|
||||
{
|
||||
|
@ -29,15 +27,25 @@ class FantaPaginatorAdapter implements AdapterInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns Facets
|
||||
* Returns Facets.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFacets()
|
||||
{
|
||||
return $this->adapter->getFacets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Aggregations.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getFacets()
|
||||
public function getAggregations()
|
||||
{
|
||||
return $this->adapter->getFacets();
|
||||
return $this->adapter->getAggregations();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,8 +55,6 @@ class FantaPaginatorAdapter implements AdapterInterface
|
|||
* @param integer $length The length.
|
||||
*
|
||||
* @return array|\Traversable The slice.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getSlice($offset, $length)
|
||||
{
|
||||
|
|
|
@ -8,10 +8,8 @@ interface PaginatorAdapterInterface
|
|||
* Returns the number of results.
|
||||
*
|
||||
* @return integer The number of results.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
function getTotalHits();
|
||||
public function getTotalHits();
|
||||
|
||||
/**
|
||||
* Returns an slice of the results.
|
||||
|
@ -20,15 +18,20 @@ interface PaginatorAdapterInterface
|
|||
* @param integer $length The length.
|
||||
*
|
||||
* @return PartialResultsInterface
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
function getResults($offset, $length);
|
||||
public function getResults($offset, $length);
|
||||
|
||||
/**
|
||||
* Returns Facets
|
||||
* Returns Facets.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function getFacets();
|
||||
public function getFacets();
|
||||
|
||||
/**
|
||||
* Returns Aggregations.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAggregations();
|
||||
}
|
||||
|
|
|
@ -8,24 +8,27 @@ interface PartialResultsInterface
|
|||
* Returns the paginated results.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
function toArray();
|
||||
public function toArray();
|
||||
|
||||
/**
|
||||
* Returns the number of results.
|
||||
*
|
||||
* @return integer The number of results.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
function getTotalHits();
|
||||
public function getTotalHits();
|
||||
|
||||
/**
|
||||
* Returns the facets
|
||||
* Returns the facets.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getFacets();
|
||||
}
|
||||
public function getFacets();
|
||||
|
||||
/**
|
||||
* Returns the aggregations.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAggregations();
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use Elastica\ResultSet;
|
|||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Allows pagination of Elastica\Query. Does not map results
|
||||
* Allows pagination of Elastica\Query. Does not map results.
|
||||
*/
|
||||
class RawPaginatorAdapter implements PaginatorAdapterInterface
|
||||
{
|
||||
|
@ -37,11 +37,16 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
*/
|
||||
private $facets;
|
||||
|
||||
/**
|
||||
* @var array for the aggregations
|
||||
*/
|
||||
private $aggregations;
|
||||
|
||||
/**
|
||||
* @see PaginatorAdapterInterface::__construct
|
||||
*
|
||||
* @param SearchableInterface $searchable the object to search in
|
||||
* @param Query $query the query to search
|
||||
* @param Query $query the query to search
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(SearchableInterface $searchable, Query $query, array $options = array())
|
||||
|
@ -56,7 +61,9 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
*
|
||||
* @param integer $offset
|
||||
* @param integer $itemCountPerPage
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return ResultSet
|
||||
*/
|
||||
protected function getElasticaResults($offset, $itemCountPerPage)
|
||||
|
@ -67,7 +74,7 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
? (integer) $this->query->getParam('size')
|
||||
: null;
|
||||
|
||||
if ($size && $size < $offset + $itemCountPerPage) {
|
||||
if (null !== $size && $size < $offset + $itemCountPerPage) {
|
||||
$itemCountPerPage = $size - $offset;
|
||||
}
|
||||
|
||||
|
@ -82,6 +89,8 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -90,6 +99,7 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
*
|
||||
* @param int $offset
|
||||
* @param int $itemCountPerPage
|
||||
*
|
||||
* @return PartialResultsInterface
|
||||
*/
|
||||
public function getResults($offset, $itemCountPerPage)
|
||||
|
@ -100,27 +110,33 @@ 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()
|
||||
public function getTotalHits($genuineTotal = false)
|
||||
{
|
||||
if ( ! isset($this->totalHits)) {
|
||||
$this->totalHits = $this->searchable->search($this->query)->getTotalHits();
|
||||
if (! isset($this->totalHits)) {
|
||||
$this->totalHits = $this->searchable->count($this->query);
|
||||
}
|
||||
|
||||
return $this->query->hasParam('size')
|
||||
return $this->query->hasParam('size') && !$genuineTotal
|
||||
? min($this->totalHits, (integer) $this->query->getParam('size'))
|
||||
: $this->totalHits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Facets
|
||||
* Returns Facets.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFacets()
|
||||
{
|
||||
if ( ! isset($this->facets)) {
|
||||
if (! isset($this->facets)) {
|
||||
$this->facets = $this->searchable->search($this->query)->getFacets();
|
||||
}
|
||||
|
||||
|
@ -128,7 +144,21 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the Query
|
||||
* 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.
|
||||
*
|
||||
* @return Query the search query
|
||||
*/
|
||||
|
|
|
@ -6,7 +6,7 @@ use Elastica\ResultSet;
|
|||
use Elastica\Result;
|
||||
|
||||
/**
|
||||
* Raw partial results transforms to a simple array
|
||||
* Raw partial results transforms to a simple array.
|
||||
*/
|
||||
class RawPartialResults implements PartialResultsInterface
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ class RawPartialResults implements PartialResultsInterface
|
|||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return array_map(function(Result $result) {
|
||||
return array_map(function (Result $result) {
|
||||
return $result->getSource();
|
||||
}, $this->resultSet->getResults());
|
||||
}
|
||||
|
@ -47,6 +47,18 @@ class RawPartialResults implements PartialResultsInterface
|
|||
return $this->resultSet->getFacets();
|
||||
}
|
||||
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getAggregations()
|
||||
{
|
||||
if ($this->resultSet->hasAggregations()) {
|
||||
return $this->resultSet->getAggregations();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,15 +7,15 @@ use Elastica\SearchableInterface;
|
|||
use Elastica\Query;
|
||||
|
||||
/**
|
||||
* Allows pagination of \Elastica\Query
|
||||
* Allows pagination of \Elastica\Query.
|
||||
*/
|
||||
class TransformedPaginatorAdapter extends RawPaginatorAdapter
|
||||
{
|
||||
private $transformer;
|
||||
|
||||
/**
|
||||
* @param SearchableInterface $searchable the object to search in
|
||||
* @param Query $query the query to search
|
||||
* @param SearchableInterface $searchable the object to search in
|
||||
* @param Query $query the query to search
|
||||
* @param array $options
|
||||
* @param ElasticaToModelTransformerInterface $transformer the transformer for fetching the results
|
||||
*/
|
||||
|
|
|
@ -6,14 +6,14 @@ use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
|||
use Elastica\ResultSet;
|
||||
|
||||
/**
|
||||
* Partial transformed result set
|
||||
* Partial transformed result set.
|
||||
*/
|
||||
class TransformedPartialResults extends RawPartialResults
|
||||
{
|
||||
protected $transformer;
|
||||
|
||||
/**
|
||||
* @param ResultSet $resultSet
|
||||
* @param ResultSet $resultSet
|
||||
* @param \FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface $transformer
|
||||
*/
|
||||
public function __construct(ResultSet $resultSet, ElasticaToModelTransformerInterface $transformer)
|
||||
|
@ -30,4 +30,4 @@ class TransformedPartialResults extends RawPartialResults
|
|||
{
|
||||
return $this->transformer->transform($this->resultSet->getResults());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use Elastica\Document;
|
|||
|
||||
/**
|
||||
* Inserts, replaces and deletes single documents in an elastica type
|
||||
* Accepts domain model objects and converts them to elastica documents
|
||||
* Accepts domain model objects and converts them to elastica documents.
|
||||
*
|
||||
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
|
||||
*/
|
||||
|
@ -34,6 +34,7 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
* If the ObjectPersister handles a given object.
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function handlesObject($object)
|
||||
|
@ -41,17 +42,20 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
return $object instanceof $this->objectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LoggerInterface $logger
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log exception if logger defined for persister belonging to the current listener, otherwise re-throw
|
||||
* Log exception if logger defined for persister belonging to the current listener, otherwise re-throw.
|
||||
*
|
||||
* @param BulkException $e
|
||||
*
|
||||
* @throws BulkException
|
||||
* @return null
|
||||
*/
|
||||
private function log(BulkException $e)
|
||||
{
|
||||
|
@ -64,7 +68,7 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
|
||||
/**
|
||||
* Insert one object into the type
|
||||
* The object will be transformed to an elastica document
|
||||
* The object will be transformed to an elastica document.
|
||||
*
|
||||
* @param object $object
|
||||
*/
|
||||
|
@ -74,10 +78,9 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Replaces one object in the type
|
||||
* Replaces one object in the type.
|
||||
*
|
||||
* @param object $object
|
||||
* @return null
|
||||
**/
|
||||
public function replaceOne($object)
|
||||
{
|
||||
|
@ -85,10 +88,9 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Deletes one object in the type
|
||||
* Deletes one object in the type.
|
||||
*
|
||||
* @param object $object
|
||||
* @return null
|
||||
**/
|
||||
public function deleteOne($object)
|
||||
{
|
||||
|
@ -96,11 +98,9 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Deletes one object in the type by id
|
||||
* Deletes one object in the type by id.
|
||||
*
|
||||
* @param mixed $id
|
||||
*
|
||||
* @return null
|
||||
**/
|
||||
public function deleteById($id)
|
||||
{
|
||||
|
@ -108,7 +108,7 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Bulk insert an array of objects in the type for the given method
|
||||
* Bulk insert an array of objects in the type for the given method.
|
||||
*
|
||||
* @param array $objects array of domain model objects
|
||||
* @param string Method to call
|
||||
|
@ -148,7 +148,7 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Bulk deletes an array of objects in the type
|
||||
* Bulk deletes an array of objects in the type.
|
||||
*
|
||||
* @param array $objects array of domain model objects
|
||||
*/
|
||||
|
@ -166,7 +166,7 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Bulk deletes records from an array of identifiers
|
||||
* Bulk deletes records from an array of identifiers.
|
||||
*
|
||||
* @param array $identifiers array of domain model object identifiers
|
||||
*/
|
||||
|
@ -180,9 +180,10 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Transforms an object to an elastica document
|
||||
* Transforms an object to an elastica document.
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @return Document the elastica document
|
||||
*/
|
||||
public function transformToElasticaDocument($object)
|
||||
|
|
|
@ -4,66 +4,73 @@ namespace FOS\ElasticaBundle\Persister;
|
|||
|
||||
/**
|
||||
* Inserts, replaces and deletes single documents in an elastica type
|
||||
* Accepts domain model objects and converts them to elastica documents
|
||||
* Accepts domain model objects and converts them to elastica documents.
|
||||
*
|
||||
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
|
||||
*/
|
||||
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
|
||||
* The object will be transformed to an elastica document.
|
||||
*
|
||||
* @param object $object
|
||||
*/
|
||||
function insertOne($object);
|
||||
public function insertOne($object);
|
||||
|
||||
/**
|
||||
* Replaces one object in the type
|
||||
* Replaces one object in the type.
|
||||
*
|
||||
* @param object $object
|
||||
**/
|
||||
function replaceOne($object);
|
||||
public function replaceOne($object);
|
||||
|
||||
/**
|
||||
* Deletes one object in the type
|
||||
* Deletes one object in the type.
|
||||
*
|
||||
* @param object $object
|
||||
**/
|
||||
function deleteOne($object);
|
||||
public function deleteOne($object);
|
||||
|
||||
/**
|
||||
* Deletes one object in the type by id
|
||||
* Deletes one object in the type by id.
|
||||
*
|
||||
* @param mixed $id
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
function deleteById($id);
|
||||
public function deleteById($id);
|
||||
|
||||
/**
|
||||
* Bulk inserts an array of objects in the type
|
||||
* Bulk inserts an array of objects in the type.
|
||||
*
|
||||
* @param array $objects array of domain model objects
|
||||
*/
|
||||
function insertMany(array $objects);
|
||||
public function insertMany(array $objects);
|
||||
|
||||
/**
|
||||
* Bulk updates an array of objects in the type
|
||||
* Bulk updates an array of objects in the type.
|
||||
*
|
||||
* @param array $objects array of domain model objects
|
||||
*/
|
||||
function replaceMany(array $objects);
|
||||
public function replaceMany(array $objects);
|
||||
|
||||
/**
|
||||
* Bulk deletes an array of objects in the type
|
||||
* Bulk deletes an array of objects in the type.
|
||||
*
|
||||
* @param array $objects array of domain model objects
|
||||
*/
|
||||
function deleteMany(array $objects);
|
||||
public function deleteMany(array $objects);
|
||||
|
||||
/**
|
||||
* Bulk deletes records from an array of identifiers
|
||||
* Bulk deletes records from an array of identifiers.
|
||||
*
|
||||
* @param array $identifiers array of domain model object identifiers
|
||||
*/
|
||||
|
|
|
@ -9,7 +9,7 @@ use FOS\ElasticaBundle\Transformer\ModelToElasticaTransformerInterface;
|
|||
/**
|
||||
* Inserts, replaces and deletes single objects in an elastica type, making use
|
||||
* of elastica's serializer support to convert objects in to elastica documents.
|
||||
* Accepts domain model objects and passes them directly to elastica
|
||||
* Accepts domain model objects and passes them directly to elastica.
|
||||
*
|
||||
* @author Lea Haensenberber <lea.haensenberger@gmail.com>
|
||||
*/
|
||||
|
@ -18,19 +18,24 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an object to an elastica document
|
||||
* with just the identifier set
|
||||
* with just the identifier set.
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @return Document the elastica document
|
||||
*/
|
||||
public function transformToElasticaDocument($object)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace FOS\ElasticaBundle\Propel;
|
||||
|
||||
use FOS\ElasticaBundle\HybridResult;
|
||||
use FOS\ElasticaBundle\Transformer\AbstractElasticaToModelTransformer;
|
||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
|
@ -14,7 +15,7 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
|||
*
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
*/
|
||||
class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
||||
{
|
||||
/**
|
||||
* Propel model class to map to Elastica documents.
|
||||
|
@ -33,18 +34,11 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
'identifier' => 'id',
|
||||
);
|
||||
|
||||
/**
|
||||
* PropertyAccessor instance.
|
||||
*
|
||||
* @var PropertyAccessorInterface
|
||||
*/
|
||||
protected $propertyAccessor;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $objectClass
|
||||
* @param array $options
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($objectClass, array $options = array())
|
||||
{
|
||||
|
@ -52,21 +46,12 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
$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.
|
||||
*
|
||||
* @param array $elasticaObjects
|
||||
*
|
||||
* @return array|\ArrayObject
|
||||
*/
|
||||
public function transform(array $elasticaObjects)
|
||||
|
@ -81,11 +66,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
// Sort objects in the order of their IDs
|
||||
$idPos = array_flip($ids);
|
||||
$identifier = $this->options['identifier'];
|
||||
$propertyAccessor = $this->propertyAccessor;
|
||||
|
||||
$sortCallback = function($a, $b) use ($idPos, $identifier, $propertyAccessor) {
|
||||
return $idPos[$propertyAccessor->getValue($a, $identifier)] > $idPos[$propertyAccessor->getValue($b, $identifier)];
|
||||
};
|
||||
$sortCallback = $this->getSortingClosure($idPos, $identifier);
|
||||
|
||||
if (is_object($objects)) {
|
||||
$objects->uasort($sortCallback);
|
||||
|
@ -104,7 +85,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
$objects = $this->transform($elasticaObjects);
|
||||
|
||||
$result = array();
|
||||
for ($i = 0; $i < count($elasticaObjects); $i++) {
|
||||
for ($i = 0, $j = count($elasticaObjects); $i < $j; $i++) {
|
||||
$result[] = new HybridResult($elasticaObjects[$i], $objects[$i]);
|
||||
}
|
||||
|
||||
|
@ -135,6 +116,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
*
|
||||
* @param array $identifierValues Identifier values
|
||||
* @param boolean $hydrate Whether or not to hydrate the results
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
||||
|
@ -145,7 +127,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
|
||||
$query = $this->createQuery($this->objectClass, $this->options['identifier'], $identifierValues);
|
||||
|
||||
if ( ! $hydrate) {
|
||||
if (! $hydrate) {
|
||||
return $query->toArray();
|
||||
}
|
||||
|
||||
|
@ -158,6 +140,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
* @param string $class Propel model class
|
||||
* @param string $identifierField Identifier field name (e.g. "id")
|
||||
* @param array $identifierValues Identifier values
|
||||
*
|
||||
* @return \ModelCriteria
|
||||
*/
|
||||
protected function createQuery($class, $identifierField, array $identifierValues)
|
||||
|
@ -170,6 +153,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
|
||||
/**
|
||||
* @see https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Util/Inflector.php
|
||||
*
|
||||
* @param string $str
|
||||
*/
|
||||
private function camelize($str)
|
||||
|
|
|
@ -5,53 +5,69 @@ namespace FOS\ElasticaBundle\Propel;
|
|||
use FOS\ElasticaBundle\Provider\AbstractProvider;
|
||||
|
||||
/**
|
||||
* Propel provider
|
||||
* Propel provider.
|
||||
*
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
*/
|
||||
class Provider extends AbstractProvider
|
||||
{
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Provider\ProviderInterface::populate()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function populate(\Closure $loggerClosure = null, array $options = array())
|
||||
public function doPopulate($options, \Closure $loggerClosure = null)
|
||||
{
|
||||
$queryClass = $this->objectClass . 'Query';
|
||||
$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'];
|
||||
|
||||
for (; $offset < $nbObjects; $offset += $batchSize) {
|
||||
if ($loggerClosure) {
|
||||
$stepStartTime = microtime(true);
|
||||
}
|
||||
$offset = $options['offset'];
|
||||
|
||||
for (; $offset < $nbObjects; $offset += $options['batch_size']) {
|
||||
$objects = $queryClass::create()
|
||||
->limit($batchSize)
|
||||
->limit($options['batch_size'])
|
||||
->offset($offset)
|
||||
->find()
|
||||
->getArrayCopy();
|
||||
if ($loggerClosure) {
|
||||
$stepNbObjects = count($objects);
|
||||
}
|
||||
$objects = array_filter($objects, array($this, 'isObjectIndexable'));
|
||||
if (!$objects) {
|
||||
$loggerClosure('<info>Entire batch was filtered away, skipping...</info>');
|
||||
|
||||
continue;
|
||||
$objects = $this->filterObjects($options, $objects);
|
||||
if (!empty($objects)) {
|
||||
$this->objectPersister->insertMany($objects);
|
||||
}
|
||||
|
||||
$this->objectPersister->insertMany($objects);
|
||||
|
||||
usleep($sleep);
|
||||
usleep($options['sleep']);
|
||||
|
||||
if ($loggerClosure) {
|
||||
$stepCount = $stepNbObjects + $offset;
|
||||
$percentComplete = 100 * $stepCount / $nbObjects;
|
||||
$objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
|
||||
$loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s %s', $percentComplete, $stepCount, $nbObjects, $objectsPerSecond, $this->getMemoryUsage()));
|
||||
$loggerClosure($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' => null,
|
||||
'sleep' => 0
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function disableLogging()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function enableLogging($logger)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,16 +3,17 @@
|
|||
namespace FOS\ElasticaBundle\Provider;
|
||||
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* AbstractProvider
|
||||
* AbstractProvider.
|
||||
*/
|
||||
abstract class AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var ObjectPersisterInterface
|
||||
* @var array
|
||||
*/
|
||||
protected $objectPersister;
|
||||
protected $baseOptions;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
@ -20,12 +21,17 @@ abstract class AbstractProvider implements ProviderInterface
|
|||
protected $objectClass;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @var ObjectPersisterInterface
|
||||
*/
|
||||
protected $options;
|
||||
protected $objectPersister;
|
||||
|
||||
/**
|
||||
* @var Indexable
|
||||
* @var OptionsResolver
|
||||
*/
|
||||
protected $resolver;
|
||||
|
||||
/**
|
||||
* @var IndexableInterface
|
||||
*/
|
||||
private $indexable;
|
||||
|
||||
|
@ -33,42 +39,136 @@ abstract class AbstractProvider implements ProviderInterface
|
|||
* Constructor.
|
||||
*
|
||||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param IndexableInterface $indexable
|
||||
* @param string $objectClass
|
||||
* @param array $options
|
||||
* @param IndexableInterface $indexable
|
||||
* @param string $objectClass
|
||||
* @param array $baseOptions
|
||||
*/
|
||||
public function __construct(
|
||||
ObjectPersisterInterface $objectPersister,
|
||||
IndexableInterface $indexable,
|
||||
$objectClass,
|
||||
array $options = array()
|
||||
array $baseOptions = array()
|
||||
) {
|
||||
$this->baseOptions = $baseOptions;
|
||||
$this->indexable = $indexable;
|
||||
$this->objectClass = $objectClass;
|
||||
$this->objectPersister = $objectPersister;
|
||||
$this->resolver = new OptionsResolver();
|
||||
$this->configureOptions();
|
||||
}
|
||||
|
||||
$this->options = array_merge(array(
|
||||
/**
|
||||
* {@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(
|
||||
'batch_size' => 100,
|
||||
), $options);
|
||||
'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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given object should be indexed or not.
|
||||
*
|
||||
* @deprecated To be removed in 4.0
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isObjectIndexable($object)
|
||||
{
|
||||
return $this->indexable->isObjectIndexable(
|
||||
$this->options['indexName'],
|
||||
$this->options['typeName'],
|
||||
$this->baseOptions['indexName'],
|
||||
$this->baseOptions['typeName'],
|
||||
$object
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string with RAM usage information (current and peak)
|
||||
* Get string with RAM usage information (current and peak).
|
||||
*
|
||||
* @deprecated To be removed in 4.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
@ -79,4 +179,17 @@ 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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class Indexable implements IndexableInterface
|
|||
private $container;
|
||||
|
||||
/**
|
||||
* An instance of ExpressionLanguage
|
||||
* An instance of ExpressionLanguage.
|
||||
*
|
||||
* @var ExpressionLanguage
|
||||
*/
|
||||
|
@ -47,7 +47,7 @@ class Indexable implements IndexableInterface
|
|||
private $initialisedCallbacks = array();
|
||||
|
||||
/**
|
||||
* PropertyAccessor instance
|
||||
* PropertyAccessor instance.
|
||||
*
|
||||
* @var PropertyAccessorInterface
|
||||
*/
|
||||
|
@ -55,6 +55,7 @@ class Indexable implements IndexableInterface
|
|||
|
||||
/**
|
||||
* @param array $callbacks
|
||||
* @param ContainerInterface $container
|
||||
*/
|
||||
public function __construct(array $callbacks, ContainerInterface $container)
|
||||
{
|
||||
|
@ -68,7 +69,8 @@ class Indexable implements IndexableInterface
|
|||
*
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param mixed $object
|
||||
* @param mixed $object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isObjectIndexable($indexName, $typeName, $object)
|
||||
|
@ -80,9 +82,9 @@ class Indexable implements IndexableInterface
|
|||
}
|
||||
|
||||
if ($callback instanceof Expression) {
|
||||
return $this->getExpressionLanguage()->evaluate($callback, array(
|
||||
return (bool) $this->getExpressionLanguage()->evaluate($callback, array(
|
||||
'object' => $object,
|
||||
$this->getExpressionVar($object) => $object
|
||||
$this->getExpressionVar($object) => $object,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -96,12 +98,13 @@ class Indexable implements IndexableInterface
|
|||
*
|
||||
* @param string $type
|
||||
* @param object $object
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function buildCallback($type, $object)
|
||||
{
|
||||
if (!array_key_exists($type, $this->callbacks)) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
$callback = $this->callbacks[$type];
|
||||
|
@ -110,44 +113,54 @@ class Indexable implements IndexableInterface
|
|||
return $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_array($callback) && !is_object($callback[0])) {
|
||||
return $this->processArrayToCallback($type, $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);
|
||||
}
|
||||
if (is_string($callback)) {
|
||||
return $this->buildExpressionCallback($type, $object, $callback);
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @param string $type
|
||||
* @param object $object
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function getCallback($type, $object)
|
||||
|
@ -160,15 +173,13 @@ class Indexable implements IndexableInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* @return bool|ExpressionLanguage
|
||||
* Returns the ExpressionLanguage class if it is available.
|
||||
*
|
||||
* @return ExpressionLanguage|null
|
||||
*/
|
||||
private function getExpressionLanguage()
|
||||
{
|
||||
if (null === $this->expressionLanguage) {
|
||||
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (null === $this->expressionLanguage && class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
||||
$this->expressionLanguage = new ExpressionLanguage();
|
||||
}
|
||||
|
||||
|
@ -176,13 +187,54 @@ 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
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@ interface IndexableInterface
|
|||
*
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param mixed $object
|
||||
* @param mixed $object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isObjectIndexable($indexName, $typeName, $object);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace FOS\ElasticaBundle\Provider;
|
||||
|
||||
/**
|
||||
* Insert application domain objects into elastica types
|
||||
* Insert application domain objects into elastica types.
|
||||
*
|
||||
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
|
||||
*/
|
||||
|
@ -12,9 +12,15 @@ 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
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
function populate(\Closure $loggerClosure = null, array $options = array());
|
||||
public function populate(\Closure $loggerClosure = null, array $options = array());
|
||||
}
|
||||
|
|
|
@ -57,8 +57,10 @@ class ProviderRegistry implements ContainerAwareInterface
|
|||
*
|
||||
* Providers will be indexed by "type" strings in the returned array.
|
||||
*
|
||||
* @param string $index
|
||||
* @return array of ProviderInterface instances
|
||||
* @param string $index
|
||||
*
|
||||
* @return ProviderInterface[]
|
||||
*
|
||||
* @throws \InvalidArgumentException if no providers were registered for the index
|
||||
*/
|
||||
public function getIndexProviders($index)
|
||||
|
@ -81,7 +83,9 @@ class ProviderRegistry implements ContainerAwareInterface
|
|||
*
|
||||
* @param string $index
|
||||
* @param string $type
|
||||
*
|
||||
* @return ProviderInterface
|
||||
*
|
||||
* @throws \InvalidArgumentException if no provider was registered for the index and type
|
||||
*/
|
||||
public function getProvider($index, $type)
|
||||
|
|
|
@ -11,15 +11,16 @@ 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.0.x (master)](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/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 2.1.x](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/2.1.x/README.md)
|
||||
[Read the documentation for 3.0.x](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/3.0.x/Resources/doc/index.md)
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
|
|
@ -20,9 +20,10 @@ class Repository
|
|||
}
|
||||
|
||||
/**
|
||||
* @param mixed $query
|
||||
* @param mixed $query
|
||||
* @param integer $limit
|
||||
* @param array $options
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function find($query, $limit = null, $options = array())
|
||||
|
@ -31,9 +32,10 @@ class Repository
|
|||
}
|
||||
|
||||
/**
|
||||
* @param mixed $query
|
||||
* @param mixed $query
|
||||
* @param integer $limit
|
||||
* @param array $options
|
||||
* @param array $options
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function findHybrid($query, $limit = null, $options = array())
|
||||
|
@ -44,6 +46,7 @@ class Repository
|
|||
/**
|
||||
* @param mixed $query
|
||||
* @param array $options
|
||||
*
|
||||
* @return \Pagerfanta\Pagerfanta
|
||||
*/
|
||||
public function findPaginated($query, $options = array())
|
||||
|
@ -53,7 +56,8 @@ class Repository
|
|||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $options
|
||||
* @param array $options
|
||||
*
|
||||
* @return Paginator\PaginatorAdapterInterface
|
||||
*/
|
||||
public function createPaginatorAdapter($query, $options = array())
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
<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,31 @@
|
|||
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>
|
||||
|
||||
<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" />
|
||||
<argument type="service" id="doctrine_mongodb" /> <!-- manager registry -->
|
||||
<argument type="service" id="fos_elastica.slice_fetcher.mongodb" /> <!-- slice fetcher -->
|
||||
</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 /> <!-- logger -->
|
||||
<argument>null</argument> <!-- logger -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.elastica_to_model_transformer.prototype.mongodb" class="%fos_elastica.elastica_to_model_transformer.prototype.mongodb.class%" public="false" abstract="true">
|
||||
|
|
|
@ -4,28 +4,32 @@
|
|||
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.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.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>
|
||||
|
||||
<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" />
|
||||
<argument type="service" id="doctrine" /> <!-- manager registry -->
|
||||
<argument type="service" id="fos_elastica.slice_fetcher.orm" /> <!-- slice fetcher -->
|
||||
</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 on-invalid="ignore" /> <!-- logger -->
|
||||
<argument>null</argument> <!-- logger -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.elastica_to_model_transformer.prototype.orm" class="%fos_elastica.elastica_to_model_transformer.prototype.orm.class%" public="false" abstract="true">
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
|
||||
<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="collection" /> <!-- options -->
|
||||
<argument type="service" id="event_dispatcher" /> <!-- options -->
|
||||
<call method="setPropertyAccessor">
|
||||
<argument type="service" id="fos_elastica.property_accessor" />
|
||||
</call>
|
||||
|
|
33
Resources/doc/cookbook/custom-properties.md
Normal file
33
Resources/doc/cookbook/custom-properties.md
Normal file
|
@ -0,0 +1,33 @@
|
|||
##### Custom Properties
|
||||
|
||||
Since FOSElasticaBundle 3.1.0, we now dispatch an event for each transformation of an
|
||||
object into an Elastica document which allows you to set custom properties on the Elastica
|
||||
document for indexing.
|
||||
|
||||
Set up an event listener or subscriber for
|
||||
`FOS\ElasticaBundle\Event\TransformEvent::POST_TRANSFORM` to be able to inject your own
|
||||
parameters.
|
||||
|
||||
```php
|
||||
class CustomPropertyListener implements EventSubscriberInterface
|
||||
{
|
||||
private $anotherService;
|
||||
|
||||
// ...
|
||||
|
||||
public function addCustomProperty(TransformEvent $event)
|
||||
{
|
||||
$document = $event->getDocument();
|
||||
$custom = $this->anotherService->calculateCustom($event->getObject());
|
||||
|
||||
$document->set('custom', $custom);
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TransformEvent::POST_TRANSFORM => 'addCustomProperty',
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
|
@ -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,37 +23,41 @@ class UserRepository extends Repository
|
|||
|
||||
To use the custom repository specify it in the mapping for the entity:
|
||||
|
||||
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
|
||||
```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
|
||||
```
|
||||
|
||||
Then the custom queries will be available when using the repository returned from the manager:
|
||||
|
||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
||||
$repositoryManager = $container->get('fos_elastica.manager');
|
||||
```php
|
||||
/** 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;
|
||||
|
@ -69,4 +73,4 @@ class User
|
|||
//---
|
||||
|
||||
}
|
||||
```
|
||||
```
|
||||
|
|
|
@ -8,7 +8,7 @@ index and type for which the service will provide.
|
|||
# app/config/config.yml
|
||||
services:
|
||||
acme.search_provider.user:
|
||||
class: Acme\UserBundle\Search\UserProvider
|
||||
class: Acme\UserBundle\Provider\UserProvider
|
||||
arguments:
|
||||
- @fos_elastica.index.website.user
|
||||
tags:
|
||||
|
|
|
@ -11,6 +11,11 @@ fos_elastica:
|
|||
connections:
|
||||
- url: http://es1.example.net:9200
|
||||
- url: http://es2.example.net:9200
|
||||
connection_strategy: RoundRobin
|
||||
```
|
||||
|
||||
Elastica allows for definition of different connection strategies and by default
|
||||
supports `RoundRobin` and `Simple`. You can see definitions for these strategies
|
||||
in the `Elastica\Connection\Strategy` namespace.
|
||||
|
||||
For more information on Elastica clustering see http://elastica.io/getting-started/installation.html#section-connect-cluster
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace Acme\ElasticaBundle;
|
|||
use Elastica\Exception\ExceptionInterface;
|
||||
use Elastica\Request;
|
||||
use Elastica\Response;
|
||||
use FOS\ElasticaBundle\Client as BaseClient;
|
||||
use FOS\ElasticaBundle\Elastica\Client as BaseClient;
|
||||
|
||||
class Client extends BaseClient
|
||||
{
|
||||
|
|
|
@ -13,6 +13,7 @@ Cookbook Entries
|
|||
----------------
|
||||
|
||||
* [Aliased Indexes](cookbook/aliased-indexes.md)
|
||||
* [Custom Indexed Properties](cookbook/custom-properties.md)
|
||||
* [Custom Repositories](cookbook/custom-repositories.md)
|
||||
* [HTTP Headers for Elastica](cookbook/elastica-client-http-headers.md)
|
||||
* Performance - [Logging](cookbook/logging.md)
|
||||
|
|
|
@ -1,40 +1,50 @@
|
|||
Step 1: Setting up the bundle
|
||||
=============================
|
||||
|
||||
A) Install FOSElasticaBundle
|
||||
----------------------------
|
||||
A: Download the Bundle
|
||||
----------------------
|
||||
|
||||
FOSElasticaBundle is installed using [Composer](https://getcomposer.org).
|
||||
Open a command console, enter your project directory and execute the
|
||||
following command to download the latest stable version of this bundle:
|
||||
|
||||
```bash
|
||||
$ php composer.phar require friendsofsymfony/elastica-bundle "~3.0"
|
||||
$ composer require friendsofsymfony/elastica-bundle
|
||||
```
|
||||
|
||||
This command requires you to have Composer installed globally, as explained
|
||||
in the [installation chapter](https://getcomposer.org/doc/00-intro.md)
|
||||
of the Composer documentation.
|
||||
|
||||
### Elasticsearch
|
||||
|
||||
Instructions for installing and deploying Elasticsearch may be found
|
||||
[here](http://www.elasticsearch.org/guide/reference/setup/installation/).
|
||||
Instructions for installing and deploying Elasticsearch may be found [here](https://www.elastic.co/downloads/elasticsearch).
|
||||
|
||||
Step 2: Enable the Bundle
|
||||
-------------------------
|
||||
|
||||
B) Enable FOSElasticaBundle
|
||||
---------------------------
|
||||
|
||||
Enable FOSElasticaBundle in your AppKernel:
|
||||
Then, enable the bundle by adding the following line in the `app/AppKernel.php`
|
||||
file of your project:
|
||||
|
||||
```php
|
||||
<?php
|
||||
// app/AppKernel.php
|
||||
|
||||
public function registerBundles()
|
||||
// ...
|
||||
class AppKernel extends Kernel
|
||||
{
|
||||
$bundles = array(
|
||||
public function registerBundles()
|
||||
{
|
||||
$bundles = array(
|
||||
// ...
|
||||
new FOS\ElasticaBundle\FOSElasticaBundle(),
|
||||
);
|
||||
|
||||
// ...
|
||||
new FOS\ElasticaBundle\FOSElasticaBundle(),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
C) Basic Bundle Configuration
|
||||
C: Basic Bundle Configuration
|
||||
-----------------------------
|
||||
|
||||
The basic minimal configuration for FOSElasticaBundle is one client with one Elasticsearch
|
||||
|
@ -48,27 +58,30 @@ fos_elastica:
|
|||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
indexes:
|
||||
search: ~
|
||||
app: ~
|
||||
```
|
||||
|
||||
In this example, an Elastica index (an instance of `Elastica\Index`) is available as a
|
||||
service with the key `fos_elastica.index.search`.
|
||||
service with the key `fos_elastica.index.app`.
|
||||
|
||||
If the Elasticsearch index name needs to be different to the service name in your
|
||||
application, for example, renaming the search index based on different environments.
|
||||
You may want the index `app` to be named something else on ElasticSearch depending on
|
||||
if your application is running in a different env or other conditions that suit your
|
||||
application. To set your customer index to a name that depends on the environment of your
|
||||
Symfony application, use the example below:
|
||||
|
||||
```yaml
|
||||
#app/config/config.yml
|
||||
fos_elastica:
|
||||
indexes:
|
||||
search:
|
||||
index_name: search_dev
|
||||
app:
|
||||
index_name: app_%kernel.environment%
|
||||
```
|
||||
|
||||
In this case, the service `fos_elastica.index.search` will be using an Elasticsearch
|
||||
index of search_dev.
|
||||
In this case, the service `fos_elastica.index.app` will relate to an ElasticSearch index
|
||||
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.
|
||||
|
@ -81,7 +94,7 @@ will end up being indexed.
|
|||
```yaml
|
||||
fos_elastica:
|
||||
indexes:
|
||||
search:
|
||||
app:
|
||||
types:
|
||||
user:
|
||||
mappings:
|
||||
|
@ -92,7 +105,7 @@ fos_elastica:
|
|||
```
|
||||
|
||||
Each defined type is made available as a service, and in this case the service key is
|
||||
`fos_elastica.index.search.user` and is an instance of `Elastica\Type`.
|
||||
`fos_elastica.index.app.user` and is an instance of `Elastica\Type`.
|
||||
|
||||
FOSElasticaBundle requires a provider for each type that will notify when an object
|
||||
that maps to a type has been modified. The bundle ships with support for Doctrine and
|
||||
|
@ -122,7 +135,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
|
||||
|
@ -137,7 +150,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,6 +1,34 @@
|
|||
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
|
||||
-----------------------------------------------
|
||||
|
||||
|
@ -173,13 +201,18 @@ index enabled users.
|
|||
The callback option supports multiple approaches:
|
||||
|
||||
* A method on the object itself provided as a string. `enabled` will call
|
||||
`Object->enabled()`
|
||||
`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.
|
||||
* 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,7 +26,9 @@ $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
|
||||
|
@ -160,7 +162,7 @@ fos_elastica:
|
|||
site:
|
||||
settings:
|
||||
index:
|
||||
analysis:
|
||||
analysis:
|
||||
analyzer:
|
||||
my_analyzer:
|
||||
type: snowball
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1 KiB |
|
@ -23,7 +23,7 @@
|
|||
|
||||
{% block menu %}
|
||||
<span class="label">
|
||||
<span class="icon"><img src="{{ asset('bundles/foselastica/images/elastica.png') }}" alt="" /></span>
|
||||
<span class="icon"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAcCAQAAADPJofWAAAAAXNSR0IArs4c6QAAAAJiS0dEAP+Hj8y/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH2woEDg0xnxGaxwAAA51JREFUOMt100ts1FUUx/Hvufc/8x9mOrTTDpbysEVsqTzUqAsRgpqQGIJiIhtZSEJcqNGFJrSiZSPYkKALBUyAiLEuWKjBhdFAaEwwPNQqEAgEaA0FEWh4lE477/+9x0WLsST+Fmf1ycnJvecI/0knW4ENgZkhr5gVMtcEMmB64z1msCOCboR70tlkOuQdQREMBovF7tQt71/mXtzZQo8s89jLycFETiSqKd/v5wSY32VN18Ak3JGiR1d7an+Y3Z/Np7yVeCY/9czU4efF2iPxZ+wLXPgXP7WCTZ7MvvlnZlbqSJnGGbMeam5pk6HTIw9qs7tiH6D9vrbXW9e1Tus/sbhHZ8VOtfU1uZSExCUe1ExLtqayjcPnx4qNssjOs3TQrY9rYdVB2aFRw7HWq2kbJxCjFAsDoY+3hbkbw9ebNBsQ6EJVNlb3yQIDY8lcaAIsBisxiSXGToTNWqi/JUWXMIoabrK/95ymDNYHzmDUYAiwWOImOle+EI2AxyiKVydQGVAIoyneiBUNsAQEWIn+LA2NJqOYw3hUFQV2DFbu+FSuKTk9kRGMBoyPE7m8vza7UhMrmQNlV+NwAlDZXmXkkcHaeNamQBCEiLyeb7w631C71yxfr8/6YT8GUP6ifKlcf3xJX26kWNQyJfL+TvVs3S8rCw31hfRm2/KzL+iGQz8BnLjz8EVd5TPXF9wI/E0ZK/gr6SOLj7+Yz9RRu2bmMXm6y2/0A/rS0f6JL985+prVQAJiKEU8kLid6ezaA8Gh7iVP6BKtHad7njQr+xmSeFELLi2YovwlJ5Nbu07DNoLFoVNVrwBvh6feSMwSDcVuLvWF0y3Brd9mDlRlTsuVweGPCRzI+NOBW2rW3iYv5ZPfbrm7ji2fx1/Vkn7U/OGlivE4HA5Yh+yqUKFAYa3QfXcVf/R7Segy3wDGo4x3Tr3H3LLmqX6mZ5+jC4A5DO7jLR3SmAoYj8eTqx5uMR+UKErp78q2/e7ARNuLgE940bR1E9jpcKx+16MxJUd5+68XJp+a5DnqFxU/yYZG8XiSfJ+awkIW9WW+zLF8sh6NeqJb0cs+biIcKjW8u3T3xj+uZXZ/PbSe3km2nHWbogb3ZlSgPWz/bt71tsf432Qa64bSh5PTIFAU0pqdG3oRIiCgBBgsVTwRpYwmnWgMAlUd1aR+6m+DA4QqBlDKRDgifCqq8WO+CoFW9Ctd7dvBA+NV8DgiognuSv4bbsA/e9y1PQRZggAAAAAASUVORK5CYII=" alt="" /></span>
|
||||
<strong>Elastica</strong>
|
||||
<span class="count">
|
||||
<span>{{ collector.querycount }}</span>
|
||||
|
|
|
@ -8,7 +8,7 @@ use JMS\Serializer\SerializerInterface;
|
|||
class Callback
|
||||
{
|
||||
protected $serializer;
|
||||
protected $groups;
|
||||
protected $groups = array();
|
||||
protected $version;
|
||||
|
||||
public function setSerializer($serializer)
|
||||
|
@ -23,10 +23,8 @@ class Callback
|
|||
{
|
||||
$this->groups = $groups;
|
||||
|
||||
if ($this->groups) {
|
||||
if (!$this->serializer instanceof SerializerInterface) {
|
||||
throw new \RuntimeException('Setting serialization groups requires using "JMS\Serializer\Serializer".');
|
||||
}
|
||||
if (!empty($this->groups) && !$this->serializer instanceof SerializerInterface) {
|
||||
throw new \RuntimeException('Setting serialization groups requires using "JMS\Serializer\Serializer".');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,10 +32,8 @@ class Callback
|
|||
{
|
||||
$this->version = $version;
|
||||
|
||||
if ($this->version) {
|
||||
if (!$this->serializer instanceof SerializerInterface) {
|
||||
throw new \RuntimeException('Setting serialization version requires using "JMS\Serializer\Serializer".');
|
||||
}
|
||||
if ($this->version && !$this->serializer instanceof SerializerInterface) {
|
||||
throw new \RuntimeException('Setting serialization version requires using "JMS\Serializer\Serializer".');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +41,7 @@ class Callback
|
|||
{
|
||||
$context = $this->serializer instanceof SerializerInterface ? SerializationContext::create()->enableMaxDepthChecks() : array();
|
||||
|
||||
if ($this->groups) {
|
||||
if (!empty($this->groups)) {
|
||||
$context->setGroups($this->groups);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,13 +32,17 @@ class PaginateElasticaQuerySubscriber implements EventSubscriberInterface
|
|||
if (null != $facets) {
|
||||
$event->setCustomPaginationParameter('facets', $facets);
|
||||
}
|
||||
$aggregations = $results->getAggregations();
|
||||
if (null != $aggregations) {
|
||||
$event->setCustomPaginationParameter('aggregations', $aggregations);
|
||||
}
|
||||
|
||||
$event->stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds knp paging sort to query
|
||||
* Adds knp paging sort to query.
|
||||
*
|
||||
* @param ItemsEvent $event
|
||||
*/
|
||||
|
@ -70,7 +74,7 @@ class PaginateElasticaQuerySubscriber implements EventSubscriberInterface
|
|||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'knp_pager.items' => array('items', 1)
|
||||
'knp_pager.items' => array('items', 1),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Tests\Command;
|
||||
|
||||
|
||||
use FOS\ElasticaBundle\Command\ResetCommand;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
|
@ -10,8 +9,8 @@ use Symfony\Component\DependencyInjection\Container;
|
|||
|
||||
class ResetCommandTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $command;
|
||||
private $resetter;
|
||||
|
||||
private $indexManager;
|
||||
|
||||
public function setup()
|
||||
|
@ -88,4 +87,4 @@ class ResetCommandTest extends \PHPUnit_Framework_TestCase
|
|||
new NullOutput()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use FOS\ElasticaBundle\DependencyInjection\Configuration;
|
|||
use Symfony\Component\Config\Definition\Processor;
|
||||
|
||||
/**
|
||||
* ConfigurationTest
|
||||
* ConfigurationTest.
|
||||
*/
|
||||
class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
@ -34,7 +34,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertSame(array(
|
||||
'clients' => array(),
|
||||
'indexes' => array(),
|
||||
'default_manager' => 'orm'
|
||||
'default_manager' => 'orm',
|
||||
), $configuration);
|
||||
}
|
||||
|
||||
|
@ -50,18 +50,18 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
|||
array(
|
||||
'url' => 'http://es1:9200',
|
||||
'headers' => array(
|
||||
'Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
|
||||
)
|
||||
'Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'url' => 'http://es2:9200',
|
||||
'headers' => array(
|
||||
'Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
|
||||
)
|
||||
'Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==',
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
$this->assertCount(2, $configuration['clients']);
|
||||
|
@ -91,9 +91,9 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
|||
),
|
||||
'logging_custom' => array(
|
||||
'url' => 'http://localhost:9200',
|
||||
'logger' => 'custom.service'
|
||||
'logger' => 'custom.service',
|
||||
),
|
||||
)
|
||||
),
|
||||
));
|
||||
|
||||
$this->assertCount(4, $configuration['clients']);
|
||||
|
@ -131,8 +131,8 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
|||
),
|
||||
'serializer' => array(
|
||||
'groups' => array('Search'),
|
||||
'version' => 1
|
||||
)
|
||||
'version' => 1,
|
||||
),
|
||||
),
|
||||
'types' => array(
|
||||
'test' => array(
|
||||
|
@ -144,20 +144,20 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
|||
'persistence' => array(
|
||||
'listener' => array(
|
||||
'logger' => true,
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
'test2' => array(
|
||||
'mappings' => array(
|
||||
'title' => null,
|
||||
'children' => array(
|
||||
'type' => 'nested',
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
|||
'host' => 'localhost',
|
||||
'port' => 9200,
|
||||
),
|
||||
)
|
||||
),
|
||||
));
|
||||
|
||||
$this->assertTrue(empty($configuration['clients']['default']['connections'][0]['url']));
|
||||
|
@ -189,16 +189,34 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
|||
'title' => array(),
|
||||
'published' => array('type' => 'datetime'),
|
||||
'body' => null,
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
$this->assertCount(3, $configuration['indexes']['test']['types']['test']['properties']);
|
||||
}
|
||||
|
||||
public function testUnconfiguredType()
|
||||
{
|
||||
$configuration = $this->getConfigs(array(
|
||||
'clients' => array(
|
||||
'default' => array('url' => 'http://localhost:9200'),
|
||||
),
|
||||
'indexes' => array(
|
||||
'test' => array(
|
||||
'types' => array(
|
||||
'test' => null,
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
$this->assertArrayHasKey('properties', $configuration['indexes']['test']['types']['test']);
|
||||
}
|
||||
|
||||
public function testNestedProperties()
|
||||
{
|
||||
$this->getConfigs(array(
|
||||
|
@ -225,23 +243,23 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
|||
'type' => 'nested',
|
||||
'properties' => array(
|
||||
'nested_field1' => array(
|
||||
'type' => 'integer'
|
||||
'type' => 'integer',
|
||||
),
|
||||
'nested_field2' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'type' => 'integer'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
'type' => 'integer',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
32
Tests/DependencyInjection/FOSElasticaExtensionTest.php
Normal file
32
Tests/DependencyInjection/FOSElasticaExtensionTest.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\DependencyInjection;
|
||||
|
||||
use FOS\ElasticaBundle\DependencyInjection\FOSElasticaExtension;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class FOSElasticaExtensionTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testShouldAddParentParamToObjectPersisterCall()
|
||||
{
|
||||
$config = Yaml::parse(file_get_contents(__DIR__.'/fixtures/config.yml'));
|
||||
|
||||
$containerBuilder = new ContainerBuilder();
|
||||
$containerBuilder->setParameter('kernel.debug', true);
|
||||
|
||||
$extension = new FOSElasticaExtension();
|
||||
|
||||
$extension->load($config, $containerBuilder);
|
||||
|
||||
$this->assertTrue($containerBuilder->hasDefinition('fos_elastica.object_persister.test_index.child_field'));
|
||||
|
||||
$persisterCallDefinition = $containerBuilder->getDefinition('fos_elastica.object_persister.test_index.child_field');
|
||||
|
||||
$arguments = $persisterCallDefinition->getArguments();
|
||||
$arguments = $arguments['index_3'];
|
||||
|
||||
$this->assertArrayHasKey('_parent', $arguments);
|
||||
$this->assertEquals('parent_field', $arguments['_parent']['type']);
|
||||
}
|
||||
}
|
21
Tests/DependencyInjection/fixtures/config.yml
Normal file
21
Tests/DependencyInjection/fixtures/config.yml
Normal file
|
@ -0,0 +1,21 @@
|
|||
fos_elastica:
|
||||
clients:
|
||||
default:
|
||||
url: http://localhost:9200
|
||||
indexes:
|
||||
test_index:
|
||||
client: default
|
||||
types:
|
||||
parent_field:
|
||||
mappings:
|
||||
text: ~
|
||||
persistence:
|
||||
driver: orm
|
||||
model: foo_model
|
||||
child_field:
|
||||
mappings:
|
||||
text: ~
|
||||
persistence:
|
||||
driver: orm
|
||||
model: foo_model
|
||||
_parent: { type: "parent_field", property: "parent" }
|
|
@ -19,7 +19,7 @@ class AbstractElasticaToModelTransformerTest extends \PHPUnit_Framework_TestCase
|
|||
protected $objectClass = 'stdClass';
|
||||
|
||||
/**
|
||||
* Tests if ignore_missing option is properly handled in transformHybrid() method
|
||||
* Tests if ignore_missing option is properly handled in transformHybrid() method.
|
||||
*/
|
||||
public function testIgnoreMissingOptionDuringTransformHybrid()
|
||||
{
|
||||
|
@ -55,10 +55,6 @@ class AbstractElasticaToModelTransformerTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
protected function setUp()
|
||||
{
|
||||
if (!interface_exists('Doctrine\Common\Persistence\ManagerRegistry')) {
|
||||
$this->markTestSkipped('Doctrine Common is not present');
|
||||
}
|
||||
|
||||
$this->registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace FOS\ElasticaBundle\Tests\Doctrine;
|
||||
|
||||
/**
|
||||
* See concrete MongoDB/ORM instances of this abstract test
|
||||
* See concrete MongoDB/ORM instances of this abstract test.
|
||||
*
|
||||
* @author Richard Miller <info@limethinking.co.uk>
|
||||
*/
|
||||
|
@ -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, array(), $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener = $this->createListener($persister, $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, array(), $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener = $this->createListener($persister, $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, array(), $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener = $this->createListener($persister, $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, array(), $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener = $this->createListener($persister, $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, array(), $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener = $this->createListener($persister, $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, array(), $indexable, array('identifier' => 'identifier', 'indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener = $this->createListener($persister, $indexable, array('identifier' => 'identifier', 'indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener->preRemove($eventArgs);
|
||||
|
||||
$this->assertEquals($entity->identifier, current($listener->scheduledForDeletion));
|
||||
|
@ -213,8 +213,8 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
/**
|
||||
* @param Listener\Entity $object
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*/
|
||||
private function getMockPersister($object, $indexName, $typeName)
|
||||
{
|
||||
|
@ -247,10 +247,10 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
}
|
||||
|
||||
/**
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param Listener\Entity $object
|
||||
* @param boolean $return
|
||||
* @param boolean $return
|
||||
*/
|
||||
private function getMockIndexable($indexName, $typeName, $object, $return = null)
|
||||
{
|
||||
|
@ -272,6 +272,7 @@ namespace FOS\ElasticaBundle\Tests\Doctrine\Listener;
|
|||
class Entity
|
||||
{
|
||||
private $id;
|
||||
public $identifier;
|
||||
|
||||
/**
|
||||
* @param integer $id
|
||||
|
@ -286,4 +287,3 @@ class Entity
|
|||
return $this->id;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,13 +13,10 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
private $options;
|
||||
private $managerRegistry;
|
||||
private $indexable;
|
||||
private $sliceFetcher;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
if (!interface_exists('Doctrine\Common\Persistence\ManagerRegistry')) {
|
||||
$this->markTestSkipped('Doctrine Common is not available.');
|
||||
}
|
||||
|
||||
$this->objectClass = 'objectClass';
|
||||
$this->options = array('debug_logging' => true, 'indexName' => 'index', 'typeName' => 'type');
|
||||
|
||||
|
@ -32,6 +29,8 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
->method('getManagerForClass')
|
||||
->with($this->objectClass)
|
||||
->will($this->returnValue($this->objectManager));
|
||||
|
||||
$this->sliceFetcher = $this->getMockSliceFetcher();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,6 +44,53 @@ 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));
|
||||
|
@ -84,7 +130,7 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
return array(
|
||||
array(
|
||||
100,
|
||||
array(range(1,100)),
|
||||
array(range(1, 100)),
|
||||
100,
|
||||
),
|
||||
array(
|
||||
|
@ -107,8 +153,8 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
->method('countObjects')
|
||||
->will($this->returnValue($nbObjects));
|
||||
|
||||
$provider->expects($this->any())
|
||||
->method('fetchSlice')
|
||||
$this->sliceFetcher->expects($this->any())
|
||||
->method('fetch')
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->indexable->expects($this->any())
|
||||
|
@ -122,6 +168,32 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
$provider->populate();
|
||||
}
|
||||
|
||||
public function testPopulateShouldClearObjectManagerForFilteredBatch()
|
||||
{
|
||||
$nbObjects = 1;
|
||||
$objects = array(1);
|
||||
|
||||
$provider = $this->getMockAbstractProvider(true);
|
||||
|
||||
$provider->expects($this->any())
|
||||
->method('countObjects')
|
||||
->will($this->returnValue($nbObjects));
|
||||
|
||||
$this->sliceFetcher->expects($this->any())
|
||||
->method('fetch')
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->indexable->expects($this->any())
|
||||
->method('isObjectIndexable')
|
||||
->with('index', 'type', $this->anything())
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$this->objectManager->expects($this->once())
|
||||
->method('clear');
|
||||
|
||||
$provider->populate();
|
||||
}
|
||||
|
||||
public function testPopulateInvokesLoggerClosure()
|
||||
{
|
||||
$nbObjects = 1;
|
||||
|
@ -133,8 +205,8 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
->method('countObjects')
|
||||
->will($this->returnValue($nbObjects));
|
||||
|
||||
$provider->expects($this->any())
|
||||
->method('fetchSlice')
|
||||
$this->sliceFetcher->expects($this->any())
|
||||
->method('fetch')
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->indexable->expects($this->any())
|
||||
|
@ -165,8 +237,8 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
->method('countObjects')
|
||||
->will($this->returnValue($nbObjects));
|
||||
|
||||
$provider->expects($this->any())
|
||||
->method('fetchSlice')
|
||||
$this->sliceFetcher->expects($this->any())
|
||||
->method('fetch')
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->indexable->expects($this->any())
|
||||
|
@ -180,7 +252,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()
|
||||
|
@ -192,8 +264,9 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
$provider->expects($this->any())
|
||||
->method('countObjects')
|
||||
->will($this->returnValue($nbObjects));
|
||||
$provider->expects($this->any())
|
||||
->method('fetchSlice')
|
||||
|
||||
$this->sliceFetcher->expects($this->any())
|
||||
->method('fetch')
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->indexable->expects($this->at(0))
|
||||
|
@ -205,18 +278,19 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
->with('index', 'type', 2)
|
||||
->will($this->returnValue(true));
|
||||
|
||||
|
||||
$this->objectPersister->expects($this->once())
|
||||
->method('insertMany')
|
||||
->with(array(1 => 2));
|
||||
->with(array(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()
|
||||
private function getMockAbstractProvider($setSliceFetcher = true)
|
||||
{
|
||||
return $this->getMockForAbstractClass('FOS\ElasticaBundle\Doctrine\AbstractProvider', array(
|
||||
$this->objectPersister,
|
||||
|
@ -224,6 +298,7 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
$this->objectClass,
|
||||
$this->options,
|
||||
$this->managerRegistry,
|
||||
$setSliceFetcher ? $this->sliceFetcher : null
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -233,7 +308,7 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
private function getMockBulkResponseException()
|
||||
{
|
||||
return $this->getMock('Elastica\Exception\Bulk\ResponseException', null, array(
|
||||
new ResponseSet(new Response(array()), array())
|
||||
new ResponseSet(new Response(array()), array()),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -250,7 +325,17 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
private function getMockObjectManager()
|
||||
{
|
||||
return $this->getMock(__NAMESPACE__ . '\ObjectManager');
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -268,6 +353,14 @@ 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');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -276,5 +369,7 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
interface ObjectManager
|
||||
{
|
||||
function clear();
|
||||
public function clear();
|
||||
public function getClassMetadata();
|
||||
public function getIdentifierFieldNames();
|
||||
}
|
||||
|
|
|
@ -82,13 +82,6 @@ class ElasticaToModelTransformerTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
protected function setUp()
|
||||
{
|
||||
if (!interface_exists('Doctrine\Common\Persistence\ManagerRegistry')) {
|
||||
$this->markTestSkipped('Doctrine Common is not present');
|
||||
}
|
||||
if (!class_exists('Doctrine\ORM\EntityManager')) {
|
||||
$this->markTestSkipped('Doctrine Common is not present');
|
||||
}
|
||||
|
||||
$this->registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
@ -109,7 +102,7 @@ class ElasticaToModelTransformerTest extends \PHPUnit_Framework_TestCase
|
|||
'findAll',
|
||||
'findBy',
|
||||
'findOneBy',
|
||||
'getClassName'
|
||||
'getClassName',
|
||||
));
|
||||
|
||||
$this->manager->expects($this->any())
|
||||
|
|
|
@ -6,13 +6,6 @@ use FOS\ElasticaBundle\Tests\Doctrine\ListenerTest as BaseListenerTest;
|
|||
|
||||
class ListenerTest extends BaseListenerTest
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
if (!class_exists('Doctrine\ORM\EntityManager')) {
|
||||
$this->markTestSkipped('Doctrine ORM is not available.');
|
||||
}
|
||||
}
|
||||
|
||||
protected function getClassMetadataClass()
|
||||
{
|
||||
return 'Doctrine\ORM\Mapping\ClassMetadata';
|
||||
|
|
|
@ -4,22 +4,19 @@ namespace FOS\ElasticaBundle\Tests\Doctrine;
|
|||
|
||||
use FOS\ElasticaBundle\Doctrine\RepositoryManager;
|
||||
|
||||
class CustomRepository{}
|
||||
class CustomRepository
|
||||
{
|
||||
}
|
||||
|
||||
class Entity{}
|
||||
class Entity
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Richard Miller <info@limethinking.co.uk>
|
||||
*/
|
||||
class RepositoryManagerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
if (!interface_exists('Doctrine\Common\Persistence\ManagerRegistry')) {
|
||||
$this->markTestSkipped('Doctrine Common is not available.');
|
||||
}
|
||||
}
|
||||
|
||||
public function testThatGetRepositoryReturnsDefaultRepository()
|
||||
{
|
||||
/** @var $finderMock \PHPUnit_Framework_MockObject_MockObject|\FOS\ElasticaBundle\Finder\TransformedFinder */
|
||||
|
|
|
@ -5,11 +5,11 @@ namespace FOS\ElasticaBundle\Tests\Client;
|
|||
use Elastica\Request;
|
||||
use Elastica\Transport\Null as NullTransport;
|
||||
|
||||
class LoggingClientTest extends \PHPUnit_Framework_TestCase
|
||||
class ClientTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testRequestsAreLogged()
|
||||
{
|
||||
$transport = new NullTransport;
|
||||
$transport = new NullTransport();
|
||||
|
||||
$connection = $this->getMock('Elastica\Connection');
|
||||
$connection->expects($this->any())->method('getTransportObject')->will($this->returnValue($transport));
|
||||
|
|
|
@ -16,7 +16,6 @@ class FOSElasticaBundleTest extends \PHPUnit_Framework_TestCase
|
|||
->method('addCompilerPass')
|
||||
->with($this->isInstanceOf('Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface'));
|
||||
|
||||
|
||||
$bundle = new FOSElasticaBundle();
|
||||
$bundle->build($container);
|
||||
}
|
||||
|
|
48
Tests/Functional/ClientTest.php
Normal file
48
Tests/Functional/ClientTest.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?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 Symfony\Bundle\FrameworkBundle\Client;
|
||||
|
||||
/**
|
||||
* @group functional
|
||||
*/
|
||||
class ClientTest extends WebTestCase
|
||||
{
|
||||
public function testContainerSource()
|
||||
{
|
||||
$client = $this->createClient(array('test_case' => 'Basic'));
|
||||
|
||||
$es = $client->getContainer()->get('fos_elastica.client.default');
|
||||
$this->assertInstanceOf('Elastica\\Connection\\Strategy\\RoundRobin', $es->getConnectionStrategy());
|
||||
|
||||
$es = $client->getContainer()->get('fos_elastica.client.second_server');
|
||||
$this->assertInstanceOf('Elastica\\Connection\\Strategy\\RoundRobin', $es->getConnectionStrategy());
|
||||
|
||||
$es = $client->getContainer()->get('fos_elastica.client.third');
|
||||
$this->assertInstanceOf('Elastica\\Connection\\Strategy\\Simple', $es->getConnectionStrategy());
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->deleteTmpDir('Basic');
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
$this->deleteTmpDir('Basic');
|
||||
}
|
||||
}
|
|
@ -47,6 +47,7 @@ class ConfigurationManagerTest extends WebTestCase
|
|||
|
||||
/**
|
||||
* @param Client $client
|
||||
*
|
||||
* @return \FOS\ElasticaBundle\Configuration\ConfigManager
|
||||
*/
|
||||
private function getManager(Client $client)
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace FOS\ElasticaBundle\Tests\Functional;
|
|||
class IndexableCallbackTest extends WebTestCase
|
||||
{
|
||||
/**
|
||||
* 2 reasons for this test:
|
||||
* 2 reasons for this test:.
|
||||
*
|
||||
* 1) To test that the configuration rename from is_indexable_callback under the listener
|
||||
* key is respected, and
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue