Compare commits
194 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 30e351b1d5 | |||
| 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 | ||
|
|
b3f87e414f | ||
|
|
71a86cada5 | ||
|
|
d0ce82ac2a | ||
|
|
428a1014ca | ||
|
|
1d5fe44ca4 | ||
|
|
cf586a4ef4 | ||
|
|
c4210a5c6d | ||
|
|
029ebb153a | ||
|
|
736163551c | ||
|
|
76dcd2f62e | ||
|
|
2958833012 | ||
|
|
6bea3c2154 | ||
|
|
001daeeac0 | ||
|
|
c08d86124a | ||
|
|
49c5b2ee63 | ||
|
|
27385046ca | ||
|
|
517d6e679c | ||
|
|
229d4cb982 | ||
|
|
9d45de8b64 | ||
|
|
659468ae3a | ||
|
|
001b38cf59 | ||
|
|
1ef55b1239 | ||
|
|
714502fa1f |
129 changed files with 3229 additions and 1225 deletions
10
.travis.yml
10
.travis.yml
|
|
@ -1,7 +1,12 @@
|
||||||
language: php
|
language: php
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.composer/cache
|
||||||
|
|
||||||
php:
|
php:
|
||||||
- 5.3
|
- 5.3
|
||||||
|
- 5.4
|
||||||
- 5.5
|
- 5.5
|
||||||
- 5.6
|
- 5.6
|
||||||
|
|
||||||
|
|
@ -11,8 +16,6 @@ matrix:
|
||||||
env: SYMFONY_VERSION='2.3.*'
|
env: SYMFONY_VERSION='2.3.*'
|
||||||
- php: 5.5
|
- php: 5.5
|
||||||
env: SYMFONY_VERSION='2.5.*'
|
env: SYMFONY_VERSION='2.5.*'
|
||||||
- php: 5.5
|
|
||||||
env: SYMFONY_VERSION='dev-master'
|
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- /usr/share/elasticsearch/bin/elasticsearch -v
|
- /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;'
|
- sh -c 'if [ "$SYMFONY_VERSION" != "" ]; then composer require --dev --no-update symfony/symfony=$SYMFONY_VERSION; fi;'
|
||||||
- composer install --dev --prefer-source
|
- composer install --dev --prefer-source
|
||||||
|
|
||||||
script: vendor/bin/phpunit --coverage-clover=coverage.clover
|
script:
|
||||||
|
- vendor/bin/phpunit --coverage-clover=coverage.clover
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- elasticsearch
|
- 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
|
To generate a changelog summary since the last version, run
|
||||||
`git log --no-merges --oneline v3.0.0...3.0.x`
|
`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
|
* 3.0.0-ALPHA6
|
||||||
|
|
||||||
* Moved `is_indexable_callback` from the listener properties to a type property called
|
* 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;
|
namespace FOS\ElasticaBundle\Command;
|
||||||
|
|
||||||
|
use FOS\ElasticaBundle\Event\IndexPopulateEvent;
|
||||||
|
use FOS\ElasticaBundle\Event\TypePopulateEvent;
|
||||||
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
|
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
|
||||||
use Symfony\Component\Console\Helper\DialogHelper;
|
use Symfony\Component\Console\Helper\DialogHelper;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
|
@ -10,18 +12,28 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use FOS\ElasticaBundle\IndexManager;
|
use FOS\ElasticaBundle\IndexManager;
|
||||||
use FOS\ElasticaBundle\Provider\ProviderRegistry;
|
use FOS\ElasticaBundle\Provider\ProviderRegistry;
|
||||||
use FOS\ElasticaBundle\Resetter;
|
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
|
class PopulateCommand extends ContainerAwareCommand
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||||
|
*/
|
||||||
|
private $dispatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var IndexManager
|
* @var IndexManager
|
||||||
*/
|
*/
|
||||||
private $indexManager;
|
private $indexManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ProgressClosureBuilder
|
||||||
|
*/
|
||||||
|
private $progressClosureBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ProviderRegistry
|
* @var ProviderRegistry
|
||||||
*/
|
*/
|
||||||
|
|
@ -46,31 +58,46 @@ class PopulateCommand extends ContainerAwareCommand
|
||||||
->addOption('sleep', null, InputOption::VALUE_REQUIRED, 'Sleep time between persisting iterations (microseconds)', 0)
|
->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('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('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')
|
->setDescription('Populates search indexes from providers')
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see Symfony\Component\Console\Command\Command::initialize()
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function initialize(InputInterface $input, OutputInterface $output)
|
protected function initialize(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
|
$this->dispatcher = $this->getContainer()->get('event_dispatcher');
|
||||||
$this->indexManager = $this->getContainer()->get('fos_elastica.index_manager');
|
$this->indexManager = $this->getContainer()->get('fos_elastica.index_manager');
|
||||||
$this->providerRegistry = $this->getContainer()->get('fos_elastica.provider_registry');
|
$this->providerRegistry = $this->getContainer()->get('fos_elastica.provider_registry');
|
||||||
$this->resetter = $this->getContainer()->get('fos_elastica.resetter');
|
$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)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$index = $input->getOption('index');
|
$index = $input->getOption('index');
|
||||||
$type = $input->getOption('type');
|
$type = $input->getOption('type');
|
||||||
$reset = !$input->getOption('no-reset');
|
$reset = !$input->getOption('no-reset');
|
||||||
$options = $input->getOptions();
|
$options = array(
|
||||||
|
'ignore_errors' => $input->getOption('ignore-errors'),
|
||||||
$options['ignore-errors'] = $input->hasOption('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')) {
|
if ($input->isInteractive() && $reset && $input->getOption('offset')) {
|
||||||
/** @var DialogHelper $dialog */
|
/** @var DialogHelper $dialog */
|
||||||
|
|
@ -109,25 +136,22 @@ class PopulateCommand extends ContainerAwareCommand
|
||||||
*/
|
*/
|
||||||
private function populateIndex(OutputInterface $output, $index, $reset, $options)
|
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));
|
$output->writeln(sprintf('<info>Resetting</info> <comment>%s</comment>', $index));
|
||||||
$this->resetter->resetIndex($index, true);
|
$this->resetter->resetIndex($index, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var $providers ProviderInterface[] */
|
$types = array_keys($this->providerRegistry->getIndexProviders($index));
|
||||||
$providers = $this->providerRegistry->getIndexProviders($index);
|
foreach ($types as $type) {
|
||||||
|
$this->populateIndexType($output, $index, $type, false, $event->getOptions());
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$output->writeln(sprintf('<info>Refreshing</info> <comment>%s</comment>', $index));
|
$this->dispatcher->dispatch(IndexPopulateEvent::POST_INDEX_POPULATE, $event);
|
||||||
$this->resetter->postPopulate($index);
|
|
||||||
$this->indexManager->getIndex($index)->refresh();
|
$this->refreshIndex($output, $index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -141,17 +165,35 @@ class PopulateCommand extends ContainerAwareCommand
|
||||||
*/
|
*/
|
||||||
private function populateIndexType(OutputInterface $output, $index, $type, $reset, $options)
|
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));
|
$output->writeln(sprintf('<info>Resetting</info> <comment>%s/%s</comment>', $index, $type));
|
||||||
$this->resetter->resetIndexType($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 = $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));
|
$output->writeln(sprintf('<info>Refreshing</info> <comment>%s</comment>', $index));
|
||||||
$this->indexManager->getIndex($index)->refresh();
|
$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;
|
use FOS\ElasticaBundle\Resetter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset search indexes
|
* Reset search indexes.
|
||||||
*/
|
*/
|
||||||
class ResetCommand extends ContainerAwareCommand
|
class ResetCommand extends ContainerAwareCommand
|
||||||
{
|
{
|
||||||
|
|
@ -33,6 +33,7 @@ class ResetCommand extends ContainerAwareCommand
|
||||||
->setName('fos:elastica:reset')
|
->setName('fos:elastica:reset')
|
||||||
->addOption('index', null, InputOption::VALUE_OPTIONAL, 'The index to reset')
|
->addOption('index', null, InputOption::VALUE_OPTIONAL, 'The index to reset')
|
||||||
->addOption('type', null, InputOption::VALUE_OPTIONAL, 'The type to reset')
|
->addOption('type', null, InputOption::VALUE_OPTIONAL, 'The type to reset')
|
||||||
|
->addOption('force', null, InputOption::VALUE_NONE, 'Force index deletion if same name as alias')
|
||||||
->setDescription('Reset search indexes')
|
->setDescription('Reset search indexes')
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
@ -51,8 +52,9 @@ class ResetCommand extends ContainerAwareCommand
|
||||||
*/
|
*/
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$index = $input->getOption('index');
|
$index = $input->getOption('index');
|
||||||
$type = $input->getOption('type');
|
$type = $input->getOption('type');
|
||||||
|
$force = (bool) $input->getOption('force');
|
||||||
|
|
||||||
if (null === $index && null !== $type) {
|
if (null === $index && null !== $type) {
|
||||||
throw new \InvalidArgumentException('Cannot specify type option without an index.');
|
throw new \InvalidArgumentException('Cannot specify type option without an index.');
|
||||||
|
|
@ -69,7 +71,7 @@ class ResetCommand extends ContainerAwareCommand
|
||||||
|
|
||||||
foreach ($indexes as $index) {
|
foreach ($indexes as $index) {
|
||||||
$output->writeln(sprintf('<info>Resetting</info> <comment>%s</comment>', $index));
|
$output->writeln(sprintf('<info>Resetting</info> <comment>%s</comment>', $index));
|
||||||
$this->resetter->resetIndex($index);
|
$this->resetter->resetIndex($index, false, $force);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use Elastica\Query;
|
||||||
use Elastica\Result;
|
use Elastica\Result;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches a type
|
* Searches a type.
|
||||||
*/
|
*/
|
||||||
class SearchCommand extends ContainerAwareCommand
|
class SearchCommand extends ContainerAwareCommand
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -53,9 +53,9 @@ class IndexConfig
|
||||||
/**
|
/**
|
||||||
* Constructor expects an array as generated by the Container Configuration builder.
|
* Constructor expects an array as generated by the Container Configuration builder.
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param TypeConfig[] $types
|
* @param TypeConfig[] $types
|
||||||
* @param array $config
|
* @param array $config
|
||||||
*/
|
*/
|
||||||
public function __construct($name, array $types, array $config)
|
public function __construct($name, array $types, array $config)
|
||||||
{
|
{
|
||||||
|
|
@ -92,7 +92,9 @@ class IndexConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $typeName
|
* @param string $typeName
|
||||||
|
*
|
||||||
* @return TypeConfig
|
* @return TypeConfig
|
||||||
|
*
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
public function getType($typeName)
|
public function getType($typeName)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ interface ManagerInterface
|
||||||
* Returns configuration for an index.
|
* Returns configuration for an index.
|
||||||
*
|
*
|
||||||
* @param $index
|
* @param $index
|
||||||
|
*
|
||||||
* @return IndexConfig
|
* @return IndexConfig
|
||||||
*/
|
*/
|
||||||
public function getIndexConfiguration($index);
|
public function getIndexConfiguration($index);
|
||||||
|
|
@ -36,6 +37,7 @@ interface ManagerInterface
|
||||||
*
|
*
|
||||||
* @param string $index
|
* @param string $index
|
||||||
* @param string $type
|
* @param string $type
|
||||||
|
*
|
||||||
* @return TypeConfig
|
* @return TypeConfig
|
||||||
*/
|
*/
|
||||||
public function getTypeConfiguration($index, $type);
|
public function getTypeConfiguration($index, $type);
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,10 @@ use FOS\ElasticaBundle\Annotation\Search as BaseSearch;
|
||||||
* Annotation class for setting search repository.
|
* Annotation class for setting search repository.
|
||||||
*
|
*
|
||||||
* @Annotation
|
* @Annotation
|
||||||
|
*
|
||||||
* @deprecated Use FOS\ElasticaBundle\Annotation\Search instead
|
* @deprecated Use FOS\ElasticaBundle\Annotation\Search instead
|
||||||
* @Target("CLASS")
|
* @Target("CLASS")
|
||||||
*/
|
*/
|
||||||
class Search extends BaseSearch
|
class Search extends BaseSearch
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,16 +40,7 @@ class ContainerSource implements SourceInterface
|
||||||
{
|
{
|
||||||
$indexes = array();
|
$indexes = array();
|
||||||
foreach ($this->configArray as $config) {
|
foreach ($this->configArray as $config) {
|
||||||
$types = array();
|
$types = $this->getTypes($config);
|
||||||
foreach ($config['types'] as $typeConfig) {
|
|
||||||
$types[$typeConfig['name']] = new TypeConfig(
|
|
||||||
$typeConfig['name'],
|
|
||||||
$typeConfig['mapping'],
|
|
||||||
$typeConfig['config']
|
|
||||||
);
|
|
||||||
// TODO: handle prototypes..
|
|
||||||
}
|
|
||||||
|
|
||||||
$index = new IndexConfig($config['name'], $types, array(
|
$index = new IndexConfig($config['name'], $types, array(
|
||||||
'elasticSearchName' => $config['elasticsearch_name'],
|
'elasticSearchName' => $config['elasticsearch_name'],
|
||||||
'settings' => $config['settings'],
|
'settings' => $config['settings'],
|
||||||
|
|
@ -61,4 +52,29 @@ class ContainerSource implements SourceInterface
|
||||||
|
|
||||||
return $indexes;
|
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[]
|
* @return \FOS\ElasticaBundle\Configuration\IndexConfig[]
|
||||||
*/
|
*/
|
||||||
public function getConfiguration();
|
public function getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,22 @@ class TypeConfig
|
||||||
$this->name = $name;
|
$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
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
|
|
@ -61,6 +77,14 @@ class TypeConfig
|
||||||
null;
|
null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool|null
|
||||||
|
*/
|
||||||
|
public function getNumericDetection()
|
||||||
|
{
|
||||||
|
return $this->getConfig('numeric_detection');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
|
@ -77,6 +101,9 @@ class TypeConfig
|
||||||
return $this->getConfig('search_analyzer');
|
return $this->getConfig('search_analyzer');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
*/
|
||||||
private function getConfig($key)
|
private function getConfig($key)
|
||||||
{
|
{
|
||||||
return isset($this->config[$key]) ?
|
return isset($this->config[$key]) ?
|
||||||
|
|
|
||||||
|
|
@ -33,4 +33,4 @@ class ConfigSourcePass implements CompilerPassInterface
|
||||||
|
|
||||||
$container->getDefinition('fos_elastica.config_manager')->replaceArgument(0, $sources);
|
$container->getDefinition('fos_elastica.config_manager')->replaceArgument(0, $sources);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ class RegisterProvidersPass implements CompilerPassInterface
|
||||||
* Returns whether the class implements ProviderInterface.
|
* Returns whether the class implements ProviderInterface.
|
||||||
*
|
*
|
||||||
* @param string $class
|
* @param string $class
|
||||||
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
private function isProviderImplementation($class)
|
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.');
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ class Configuration implements ConfigurationInterface
|
||||||
/**
|
/**
|
||||||
* Generates the configuration tree.
|
* Generates the configuration tree.
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\Config\Definition\NodeInterface
|
* @return TreeBuilder
|
||||||
*/
|
*/
|
||||||
public function getConfigTreeBuilder()
|
public function getConfigTreeBuilder()
|
||||||
{
|
{
|
||||||
|
|
@ -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)
|
private function addClientsSection(ArrayNodeDefinition $rootNode)
|
||||||
{
|
{
|
||||||
|
|
@ -76,20 +76,30 @@ class Configuration implements ConfigurationInterface
|
||||||
->performNoDeepMerging()
|
->performNoDeepMerging()
|
||||||
// BC - Renaming 'servers' node to 'connections'
|
// BC - Renaming 'servers' node to 'connections'
|
||||||
->beforeNormalization()
|
->beforeNormalization()
|
||||||
->ifTrue(function($v) { return isset($v['servers']); })
|
->ifTrue(function ($v) { return isset($v['servers']); })
|
||||||
->then(function($v) {
|
->then(function ($v) {
|
||||||
$v['connections'] = $v['servers'];
|
$v['connections'] = $v['servers'];
|
||||||
unset($v['servers']);
|
unset($v['servers']);
|
||||||
|
|
||||||
return $v;
|
return $v;
|
||||||
})
|
})
|
||||||
->end()
|
->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.
|
// If there is no connections array key defined, assume a single connection.
|
||||||
->beforeNormalization()
|
->beforeNormalization()
|
||||||
->ifTrue(function ($v) { return is_array($v) && !array_key_exists('connections', $v); })
|
->ifTrue(function ($v) { return is_array($v) && !array_key_exists('connections', $v); })
|
||||||
->then(function ($v) {
|
->then(function ($v) {
|
||||||
return array(
|
return array(
|
||||||
'connections' => array($v)
|
'connections' => array($v),
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
->end()
|
->end()
|
||||||
|
|
@ -101,8 +111,8 @@ class Configuration implements ConfigurationInterface
|
||||||
->children()
|
->children()
|
||||||
->scalarNode('url')
|
->scalarNode('url')
|
||||||
->validate()
|
->validate()
|
||||||
->ifTrue(function($url) { return $url && substr($url, -1) !== '/'; })
|
->ifTrue(function ($url) { return $url && substr($url, -1) !== '/'; })
|
||||||
->then(function($url) { return $url.'/'; })
|
->then(function ($url) { return $url.'/'; })
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
->scalarNode('host')->end()
|
->scalarNode('host')->end()
|
||||||
|
|
@ -124,6 +134,7 @@ class Configuration implements ConfigurationInterface
|
||||||
->end()
|
->end()
|
||||||
->scalarNode('timeout')->end()
|
->scalarNode('timeout')->end()
|
||||||
->scalarNode('headers')->end()
|
->scalarNode('headers')->end()
|
||||||
|
->scalarNode('connectionStrategy')->defaultValue('Simple')->end()
|
||||||
->end()
|
->end()
|
||||||
->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)
|
private function addIndexesSection(ArrayNodeDefinition $rootNode)
|
||||||
{
|
{
|
||||||
|
|
@ -181,10 +192,14 @@ class Configuration implements ConfigurationInterface
|
||||||
->useAttributeAsKey('name')
|
->useAttributeAsKey('name')
|
||||||
->prototype('array')
|
->prototype('array')
|
||||||
->treatNullLike(array())
|
->treatNullLike(array())
|
||||||
|
->beforeNormalization()
|
||||||
|
->ifNull()
|
||||||
|
->thenEmptyArray()
|
||||||
|
->end()
|
||||||
// BC - Renaming 'mappings' node to 'properties'
|
// BC - Renaming 'mappings' node to 'properties'
|
||||||
->beforeNormalization()
|
->beforeNormalization()
|
||||||
->ifTrue(function($v) { return array_key_exists('mappings', $v); })
|
->ifTrue(function ($v) { return array_key_exists('mappings', $v); })
|
||||||
->then(function($v) {
|
->then(function ($v) {
|
||||||
$v['properties'] = $v['mappings'];
|
$v['properties'] = $v['mappings'];
|
||||||
unset($v['mappings']);
|
unset($v['mappings']);
|
||||||
|
|
||||||
|
|
@ -199,7 +214,17 @@ class Configuration implements ConfigurationInterface
|
||||||
isset($v['persistence']['listener']['is_indexable_callback']);
|
isset($v['persistence']['listener']['is_indexable_callback']);
|
||||||
})
|
})
|
||||||
->then(function ($v) {
|
->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']);
|
unset($v['persistence']['listener']['is_indexable_callback']);
|
||||||
|
|
||||||
return $v;
|
return $v;
|
||||||
|
|
@ -225,7 +250,10 @@ class Configuration implements ConfigurationInterface
|
||||||
})
|
})
|
||||||
->end()
|
->end()
|
||||||
->children()
|
->children()
|
||||||
|
->booleanNode('date_detection')->end()
|
||||||
|
->arrayNode('dynamic_date_formats')->prototype('scalar')->end()->end()
|
||||||
->scalarNode('index_analyzer')->end()
|
->scalarNode('index_analyzer')->end()
|
||||||
|
->booleanNode('numeric_detection')->end()
|
||||||
->scalarNode('search_analyzer')->end()
|
->scalarNode('search_analyzer')->end()
|
||||||
->variableNode('indexable_callback')->end()
|
->variableNode('indexable_callback')->end()
|
||||||
->append($this->getPersistenceNode())
|
->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()
|
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()
|
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()
|
protected function getTtlNode()
|
||||||
{
|
{
|
||||||
|
|
@ -463,9 +491,9 @@ class Configuration implements ConfigurationInterface
|
||||||
|
|
||||||
$node
|
$node
|
||||||
->validate()
|
->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')
|
->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')
|
->thenInvalid('Propel doesn\'t support the "repository" parameter')
|
||||||
->end()
|
->end()
|
||||||
->children()
|
->children()
|
||||||
|
|
@ -480,9 +508,13 @@ class Configuration implements ConfigurationInterface
|
||||||
->scalarNode('identifier')->defaultValue('id')->end()
|
->scalarNode('identifier')->defaultValue('id')->end()
|
||||||
->arrayNode('provider')
|
->arrayNode('provider')
|
||||||
->children()
|
->children()
|
||||||
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
|
|
||||||
->scalarNode('batch_size')->defaultValue(100)->end()
|
->scalarNode('batch_size')->defaultValue(100)->end()
|
||||||
->scalarNode('clear_object_manager')->defaultTrue()->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()
|
->scalarNode('service')->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
|
|
|
||||||
|
|
@ -80,9 +80,10 @@ class FOSElasticaExtension extends Extension
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $config
|
* @param array $config
|
||||||
* @param ContainerBuilder $container
|
* @param ContainerBuilder $container
|
||||||
* @return Configuration|null|\Symfony\Component\Config\Definition\ConfigurationInterface
|
*
|
||||||
|
* @return Configuration
|
||||||
*/
|
*/
|
||||||
public function getConfiguration(array $config, ContainerBuilder $container)
|
public function getConfiguration(array $config, ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
|
|
@ -92,8 +93,9 @@ class FOSElasticaExtension extends Extension
|
||||||
/**
|
/**
|
||||||
* Loads the configured clients.
|
* 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
|
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function loadClients(array $clients, ContainerBuilder $container)
|
private function loadClients(array $clients, ContainerBuilder $container)
|
||||||
|
|
@ -114,7 +116,7 @@ class FOSElasticaExtension extends Extension
|
||||||
|
|
||||||
$this->clients[$name] = array(
|
$this->clients[$name] = array(
|
||||||
'id' => $clientId,
|
'id' => $clientId,
|
||||||
'reference' => new Reference($clientId)
|
'reference' => new Reference($clientId),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -122,9 +124,11 @@ class FOSElasticaExtension extends Extension
|
||||||
/**
|
/**
|
||||||
* Loads the configured indexes.
|
* 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
|
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||||
|
*
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function loadIndexes(array $indexes, ContainerBuilder $container)
|
private function loadIndexes(array $indexes, ContainerBuilder $container)
|
||||||
|
|
@ -133,7 +137,7 @@ class FOSElasticaExtension extends Extension
|
||||||
|
|
||||||
foreach ($indexes as $name => $index) {
|
foreach ($indexes as $name => $index) {
|
||||||
$indexId = sprintf('fos_elastica.index.%s', $name);
|
$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 = new DefinitionDecorator('fos_elastica.index_prototype');
|
||||||
$indexDef->replaceArgument(0, $indexName);
|
$indexDef->replaceArgument(0, $indexName);
|
||||||
|
|
@ -173,8 +177,9 @@ class FOSElasticaExtension extends Extension
|
||||||
* Loads the configured index finders.
|
* Loads the configured index finders.
|
||||||
*
|
*
|
||||||
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
||||||
* @param string $name The index name
|
* @param string $name The index name
|
||||||
* @param Reference $index Reference to the related index
|
* @param Reference $index Reference to the related index
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function loadIndexFinder(ContainerBuilder $container, $name, Reference $index)
|
private function loadIndexFinder(ContainerBuilder $container, $name, Reference $index)
|
||||||
|
|
@ -197,10 +202,10 @@ class FOSElasticaExtension extends Extension
|
||||||
/**
|
/**
|
||||||
* Loads the configured types.
|
* Loads the configured types.
|
||||||
*
|
*
|
||||||
* @param array $types
|
* @param array $types
|
||||||
* @param ContainerBuilder $container
|
* @param ContainerBuilder $container
|
||||||
* @param array $indexConfig
|
* @param array $indexConfig
|
||||||
* @param array $indexableCallbacks
|
* @param array $indexableCallbacks
|
||||||
*/
|
*/
|
||||||
private function loadTypes(array $types, ContainerBuilder $container, array $indexConfig, array &$indexableCallbacks)
|
private function loadTypes(array $types, ContainerBuilder $container, array $indexConfig, array &$indexableCallbacks)
|
||||||
{
|
{
|
||||||
|
|
@ -241,6 +246,9 @@ class FOSElasticaExtension extends Extension
|
||||||
'serializer',
|
'serializer',
|
||||||
'index_analyzer',
|
'index_analyzer',
|
||||||
'search_analyzer',
|
'search_analyzer',
|
||||||
|
'date_detection',
|
||||||
|
'dynamic_date_formats',
|
||||||
|
'numeric_detection',
|
||||||
) as $field) {
|
) as $field) {
|
||||||
$typeConfig['config'][$field] = array_key_exists($field, $type) ?
|
$typeConfig['config'][$field] = array_key_exists($field, $type) ?
|
||||||
$type[$field] :
|
$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 ContainerBuilder $container
|
||||||
* @param Reference $typeRef
|
* @param Reference $typeRef
|
||||||
* @param string $indexName
|
* @param string $indexName
|
||||||
* @param string $typeName
|
* @param string $typeName
|
||||||
*/
|
*/
|
||||||
private function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Reference $typeRef, $indexName, $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.
|
* Creates and loads an ElasticaToModelTransformer.
|
||||||
*
|
*
|
||||||
* @param array $typeConfig
|
* @param array $typeConfig
|
||||||
* @param ContainerBuilder $container
|
* @param ContainerBuilder $container
|
||||||
* @param string $indexName
|
* @param string $indexName
|
||||||
* @param string $typeName
|
* @param string $typeName
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function loadElasticaToModelTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
|
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.
|
* Creates and loads a ModelToElasticaTransformer for an index/type.
|
||||||
*
|
*
|
||||||
* @param array $typeConfig
|
* @param array $typeConfig
|
||||||
* @param ContainerBuilder $container
|
* @param ContainerBuilder $container
|
||||||
* @param string $indexName
|
* @param string $indexName
|
||||||
* @param string $typeName
|
* @param string $typeName
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function loadModelToElasticaTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
|
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);
|
$serviceId = sprintf('fos_elastica.model_to_elastica_transformer.%s.%s', $indexName, $typeName);
|
||||||
$serviceDef = new DefinitionDecorator($abstractId);
|
$serviceDef = new DefinitionDecorator($abstractId);
|
||||||
$serviceDef->replaceArgument(0, array(
|
$serviceDef->replaceArgument(0, array(
|
||||||
'identifier' => $typeConfig['identifier']
|
'identifier' => $typeConfig['identifier'],
|
||||||
));
|
));
|
||||||
$container->setDefinition($serviceId, $serviceDef);
|
$container->setDefinition($serviceId, $serviceDef);
|
||||||
|
|
||||||
|
|
@ -371,12 +381,13 @@ class FOSElasticaExtension extends Extension
|
||||||
/**
|
/**
|
||||||
* Creates and loads an object persister for a type.
|
* Creates and loads an object persister for a type.
|
||||||
*
|
*
|
||||||
* @param array $typeConfig
|
* @param array $typeConfig
|
||||||
* @param Reference $typeRef
|
* @param Reference $typeRef
|
||||||
* @param ContainerBuilder $container
|
* @param ContainerBuilder $container
|
||||||
* @param string $indexName
|
* @param string $indexName
|
||||||
* @param string $typeName
|
* @param string $typeName
|
||||||
* @param string $transformerId
|
* @param string $transformerId
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function loadObjectPersister(array $typeConfig, Reference $typeRef, ContainerBuilder $container, $indexName, $typeName, $transformerId)
|
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');
|
$arguments[] = array(new Reference($callbackId), 'serialize');
|
||||||
} else {
|
} else {
|
||||||
$abstractId = 'fos_elastica.object_persister';
|
$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);
|
$serviceId = sprintf('fos_elastica.object_persister.%s.%s', $indexName, $typeName);
|
||||||
|
|
@ -410,11 +426,12 @@ class FOSElasticaExtension extends Extension
|
||||||
/**
|
/**
|
||||||
* Loads a provider for a type.
|
* Loads a provider for a type.
|
||||||
*
|
*
|
||||||
* @param array $typeConfig
|
* @param array $typeConfig
|
||||||
* @param ContainerBuilder $container
|
* @param ContainerBuilder $container
|
||||||
* @param string $objectPersisterId
|
* @param string $objectPersisterId
|
||||||
* @param string $indexName
|
* @param string $indexName
|
||||||
* @param string $typeName
|
* @param string $typeName
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function loadTypeProvider(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
|
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.
|
* index and type names were "prototype" and a driver, respectively.
|
||||||
*/
|
*/
|
||||||
$providerId = sprintf('fos_elastica.provider.%s.%s', $indexName, $typeName);
|
$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->addTag('fos_elastica.provider', array('index' => $indexName, 'type' => $typeName));
|
||||||
$providerDef->replaceArgument(0, new Reference($objectPersisterId));
|
$providerDef->replaceArgument(0, new Reference($objectPersisterId));
|
||||||
$providerDef->replaceArgument(2, $typeConfig['model']);
|
$providerDef->replaceArgument(2, $typeConfig['model']);
|
||||||
|
|
@ -444,11 +461,12 @@ class FOSElasticaExtension extends Extension
|
||||||
/**
|
/**
|
||||||
* Loads doctrine listeners to handle indexing of new or updated objects.
|
* Loads doctrine listeners to handle indexing of new or updated objects.
|
||||||
*
|
*
|
||||||
* @param array $typeConfig
|
* @param array $typeConfig
|
||||||
* @param ContainerBuilder $container
|
* @param ContainerBuilder $container
|
||||||
* @param string $objectPersisterId
|
* @param string $objectPersisterId
|
||||||
* @param string $indexName
|
* @param string $indexName
|
||||||
* @param string $typeName
|
* @param string $typeName
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function loadTypeListener(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
|
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);
|
$listenerId = sprintf('fos_elastica.listener.%s.%s', $indexName, $typeName);
|
||||||
$listenerDef = new DefinitionDecorator($abstractListenerId);
|
$listenerDef = new DefinitionDecorator($abstractListenerId);
|
||||||
$listenerDef->replaceArgument(0, new Reference($objectPersisterId));
|
$listenerDef->replaceArgument(0, new Reference($objectPersisterId));
|
||||||
$listenerDef->replaceArgument(1, $this->getDoctrineEvents($typeConfig));
|
$listenerDef->replaceArgument(2, array(
|
||||||
$listenerDef->replaceArgument(3, array(
|
|
||||||
'identifier' => $typeConfig['identifier'],
|
'identifier' => $typeConfig['identifier'],
|
||||||
'indexName' => $indexName,
|
'indexName' => $indexName,
|
||||||
'typeName' => $typeName,
|
'typeName' => $typeName,
|
||||||
));
|
));
|
||||||
if ($typeConfig['listener']['logger']) {
|
$listenerDef->replaceArgument(3, $typeConfig['listener']['logger'] ?
|
||||||
$listenerDef->replaceArgument(4, new Reference($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']) {
|
if (null !== $tagName) {
|
||||||
case 'orm': $listenerDef->addTag('doctrine.event_subscriber'); break;
|
foreach ($this->getDoctrineEvents($typeConfig) as $event) {
|
||||||
case 'mongodb': $listenerDef->addTag('doctrine_mongodb.odm.event_subscriber'); break;
|
$listenerDef->addTag($tagName, array('event' => $event));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$container->setDefinition($listenerId, $listenerDef);
|
$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)
|
private function getDoctrineEvents(array $typeConfig)
|
||||||
{
|
{
|
||||||
|
|
@ -498,7 +527,6 @@ class FOSElasticaExtension extends Extension
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new InvalidArgumentException(sprintf('Cannot determine events for driver "%s"', $typeConfig['driver']));
|
throw new InvalidArgumentException(sprintf('Cannot determine events for driver "%s"', $typeConfig['driver']));
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$events = array();
|
$events = array();
|
||||||
|
|
@ -506,7 +534,7 @@ class FOSElasticaExtension extends Extension
|
||||||
'insert' => array(constant($eventsClass.'::postPersist')),
|
'insert' => array(constant($eventsClass.'::postPersist')),
|
||||||
'update' => array(constant($eventsClass.'::postUpdate')),
|
'update' => array(constant($eventsClass.'::postUpdate')),
|
||||||
'delete' => array(constant($eventsClass.'::preRemove')),
|
'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) {
|
foreach ($eventMapping as $event => $doctrineEvents) {
|
||||||
|
|
@ -521,12 +549,13 @@ class FOSElasticaExtension extends Extension
|
||||||
/**
|
/**
|
||||||
* Loads a Type specific Finder.
|
* Loads a Type specific Finder.
|
||||||
*
|
*
|
||||||
* @param array $typeConfig
|
* @param array $typeConfig
|
||||||
* @param ContainerBuilder $container
|
* @param ContainerBuilder $container
|
||||||
* @param $elasticaToModelId
|
* @param string $elasticaToModelId
|
||||||
* @param Reference $typeRef
|
* @param Reference $typeRef
|
||||||
* @param string $indexName
|
* @param string $indexName
|
||||||
* @param string $typeName
|
* @param string $typeName
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function loadTypeFinder(array $typeConfig, ContainerBuilder $container, $elasticaToModelId, Reference $typeRef, $indexName, $typeName)
|
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
|
* @param ContainerBuilder $container
|
||||||
**/
|
**/
|
||||||
|
|
@ -571,7 +600,7 @@ class FOSElasticaExtension extends Extension
|
||||||
* Makes sure a specific driver has been loaded.
|
* Makes sure a specific driver has been loaded.
|
||||||
*
|
*
|
||||||
* @param ContainerBuilder $container
|
* @param ContainerBuilder $container
|
||||||
* @param string $driver
|
* @param string $driver
|
||||||
*/
|
*/
|
||||||
private function loadDriver(ContainerBuilder $container, $driver)
|
private function loadDriver(ContainerBuilder $container, $driver)
|
||||||
{
|
{
|
||||||
|
|
@ -587,7 +616,7 @@ class FOSElasticaExtension extends Extension
|
||||||
/**
|
/**
|
||||||
* Loads and configures the serializer prototype.
|
* Loads and configures the serializer prototype.
|
||||||
*
|
*
|
||||||
* @param array $config
|
* @param array $config
|
||||||
* @param ContainerBuilder $container
|
* @param ContainerBuilder $container
|
||||||
*/
|
*/
|
||||||
private function loadSerializer($config, 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.
|
* Creates a default manager alias for defined default manager or the first loaded driver.
|
||||||
*
|
*
|
||||||
* @param string $defaultManager
|
* @param string $defaultManager
|
||||||
* @param ContainerBuilder $container
|
* @param ContainerBuilder $container
|
||||||
*/
|
*/
|
||||||
private function createDefaultManagerAlias($defaultManager, 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.
|
* Returns a reference to a client given its configured name.
|
||||||
*
|
*
|
||||||
* @param string $clientName
|
* @param string $clientName
|
||||||
|
*
|
||||||
* @return Reference
|
* @return Reference
|
||||||
|
*
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
private function getClient($clientName)
|
private function getClient($clientName)
|
||||||
|
|
|
||||||
|
|
@ -2,32 +2,34 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle\Doctrine;
|
namespace FOS\ElasticaBundle\Doctrine;
|
||||||
|
|
||||||
|
use Doctrine\Common\Persistence\ManagerRegistry;
|
||||||
use FOS\ElasticaBundle\HybridResult;
|
use FOS\ElasticaBundle\HybridResult;
|
||||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
use FOS\ElasticaBundle\Transformer\AbstractElasticaToModelTransformer as BaseTransformer;
|
||||||
use FOS\ElasticaBundle\Transformer\HighlightableModelInterface;
|
use FOS\ElasticaBundle\Transformer\HighlightableModelInterface;
|
||||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps Elastica documents with Doctrine objects
|
* Maps Elastica documents with Doctrine objects
|
||||||
* This mapper assumes an exact match between
|
* 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;
|
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
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $objectClass = null;
|
protected $objectClass = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional parameters
|
* Optional parameters.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
|
|
@ -39,20 +41,13 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PropertyAccessor instance
|
* Instantiates a new Mapper.
|
||||||
*
|
*
|
||||||
* @var PropertyAccessorInterface
|
* @param ManagerRegistry $registry
|
||||||
*/
|
|
||||||
protected $propertyAccessor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiates a new Mapper
|
|
||||||
*
|
|
||||||
* @param object $registry
|
|
||||||
* @param string $objectClass
|
* @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->registry = $registry;
|
||||||
$this->objectClass = $objectClass;
|
$this->objectClass = $objectClass;
|
||||||
|
|
@ -69,22 +64,14 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
||||||
return $this->objectClass;
|
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
|
* 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
|
* @param array $elasticaObjects of elastica objects
|
||||||
|
*
|
||||||
* @throws \RuntimeException
|
* @throws \RuntimeException
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
**/
|
**/
|
||||||
public function transform(array $elasticaObjects)
|
public function transform(array $elasticaObjects)
|
||||||
|
|
@ -109,11 +96,7 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
||||||
// sort objects in the order of ids
|
// sort objects in the order of ids
|
||||||
$idPos = array_flip($ids);
|
$idPos = array_flip($ids);
|
||||||
$identifier = $this->options['identifier'];
|
$identifier = $this->options['identifier'];
|
||||||
$propertyAccessor = $this->propertyAccessor;
|
usort($objects, $this->getSortingClosure($idPos, $identifier));
|
||||||
usort($objects, function($a, $b) use ($idPos, $identifier, $propertyAccessor)
|
|
||||||
{
|
|
||||||
return $idPos[$propertyAccessor->getValue($a, $identifier)] > $idPos[$propertyAccessor->getValue($b, $identifier)];
|
|
||||||
});
|
|
||||||
|
|
||||||
return $objects;
|
return $objects;
|
||||||
}
|
}
|
||||||
|
|
@ -137,7 +120,7 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function getIdentifierField()
|
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
|
* @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\Persister\ObjectPersisterInterface;
|
||||||
use FOS\ElasticaBundle\Provider\AbstractProvider as BaseAbstractProvider;
|
use FOS\ElasticaBundle\Provider\AbstractProvider as BaseAbstractProvider;
|
||||||
use FOS\ElasticaBundle\Provider\IndexableInterface;
|
use FOS\ElasticaBundle\Provider\IndexableInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
abstract class AbstractProvider extends BaseAbstractProvider
|
abstract class AbstractProvider extends BaseAbstractProvider
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var SliceFetcherInterface
|
||||||
|
*/
|
||||||
|
private $sliceFetcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ManagerRegistry
|
||||||
|
*/
|
||||||
protected $managerRegistry;
|
protected $managerRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param ObjectPersisterInterface $objectPersister
|
* @param ObjectPersisterInterface $objectPersister
|
||||||
* @param IndexableInterface $indexable
|
* @param IndexableInterface $indexable
|
||||||
* @param string $objectClass
|
* @param string $objectClass
|
||||||
* @param array $options
|
* @param array $baseOptions
|
||||||
* @param ManagerRegistry $managerRegistry
|
* @param ManagerRegistry $managerRegistry
|
||||||
|
* @param SliceFetcherInterface $sliceFetcher
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ObjectPersisterInterface $objectPersister,
|
ObjectPersisterInterface $objectPersister,
|
||||||
IndexableInterface $indexable,
|
IndexableInterface $indexable,
|
||||||
$objectClass,
|
$objectClass,
|
||||||
array $options,
|
array $baseOptions,
|
||||||
ManagerRegistry $managerRegistry
|
ManagerRegistry $managerRegistry,
|
||||||
|
SliceFetcherInterface $sliceFetcher = null
|
||||||
) {
|
) {
|
||||||
parent::__construct($objectPersister, $indexable, $objectClass, array_merge(array(
|
parent::__construct($objectPersister, $indexable, $objectClass, $baseOptions);
|
||||||
'clear_object_manager' => true,
|
|
||||||
'debug_logging' => false,
|
|
||||||
'ignore_errors' => false,
|
|
||||||
'query_builder_method' => 'createQueryBuilder',
|
|
||||||
), $options));
|
|
||||||
|
|
||||||
$this->managerRegistry = $managerRegistry;
|
$this->managerRegistry = $managerRegistry;
|
||||||
}
|
$this->sliceFetcher = $sliceFetcher;
|
||||||
|
|
||||||
/**
|
|
||||||
* @see FOS\ElasticaBundle\Provider\ProviderInterface::populate()
|
|
||||||
*/
|
|
||||||
public function populate(\Closure $loggerClosure = null, array $options = array())
|
|
||||||
{
|
|
||||||
if (!$this->options['debug_logging']) {
|
|
||||||
$logger = $this->disableLogging();
|
|
||||||
}
|
|
||||||
|
|
||||||
$queryBuilder = $this->createQueryBuilder();
|
|
||||||
$nbObjects = $this->countObjects($queryBuilder);
|
|
||||||
$offset = isset($options['offset']) ? intval($options['offset']) : 0;
|
|
||||||
$sleep = isset($options['sleep']) ? intval($options['sleep']) : 0;
|
|
||||||
$batchSize = isset($options['batch-size']) ? intval($options['batch-size']) : $this->options['batch_size'];
|
|
||||||
$ignoreErrors = isset($options['ignore-errors']) ? $options['ignore-errors'] : $this->options['ignore_errors'];
|
|
||||||
$manager = $this->managerRegistry->getManagerForClass($this->objectClass);
|
|
||||||
|
|
||||||
for (; $offset < $nbObjects; $offset += $batchSize) {
|
|
||||||
if ($loggerClosure) {
|
|
||||||
$stepStartTime = microtime(true);
|
|
||||||
}
|
|
||||||
$objects = $this->fetchSlice($queryBuilder, $batchSize, $offset);
|
|
||||||
if ($loggerClosure) {
|
|
||||||
$stepNbObjects = count($objects);
|
|
||||||
}
|
|
||||||
$objects = array_filter($objects, array($this, 'isObjectIndexable'));
|
|
||||||
if (!$objects) {
|
|
||||||
if ($loggerClosure) {
|
|
||||||
$loggerClosure('<info>Entire batch was filtered away, skipping...</info>');
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts objects that would be indexed using the query builder.
|
* Counts objects that would be indexed using the query builder.
|
||||||
*
|
*
|
||||||
* @param object $queryBuilder
|
* @param object $queryBuilder
|
||||||
|
*
|
||||||
* @return integer
|
* @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
|
* @param string $method
|
||||||
*/
|
|
||||||
protected abstract function disableLogging();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reenables the logger with the previously returned logger from disableLogging();
|
|
||||||
*
|
*
|
||||||
* @param mixed $logger
|
* @return object
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
protected abstract function enableLogging($logger);
|
abstract protected function createQueryBuilder($method);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches a slice of objects using the query builder.
|
* Fetches a slice of objects using the query builder.
|
||||||
|
|
@ -133,14 +69,102 @@ abstract class AbstractProvider extends BaseAbstractProvider
|
||||||
* @param object $queryBuilder
|
* @param object $queryBuilder
|
||||||
* @param integer $limit
|
* @param integer $limit
|
||||||
* @param integer $offset
|
* @param integer $offset
|
||||||
|
*
|
||||||
* @return array
|
* @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.
|
* {@inheritDoc}
|
||||||
*
|
|
||||||
* @return object
|
|
||||||
*/
|
*/
|
||||||
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;
|
namespace FOS\ElasticaBundle\Doctrine;
|
||||||
|
|
||||||
use Doctrine\Common\EventArgs;
|
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||||
use Doctrine\Common\EventSubscriber;
|
|
||||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
|
||||||
use FOS\ElasticaBundle\Persister\ObjectPersister;
|
use FOS\ElasticaBundle\Persister\ObjectPersister;
|
||||||
|
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||||
use FOS\ElasticaBundle\Provider\IndexableInterface;
|
use FOS\ElasticaBundle\Provider\IndexableInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||||
|
|
||||||
|
|
@ -14,49 +14,52 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||||
* Automatically update ElasticSearch based on changes to the Doctrine source
|
* Automatically update ElasticSearch based on changes to the Doctrine source
|
||||||
* data. One listener is generated for each Doctrine entity / ElasticSearch type.
|
* 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;
|
protected $objectPersister;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of subscribed events
|
* Configuration for the listener.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $events;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration for the listener
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $config;
|
private $config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Objects scheduled for insertion and replacement
|
* Objects scheduled for insertion.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $scheduledForInsertion = array();
|
public $scheduledForInsertion = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Objects scheduled to be updated or removed.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
public $scheduledForUpdate = array();
|
public $scheduledForUpdate = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IDs of objects scheduled for removal
|
* IDs of objects scheduled for removal.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $scheduledForDeletion = array();
|
public $scheduledForDeletion = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PropertyAccessor instance
|
* PropertyAccessor instance.
|
||||||
*
|
*
|
||||||
* @var PropertyAccessorInterface
|
* @var PropertyAccessorInterface
|
||||||
*/
|
*/
|
||||||
protected $propertyAccessor;
|
protected $propertyAccessor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \FOS\ElasticaBundle\Provider\IndexableInterface
|
* @var IndexableInterface
|
||||||
*/
|
*/
|
||||||
private $indexable;
|
private $indexable;
|
||||||
|
|
||||||
|
|
@ -64,71 +67,50 @@ class Listener implements EventSubscriber
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param ObjectPersisterInterface $objectPersister
|
* @param ObjectPersisterInterface $objectPersister
|
||||||
* @param array $events
|
* @param IndexableInterface $indexable
|
||||||
* @param IndexableInterface $indexable
|
* @param array $config
|
||||||
* @param array $config
|
* @param LoggerInterface $logger
|
||||||
* @param null $logger
|
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ObjectPersisterInterface $objectPersister,
|
ObjectPersisterInterface $objectPersister,
|
||||||
array $events,
|
|
||||||
IndexableInterface $indexable,
|
IndexableInterface $indexable,
|
||||||
array $config = array(),
|
array $config = array(),
|
||||||
$logger = null
|
LoggerInterface $logger = null
|
||||||
) {
|
) {
|
||||||
$this->config = array_merge(array(
|
$this->config = array_merge(array(
|
||||||
'identifier' => 'id',
|
'identifier' => 'id',
|
||||||
), $config);
|
), $config);
|
||||||
$this->events = $events;
|
|
||||||
$this->indexable = $indexable;
|
$this->indexable = $indexable;
|
||||||
$this->objectPersister = $objectPersister;
|
$this->objectPersister = $objectPersister;
|
||||||
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
|
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
|
||||||
|
|
||||||
if ($logger) {
|
if ($logger && $this->objectPersister instanceof ObjectPersister) {
|
||||||
$this->objectPersister->setLogger($logger);
|
$this->objectPersister->setLogger($logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see Doctrine\Common\EventSubscriber::getSubscribedEvents()
|
* Looks for new objects that should be indexed.
|
||||||
*/
|
|
||||||
public function getSubscribedEvents()
|
|
||||||
{
|
|
||||||
return $this->events;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides unified method for retrieving a doctrine object from an EventArgs instance
|
|
||||||
*
|
*
|
||||||
* @param EventArgs $eventArgs
|
* @param LifecycleEventArgs $eventArgs
|
||||||
* @return object Entity | Document
|
|
||||||
* @throws \RuntimeException if no valid getter is found.
|
|
||||||
*/
|
*/
|
||||||
private function getDoctrineObject(EventArgs $eventArgs)
|
public function postPersist(LifecycleEventArgs $eventArgs)
|
||||||
{
|
{
|
||||||
if (method_exists($eventArgs, 'getObject')) {
|
$entity = $eventArgs->getObject();
|
||||||
return $eventArgs->getObject();
|
|
||||||
} elseif (method_exists($eventArgs, 'getEntity')) {
|
|
||||||
return $eventArgs->getEntity();
|
|
||||||
} elseif (method_exists($eventArgs, 'getDocument')) {
|
|
||||||
return $eventArgs->getDocument();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \RuntimeException('Unable to retrieve object from EventArgs.');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function postPersist(EventArgs $eventArgs)
|
|
||||||
{
|
|
||||||
$entity = $this->getDoctrineObject($eventArgs);
|
|
||||||
|
|
||||||
if ($this->objectPersister->handlesObject($entity) && $this->isObjectIndexable($entity)) {
|
if ($this->objectPersister->handlesObject($entity) && $this->isObjectIndexable($entity)) {
|
||||||
$this->scheduledForInsertion[] = $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->objectPersister->handlesObject($entity)) {
|
||||||
if ($this->isObjectIndexable($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
|
* 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)) {
|
if ($this->objectPersister->handlesObject($entity)) {
|
||||||
$this->scheduleForDeletion($entity);
|
$this->scheduleForDeletion($entity);
|
||||||
|
|
@ -155,7 +139,7 @@ class Listener implements EventSubscriber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persist scheduled objects to ElasticSearch
|
* 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()
|
private function persistScheduled()
|
||||||
{
|
{
|
||||||
|
|
@ -174,29 +158,36 @@ class Listener implements EventSubscriber
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterate through scheduled actions before flushing to emulate 2.x behavior. Note that the ElasticSearch index
|
* Iterate through scheduled actions before flushing to emulate 2.x behavior.
|
||||||
* will fall out of sync with the source data in the event of a crash during flush.
|
* Note that the ElasticSearch index will fall out of sync with the source
|
||||||
|
* 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(EventArgs $eventArgs)
|
public function preFlush()
|
||||||
{
|
{
|
||||||
$this->persistScheduled();
|
$this->persistScheduled();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterating through scheduled actions *after* flushing ensures that the ElasticSearch index will be affected
|
* Iterating through scheduled actions *after* flushing ensures that the
|
||||||
* only if the query is successful
|
* ElasticSearch index will be affected only if the query is successful.
|
||||||
*/
|
*/
|
||||||
public function postFlush(EventArgs $eventArgs)
|
public function postFlush()
|
||||||
{
|
{
|
||||||
$this->persistScheduled();
|
$this->persistScheduled();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record the specified identifier to delete. Do not need to entire object.
|
* Record the specified identifier to delete. Do not need to entire object.
|
||||||
* @param mixed $object
|
*
|
||||||
* @return mixed
|
* @param object $object
|
||||||
*/
|
*/
|
||||||
protected function scheduleForDeletion($object)
|
private function scheduleForDeletion($object)
|
||||||
{
|
{
|
||||||
if ($identifierValue = $this->propertyAccessor->getValue($object, $this->config['identifier'])) {
|
if ($identifierValue = $this->propertyAccessor->getValue($object, $this->config['identifier'])) {
|
||||||
$this->scheduledForDeletion[] = $identifierValue;
|
$this->scheduledForDeletion[] = $identifierValue;
|
||||||
|
|
@ -207,6 +198,7 @@ class Listener implements EventSubscriber
|
||||||
* Checks if the object is indexable or not.
|
* Checks if the object is indexable or not.
|
||||||
*
|
*
|
||||||
* @param object $object
|
* @param object $object
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
private function isObjectIndexable($object)
|
private function isObjectIndexable($object)
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,16 @@ use FOS\ElasticaBundle\Doctrine\AbstractElasticaToModelTransformer;
|
||||||
/**
|
/**
|
||||||
* Maps Elastica documents with Doctrine objects
|
* Maps Elastica documents with Doctrine objects
|
||||||
* This mapper assumes an exact match between
|
* 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
|
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
|
* @return array of objects or arrays
|
||||||
*/
|
*/
|
||||||
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
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
|
* @param mixed $logger
|
||||||
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
protected function enableLogging($logger)
|
protected function enableLogging($logger)
|
||||||
|
|
@ -43,7 +44,7 @@ class Provider extends AbstractProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function countObjects($queryBuilder)
|
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)
|
protected function fetchSlice($queryBuilder, $limit, $offset)
|
||||||
{
|
{
|
||||||
|
|
@ -66,21 +67,21 @@ class Provider extends AbstractProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
return $queryBuilder
|
return $queryBuilder
|
||||||
->limit($limit)
|
|
||||||
->skip($offset)
|
->skip($offset)
|
||||||
|
->limit($limit)
|
||||||
->getQuery()
|
->getQuery()
|
||||||
->execute()
|
->execute()
|
||||||
->toArray();
|
->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::createQueryBuilder()
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function createQueryBuilder()
|
protected function createQueryBuilder($method)
|
||||||
{
|
{
|
||||||
return $this->managerRegistry
|
return $this->managerRegistry
|
||||||
->getManagerForClass($this->objectClass)
|
->getManagerForClass($this->objectClass)
|
||||||
->getRepository($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
|
* Maps Elastica documents with Doctrine objects
|
||||||
* This mapper assumes an exact match between
|
* 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
|
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
||||||
{
|
{
|
||||||
const ENTITY_ALIAS = 'o';
|
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
|
* @return array of objects or arrays
|
||||||
*/
|
*/
|
||||||
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
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
|
* @return \Doctrine\ORM\QueryBuilder
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
namespace FOS\ElasticaBundle\Doctrine\ORM;
|
namespace FOS\ElasticaBundle\Doctrine\ORM;
|
||||||
|
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Elastica\Exception\Bulk\ResponseException as BulkResponseException;
|
|
||||||
use FOS\ElasticaBundle\Doctrine\AbstractProvider;
|
use FOS\ElasticaBundle\Doctrine\AbstractProvider;
|
||||||
use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
|
use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
|
||||||
|
|
||||||
|
|
@ -30,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
|
* @param mixed $logger
|
||||||
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
protected function enableLogging($logger)
|
protected function enableLogging($logger)
|
||||||
|
|
@ -46,7 +46,7 @@ class Provider extends AbstractProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function countObjects($queryBuilder)
|
protected function countObjects($queryBuilder)
|
||||||
{
|
{
|
||||||
|
|
@ -69,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)
|
protected function fetchSlice($queryBuilder, $limit, $offset)
|
||||||
{
|
{
|
||||||
|
|
@ -77,8 +79,8 @@ class Provider extends AbstractProvider
|
||||||
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
|
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://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.postgresql.org/docs/current/static/queries-limit.html
|
||||||
* @see http://www.sqlite.org/lang_select.html#orderby
|
* @see http://www.sqlite.org/lang_select.html#orderby
|
||||||
|
|
@ -103,14 +105,14 @@ class Provider extends AbstractProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::createQueryBuilder()
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function createQueryBuilder()
|
protected function createQueryBuilder($method)
|
||||||
{
|
{
|
||||||
return $this->managerRegistry
|
return $this->managerRegistry
|
||||||
->getManagerForClass($this->objectClass)
|
->getManagerForClass($this->objectClass)
|
||||||
->getRepository($this->objectClass)
|
->getRepository($this->objectClass)
|
||||||
// ORM query builders require an alias argument
|
// 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 custom repository if one specified otherwise
|
||||||
* returns a basic repository.
|
* returns a basic repository.
|
||||||
|
|
@ -35,7 +35,7 @@ class RepositoryManager extends BaseManager
|
||||||
$realEntityName = $entityName;
|
$realEntityName = $entityName;
|
||||||
if (strpos($entityName, ':') !== false) {
|
if (strpos($entityName, ':') !== false) {
|
||||||
list($namespaceAlias, $simpleClassName) = explode(':', $entityName);
|
list($namespaceAlias, $simpleClassName) = explode(':', $entityName);
|
||||||
$realEntityName = $this->managerRegistry->getAliasNamespace($namespaceAlias) . '\\' . $simpleClassName;
|
$realEntityName = $this->managerRegistry->getAliasNamespace($namespaceAlias).'\\'.$simpleClassName;
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::getRepository($realEntityName);
|
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,14 +23,19 @@ class Client extends BaseClient
|
||||||
private $indexCache = array();
|
private $indexCache = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Symfony's debugging Stopwatch
|
* Symfony's debugging Stopwatch.
|
||||||
*
|
*
|
||||||
* @var Stopwatch|null
|
* @var Stopwatch|null
|
||||||
*/
|
*/
|
||||||
private $stopwatch;
|
private $stopwatch;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* @param string $path
|
||||||
|
* @param string $method
|
||||||
|
* @param array $data
|
||||||
|
* @param array $query
|
||||||
|
*
|
||||||
|
* @return \Elastica\Response
|
||||||
*/
|
*/
|
||||||
public function request($path, $method = Request::GET, $data = array(), array $query = array())
|
public function request($path, $method = Request::GET, $data = array(), array $query = array())
|
||||||
{
|
{
|
||||||
|
|
@ -41,20 +46,7 @@ class Client extends BaseClient
|
||||||
$start = microtime(true);
|
$start = microtime(true);
|
||||||
$response = parent::request($path, $method, $data, $query);
|
$response = parent::request($path, $method, $data, $query);
|
||||||
|
|
||||||
if ($this->_logger and $this->_logger instanceof ElasticaLogger) {
|
$this->logQuery($path, $method, $data, $query, $start);
|
||||||
$time = microtime(true) - $start;
|
|
||||||
|
|
||||||
$connection = $this->getLastRequest()->getConnection();
|
|
||||||
|
|
||||||
$connection_array = array(
|
|
||||||
'host' => $connection->getHost(),
|
|
||||||
'port' => $connection->getPort(),
|
|
||||||
'transport' => $connection->getTransport(),
|
|
||||||
'headers' => $connection->hasConfig('headers') ? $connection->getConfig('headers') : array(),
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->_logger->logQuery($path, $method, $data, $time, $connection_array, $query);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->stopwatch) {
|
if ($this->stopwatch) {
|
||||||
$this->stopwatch->stop('es_request');
|
$this->stopwatch->stop('es_request');
|
||||||
|
|
@ -81,4 +73,32 @@ class Client extends BaseClient
|
||||||
{
|
{
|
||||||
$this->stopwatch = $stopwatch;
|
$this->stopwatch = $stopwatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log the query if we have an instance of ElasticaLogger.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @param string $method
|
||||||
|
* @param array $data
|
||||||
|
* @param array $query
|
||||||
|
* @param int $start
|
||||||
|
*/
|
||||||
|
private function logQuery($path, $method, $data, array $query, $start)
|
||||||
|
{
|
||||||
|
if (!$this->_logger or !$this->_logger instanceof ElasticaLogger) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$time = microtime(true) - $start;
|
||||||
|
$connection = $this->getLastRequest()->getConnection();
|
||||||
|
|
||||||
|
$connection_array = array(
|
||||||
|
'host' => $connection->getHost(),
|
||||||
|
'port' => $connection->getPort(),
|
||||||
|
'transport' => $connection->getTransport(),
|
||||||
|
'headers' => $connection->hasConfig('headers') ? $connection->getConfig('headers') : array(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->_logger->logQuery($path, $method, $data, $time, $connection_array, $query);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
namespace FOS\ElasticaBundle\Elastica;
|
namespace FOS\ElasticaBundle\Elastica;
|
||||||
|
|
||||||
use Elastica\Index as BaseIndex;
|
use Elastica\Index as BaseIndex;
|
||||||
use Elastica\Type;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overridden Elastica Index class that provides dynamic index name changes.
|
* Overridden Elastica Index class that provides dynamic index name changes.
|
||||||
|
|
@ -32,6 +31,9 @@ class Index extends BaseIndex
|
||||||
return $this->originalName ?: $this->_name;
|
return $this->originalName ?: $this->_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $type
|
||||||
|
*/
|
||||||
public function getType($type)
|
public function getType($type)
|
||||||
{
|
{
|
||||||
if (isset($this->typeCache[$type])) {
|
if (isset($this->typeCache[$type])) {
|
||||||
|
|
@ -42,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
|
* 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
|
* since it's used for a very specific case and normally should not be used
|
||||||
*
|
*
|
||||||
* @param string $name Index name
|
* @param string $name Index name
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
public function overrideName($name)
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Exception/AliasIsIndexException.php
Normal file
11
Exception/AliasIsIndexException.php
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace FOS\ElasticaBundle\Exception;
|
||||||
|
|
||||||
|
class AliasIsIndexException extends \Exception
|
||||||
|
{
|
||||||
|
public function __construct($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.
|
* Bundle.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
class FOSElasticaBundle extends Bundle
|
class FOSElasticaBundle extends Bundle
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,13 @@ namespace FOS\ElasticaBundle\Finder;
|
||||||
interface FinderInterface
|
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 mixed $query Can be a string, an array or an \Elastica\Query object
|
||||||
* @param int $limit How many results to get
|
* @param int $limit How many results to get
|
||||||
* @param array $options
|
* @param array $options
|
||||||
|
*
|
||||||
* @return array results
|
* @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
|
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
|
* @param array $options
|
||||||
|
*
|
||||||
* @return Pagerfanta paginated results
|
* @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 mixed $query
|
||||||
* @param array $options
|
* @param array $options
|
||||||
|
*
|
||||||
* @return PaginatorAdapterInterface
|
* @return PaginatorAdapterInterface
|
||||||
*/
|
*/
|
||||||
function createPaginatorAdapter($query, $options = array());
|
public function createPaginatorAdapter($query, $options = array());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use Elastica\SearchableInterface;
|
||||||
use Elastica\Query;
|
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
|
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 integer $limit
|
||||||
* @param array $options
|
* @param array $options
|
||||||
|
*
|
||||||
* @return array of model objects
|
* @return array of model objects
|
||||||
**/
|
**/
|
||||||
public function find($query, $limit = null, $options = array())
|
public function find($query, $limit = null, $options = array())
|
||||||
|
|
@ -50,8 +51,9 @@ class TransformedFinder implements PaginatedFinderInterface
|
||||||
* Find documents similar to one with passed id.
|
* Find documents similar to one with passed id.
|
||||||
*
|
*
|
||||||
* @param integer $id
|
* @param integer $id
|
||||||
* @param array $params
|
* @param array $params
|
||||||
* @param array $query
|
* @param array $query
|
||||||
|
*
|
||||||
* @return array of model objects
|
* @return array of model objects
|
||||||
**/
|
**/
|
||||||
public function moreLikeThis($id, $params = array(), $query = array())
|
public function moreLikeThis($id, $params = array(), $query = array())
|
||||||
|
|
@ -65,7 +67,8 @@ class TransformedFinder implements PaginatedFinderInterface
|
||||||
/**
|
/**
|
||||||
* @param $query
|
* @param $query
|
||||||
* @param null|int $limit
|
* @param null|int $limit
|
||||||
* @param array $options
|
* @param array $options
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function search($query, $limit = null, $options = 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 string $query
|
||||||
* @param array $options
|
* @param array $options
|
||||||
|
*
|
||||||
* @return Pagerfanta
|
* @return Pagerfanta
|
||||||
*/
|
*/
|
||||||
public function findPaginated($query, $options = array())
|
public function findPaginated($query, $options = array())
|
||||||
|
|
|
||||||
|
|
@ -24,4 +24,4 @@ class HybridResult
|
||||||
{
|
{
|
||||||
return $this->result;
|
return $this->result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,12 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle\Index;
|
namespace FOS\ElasticaBundle\Index;
|
||||||
|
|
||||||
|
use Elastica\Client;
|
||||||
use Elastica\Exception\ExceptionInterface;
|
use Elastica\Exception\ExceptionInterface;
|
||||||
|
use Elastica\Request;
|
||||||
use FOS\ElasticaBundle\Configuration\IndexConfig;
|
use FOS\ElasticaBundle\Configuration\IndexConfig;
|
||||||
use FOS\ElasticaBundle\Elastica\Client;
|
|
||||||
use FOS\ElasticaBundle\Elastica\Index;
|
use FOS\ElasticaBundle\Elastica\Index;
|
||||||
|
use FOS\ElasticaBundle\Exception\AliasIsIndexException;
|
||||||
|
|
||||||
class AliasProcessor
|
class AliasProcessor
|
||||||
{
|
{
|
||||||
|
|
@ -22,115 +24,174 @@ class AliasProcessor
|
||||||
* Sets the randomised root name for an index.
|
* Sets the randomised root name for an index.
|
||||||
*
|
*
|
||||||
* @param IndexConfig $indexConfig
|
* @param IndexConfig $indexConfig
|
||||||
* @param Index $index
|
* @param Index $index
|
||||||
*/
|
*/
|
||||||
public function setRootName(IndexConfig $indexConfig, 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')
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switches an index to become the new target for an alias. Only applies for
|
* Switches an index to become the new target for an alias. Only applies for
|
||||||
* indexes that are set to use aliases.
|
* indexes that are set to use aliases.
|
||||||
*
|
*
|
||||||
|
* $force will delete an index encountered where an alias is expected.
|
||||||
|
*
|
||||||
* @param IndexConfig $indexConfig
|
* @param IndexConfig $indexConfig
|
||||||
* @param Index $index
|
* @param Index $index
|
||||||
|
* @param bool $force
|
||||||
|
*
|
||||||
|
* @throws AliasIsIndexException
|
||||||
* @throws \RuntimeException
|
* @throws \RuntimeException
|
||||||
*/
|
*/
|
||||||
public function switchIndexAlias(IndexConfig $indexConfig, Index $index)
|
public function switchIndexAlias(IndexConfig $indexConfig, Index $index, $force = false)
|
||||||
{
|
{
|
||||||
$client = $index->getClient();
|
$client = $index->getClient();
|
||||||
|
|
||||||
$aliasName = $indexConfig->getElasticSearchName();
|
$aliasName = $indexConfig->getElasticSearchName();
|
||||||
$oldIndexName = false;
|
$oldIndexName = null;
|
||||||
$newIndexName = $index->getName();
|
$newIndexName = $index->getName();
|
||||||
|
|
||||||
$aliasedIndexes = $this->getAliasedIndexes($client, $aliasName);
|
try {
|
||||||
|
$oldIndexName = $this->getAliasedIndex($client, $aliasName);
|
||||||
|
} catch (AliasIsIndexException $e) {
|
||||||
|
if (!$force) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
if (count($aliasedIndexes) > 1) {
|
$this->deleteIndex($client, $aliasName);
|
||||||
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());
|
$aliasUpdateRequest = array('actions' => array());
|
||||||
if (count($aliasedIndexes) == 1) {
|
if (null !== $aliasedIndex) {
|
||||||
// if the alias is set - add an action to remove it
|
// if the alias is set - add an action to remove it
|
||||||
$oldIndexName = $aliasedIndexes[0];
|
|
||||||
$aliasUpdateRequest['actions'][] = array(
|
$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
|
// add an action to point the alias to the new index
|
||||||
$aliasUpdateRequest['actions'][] = array(
|
$aliasUpdateRequest['actions'][] = array(
|
||||||
'add' => array('index' => $newIndexName, 'alias' => $aliasName)
|
'add' => array('index' => $newIndexName, 'alias' => $aliasName),
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
return $aliasUpdateRequest;
|
||||||
$client->request('_aliases', 'POST', $aliasUpdateRequest);
|
}
|
||||||
} catch (ExceptionInterface $renameAliasException) {
|
|
||||||
$additionalError = '';
|
|
||||||
// if we failed to move the alias, delete the newly built index
|
|
||||||
try {
|
|
||||||
$index->delete();
|
|
||||||
} catch (ExceptionInterface $deleteNewIndexException) {
|
|
||||||
$additionalError = sprintf(
|
|
||||||
'Tried to delete newly built index %s, but also failed: %s',
|
|
||||||
$newIndexName,
|
|
||||||
$deleteNewIndexException->getMessage()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \RuntimeException(
|
/**
|
||||||
sprintf(
|
* Cleans up an index when we encounter a failure to rename the alias.
|
||||||
'Failed to updated index alias: %s. %s',
|
*
|
||||||
$renameAliasException->getMessage(),
|
* @param Client $client
|
||||||
$additionalError ?: sprintf('Newly built index %s was deleted', $newIndexName)
|
* @param string $indexName
|
||||||
), 0, $renameAliasException
|
* @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
|
throw new \RuntimeException(sprintf(
|
||||||
if ($oldIndexName) {
|
'Failed to updated index alias: %s. %s',
|
||||||
$oldIndex = new Index($client, $oldIndexName);
|
$renameAliasException->getMessage(),
|
||||||
try {
|
$additionalError ?: sprintf('Newly built index %s was deleted', $indexName)
|
||||||
$oldIndex->delete();
|
), 0, $renameAliasException);
|
||||||
} catch (ExceptionInterface $deleteOldIndexException) {
|
}
|
||||||
throw new \RuntimeException(
|
|
||||||
sprintf(
|
/**
|
||||||
'Failed to delete old index %s with message: %s',
|
* Delete an index.
|
||||||
$oldIndexName,
|
*
|
||||||
$deleteOldIndexException->getMessage()
|
* @param Client $client
|
||||||
), 0, $deleteOldIndexException
|
* @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
|
* @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();
|
$aliasesInfo = $client->request('_aliases', 'GET')->getData();
|
||||||
$aliasedIndexes = array();
|
$aliasedIndexes = array();
|
||||||
|
|
||||||
foreach ($aliasesInfo as $indexName => $indexInfo) {
|
foreach ($aliasesInfo as $indexName => $indexInfo) {
|
||||||
|
if ($indexName === $aliasName) {
|
||||||
|
throw new AliasIsIndexException($indexName);
|
||||||
|
}
|
||||||
|
if (!isset($indexInfo['aliases'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$aliases = array_keys($indexInfo['aliases']);
|
$aliases = array_keys($indexInfo['aliases']);
|
||||||
if (in_array($aliasName, $aliases)) {
|
if (in_array($aliasName, $aliases)) {
|
||||||
$aliasedIndexes[] = $indexName;
|
$aliasedIndexes[] = $indexName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_shift($aliasedIndexes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,11 @@ use FOS\ElasticaBundle\Elastica\Index;
|
||||||
|
|
||||||
class IndexManager
|
class IndexManager
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var Index
|
||||||
|
*/
|
||||||
|
private $defaultIndex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
|
|
@ -22,7 +27,7 @@ class IndexManager
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all registered indexes
|
* Gets all registered indexes.
|
||||||
*
|
*
|
||||||
* @return array
|
* @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
|
* @param string $name Index to return, or the default index if null
|
||||||
|
*
|
||||||
* @return Index
|
* @return Index
|
||||||
|
*
|
||||||
* @throws \InvalidArgumentException if no index exists for the given name
|
* @throws \InvalidArgumentException if no index exists for the given name
|
||||||
*/
|
*/
|
||||||
public function getIndex($name = null)
|
public function getIndex($name = null)
|
||||||
|
|
@ -52,7 +59,7 @@ class IndexManager
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the default index
|
* Gets the default index.
|
||||||
*
|
*
|
||||||
* @return Index
|
* @return Index
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ class MappingBuilder
|
||||||
* Builds mappings for an entire index.
|
* Builds mappings for an entire index.
|
||||||
*
|
*
|
||||||
* @param IndexConfig $indexConfig
|
* @param IndexConfig $indexConfig
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function buildIndexMapping(IndexConfig $indexConfig)
|
public function buildIndexMapping(IndexConfig $indexConfig)
|
||||||
|
|
@ -37,13 +38,13 @@ class MappingBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
$mapping = array();
|
$mapping = array();
|
||||||
if ($typeMappings) {
|
if (!empty($typeMappings)) {
|
||||||
$mapping['mappings'] = $typeMappings;
|
$mapping['mappings'] = $typeMappings;
|
||||||
}
|
}
|
||||||
// 'warmers' => $indexConfig->getWarmers(),
|
// 'warmers' => $indexConfig->getWarmers(),
|
||||||
|
|
||||||
$settings = $indexConfig->getSettings();
|
$settings = $indexConfig->getSettings();
|
||||||
if ($settings) {
|
if (!empty($settings)) {
|
||||||
$mapping['settings'] = $settings;
|
$mapping['settings'] = $settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,17 +55,24 @@ class MappingBuilder
|
||||||
* Builds mappings for a single type.
|
* Builds mappings for a single type.
|
||||||
*
|
*
|
||||||
* @param TypeConfig $typeConfig
|
* @param TypeConfig $typeConfig
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function buildTypeMapping(TypeConfig $typeConfig)
|
public function buildTypeMapping(TypeConfig $typeConfig)
|
||||||
{
|
{
|
||||||
$mapping = array_merge($typeConfig->getMapping(), array(
|
$mapping = $typeConfig->getMapping();
|
||||||
// 'date_detection' => true,
|
|
||||||
// 'dynamic_date_formats' => array()
|
if (null !== $typeConfig->getDynamicDateFormats()) {
|
||||||
// 'dynamic_templates' => $typeConfig->getDynamicTemplates(),
|
$mapping['dynamic_date_formats'] = $typeConfig->getDynamicDateFormats();
|
||||||
// 'numeric_detection' => false,
|
}
|
||||||
// 'properties' => array(),
|
|
||||||
));
|
if (null !== $typeConfig->getDateDetection()) {
|
||||||
|
$mapping['date_detection'] = $typeConfig->getDateDetection();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $typeConfig->getNumericDetection()) {
|
||||||
|
$mapping['numeric_detection'] = $typeConfig->getNumericDetection();
|
||||||
|
}
|
||||||
|
|
||||||
if ($typeConfig->getIndexAnalyzer()) {
|
if ($typeConfig->getIndexAnalyzer()) {
|
||||||
$mapping['index_analyzer'] = $typeConfig->getIndexAnalyzer();
|
$mapping['index_analyzer'] = $typeConfig->getIndexAnalyzer();
|
||||||
|
|
@ -87,9 +95,9 @@ class MappingBuilder
|
||||||
$mapping['_meta']['model'] = $typeConfig->getModel();
|
$mapping['_meta']['model'] = $typeConfig->getModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$mapping) {
|
if (empty($mapping)) {
|
||||||
// Empty mapping, we want it encoded as a {} instead of a []
|
// Empty mapping, we want it encoded as a {} instead of a []
|
||||||
$mapping = new \stdClass;
|
$mapping = new \stdClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $mapping;
|
return $mapping;
|
||||||
|
|
@ -104,9 +112,14 @@ class MappingBuilder
|
||||||
private function fixProperties(&$properties)
|
private function fixProperties(&$properties)
|
||||||
{
|
{
|
||||||
foreach ($properties as $name => &$property) {
|
foreach ($properties as $name => &$property) {
|
||||||
|
unset($property['property_path']);
|
||||||
|
|
||||||
if (!isset($property['type'])) {
|
if (!isset($property['type'])) {
|
||||||
$property['type'] = 'string';
|
$property['type'] = 'string';
|
||||||
}
|
}
|
||||||
|
if ($property['type'] == 'multi_field' && isset($property['fields'])) {
|
||||||
|
$this->fixProperties($property['fields']);
|
||||||
|
}
|
||||||
if (isset($property['properties'])) {
|
if (isset($property['properties'])) {
|
||||||
$this->fixProperties($property['properties']);
|
$this->fixProperties($property['properties']);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,13 @@ namespace FOS\ElasticaBundle\Index;
|
||||||
use Elastica\Index;
|
use Elastica\Index;
|
||||||
use Elastica\Exception\ResponseException;
|
use Elastica\Exception\ResponseException;
|
||||||
use Elastica\Type\Mapping;
|
use Elastica\Type\Mapping;
|
||||||
use FOS\ElasticaBundle\Configuration\IndexConfig;
|
|
||||||
use FOS\ElasticaBundle\Configuration\ConfigManager;
|
use FOS\ElasticaBundle\Configuration\ConfigManager;
|
||||||
use FOS\ElasticaBundle\Elastica\Client;
|
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
|
class Resetter
|
||||||
{
|
{
|
||||||
|
|
@ -20,10 +21,15 @@ class Resetter
|
||||||
private $aliasProcessor;
|
private $aliasProcessor;
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* @var \FOS\ElasticaBundle\Configuration\Manager
|
* @var ConfigManager
|
||||||
*/
|
*/
|
||||||
private $configManager;
|
private $configManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var EventDispatcherInterface
|
||||||
|
*/
|
||||||
|
private $dispatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var IndexManager
|
* @var IndexManager
|
||||||
*/
|
*/
|
||||||
|
|
@ -34,21 +40,37 @@ class Resetter
|
||||||
*/
|
*/
|
||||||
private $mappingBuilder;
|
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->aliasProcessor = $aliasProcessor;
|
||||||
$this->configManager = $configManager;
|
$this->configManager = $configManager;
|
||||||
|
$this->dispatcher = $eventDispatcher;
|
||||||
$this->indexManager = $indexManager;
|
$this->indexManager = $indexManager;
|
||||||
$this->mappingBuilder = $mappingBuilder;
|
$this->mappingBuilder = $mappingBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes and recreates all indexes
|
* Deletes and recreates all indexes.
|
||||||
|
*
|
||||||
|
* @param bool $populating
|
||||||
|
* @param bool $force
|
||||||
*/
|
*/
|
||||||
public function resetAllIndexes($populating = false)
|
public function resetAllIndexes($populating = false, $force = false)
|
||||||
{
|
{
|
||||||
foreach ($this->configManager->getIndexNames() as $name) {
|
foreach ($this->configManager->getIndexNames() as $name) {
|
||||||
$this->resetIndex($name, $populating);
|
$this->resetIndex($name, $populating, $force);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,14 +79,19 @@ class Resetter
|
||||||
* with a randomised name for an alias to be set after population.
|
* with a randomised name for an alias to be set after population.
|
||||||
*
|
*
|
||||||
* @param string $indexName
|
* @param string $indexName
|
||||||
* @param bool $populating
|
* @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
|
* @throws \InvalidArgumentException if no index exists for the given name
|
||||||
*/
|
*/
|
||||||
public function resetIndex($indexName, $populating = false)
|
public function resetIndex($indexName, $populating = false, $force = false)
|
||||||
{
|
{
|
||||||
$indexConfig = $this->configManager->getIndexConfiguration($indexName);
|
$indexConfig = $this->configManager->getIndexConfiguration($indexName);
|
||||||
$index = $this->indexManager->getIndex($indexName);
|
$index = $this->indexManager->getIndex($indexName);
|
||||||
|
|
||||||
|
$event = new IndexResetEvent($indexName, $populating, $force);
|
||||||
|
$this->dispatcher->dispatch(IndexResetEvent::PRE_INDEX_RESET, $event);
|
||||||
|
|
||||||
if ($indexConfig->isUseAlias()) {
|
if ($indexConfig->isUseAlias()) {
|
||||||
$this->aliasProcessor->setRootName($indexConfig, $index);
|
$this->aliasProcessor->setRootName($indexConfig, $index);
|
||||||
}
|
}
|
||||||
|
|
@ -73,15 +100,18 @@ class Resetter
|
||||||
$index->create($mapping, true);
|
$index->create($mapping, true);
|
||||||
|
|
||||||
if (!$populating and $indexConfig->isUseAlias()) {
|
if (!$populating and $indexConfig->isUseAlias()) {
|
||||||
$this->aliasProcessor->switchIndexAlias($indexConfig, $index);
|
$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 $indexName
|
||||||
* @param string $typeName
|
* @param string $typeName
|
||||||
|
*
|
||||||
* @throws \InvalidArgumentException if no index or type mapping exists for the given names
|
* @throws \InvalidArgumentException if no index or type mapping exists for the given names
|
||||||
* @throws ResponseException
|
* @throws ResponseException
|
||||||
*/
|
*/
|
||||||
|
|
@ -90,6 +120,9 @@ class Resetter
|
||||||
$typeConfig = $this->configManager->getTypeConfiguration($indexName, $typeName);
|
$typeConfig = $this->configManager->getTypeConfiguration($indexName, $typeName);
|
||||||
$type = $this->indexManager->getIndex($indexName)->getType($typeName);
|
$type = $this->indexManager->getIndex($indexName)->getType($typeName);
|
||||||
|
|
||||||
|
$event = new TypeResetEvent($indexName, $typeName);
|
||||||
|
$this->dispatcher->dispatch(TypeResetEvent::PRE_TYPE_RESET, $event);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$type->delete();
|
$type->delete();
|
||||||
} catch (ResponseException $e) {
|
} catch (ResponseException $e) {
|
||||||
|
|
@ -98,18 +131,20 @@ class Resetter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$mapping = new Mapping;
|
$mapping = new Mapping();
|
||||||
foreach ($this->mappingBuilder->buildTypeMapping($typeConfig) as $name => $field) {
|
foreach ($this->mappingBuilder->buildTypeMapping($typeConfig) as $name => $field) {
|
||||||
$mapping->setParam($name, $field);
|
$mapping->setParam($name, $field);
|
||||||
}
|
}
|
||||||
|
|
||||||
$type->setMapping($mapping);
|
$type->setMapping($mapping);
|
||||||
|
|
||||||
|
$this->dispatcher->dispatch(TypeResetEvent::POST_TYPE_RESET, $event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A command run when a population has finished.
|
* A command run when a population has finished.
|
||||||
*
|
*
|
||||||
* @param $indexName
|
* @param string $indexName
|
||||||
*/
|
*/
|
||||||
public function postPopulate($indexName)
|
public function postPopulate($indexName)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,13 @@ class RepositoryManager implements RepositoryManagerInterface
|
||||||
|
|
||||||
public function addEntity($entityName, FinderInterface $finder, $repositoryName = null)
|
public function addEntity($entityName, FinderInterface $finder, $repositoryName = null)
|
||||||
{
|
{
|
||||||
$this->entities[$entityName]= array();
|
$this->entities[$entityName] = array();
|
||||||
$this->entities[$entityName]['finder'] = $finder;
|
$this->entities[$entityName]['finder'] = $finder;
|
||||||
$this->entities[$entityName]['repositoryName'] = $repositoryName;
|
$this->entities[$entityName]['repositoryName'] = $repositoryName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return repository for entity
|
* Return repository for entity.
|
||||||
*
|
*
|
||||||
* Returns custom repository if one specified otherwise
|
* Returns custom repository if one specified otherwise
|
||||||
* returns a basic repository.
|
* returns a basic repository.
|
||||||
|
|
@ -63,12 +63,16 @@ class RepositoryManager implements RepositoryManagerInterface
|
||||||
if ($annotation) {
|
if ($annotation) {
|
||||||
$this->entities[$entityName]['repositoryName']
|
$this->entities[$entityName]['repositoryName']
|
||||||
= $annotation->repositoryClass;
|
= $annotation->repositoryClass;
|
||||||
|
|
||||||
return $annotation->repositoryClass;
|
return $annotation->repositoryClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'FOS\ElasticaBundle\Repository';
|
return 'FOS\ElasticaBundle\Repository';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $entityName
|
||||||
|
*/
|
||||||
private function createRepository($entityName)
|
private function createRepository($entityName)
|
||||||
{
|
{
|
||||||
if (!class_exists($repositoryName = $this->getRepositoryName($entityName))) {
|
if (!class_exists($repositoryName = $this->getRepositoryName($entityName))) {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ use FOS\ElasticaBundle\Finder\FinderInterface;
|
||||||
*/
|
*/
|
||||||
interface RepositoryManagerInterface
|
interface RepositoryManagerInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds entity name and its finder.
|
* Adds entity name and its finder.
|
||||||
* Custom repository class name can also be added.
|
* Custom repository class name can also be added.
|
||||||
|
|
@ -24,7 +23,7 @@ interface RepositoryManagerInterface
|
||||||
public function addEntity($entityName, FinderInterface $finder, $repositoryName = null);
|
public function addEntity($entityName, FinderInterface $finder, $repositoryName = null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return repository for entity
|
* Return repository for entity.
|
||||||
*
|
*
|
||||||
* Returns custom repository if one specified otherwise
|
* Returns custom repository if one specified otherwise
|
||||||
* returns a basic repository.
|
* returns a basic repository.
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,6 @@ class FantaPaginatorAdapter implements AdapterInterface
|
||||||
* Returns the number of results.
|
* Returns the number of results.
|
||||||
*
|
*
|
||||||
* @return integer The number of results.
|
* @return integer The number of results.
|
||||||
*
|
|
||||||
* @api
|
|
||||||
*/
|
*/
|
||||||
public function getNbResults()
|
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
|
* @return mixed
|
||||||
*
|
*
|
||||||
* @api
|
* @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.
|
* @param integer $length The length.
|
||||||
*
|
*
|
||||||
* @return array|\Traversable The slice.
|
* @return array|\Traversable The slice.
|
||||||
*
|
|
||||||
* @api
|
|
||||||
*/
|
*/
|
||||||
public function getSlice($offset, $length)
|
public function getSlice($offset, $length)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,8 @@ interface PaginatorAdapterInterface
|
||||||
* Returns the number of results.
|
* Returns the number of results.
|
||||||
*
|
*
|
||||||
* @return integer The number of results.
|
* @return integer The number of results.
|
||||||
*
|
|
||||||
* @api
|
|
||||||
*/
|
*/
|
||||||
function getTotalHits();
|
public function getTotalHits();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an slice of the results.
|
* Returns an slice of the results.
|
||||||
|
|
@ -20,15 +18,20 @@ interface PaginatorAdapterInterface
|
||||||
* @param integer $length The length.
|
* @param integer $length The length.
|
||||||
*
|
*
|
||||||
* @return PartialResultsInterface
|
* @return PartialResultsInterface
|
||||||
*
|
|
||||||
* @api
|
|
||||||
*/
|
*/
|
||||||
function getResults($offset, $length);
|
public function getResults($offset, $length);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns Facets
|
* Returns Facets.
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
function getFacets();
|
public function getFacets();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Aggregations.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getAggregations();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,24 +8,27 @@ interface PartialResultsInterface
|
||||||
* Returns the paginated results.
|
* Returns the paginated results.
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*
|
|
||||||
* @api
|
|
||||||
*/
|
*/
|
||||||
function toArray();
|
public function toArray();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of results.
|
* Returns the number of results.
|
||||||
*
|
*
|
||||||
* @return integer The number of results.
|
* @return integer The number of results.
|
||||||
*
|
|
||||||
* @api
|
|
||||||
*/
|
*/
|
||||||
function getTotalHits();
|
public function getTotalHits();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the facets
|
* Returns the facets.
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
function getFacets();
|
public function getFacets();
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Returns the aggregations.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getAggregations();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use Elastica\ResultSet;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows pagination of Elastica\Query. Does not map results
|
* Allows pagination of Elastica\Query. Does not map results.
|
||||||
*/
|
*/
|
||||||
class RawPaginatorAdapter implements PaginatorAdapterInterface
|
class RawPaginatorAdapter implements PaginatorAdapterInterface
|
||||||
{
|
{
|
||||||
|
|
@ -37,11 +37,16 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
||||||
*/
|
*/
|
||||||
private $facets;
|
private $facets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array for the aggregations
|
||||||
|
*/
|
||||||
|
private $aggregations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see PaginatorAdapterInterface::__construct
|
* @see PaginatorAdapterInterface::__construct
|
||||||
*
|
*
|
||||||
* @param SearchableInterface $searchable the object to search in
|
* @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
|
* @param array $options
|
||||||
*/
|
*/
|
||||||
public function __construct(SearchableInterface $searchable, Query $query, array $options = array())
|
public function __construct(SearchableInterface $searchable, Query $query, array $options = array())
|
||||||
|
|
@ -54,9 +59,11 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
||||||
/**
|
/**
|
||||||
* Returns the paginated results.
|
* Returns the paginated results.
|
||||||
*
|
*
|
||||||
* @param $offset
|
* @param integer $offset
|
||||||
* @param $itemCountPerPage
|
* @param integer $itemCountPerPage
|
||||||
|
*
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
* @return ResultSet
|
* @return ResultSet
|
||||||
*/
|
*/
|
||||||
protected function getElasticaResults($offset, $itemCountPerPage)
|
protected function getElasticaResults($offset, $itemCountPerPage)
|
||||||
|
|
@ -67,7 +74,7 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
||||||
? (integer) $this->query->getParam('size')
|
? (integer) $this->query->getParam('size')
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if ($size && $size < $offset + $itemCountPerPage) {
|
if (null !== $size && $size < $offset + $itemCountPerPage) {
|
||||||
$itemCountPerPage = $size - $offset;
|
$itemCountPerPage = $size - $offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,6 +89,8 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
||||||
$resultSet = $this->searchable->search($query, $this->options);
|
$resultSet = $this->searchable->search($query, $this->options);
|
||||||
$this->totalHits = $resultSet->getTotalHits();
|
$this->totalHits = $resultSet->getTotalHits();
|
||||||
$this->facets = $resultSet->getFacets();
|
$this->facets = $resultSet->getFacets();
|
||||||
|
$this->aggregations = $resultSet->getAggregations();
|
||||||
|
|
||||||
return $resultSet;
|
return $resultSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,6 +99,7 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
||||||
*
|
*
|
||||||
* @param int $offset
|
* @param int $offset
|
||||||
* @param int $itemCountPerPage
|
* @param int $itemCountPerPage
|
||||||
|
*
|
||||||
* @return PartialResultsInterface
|
* @return PartialResultsInterface
|
||||||
*/
|
*/
|
||||||
public function getResults($offset, $itemCountPerPage)
|
public function getResults($offset, $itemCountPerPage)
|
||||||
|
|
@ -100,27 +110,33 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
||||||
/**
|
/**
|
||||||
* Returns the number of results.
|
* 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.
|
* @return integer The number of results.
|
||||||
*/
|
*/
|
||||||
public function getTotalHits()
|
public function getTotalHits($genuineTotal = false)
|
||||||
{
|
{
|
||||||
if ( ! isset($this->totalHits)) {
|
if (! isset($this->totalHits)) {
|
||||||
$this->totalHits = $this->searchable->search($this->query)->getTotalHits();
|
$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'))
|
? min($this->totalHits, (integer) $this->query->getParam('size'))
|
||||||
: $this->totalHits;
|
: $this->totalHits;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns Facets
|
* Returns Facets.
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function getFacets()
|
public function getFacets()
|
||||||
{
|
{
|
||||||
if ( ! isset($this->facets)) {
|
if (! isset($this->facets)) {
|
||||||
$this->facets = $this->searchable->search($this->query)->getFacets();
|
$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
|
* @return Query the search query
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use Elastica\ResultSet;
|
||||||
use Elastica\Result;
|
use Elastica\Result;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raw partial results transforms to a simple array
|
* Raw partial results transforms to a simple array.
|
||||||
*/
|
*/
|
||||||
class RawPartialResults implements PartialResultsInterface
|
class RawPartialResults implements PartialResultsInterface
|
||||||
{
|
{
|
||||||
|
|
@ -25,7 +25,7 @@ class RawPartialResults implements PartialResultsInterface
|
||||||
*/
|
*/
|
||||||
public function toArray()
|
public function toArray()
|
||||||
{
|
{
|
||||||
return array_map(function(Result $result) {
|
return array_map(function (Result $result) {
|
||||||
return $result->getSource();
|
return $result->getSource();
|
||||||
}, $this->resultSet->getResults());
|
}, $this->resultSet->getResults());
|
||||||
}
|
}
|
||||||
|
|
@ -47,6 +47,18 @@ class RawPartialResults implements PartialResultsInterface
|
||||||
return $this->resultSet->getFacets();
|
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;
|
use Elastica\Query;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows pagination of \Elastica\Query
|
* Allows pagination of \Elastica\Query.
|
||||||
*/
|
*/
|
||||||
class TransformedPaginatorAdapter extends RawPaginatorAdapter
|
class TransformedPaginatorAdapter extends RawPaginatorAdapter
|
||||||
{
|
{
|
||||||
private $transformer;
|
private $transformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param SearchableInterface $searchable the object to search in
|
* @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
|
* @param array $options
|
||||||
* @param ElasticaToModelTransformerInterface $transformer the transformer for fetching the results
|
* @param ElasticaToModelTransformerInterface $transformer the transformer for fetching the results
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,14 @@ use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
||||||
use Elastica\ResultSet;
|
use Elastica\ResultSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Partial transformed result set
|
* Partial transformed result set.
|
||||||
*/
|
*/
|
||||||
class TransformedPartialResults extends RawPartialResults
|
class TransformedPartialResults extends RawPartialResults
|
||||||
{
|
{
|
||||||
protected $transformer;
|
protected $transformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ResultSet $resultSet
|
* @param ResultSet $resultSet
|
||||||
* @param \FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface $transformer
|
* @param \FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface $transformer
|
||||||
*/
|
*/
|
||||||
public function __construct(ResultSet $resultSet, ElasticaToModelTransformerInterface $transformer)
|
public function __construct(ResultSet $resultSet, ElasticaToModelTransformerInterface $transformer)
|
||||||
|
|
@ -30,4 +30,4 @@ class TransformedPartialResults extends RawPartialResults
|
||||||
{
|
{
|
||||||
return $this->transformer->transform($this->resultSet->getResults());
|
return $this->transformer->transform($this->resultSet->getResults());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,13 @@ namespace FOS\ElasticaBundle\Persister;
|
||||||
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Elastica\Exception\BulkException;
|
use Elastica\Exception\BulkException;
|
||||||
use Elastica\Exception\NotFoundException;
|
|
||||||
use FOS\ElasticaBundle\Transformer\ModelToElasticaTransformerInterface;
|
use FOS\ElasticaBundle\Transformer\ModelToElasticaTransformerInterface;
|
||||||
use Elastica\Type;
|
use Elastica\Type;
|
||||||
use Elastica\Document;
|
use Elastica\Document;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts, replaces and deletes single documents in an elastica type
|
* 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>
|
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
|
||||||
*/
|
*/
|
||||||
|
|
@ -35,6 +34,7 @@ class ObjectPersister implements ObjectPersisterInterface
|
||||||
* If the ObjectPersister handles a given object.
|
* If the ObjectPersister handles a given object.
|
||||||
*
|
*
|
||||||
* @param object $object
|
* @param object $object
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function handlesObject($object)
|
public function handlesObject($object)
|
||||||
|
|
@ -42,17 +42,20 @@ class ObjectPersister implements ObjectPersisterInterface
|
||||||
return $object instanceof $this->objectClass;
|
return $object instanceof $this->objectClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LoggerInterface $logger
|
||||||
|
*/
|
||||||
public function setLogger(LoggerInterface $logger)
|
public function setLogger(LoggerInterface $logger)
|
||||||
{
|
{
|
||||||
$this->logger = $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
|
* @param BulkException $e
|
||||||
|
*
|
||||||
* @throws BulkException
|
* @throws BulkException
|
||||||
* @return null
|
|
||||||
*/
|
*/
|
||||||
private function log(BulkException $e)
|
private function log(BulkException $e)
|
||||||
{
|
{
|
||||||
|
|
@ -65,7 +68,7 @@ class ObjectPersister implements ObjectPersisterInterface
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert one object into the type
|
* 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
|
* @param object $object
|
||||||
*/
|
*/
|
||||||
|
|
@ -75,10 +78,9 @@ class ObjectPersister implements ObjectPersisterInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces one object in the type
|
* Replaces one object in the type.
|
||||||
*
|
*
|
||||||
* @param object $object
|
* @param object $object
|
||||||
* @return null
|
|
||||||
**/
|
**/
|
||||||
public function replaceOne($object)
|
public function replaceOne($object)
|
||||||
{
|
{
|
||||||
|
|
@ -86,10 +88,9 @@ class ObjectPersister implements ObjectPersisterInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes one object in the type
|
* Deletes one object in the type.
|
||||||
*
|
*
|
||||||
* @param object $object
|
* @param object $object
|
||||||
* @return null
|
|
||||||
**/
|
**/
|
||||||
public function deleteOne($object)
|
public function deleteOne($object)
|
||||||
{
|
{
|
||||||
|
|
@ -97,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
|
* @param mixed $id
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
**/
|
**/
|
||||||
public function deleteById($id)
|
public function deleteById($id)
|
||||||
{
|
{
|
||||||
|
|
@ -109,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 array $objects array of domain model objects
|
||||||
* @param string Method to call
|
* @param string Method to call
|
||||||
|
|
@ -149,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
|
* @param array $objects array of domain model objects
|
||||||
*/
|
*/
|
||||||
|
|
@ -167,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
|
* @param array $identifiers array of domain model object identifiers
|
||||||
*/
|
*/
|
||||||
|
|
@ -181,9 +180,10 @@ class ObjectPersister implements ObjectPersisterInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms an object to an elastica document
|
* Transforms an object to an elastica document.
|
||||||
*
|
*
|
||||||
* @param object $object
|
* @param object $object
|
||||||
|
*
|
||||||
* @return Document the elastica document
|
* @return Document the elastica document
|
||||||
*/
|
*/
|
||||||
public function transformToElasticaDocument($object)
|
public function transformToElasticaDocument($object)
|
||||||
|
|
|
||||||
|
|
@ -4,66 +4,73 @@ namespace FOS\ElasticaBundle\Persister;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts, replaces and deletes single documents in an elastica type
|
* 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>
|
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
|
||||||
*/
|
*/
|
||||||
interface ObjectPersisterInterface
|
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
|
* 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
|
* @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
|
* @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
|
* @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
|
* @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
|
* @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
|
* @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
|
* @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
|
* @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
|
* Inserts, replaces and deletes single objects in an elastica type, making use
|
||||||
* of elastica's serializer support to convert objects in to elastica documents.
|
* 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>
|
* @author Lea Haensenberber <lea.haensenberger@gmail.com>
|
||||||
*/
|
*/
|
||||||
|
|
@ -17,17 +17,25 @@ class ObjectSerializerPersister extends ObjectPersister
|
||||||
{
|
{
|
||||||
protected $serializer;
|
protected $serializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Type $type
|
||||||
|
* @param ModelToElasticaTransformerInterface $transformer
|
||||||
|
* @param string $objectClass
|
||||||
|
* @param callable $serializer
|
||||||
|
*/
|
||||||
public function __construct(Type $type, ModelToElasticaTransformerInterface $transformer, $objectClass, $serializer)
|
public function __construct(Type $type, ModelToElasticaTransformerInterface $transformer, $objectClass, $serializer)
|
||||||
{
|
{
|
||||||
parent::__construct($type, $transformer, $objectClass, array());
|
parent::__construct($type, $transformer, $objectClass, array());
|
||||||
$this->serializer = $serializer;
|
|
||||||
|
$this->serializer = $serializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms an object to an elastica document
|
* Transforms an object to an elastica document
|
||||||
* with just the identifier set
|
* with just the identifier set.
|
||||||
*
|
*
|
||||||
* @param object $object
|
* @param object $object
|
||||||
|
*
|
||||||
* @return Document the elastica document
|
* @return Document the elastica document
|
||||||
*/
|
*/
|
||||||
public function transformToElasticaDocument($object)
|
public function transformToElasticaDocument($object)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
namespace FOS\ElasticaBundle\Propel;
|
namespace FOS\ElasticaBundle\Propel;
|
||||||
|
|
||||||
use FOS\ElasticaBundle\HybridResult;
|
use FOS\ElasticaBundle\HybridResult;
|
||||||
|
use FOS\ElasticaBundle\Transformer\AbstractElasticaToModelTransformer;
|
||||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
||||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||||
|
|
||||||
|
|
@ -14,7 +15,7 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||||
*
|
*
|
||||||
* @author William Durand <william.durand1@gmail.com>
|
* @author William Durand <william.durand1@gmail.com>
|
||||||
*/
|
*/
|
||||||
class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Propel model class to map to Elastica documents.
|
* Propel model class to map to Elastica documents.
|
||||||
|
|
@ -33,18 +34,11 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||||
'identifier' => 'id',
|
'identifier' => 'id',
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* PropertyAccessor instance.
|
|
||||||
*
|
|
||||||
* @var PropertyAccessorInterface
|
|
||||||
*/
|
|
||||||
protected $propertyAccessor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param string $objectClass
|
* @param string $objectClass
|
||||||
* @param array $options
|
* @param array $options
|
||||||
*/
|
*/
|
||||||
public function __construct($objectClass, array $options = array())
|
public function __construct($objectClass, array $options = array())
|
||||||
{
|
{
|
||||||
|
|
@ -52,21 +46,12 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||||
$this->options = array_merge($this->options, $options);
|
$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
|
* Transforms an array of Elastica document into an array of Propel entities
|
||||||
* fetched from the database.
|
* fetched from the database.
|
||||||
*
|
*
|
||||||
* @param array $elasticaObjects
|
* @param array $elasticaObjects
|
||||||
|
*
|
||||||
* @return array|\ArrayObject
|
* @return array|\ArrayObject
|
||||||
*/
|
*/
|
||||||
public function transform(array $elasticaObjects)
|
public function transform(array $elasticaObjects)
|
||||||
|
|
@ -81,11 +66,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||||
// Sort objects in the order of their IDs
|
// Sort objects in the order of their IDs
|
||||||
$idPos = array_flip($ids);
|
$idPos = array_flip($ids);
|
||||||
$identifier = $this->options['identifier'];
|
$identifier = $this->options['identifier'];
|
||||||
$propertyAccessor = $this->propertyAccessor;
|
$sortCallback = $this->getSortingClosure($idPos, $identifier);
|
||||||
|
|
||||||
$sortCallback = function($a, $b) use ($idPos, $identifier, $propertyAccessor) {
|
|
||||||
return $idPos[$propertyAccessor->getValue($a, $identifier)] > $idPos[$propertyAccessor->getValue($b, $identifier)];
|
|
||||||
};
|
|
||||||
|
|
||||||
if (is_object($objects)) {
|
if (is_object($objects)) {
|
||||||
$objects->uasort($sortCallback);
|
$objects->uasort($sortCallback);
|
||||||
|
|
@ -104,7 +85,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||||
$objects = $this->transform($elasticaObjects);
|
$objects = $this->transform($elasticaObjects);
|
||||||
|
|
||||||
$result = array();
|
$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]);
|
$result[] = new HybridResult($elasticaObjects[$i], $objects[$i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,6 +116,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||||
*
|
*
|
||||||
* @param array $identifierValues Identifier values
|
* @param array $identifierValues Identifier values
|
||||||
* @param boolean $hydrate Whether or not to hydrate the results
|
* @param boolean $hydrate Whether or not to hydrate the results
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
||||||
|
|
@ -145,7 +127,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||||
|
|
||||||
$query = $this->createQuery($this->objectClass, $this->options['identifier'], $identifierValues);
|
$query = $this->createQuery($this->objectClass, $this->options['identifier'], $identifierValues);
|
||||||
|
|
||||||
if ( ! $hydrate) {
|
if (! $hydrate) {
|
||||||
return $query->toArray();
|
return $query->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,6 +140,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||||
* @param string $class Propel model class
|
* @param string $class Propel model class
|
||||||
* @param string $identifierField Identifier field name (e.g. "id")
|
* @param string $identifierField Identifier field name (e.g. "id")
|
||||||
* @param array $identifierValues Identifier values
|
* @param array $identifierValues Identifier values
|
||||||
|
*
|
||||||
* @return \ModelCriteria
|
* @return \ModelCriteria
|
||||||
*/
|
*/
|
||||||
protected function createQuery($class, $identifierField, array $identifierValues)
|
protected function createQuery($class, $identifierField, array $identifierValues)
|
||||||
|
|
@ -170,6 +153,8 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Util/Inflector.php
|
* @see https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Util/Inflector.php
|
||||||
|
*
|
||||||
|
* @param string $str
|
||||||
*/
|
*/
|
||||||
private function camelize($str)
|
private function camelize($str)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,53 +5,69 @@ namespace FOS\ElasticaBundle\Propel;
|
||||||
use FOS\ElasticaBundle\Provider\AbstractProvider;
|
use FOS\ElasticaBundle\Provider\AbstractProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Propel provider
|
* Propel provider.
|
||||||
*
|
*
|
||||||
* @author William Durand <william.durand1@gmail.com>
|
* @author William Durand <william.durand1@gmail.com>
|
||||||
*/
|
*/
|
||||||
class Provider extends AbstractProvider
|
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();
|
$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) {
|
$offset = $options['offset'];
|
||||||
if ($loggerClosure) {
|
|
||||||
$stepStartTime = microtime(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for (; $offset < $nbObjects; $offset += $options['batch_size']) {
|
||||||
$objects = $queryClass::create()
|
$objects = $queryClass::create()
|
||||||
->limit($batchSize)
|
->limit($options['batch_size'])
|
||||||
->offset($offset)
|
->offset($offset)
|
||||||
->find()
|
->find()
|
||||||
->getArrayCopy();
|
->getArrayCopy();
|
||||||
if ($loggerClosure) {
|
$objects = $this->filterObjects($options, $objects);
|
||||||
$stepNbObjects = count($objects);
|
if (!empty($objects)) {
|
||||||
}
|
$this->objectPersister->insertMany($objects);
|
||||||
$objects = array_filter($objects, array($this, 'isObjectIndexable'));
|
|
||||||
if (!$objects) {
|
|
||||||
$loggerClosure('<info>Entire batch was filtered away, skipping...</info>');
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->objectPersister->insertMany($objects);
|
usleep($options['sleep']);
|
||||||
|
|
||||||
usleep($sleep);
|
|
||||||
|
|
||||||
if ($loggerClosure) {
|
if ($loggerClosure) {
|
||||||
$stepCount = $stepNbObjects + $offset;
|
$loggerClosure($options['batch_size'], $nbObjects);
|
||||||
$percentComplete = 100 * $stepCount / $nbObjects;
|
|
||||||
$objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
|
|
||||||
$loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s %s', $percentComplete, $stepCount, $nbObjects, $objectsPerSecond, $this->getMemoryUsage()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected function configureOptions()
|
||||||
|
{
|
||||||
|
parent::configureOptions();
|
||||||
|
|
||||||
|
$this->resolver->setDefaults(array(
|
||||||
|
'clear_object_manager' => true,
|
||||||
|
'debug_logging' => false,
|
||||||
|
'ignore_errors' => false,
|
||||||
|
'offset' => 0,
|
||||||
|
'query_builder_method' => null,
|
||||||
|
'sleep' => 0
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected function disableLogging()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected function enableLogging($logger)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,17 @@
|
||||||
namespace FOS\ElasticaBundle\Provider;
|
namespace FOS\ElasticaBundle\Provider;
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AbstractProvider
|
* AbstractProvider.
|
||||||
*/
|
*/
|
||||||
abstract class AbstractProvider implements ProviderInterface
|
abstract class AbstractProvider implements ProviderInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var ObjectPersisterInterface
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $objectPersister;
|
protected $baseOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
|
|
@ -20,12 +21,17 @@ abstract class AbstractProvider implements ProviderInterface
|
||||||
protected $objectClass;
|
protected $objectClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var ObjectPersisterInterface
|
||||||
*/
|
*/
|
||||||
protected $options;
|
protected $objectPersister;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Indexable
|
* @var OptionsResolver
|
||||||
|
*/
|
||||||
|
protected $resolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var IndexableInterface
|
||||||
*/
|
*/
|
||||||
private $indexable;
|
private $indexable;
|
||||||
|
|
||||||
|
|
@ -33,42 +39,136 @@ abstract class AbstractProvider implements ProviderInterface
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param ObjectPersisterInterface $objectPersister
|
* @param ObjectPersisterInterface $objectPersister
|
||||||
* @param IndexableInterface $indexable
|
* @param IndexableInterface $indexable
|
||||||
* @param string $objectClass
|
* @param string $objectClass
|
||||||
* @param array $options
|
* @param array $baseOptions
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ObjectPersisterInterface $objectPersister,
|
ObjectPersisterInterface $objectPersister,
|
||||||
IndexableInterface $indexable,
|
IndexableInterface $indexable,
|
||||||
$objectClass,
|
$objectClass,
|
||||||
array $options = array()
|
array $baseOptions = array()
|
||||||
) {
|
) {
|
||||||
|
$this->baseOptions = $baseOptions;
|
||||||
$this->indexable = $indexable;
|
$this->indexable = $indexable;
|
||||||
$this->objectClass = $objectClass;
|
$this->objectClass = $objectClass;
|
||||||
$this->objectPersister = $objectPersister;
|
$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,
|
'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.
|
* Checks if a given object should be indexed or not.
|
||||||
*
|
*
|
||||||
|
* @deprecated To be removed in 4.0
|
||||||
|
*
|
||||||
* @param object $object
|
* @param object $object
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function isObjectIndexable($object)
|
protected function isObjectIndexable($object)
|
||||||
{
|
{
|
||||||
return $this->indexable->isObjectIndexable(
|
return $this->indexable->isObjectIndexable(
|
||||||
$this->options['indexName'],
|
$this->baseOptions['indexName'],
|
||||||
$this->options['typeName'],
|
$this->baseOptions['typeName'],
|
||||||
$object
|
$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
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
|
@ -79,4 +179,17 @@ abstract class AbstractProvider implements ProviderInterface
|
||||||
|
|
||||||
return sprintf('(RAM : current=%uMo peak=%uMo)', $memory, $memoryMax);
|
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;
|
private $container;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instance of ExpressionLanguage
|
* An instance of ExpressionLanguage.
|
||||||
*
|
*
|
||||||
* @var ExpressionLanguage
|
* @var ExpressionLanguage
|
||||||
*/
|
*/
|
||||||
|
|
@ -47,7 +47,7 @@ class Indexable implements IndexableInterface
|
||||||
private $initialisedCallbacks = array();
|
private $initialisedCallbacks = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PropertyAccessor instance
|
* PropertyAccessor instance.
|
||||||
*
|
*
|
||||||
* @var PropertyAccessorInterface
|
* @var PropertyAccessorInterface
|
||||||
*/
|
*/
|
||||||
|
|
@ -55,6 +55,7 @@ class Indexable implements IndexableInterface
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $callbacks
|
* @param array $callbacks
|
||||||
|
* @param ContainerInterface $container
|
||||||
*/
|
*/
|
||||||
public function __construct(array $callbacks, ContainerInterface $container)
|
public function __construct(array $callbacks, ContainerInterface $container)
|
||||||
{
|
{
|
||||||
|
|
@ -68,7 +69,8 @@ class Indexable implements IndexableInterface
|
||||||
*
|
*
|
||||||
* @param string $indexName
|
* @param string $indexName
|
||||||
* @param string $typeName
|
* @param string $typeName
|
||||||
* @param mixed $object
|
* @param mixed $object
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isObjectIndexable($indexName, $typeName, $object)
|
public function isObjectIndexable($indexName, $typeName, $object)
|
||||||
|
|
@ -80,9 +82,9 @@ class Indexable implements IndexableInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($callback instanceof Expression) {
|
if ($callback instanceof Expression) {
|
||||||
return $this->getExpressionLanguage()->evaluate($callback, array(
|
return (bool) $this->getExpressionLanguage()->evaluate($callback, array(
|
||||||
'object' => $object,
|
'object' => $object,
|
||||||
$this->getExpressionVar($object) => $object
|
$this->getExpressionVar($object) => $object,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,12 +98,13 @@ class Indexable implements IndexableInterface
|
||||||
*
|
*
|
||||||
* @param string $type
|
* @param string $type
|
||||||
* @param object $object
|
* @param object $object
|
||||||
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
private function buildCallback($type, $object)
|
private function buildCallback($type, $object)
|
||||||
{
|
{
|
||||||
if (!array_key_exists($type, $this->callbacks)) {
|
if (!array_key_exists($type, $this->callbacks)) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$callback = $this->callbacks[$type];
|
$callback = $this->callbacks[$type];
|
||||||
|
|
@ -110,44 +113,54 @@ class Indexable implements IndexableInterface
|
||||||
return $callback;
|
return $callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_array($callback)) {
|
if (is_array($callback) && !is_object($callback[0])) {
|
||||||
list($class, $method) = $callback + array(null, null);
|
return $this->processArrayToCallback($type, $callback);
|
||||||
|
|
||||||
if (is_object($class)) {
|
|
||||||
$class = get_class($class);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strpos($class, '@') === 0) {
|
|
||||||
$service = $this->container->get(substr($class, 1));
|
|
||||||
|
|
||||||
return array($service, $method);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($class && $method) {
|
|
||||||
throw new \InvalidArgumentException(sprintf('Callback for type "%s", "%s::%s()", is not callable.', $type, $class, $method));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_string($callback) && $expression = $this->getExpressionLanguage()) {
|
if (is_string($callback)) {
|
||||||
$callback = new Expression($callback);
|
return $this->buildExpressionCallback($type, $object, $callback);
|
||||||
|
|
||||||
try {
|
|
||||||
$expression->compile($callback, array('object', $this->getExpressionVar($object)));
|
|
||||||
|
|
||||||
return $callback;
|
|
||||||
} catch (SyntaxError $e) {
|
|
||||||
throw new \InvalidArgumentException(sprintf('Callback for type "%s" is an invalid expression', $type), $e->getCode(), $e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \InvalidArgumentException(sprintf('Callback for type "%s" is not a valid callback.', $type));
|
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.
|
* Retreives a cached callback, or creates a new callback if one is not found.
|
||||||
*
|
*
|
||||||
* @param string $type
|
* @param string $type
|
||||||
* @param object $object
|
* @param object $object
|
||||||
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
private function getCallback($type, $object)
|
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()
|
private function getExpressionLanguage()
|
||||||
{
|
{
|
||||||
if (null === $this->expressionLanguage) {
|
if (null === $this->expressionLanguage && class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
||||||
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->expressionLanguage = new 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
|
* @param mixed $object
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function getExpressionVar($object = null)
|
private function getExpressionVar($object = null)
|
||||||
{
|
{
|
||||||
|
if (!is_object($object)) {
|
||||||
|
return 'object';
|
||||||
|
}
|
||||||
|
|
||||||
$ref = new \ReflectionClass($object);
|
$ref = new \ReflectionClass($object);
|
||||||
|
|
||||||
return strtolower($ref->getShortName());
|
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 $indexName
|
||||||
* @param string $typeName
|
* @param string $typeName
|
||||||
* @param mixed $object
|
* @param mixed $object
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isObjectIndexable($indexName, $typeName, $object);
|
public function isObjectIndexable($indexName, $typeName, $object);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace FOS\ElasticaBundle\Provider;
|
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>
|
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
|
||||||
*/
|
*/
|
||||||
|
|
@ -12,9 +12,15 @@ interface ProviderInterface
|
||||||
/**
|
/**
|
||||||
* Persists all domain objects to ElasticSearch for this provider.
|
* 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 \Closure $loggerClosure
|
||||||
* @param array $options
|
* @param array $options
|
||||||
|
*
|
||||||
* @return
|
* @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.
|
* Providers will be indexed by "type" strings in the returned array.
|
||||||
*
|
*
|
||||||
* @param string $index
|
* @param string $index
|
||||||
* @return array of ProviderInterface instances
|
*
|
||||||
|
* @return ProviderInterface[]
|
||||||
|
*
|
||||||
* @throws \InvalidArgumentException if no providers were registered for the index
|
* @throws \InvalidArgumentException if no providers were registered for the index
|
||||||
*/
|
*/
|
||||||
public function getIndexProviders($index)
|
public function getIndexProviders($index)
|
||||||
|
|
@ -81,7 +83,9 @@ class ProviderRegistry implements ContainerAwareInterface
|
||||||
*
|
*
|
||||||
* @param string $index
|
* @param string $index
|
||||||
* @param string $type
|
* @param string $type
|
||||||
|
*
|
||||||
* @return ProviderInterface
|
* @return ProviderInterface
|
||||||
|
*
|
||||||
* @throws \InvalidArgumentException if no provider was registered for the index and type
|
* @throws \InvalidArgumentException if no provider was registered for the index and type
|
||||||
*/
|
*/
|
||||||
public function getProvider($index, $type)
|
public function getProvider($index, $type)
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,16 @@ Symfony2. Features include:
|
||||||
> **Note** Propel support is limited and contributions fixing issues are welcome!
|
> **Note** Propel support is limited and contributions fixing issues are welcome!
|
||||||
|
|
||||||
[](http://travis-ci.org/FriendsOfSymfony/FOSElasticaBundle) [](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle) [](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle) [](https://packagist.org/packages/friendsofsymfony/elastica-bundle)
|
[](http://travis-ci.org/FriendsOfSymfony/FOSElasticaBundle) [](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle) [](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle) [](https://packagist.org/packages/friendsofsymfony/elastica-bundle)
|
||||||
|
[](https://scrutinizer-ci.com/g/FriendsOfSymfony/FOSElasticaBundle/?branch=master)
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Documentation for FOSElasticaBundle is in `Resources/doc/index.md`
|
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
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|
|
||||||
|
|
@ -19,21 +19,47 @@ class Repository
|
||||||
$this->finder = $finder;
|
$this->finder = $finder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $query
|
||||||
|
* @param integer $limit
|
||||||
|
* @param array $options
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
public function find($query, $limit = null, $options = array())
|
public function find($query, $limit = null, $options = array())
|
||||||
{
|
{
|
||||||
return $this->finder->find($query, $limit, $options);
|
return $this->finder->find($query, $limit, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $query
|
||||||
|
* @param integer $limit
|
||||||
|
* @param array $options
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
public function findHybrid($query, $limit = null, $options = array())
|
public function findHybrid($query, $limit = null, $options = array())
|
||||||
{
|
{
|
||||||
return $this->finder->findHybrid($query, $limit, $options);
|
return $this->finder->findHybrid($query, $limit, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $query
|
||||||
|
* @param array $options
|
||||||
|
*
|
||||||
|
* @return \Pagerfanta\Pagerfanta
|
||||||
|
*/
|
||||||
public function findPaginated($query, $options = array())
|
public function findPaginated($query, $options = array())
|
||||||
{
|
{
|
||||||
return $this->finder->findPaginated($query, $options);
|
return $this->finder->findPaginated($query, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $query
|
||||||
|
* @param array $options
|
||||||
|
*
|
||||||
|
* @return Paginator\PaginatorAdapterInterface
|
||||||
|
*/
|
||||||
public function createPaginatorAdapter($query, $options = array())
|
public function createPaginatorAdapter($query, $options = array())
|
||||||
{
|
{
|
||||||
return $this->finder->createPaginatorAdapter($query, $options);
|
return $this->finder->createPaginatorAdapter($query, $options);
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@
|
||||||
<argument type="service" id="fos_elastica.index_manager" />
|
<argument type="service" id="fos_elastica.index_manager" />
|
||||||
<argument type="service" id="fos_elastica.alias_processor" />
|
<argument type="service" id="fos_elastica.alias_processor" />
|
||||||
<argument type="service" id="fos_elastica.mapping_builder" />
|
<argument type="service" id="fos_elastica.mapping_builder" />
|
||||||
|
<argument type="service" id="event_dispatcher"/>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<!-- Abstract definition for all finders. -->
|
<!-- Abstract definition for all finders. -->
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,35 @@
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
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">
|
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>
|
<services>
|
||||||
<service id="fos_elastica.provider.prototype.mongodb" class="FOS\ElasticaBundle\Doctrine\MongoDB\Provider" public="true" abstract="true">
|
<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 /> <!-- object persister -->
|
||||||
<argument type="service" id="fos_elastica.indexable" />
|
<argument type="service" id="fos_elastica.indexable" />
|
||||||
<argument /> <!-- model -->
|
<argument /> <!-- model -->
|
||||||
<argument type="collection" /> <!-- options -->
|
<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>
|
||||||
|
|
||||||
<service id="fos_elastica.listener.prototype.mongodb" class="FOS\ElasticaBundle\Doctrine\Listener" public="false" abstract="true">
|
<service id="fos_elastica.listener.prototype.mongodb" class="%fos_elastica.listener.prototype.mongodb.class%" public="false" abstract="true">
|
||||||
<argument /> <!-- object persister -->
|
<argument /> <!-- object persister -->
|
||||||
<argument type="collection" /> <!-- events -->
|
|
||||||
<argument type="service" id="fos_elastica.indexable" />
|
<argument type="service" id="fos_elastica.indexable" />
|
||||||
<argument type="collection" /> <!-- configuration -->
|
<argument type="collection" /> <!-- configuration -->
|
||||||
<argument /> <!-- logger -->
|
<argument>null</argument> <!-- logger -->
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="fos_elastica.elastica_to_model_transformer.prototype.mongodb" class="FOS\ElasticaBundle\Doctrine\MongoDB\ElasticaToModelTransformer" public="false" abstract="true">
|
<service id="fos_elastica.elastica_to_model_transformer.prototype.mongodb" class="%fos_elastica.elastica_to_model_transformer.prototype.mongodb.class%" public="false" abstract="true">
|
||||||
<argument type="service" id="doctrine_mongodb" />
|
<argument type="service" id="doctrine_mongodb" />
|
||||||
<argument /> <!-- model -->
|
<argument /> <!-- model -->
|
||||||
<argument type="collection" /> <!-- options -->
|
<argument type="collection" /> <!-- options -->
|
||||||
|
|
@ -30,7 +41,7 @@
|
||||||
</call>
|
</call>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="fos_elastica.manager.mongodb" class="FOS\ElasticaBundle\Doctrine\RepositoryManager">
|
<service id="fos_elastica.manager.mongodb" class="%fos_elastica.manager.mongodb.class%">
|
||||||
<argument type="service" id="doctrine_mongodb"/>
|
<argument type="service" id="doctrine_mongodb"/>
|
||||||
<argument type="service" id="annotation_reader"/>
|
<argument type="service" id="annotation_reader"/>
|
||||||
</service>
|
</service>
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,35 @@
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
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">
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
|
<parameters>
|
||||||
|
<parameter key="fos_elastica.slice_fetcher.orm.class">FOS\ElasticaBundle\Doctrine\ORM\SliceFetcher</parameter>
|
||||||
|
<parameter key="fos_elastica.provider.prototype.orm.class">FOS\ElasticaBundle\Doctrine\ORM\Provider</parameter>
|
||||||
|
<parameter key="fos_elastica.listener.prototype.orm.class">FOS\ElasticaBundle\Doctrine\Listener</parameter>
|
||||||
|
<parameter key="fos_elastica.elastica_to_model_transformer.prototype.orm.class">FOS\ElasticaBundle\Doctrine\ORM\ElasticaToModelTransformer</parameter>
|
||||||
|
<parameter key="fos_elastica.manager.orm.class">FOS\ElasticaBundle\Doctrine\RepositoryManager</parameter>
|
||||||
|
</parameters>
|
||||||
|
|
||||||
<services>
|
<services>
|
||||||
<service id="fos_elastica.provider.prototype.orm" class="FOS\ElasticaBundle\Doctrine\ORM\Provider" public="true" abstract="true">
|
<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 /> <!-- object persister -->
|
||||||
<argument type="service" id="fos_elastica.indexable" />
|
<argument type="service" id="fos_elastica.indexable" />
|
||||||
<argument /> <!-- model -->
|
<argument /> <!-- model -->
|
||||||
<argument type="collection" /> <!-- options -->
|
<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>
|
||||||
|
|
||||||
<service id="fos_elastica.listener.prototype.orm" class="FOS\ElasticaBundle\Doctrine\Listener" public="false" abstract="true">
|
<service id="fos_elastica.listener.prototype.orm" class="%fos_elastica.listener.prototype.orm.class%" public="false" abstract="true">
|
||||||
<argument /> <!-- object persister -->
|
<argument /> <!-- object persister -->
|
||||||
<argument type="collection" /> <!-- events -->
|
|
||||||
<argument type="service" id="fos_elastica.indexable" />
|
<argument type="service" id="fos_elastica.indexable" />
|
||||||
<argument type="collection" /> <!-- configuration -->
|
<argument type="collection" /> <!-- configuration -->
|
||||||
<argument on-invalid="ignore" /> <!-- logger -->
|
<argument>null</argument> <!-- logger -->
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="fos_elastica.elastica_to_model_transformer.prototype.orm" class="FOS\ElasticaBundle\Doctrine\ORM\ElasticaToModelTransformer" public="false" abstract="true">
|
<service id="fos_elastica.elastica_to_model_transformer.prototype.orm" class="%fos_elastica.elastica_to_model_transformer.prototype.orm.class%" public="false" abstract="true">
|
||||||
<argument type="service" id="doctrine" />
|
<argument type="service" id="doctrine" />
|
||||||
<argument /> <!-- model -->
|
<argument /> <!-- model -->
|
||||||
<argument type="collection" /> <!-- options -->
|
<argument type="collection" /> <!-- options -->
|
||||||
|
|
@ -30,7 +41,7 @@
|
||||||
</call>
|
</call>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="fos_elastica.manager.orm" class="FOS\ElasticaBundle\Doctrine\RepositoryManager">
|
<service id="fos_elastica.manager.orm" class="%fos_elastica.manager.orm.class%">
|
||||||
<argument type="service" id="doctrine" />
|
<argument type="service" id="doctrine" />
|
||||||
<argument type="service" id="annotation_reader" />
|
<argument type="service" id="annotation_reader" />
|
||||||
</service>
|
</service>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@
|
||||||
|
|
||||||
<services>
|
<services>
|
||||||
<service id="fos_elastica.model_to_elastica_transformer" class="%fos_elastica.model_to_elastica_transformer.class%" public="false" abstract="true">
|
<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">
|
<call method="setPropertyAccessor">
|
||||||
<argument type="service" id="fos_elastica.property_accessor" />
|
<argument type="service" id="fos_elastica.property_accessor" />
|
||||||
</call>
|
</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
|
methods for particular searches. These need to extend `FOS\ElasticaBundle\Repository` to have
|
||||||
access to the finder:
|
access to the finder:
|
||||||
|
|
||||||
```
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Acme\ElasticaBundle\SearchRepository;
|
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:
|
To use the custom repository specify it in the mapping for the entity:
|
||||||
|
|
||||||
fos_elastica:
|
```yaml
|
||||||
clients:
|
fos_elastica:
|
||||||
default: { host: localhost, port: 9200 }
|
clients:
|
||||||
indexes:
|
default: { host: localhost, port: 9200 }
|
||||||
website:
|
indexes:
|
||||||
client: default
|
website:
|
||||||
types:
|
client: default
|
||||||
user:
|
types:
|
||||||
mappings:
|
user:
|
||||||
# your mappings
|
mappings:
|
||||||
persistence:
|
# your mappings
|
||||||
driver: orm
|
persistence:
|
||||||
model: Application\UserBundle\Entity\User
|
driver: orm
|
||||||
provider: ~
|
model: Application\UserBundle\Entity\User
|
||||||
finder: ~
|
provider: ~
|
||||||
repository: Acme\ElasticaBundle\SearchRepository\UserRepository
|
finder: ~
|
||||||
|
repository: Acme\ElasticaBundle\SearchRepository\UserRepository
|
||||||
|
```
|
||||||
|
|
||||||
Then the custom queries will be available when using the repository returned from the manager:
|
Then the custom queries will be available when using the repository returned from the manager:
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
```php
|
||||||
$repositoryManager = $container->get('fos_elastica.manager');
|
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
||||||
|
$repositoryManager = $container->get('fos_elastica.manager');
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Repository */
|
/** var FOS\ElasticaBundle\Repository */
|
||||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
$repository = $repositoryManager->getRepository('UserBundle:User');
|
||||||
|
|
||||||
/** var array of Acme\UserBundle\Entity\User */
|
/** var array of Acme\UserBundle\Entity\User */
|
||||||
$users = $repository->findWithCustomQuery('bob');
|
$users = $repository->findWithCustomQuery('bob');
|
||||||
|
```
|
||||||
|
|
||||||
Alternatively you can specify the custom repository using an annotation in the entity:
|
Alternatively you can specify the custom repository using an annotation in the entity:
|
||||||
|
|
||||||
```
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Application\UserBundle\Entity;
|
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
|
# app/config/config.yml
|
||||||
services:
|
services:
|
||||||
acme.search_provider.user:
|
acme.search_provider.user:
|
||||||
class: Acme\UserBundle\Search\UserProvider
|
class: Acme\UserBundle\Provider\UserProvider
|
||||||
arguments:
|
arguments:
|
||||||
- @fos_elastica.index.website.user
|
- @fos_elastica.index.website.user
|
||||||
tags:
|
tags:
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,11 @@ fos_elastica:
|
||||||
connections:
|
connections:
|
||||||
- url: http://es1.example.net:9200
|
- url: http://es1.example.net:9200
|
||||||
- url: http://es2.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
|
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\Exception\ExceptionInterface;
|
||||||
use Elastica\Request;
|
use Elastica\Request;
|
||||||
use Elastica\Response;
|
use Elastica\Response;
|
||||||
use FOS\ElasticaBundle\Client as BaseClient;
|
use FOS\ElasticaBundle\Elastica\Client as BaseClient;
|
||||||
|
|
||||||
class Client extends BaseClient
|
class Client extends BaseClient
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ Cookbook Entries
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
* [Aliased Indexes](cookbook/aliased-indexes.md)
|
* [Aliased Indexes](cookbook/aliased-indexes.md)
|
||||||
|
* [Custom Indexed Properties](cookbook/custom-properties.md)
|
||||||
* [Custom Repositories](cookbook/custom-repositories.md)
|
* [Custom Repositories](cookbook/custom-repositories.md)
|
||||||
* [HTTP Headers for Elastica](cookbook/elastica-client-http-headers.md)
|
* [HTTP Headers for Elastica](cookbook/elastica-client-http-headers.md)
|
||||||
* Performance - [Logging](cookbook/logging.md)
|
* Performance - [Logging](cookbook/logging.md)
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,50 @@
|
||||||
Step 1: Setting up the bundle
|
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
|
```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
|
### Elasticsearch
|
||||||
|
|
||||||
Instructions for installing and deploying Elasticsearch may be found
|
Instructions for installing and deploying Elasticsearch may be found [here](https://www.elastic.co/downloads/elasticsearch).
|
||||||
[here](http://www.elasticsearch.org/guide/reference/setup/installation/).
|
|
||||||
|
|
||||||
|
Step 2: Enable the Bundle
|
||||||
|
-------------------------
|
||||||
|
|
||||||
B) Enable FOSElasticaBundle
|
Then, enable the bundle by adding the following line in the `app/AppKernel.php`
|
||||||
---------------------------
|
file of your project:
|
||||||
|
|
||||||
Enable FOSElasticaBundle in your AppKernel:
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
// app/AppKernel.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
|
The basic minimal configuration for FOSElasticaBundle is one client with one Elasticsearch
|
||||||
|
|
@ -48,27 +58,30 @@ fos_elastica:
|
||||||
clients:
|
clients:
|
||||||
default: { host: localhost, port: 9200 }
|
default: { host: localhost, port: 9200 }
|
||||||
indexes:
|
indexes:
|
||||||
search: ~
|
app: ~
|
||||||
```
|
```
|
||||||
|
|
||||||
In this example, an Elastica index (an instance of `Elastica\Index`) is available as a
|
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
|
You may want the index `app` to be named something else on ElasticSearch depending on
|
||||||
application, for example, renaming the search index based on different environments.
|
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
|
```yaml
|
||||||
#app/config/config.yml
|
#app/config/config.yml
|
||||||
fos_elastica:
|
fos_elastica:
|
||||||
indexes:
|
indexes:
|
||||||
search:
|
app:
|
||||||
index_name: search_dev
|
index_name: app_%kernel.environment%
|
||||||
```
|
```
|
||||||
|
|
||||||
In this case, the service `fos_elastica.index.search` will be using an Elasticsearch
|
In this case, the service `fos_elastica.index.app` will relate to an ElasticSearch index
|
||||||
index of search_dev.
|
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.
|
By default, FOSElasticaBundle requires each type that is to be indexed to be mapped.
|
||||||
|
|
@ -81,7 +94,7 @@ will end up being indexed.
|
||||||
```yaml
|
```yaml
|
||||||
fos_elastica:
|
fos_elastica:
|
||||||
indexes:
|
indexes:
|
||||||
search:
|
app:
|
||||||
types:
|
types:
|
||||||
user:
|
user:
|
||||||
mappings:
|
mappings:
|
||||||
|
|
@ -92,7 +105,7 @@ fos_elastica:
|
||||||
```
|
```
|
||||||
|
|
||||||
Each defined type is made available as a service, and in this case the service key is
|
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
|
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
|
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
|
There are a significant number of options available for types, that can be
|
||||||
[found here](types.md)
|
[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
|
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
|
The command will also create all indexes and types defined if they do not already exist
|
||||||
on the Elasticsearch server.
|
on the Elasticsearch server.
|
||||||
|
|
||||||
F) Usage
|
F: Usage
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Usage documentation for the bundle is available [here](usage.md)
|
Usage documentation for the bundle is available [here](usage.md)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,34 @@
|
||||||
Type configuration
|
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
|
Handling missing results with FOSElasticaBundle
|
||||||
-----------------------------------------------
|
-----------------------------------------------
|
||||||
|
|
||||||
|
|
@ -173,13 +201,18 @@ index enabled users.
|
||||||
The callback option supports multiple approaches:
|
The callback option supports multiple approaches:
|
||||||
|
|
||||||
* A method on the object itself provided as a string. `enabled` will call
|
* 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
|
* 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
|
and only argument. `[ @my_custom_service, 'userIndexable' ]` will call the userIndexable
|
||||||
method on a service defined as my_custom_service.
|
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
|
* 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' ]`
|
the object as the only argument. `[ 'Acme\DemoBundle\IndexableChecker', 'isIndexable' ]`
|
||||||
will call Acme\DemoBundle\IndexableChecker::isIndexable($object)
|
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
|
* If you have the ExpressionLanguage component installed, A valid ExpressionLanguage
|
||||||
expression provided as a string. The object being indexed will be supplied as `object`
|
expression provided as a string. The object being indexed will be supplied as `object`
|
||||||
in the expression. `object.isEnabled() or object.shouldBeIndexedAnyway()`. For more
|
in the expression. `object.isEnabled() or object.shouldBeIndexedAnyway()`. For more
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,9 @@ $userPaginator = $finder->findPaginated('bob');
|
||||||
$countOfResults = $userPaginator->getNbResults();
|
$countOfResults = $userPaginator->getNbResults();
|
||||||
|
|
||||||
// Option 3b. KnpPaginator resultset
|
// Option 3b. KnpPaginator resultset
|
||||||
|
$paginator = $this->get('knp_paginator');
|
||||||
|
$results = $finder->createPaginatorAdapter('bob');
|
||||||
|
$pagination = $paginator->paginate($results, $page, 10);
|
||||||
```
|
```
|
||||||
|
|
||||||
Faceted Searching
|
Faceted Searching
|
||||||
|
|
@ -160,7 +162,7 @@ fos_elastica:
|
||||||
site:
|
site:
|
||||||
settings:
|
settings:
|
||||||
index:
|
index:
|
||||||
analysis:
|
analysis:
|
||||||
analyzer:
|
analyzer:
|
||||||
my_analyzer:
|
my_analyzer:
|
||||||
type: snowball
|
type: snowball
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1 KiB |
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
{% block menu %}
|
{% block menu %}
|
||||||
<span class="label">
|
<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>
|
<strong>Elastica</strong>
|
||||||
<span class="count">
|
<span class="count">
|
||||||
<span>{{ collector.querycount }}</span>
|
<span>{{ collector.querycount }}</span>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use JMS\Serializer\SerializerInterface;
|
||||||
class Callback
|
class Callback
|
||||||
{
|
{
|
||||||
protected $serializer;
|
protected $serializer;
|
||||||
protected $groups;
|
protected $groups = array();
|
||||||
protected $version;
|
protected $version;
|
||||||
|
|
||||||
public function setSerializer($serializer)
|
public function setSerializer($serializer)
|
||||||
|
|
@ -23,10 +23,8 @@ class Callback
|
||||||
{
|
{
|
||||||
$this->groups = $groups;
|
$this->groups = $groups;
|
||||||
|
|
||||||
if ($this->groups) {
|
if (!empty($this->groups) && !$this->serializer instanceof SerializerInterface) {
|
||||||
if (!$this->serializer instanceof SerializerInterface) {
|
throw new \RuntimeException('Setting serialization groups requires using "JMS\Serializer\Serializer".');
|
||||||
throw new \RuntimeException('Setting serialization groups requires using "JMS\Serializer\Serializer".');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,10 +32,8 @@ class Callback
|
||||||
{
|
{
|
||||||
$this->version = $version;
|
$this->version = $version;
|
||||||
|
|
||||||
if ($this->version) {
|
if ($this->version && !$this->serializer instanceof SerializerInterface) {
|
||||||
if (!$this->serializer instanceof SerializerInterface) {
|
throw new \RuntimeException('Setting serialization version requires using "JMS\Serializer\Serializer".');
|
||||||
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();
|
$context = $this->serializer instanceof SerializerInterface ? SerializationContext::create()->enableMaxDepthChecks() : array();
|
||||||
|
|
||||||
if ($this->groups) {
|
if (!empty($this->groups)) {
|
||||||
$context->setGroups($this->groups);
|
$context->setGroups($this->groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,13 +32,17 @@ class PaginateElasticaQuerySubscriber implements EventSubscriberInterface
|
||||||
if (null != $facets) {
|
if (null != $facets) {
|
||||||
$event->setCustomPaginationParameter('facets', $facets);
|
$event->setCustomPaginationParameter('facets', $facets);
|
||||||
}
|
}
|
||||||
|
$aggregations = $results->getAggregations();
|
||||||
|
if (null != $aggregations) {
|
||||||
|
$event->setCustomPaginationParameter('aggregations', $aggregations);
|
||||||
|
}
|
||||||
|
|
||||||
$event->stopPropagation();
|
$event->stopPropagation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds knp paging sort to query
|
* Adds knp paging sort to query.
|
||||||
*
|
*
|
||||||
* @param ItemsEvent $event
|
* @param ItemsEvent $event
|
||||||
*/
|
*/
|
||||||
|
|
@ -70,7 +74,7 @@ class PaginateElasticaQuerySubscriber implements EventSubscriberInterface
|
||||||
public static function getSubscribedEvents()
|
public static function getSubscribedEvents()
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
'knp_pager.items' => array('items', 1)
|
'knp_pager.items' => array('items', 1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace FOS\ElasticaBundle\Tests\Command;
|
namespace FOS\ElasticaBundle\Tests\Command;
|
||||||
|
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Command\ResetCommand;
|
use FOS\ElasticaBundle\Command\ResetCommand;
|
||||||
use Symfony\Component\Console\Input\ArrayInput;
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
use Symfony\Component\Console\Output\NullOutput;
|
use Symfony\Component\Console\Output\NullOutput;
|
||||||
|
|
@ -10,8 +9,8 @@ use Symfony\Component\DependencyInjection\Container;
|
||||||
|
|
||||||
class ResetCommandTest extends \PHPUnit_Framework_TestCase
|
class ResetCommandTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
|
private $command;
|
||||||
private $resetter;
|
private $resetter;
|
||||||
|
|
||||||
private $indexManager;
|
private $indexManager;
|
||||||
|
|
||||||
public function setup()
|
public function setup()
|
||||||
|
|
@ -88,4 +87,4 @@ class ResetCommandTest extends \PHPUnit_Framework_TestCase
|
||||||
new NullOutput()
|
new NullOutput()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use FOS\ElasticaBundle\DependencyInjection\Configuration;
|
||||||
use Symfony\Component\Config\Definition\Processor;
|
use Symfony\Component\Config\Definition\Processor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ConfigurationTest
|
* ConfigurationTest.
|
||||||
*/
|
*/
|
||||||
class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
|
|
@ -34,7 +34,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertSame(array(
|
$this->assertSame(array(
|
||||||
'clients' => array(),
|
'clients' => array(),
|
||||||
'indexes' => array(),
|
'indexes' => array(),
|
||||||
'default_manager' => 'orm'
|
'default_manager' => 'orm',
|
||||||
), $configuration);
|
), $configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,18 +50,18 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
||||||
array(
|
array(
|
||||||
'url' => 'http://es1:9200',
|
'url' => 'http://es1:9200',
|
||||||
'headers' => array(
|
'headers' => array(
|
||||||
'Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
|
'Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==',
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'url' => 'http://es2:9200',
|
'url' => 'http://es2:9200',
|
||||||
'headers' => array(
|
'headers' => array(
|
||||||
'Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
|
'Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==',
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
$this->assertCount(2, $configuration['clients']);
|
$this->assertCount(2, $configuration['clients']);
|
||||||
|
|
@ -91,9 +91,9 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
||||||
),
|
),
|
||||||
'logging_custom' => array(
|
'logging_custom' => array(
|
||||||
'url' => 'http://localhost:9200',
|
'url' => 'http://localhost:9200',
|
||||||
'logger' => 'custom.service'
|
'logger' => 'custom.service',
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
$this->assertCount(4, $configuration['clients']);
|
$this->assertCount(4, $configuration['clients']);
|
||||||
|
|
@ -131,8 +131,8 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
||||||
),
|
),
|
||||||
'serializer' => array(
|
'serializer' => array(
|
||||||
'groups' => array('Search'),
|
'groups' => array('Search'),
|
||||||
'version' => 1
|
'version' => 1,
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
'types' => array(
|
'types' => array(
|
||||||
'test' => array(
|
'test' => array(
|
||||||
|
|
@ -144,20 +144,20 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
||||||
'persistence' => array(
|
'persistence' => array(
|
||||||
'listener' => array(
|
'listener' => array(
|
||||||
'logger' => true,
|
'logger' => true,
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
'test2' => array(
|
'test2' => array(
|
||||||
'mappings' => array(
|
'mappings' => array(
|
||||||
'title' => null,
|
'title' => null,
|
||||||
'children' => array(
|
'children' => array(
|
||||||
'type' => 'nested',
|
'type' => 'nested',
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,7 +169,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
||||||
'host' => 'localhost',
|
'host' => 'localhost',
|
||||||
'port' => 9200,
|
'port' => 9200,
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
$this->assertTrue(empty($configuration['clients']['default']['connections'][0]['url']));
|
$this->assertTrue(empty($configuration['clients']['default']['connections'][0]['url']));
|
||||||
|
|
@ -189,16 +189,34 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
||||||
'title' => array(),
|
'title' => array(),
|
||||||
'published' => array('type' => 'datetime'),
|
'published' => array('type' => 'datetime'),
|
||||||
'body' => null,
|
'body' => null,
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
$this->assertCount(3, $configuration['indexes']['test']['types']['test']['properties']);
|
$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()
|
public function testNestedProperties()
|
||||||
{
|
{
|
||||||
$this->getConfigs(array(
|
$this->getConfigs(array(
|
||||||
|
|
@ -225,23 +243,23 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
||||||
'type' => 'nested',
|
'type' => 'nested',
|
||||||
'properties' => array(
|
'properties' => array(
|
||||||
'nested_field1' => array(
|
'nested_field1' => array(
|
||||||
'type' => 'integer'
|
'type' => 'integer',
|
||||||
),
|
),
|
||||||
'nested_field2' => array(
|
'nested_field2' => array(
|
||||||
'type' => 'object',
|
'type' => 'object',
|
||||||
'properties' => array(
|
'properties' => array(
|
||||||
'id' => 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';
|
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()
|
public function testIgnoreMissingOptionDuringTransformHybrid()
|
||||||
{
|
{
|
||||||
|
|
@ -55,10 +55,6 @@ class AbstractElasticaToModelTransformerTest extends \PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
protected function setUp()
|
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')
|
$this->registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
->getMock();
|
->getMock();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace FOS\ElasticaBundle\Tests\Doctrine;
|
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>
|
* @author Richard Miller <info@limethinking.co.uk>
|
||||||
*/
|
*/
|
||||||
|
|
@ -16,7 +16,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
||||||
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
||||||
$indexable = $this->getMockIndexable('index', 'type', $entity, true);
|
$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);
|
$listener->postPersist($eventArgs);
|
||||||
|
|
||||||
$this->assertEquals($entity, current($listener->scheduledForInsertion));
|
$this->assertEquals($entity, current($listener->scheduledForInsertion));
|
||||||
|
|
@ -35,7 +35,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
||||||
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
||||||
$indexable = $this->getMockIndexable('index', 'type', $entity, false);
|
$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);
|
$listener->postPersist($eventArgs);
|
||||||
|
|
||||||
$this->assertEmpty($listener->scheduledForInsertion);
|
$this->assertEmpty($listener->scheduledForInsertion);
|
||||||
|
|
@ -55,7 +55,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
||||||
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
||||||
$indexable = $this->getMockIndexable('index', 'type', $entity, true);
|
$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);
|
$listener->postUpdate($eventArgs);
|
||||||
|
|
||||||
$this->assertEquals($entity, current($listener->scheduledForUpdate));
|
$this->assertEquals($entity, current($listener->scheduledForUpdate));
|
||||||
|
|
@ -89,7 +89,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
||||||
->with($entity, 'id')
|
->with($entity, 'id')
|
||||||
->will($this->returnValue($entity->getId()));
|
->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);
|
$listener->postUpdate($eventArgs);
|
||||||
|
|
||||||
$this->assertEmpty($listener->scheduledForUpdate);
|
$this->assertEmpty($listener->scheduledForUpdate);
|
||||||
|
|
@ -124,7 +124,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
||||||
->with($entity, 'id')
|
->with($entity, 'id')
|
||||||
->will($this->returnValue($entity->getId()));
|
->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);
|
$listener->preRemove($eventArgs);
|
||||||
|
|
||||||
$this->assertEquals($entity->getId(), current($listener->scheduledForDeletion));
|
$this->assertEquals($entity->getId(), current($listener->scheduledForDeletion));
|
||||||
|
|
@ -157,7 +157,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
||||||
->with($entity, 'identifier')
|
->with($entity, 'identifier')
|
||||||
->will($this->returnValue($entity->getId()));
|
->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);
|
$listener->preRemove($eventArgs);
|
||||||
|
|
||||||
$this->assertEquals($entity->identifier, current($listener->scheduledForDeletion));
|
$this->assertEquals($entity->identifier, current($listener->scheduledForDeletion));
|
||||||
|
|
@ -173,8 +173,14 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
abstract protected function getListenerClass();
|
abstract protected function getListenerClass();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
abstract protected function getObjectManagerClass();
|
abstract protected function getObjectManagerClass();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
abstract protected function getClassMetadataClass();
|
abstract protected function getClassMetadataClass();
|
||||||
|
|
||||||
private function createLifecycleEventArgs()
|
private function createLifecycleEventArgs()
|
||||||
|
|
@ -205,6 +211,11 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
||||||
->getMock();
|
->getMock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Listener\Entity $object
|
||||||
|
* @param string $indexName
|
||||||
|
* @param string $typeName
|
||||||
|
*/
|
||||||
private function getMockPersister($object, $indexName, $typeName)
|
private function getMockPersister($object, $indexName, $typeName)
|
||||||
{
|
{
|
||||||
$mock = $this->getMockBuilder('FOS\ElasticaBundle\Persister\ObjectPersister')
|
$mock = $this->getMockBuilder('FOS\ElasticaBundle\Persister\ObjectPersister')
|
||||||
|
|
@ -235,6 +246,12 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
||||||
return $mock;
|
return $mock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $indexName
|
||||||
|
* @param string $typeName
|
||||||
|
* @param Listener\Entity $object
|
||||||
|
* @param boolean $return
|
||||||
|
*/
|
||||||
private function getMockIndexable($indexName, $typeName, $object, $return = null)
|
private function getMockIndexable($indexName, $typeName, $object, $return = null)
|
||||||
{
|
{
|
||||||
$mock = $this->getMock('FOS\ElasticaBundle\Provider\IndexableInterface');
|
$mock = $this->getMock('FOS\ElasticaBundle\Provider\IndexableInterface');
|
||||||
|
|
@ -255,7 +272,11 @@ namespace FOS\ElasticaBundle\Tests\Doctrine\Listener;
|
||||||
class Entity
|
class Entity
|
||||||
{
|
{
|
||||||
private $id;
|
private $id;
|
||||||
|
public $identifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param integer $id
|
||||||
|
*/
|
||||||
public function __construct($id)
|
public function __construct($id)
|
||||||
{
|
{
|
||||||
$this->id = $id;
|
$this->id = $id;
|
||||||
|
|
@ -266,4 +287,3 @@ class Entity
|
||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,10 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
private $options;
|
private $options;
|
||||||
private $managerRegistry;
|
private $managerRegistry;
|
||||||
private $indexable;
|
private $indexable;
|
||||||
|
private $sliceFetcher;
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
if (!interface_exists('Doctrine\Common\Persistence\ManagerRegistry')) {
|
|
||||||
$this->markTestSkipped('Doctrine Common is not available.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->objectClass = 'objectClass';
|
$this->objectClass = 'objectClass';
|
||||||
$this->options = array('debug_logging' => true, 'indexName' => 'index', 'typeName' => 'type');
|
$this->options = array('debug_logging' => true, 'indexName' => 'index', 'typeName' => 'type');
|
||||||
|
|
||||||
|
|
@ -32,6 +29,8 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
->method('getManagerForClass')
|
->method('getManagerForClass')
|
||||||
->with($this->objectClass)
|
->with($this->objectClass)
|
||||||
->will($this->returnValue($this->objectManager));
|
->will($this->returnValue($this->objectManager));
|
||||||
|
|
||||||
|
$this->sliceFetcher = $this->getMockSliceFetcher();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -45,6 +44,53 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
$queryBuilder = new \stdClass();
|
$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())
|
$provider->expects($this->once())
|
||||||
->method('createQueryBuilder')
|
->method('createQueryBuilder')
|
||||||
->will($this->returnValue($queryBuilder));
|
->will($this->returnValue($queryBuilder));
|
||||||
|
|
@ -84,7 +130,7 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
return array(
|
return array(
|
||||||
array(
|
array(
|
||||||
100,
|
100,
|
||||||
array(range(1,100)),
|
array(range(1, 100)),
|
||||||
100,
|
100,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
|
|
@ -107,8 +153,8 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
->method('countObjects')
|
->method('countObjects')
|
||||||
->will($this->returnValue($nbObjects));
|
->will($this->returnValue($nbObjects));
|
||||||
|
|
||||||
$provider->expects($this->any())
|
$this->sliceFetcher->expects($this->any())
|
||||||
->method('fetchSlice')
|
->method('fetch')
|
||||||
->will($this->returnValue($objects));
|
->will($this->returnValue($objects));
|
||||||
|
|
||||||
$this->indexable->expects($this->any())
|
$this->indexable->expects($this->any())
|
||||||
|
|
@ -122,6 +168,32 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
$provider->populate();
|
$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()
|
public function testPopulateInvokesLoggerClosure()
|
||||||
{
|
{
|
||||||
$nbObjects = 1;
|
$nbObjects = 1;
|
||||||
|
|
@ -133,8 +205,8 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
->method('countObjects')
|
->method('countObjects')
|
||||||
->will($this->returnValue($nbObjects));
|
->will($this->returnValue($nbObjects));
|
||||||
|
|
||||||
$provider->expects($this->any())
|
$this->sliceFetcher->expects($this->any())
|
||||||
->method('fetchSlice')
|
->method('fetch')
|
||||||
->will($this->returnValue($objects));
|
->will($this->returnValue($objects));
|
||||||
|
|
||||||
$this->indexable->expects($this->any())
|
$this->indexable->expects($this->any())
|
||||||
|
|
@ -165,8 +237,8 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
->method('countObjects')
|
->method('countObjects')
|
||||||
->will($this->returnValue($nbObjects));
|
->will($this->returnValue($nbObjects));
|
||||||
|
|
||||||
$provider->expects($this->any())
|
$this->sliceFetcher->expects($this->any())
|
||||||
->method('fetchSlice')
|
->method('fetch')
|
||||||
->will($this->returnValue($objects));
|
->will($this->returnValue($objects));
|
||||||
|
|
||||||
$this->indexable->expects($this->any())
|
$this->indexable->expects($this->any())
|
||||||
|
|
@ -180,7 +252,7 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
$this->setExpectedException('Elastica\Exception\Bulk\ResponseException');
|
$this->setExpectedException('Elastica\Exception\Bulk\ResponseException');
|
||||||
|
|
||||||
$provider->populate(null, array('ignore-errors' => false));
|
$provider->populate(null, array('ignore_errors' => false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPopulateRunsIndexCallable()
|
public function testPopulateRunsIndexCallable()
|
||||||
|
|
@ -192,8 +264,9 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
$provider->expects($this->any())
|
$provider->expects($this->any())
|
||||||
->method('countObjects')
|
->method('countObjects')
|
||||||
->will($this->returnValue($nbObjects));
|
->will($this->returnValue($nbObjects));
|
||||||
$provider->expects($this->any())
|
|
||||||
->method('fetchSlice')
|
$this->sliceFetcher->expects($this->any())
|
||||||
|
->method('fetch')
|
||||||
->will($this->returnValue($objects));
|
->will($this->returnValue($objects));
|
||||||
|
|
||||||
$this->indexable->expects($this->at(0))
|
$this->indexable->expects($this->at(0))
|
||||||
|
|
@ -205,18 +278,19 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
->with('index', 'type', 2)
|
->with('index', 'type', 2)
|
||||||
->will($this->returnValue(true));
|
->will($this->returnValue(true));
|
||||||
|
|
||||||
|
|
||||||
$this->objectPersister->expects($this->once())
|
$this->objectPersister->expects($this->once())
|
||||||
->method('insertMany')
|
->method('insertMany')
|
||||||
->with(array(1 => 2));
|
->with(array(2));
|
||||||
|
|
||||||
$provider->populate();
|
$provider->populate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param boolean $setSliceFetcher Whether or not to set the slice fetcher.
|
||||||
|
*
|
||||||
* @return \FOS\ElasticaBundle\Doctrine\AbstractProvider|\PHPUnit_Framework_MockObject_MockObject
|
* @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(
|
return $this->getMockForAbstractClass('FOS\ElasticaBundle\Doctrine\AbstractProvider', array(
|
||||||
$this->objectPersister,
|
$this->objectPersister,
|
||||||
|
|
@ -224,6 +298,7 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->objectClass,
|
$this->objectClass,
|
||||||
$this->options,
|
$this->options,
|
||||||
$this->managerRegistry,
|
$this->managerRegistry,
|
||||||
|
$setSliceFetcher ? $this->sliceFetcher : null
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,7 +308,7 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
private function getMockBulkResponseException()
|
private function getMockBulkResponseException()
|
||||||
{
|
{
|
||||||
return $this->getMock('Elastica\Exception\Bulk\ResponseException', null, array(
|
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()
|
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 $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
|
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()
|
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')
|
$this->registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
->getMock();
|
->getMock();
|
||||||
|
|
@ -109,7 +102,7 @@ class ElasticaToModelTransformerTest extends \PHPUnit_Framework_TestCase
|
||||||
'findAll',
|
'findAll',
|
||||||
'findBy',
|
'findBy',
|
||||||
'findOneBy',
|
'findOneBy',
|
||||||
'getClassName'
|
'getClassName',
|
||||||
));
|
));
|
||||||
|
|
||||||
$this->manager->expects($this->any())
|
$this->manager->expects($this->any())
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,6 @@ use FOS\ElasticaBundle\Tests\Doctrine\ListenerTest as BaseListenerTest;
|
||||||
|
|
||||||
class ListenerTest extends BaseListenerTest
|
class ListenerTest extends BaseListenerTest
|
||||||
{
|
{
|
||||||
public function setUp()
|
|
||||||
{
|
|
||||||
if (!class_exists('Doctrine\ORM\EntityManager')) {
|
|
||||||
$this->markTestSkipped('Doctrine ORM is not available.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getClassMetadataClass()
|
protected function getClassMetadataClass()
|
||||||
{
|
{
|
||||||
return 'Doctrine\ORM\Mapping\ClassMetadata';
|
return 'Doctrine\ORM\Mapping\ClassMetadata';
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,19 @@ namespace FOS\ElasticaBundle\Tests\Doctrine;
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Doctrine\RepositoryManager;
|
use FOS\ElasticaBundle\Doctrine\RepositoryManager;
|
||||||
|
|
||||||
class CustomRepository{}
|
class CustomRepository
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
class Entity{}
|
class Entity
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Richard Miller <info@limethinking.co.uk>
|
* @author Richard Miller <info@limethinking.co.uk>
|
||||||
*/
|
*/
|
||||||
class RepositoryManagerTest extends \PHPUnit_Framework_TestCase
|
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()
|
public function testThatGetRepositoryReturnsDefaultRepository()
|
||||||
{
|
{
|
||||||
/** @var $finderMock \PHPUnit_Framework_MockObject_MockObject|\FOS\ElasticaBundle\Finder\TransformedFinder */
|
/** @var $finderMock \PHPUnit_Framework_MockObject_MockObject|\FOS\ElasticaBundle\Finder\TransformedFinder */
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@ namespace FOS\ElasticaBundle\Tests\Client;
|
||||||
use Elastica\Request;
|
use Elastica\Request;
|
||||||
use Elastica\Transport\Null as NullTransport;
|
use Elastica\Transport\Null as NullTransport;
|
||||||
|
|
||||||
class LoggingClientTest extends \PHPUnit_Framework_TestCase
|
class ClientTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
public function testRequestsAreLogged()
|
public function testRequestsAreLogged()
|
||||||
{
|
{
|
||||||
$transport = new NullTransport;
|
$transport = new NullTransport();
|
||||||
|
|
||||||
$connection = $this->getMock('Elastica\Connection');
|
$connection = $this->getMock('Elastica\Connection');
|
||||||
$connection->expects($this->any())->method('getTransportObject')->will($this->returnValue($transport));
|
$connection->expects($this->any())->method('getTransportObject')->will($this->returnValue($transport));
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
namespace FOS\ElasticaBundle\Tests\Resetter;
|
namespace FOS\ElasticaBundle\Tests\Resetter;
|
||||||
|
|
||||||
use FOS\ElasticaBundle\FOSElasticaBundle;
|
use FOS\ElasticaBundle\FOSElasticaBundle;
|
||||||
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
|
|
||||||
|
|
||||||
class FOSElasticaBundleTest extends \PHPUnit_Framework_TestCase
|
class FOSElasticaBundleTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
|
|
@ -17,7 +16,6 @@ class FOSElasticaBundleTest extends \PHPUnit_Framework_TestCase
|
||||||
->method('addCompilerPass')
|
->method('addCompilerPass')
|
||||||
->with($this->isInstanceOf('Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface'));
|
->with($this->isInstanceOf('Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface'));
|
||||||
|
|
||||||
|
|
||||||
$bundle = new FOSElasticaBundle();
|
$bundle = new FOSElasticaBundle();
|
||||||
$bundle->build($container);
|
$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
|
* @param Client $client
|
||||||
|
*
|
||||||
* @return \FOS\ElasticaBundle\Configuration\ConfigManager
|
* @return \FOS\ElasticaBundle\Configuration\ConfigManager
|
||||||
*/
|
*/
|
||||||
private function getManager(Client $client)
|
private function getManager(Client $client)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ namespace FOS\ElasticaBundle\Tests\Functional;
|
||||||
class IndexableCallbackTest extends WebTestCase
|
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
|
* 1) To test that the configuration rename from is_indexable_callback under the listener
|
||||||
* key is respected, and
|
* key is respected, and
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue