Compare commits
353 commits
Author | SHA1 | Date | |
---|---|---|---|
Simon Vieille | 30e351b1d5 | ||
Simon Vieille | e3abbdc700 | ||
4451bd07c6 | |||
1f8a330140 | |||
8f7f24e6d3 | |||
e71ec4ac8a | |||
adf7fb21e3 | |||
1287d9f0df | |||
c5728b5870 | |||
7baf494c56 | |||
ad20382e08 | |||
e933a49d07 | |||
69470d7e20 | |||
b6e01cd332 | |||
35276f469a | |||
ec9f23bd8d | |||
ae4cfd7e04 | |||
49a0c22724 | |||
8d8b04ead8 | |||
a59f2015b4 | |||
447d29ab9c | |||
73093beadb | |||
5181b02933 | |||
ac98549eb5 | |||
9c1c771799 | |||
3bb2f384ba | |||
d4f01e8d2e | |||
72a9dfa267 | |||
9cf0117c71 | |||
bb4618c101 | |||
6a07f7b24e | |||
dd3269d1ef | |||
84e5831a81 | |||
d5a9b7b235 | |||
4af9f442fd | |||
4081c32ca0 | |||
925410a66e | |||
b6c252aac3 | |||
133f71b88a | |||
c013ed9657 | |||
f72c51503a | |||
4e087af50d | |||
559b14b4a5 | |||
2215d07ff8 | |||
89db88c2a0 | |||
345b5d423d | |||
a8f41fa5ef | |||
e796d6179b | |||
dd388e4b25 | |||
81186e40db | |||
14af748840 | |||
19e9abaa53 | |||
5b88baeca6 | |||
b7c7f77383 | |||
181b5a0ac0 | |||
0009c858a7 | |||
f834499a2c | |||
3d69c08b5a | |||
cbb247978a | |||
a830a9b7b6 | |||
7b90c84daa | |||
034b3a9c60 | |||
cf9f7c6be8 | |||
4a564401b4 | |||
ef2671dd36 | |||
47785260a4 | |||
cb7b4c1dca | |||
4c87d24fc1 | |||
f6df88cc67 | |||
7fa14f713c | |||
55bfee22e8 | |||
07995d9b75 | |||
72a981ab51 | |||
1be1cb645c | |||
70fe702ccf | |||
3c87cc16c7 | |||
c5185a0307 | |||
e1d5ef72d2 | |||
797d066286 | |||
72589f8341 | |||
2401b1083c | |||
8e627ee011 | |||
6992beeb47 | |||
7bc88494a1 | |||
30d52bf0f0 | |||
58eed2dc7f | |||
d44525f6f3 | |||
67c0b79505 | |||
6bb2def21e | |||
e5d9c3ddbb | |||
906e2e0749 | |||
81f5f983c0 | |||
030b194c7b | |||
7f28be3c4e | |||
79e263d7a7 | |||
e772ca6450 | |||
55abe132c6 | |||
aef5940578 | |||
b9b0c1b961 | |||
64fa26e3d9 | |||
c4a2858265 | |||
7471c13d75 | |||
c901d60552 | |||
401446e1c4 | |||
1cea135dc5 | |||
32d190f554 | |||
5060fa4d4a | |||
b4c01f3641 | |||
1bf59d5b66 | |||
9188566dfe | |||
d7e9d9b8a6 | |||
a28b9d3069 | |||
c2c87a53e4 | |||
2ce2d7e610 | |||
905265ea0e | |||
ee9f7e5297 | |||
a0f11ff36f | |||
7c90660e02 | |||
7efcdad97c | |||
afbe1e03a1 | |||
303af508b2 | |||
c39b86c6c5 | |||
b6d46dba4a | |||
9f5ce217dc | |||
6ef6092f3f | |||
92aab4bcf6 | |||
e361b7c53b | |||
7fac93ff8b | |||
d731443aa5 | |||
156884527c | |||
5eaff9e61b | |||
3975ed3d5b | |||
d33e064801 | |||
1f7acc563a | |||
eaa32cbf22 | |||
f3e31e613e | |||
97848ca0d0 | |||
88e9f5aac6 | |||
ca57c42489 | |||
2664fec35e | |||
c45dcd955d | |||
7dc2f833c4 | |||
e211f31658 | |||
00df6c586f | |||
921377d5ed | |||
c4ee9fa83e | |||
b09bf3cf10 | |||
1e2da2d84f | |||
2d8903a330 | |||
e6d50c584c | |||
2a7459f327 | |||
c80b4efd3e | |||
1369a01dd7 | |||
901ea49a32 | |||
1b01aef46f | |||
be75b387a5 | |||
445f2f93f6 | |||
e7634d8ba2 | |||
419bf2ccf6 | |||
6f4e389dfd | |||
196aed6630 | |||
7c6fe4eaab | |||
d2de7ba6e8 | |||
197bb3ebad | |||
f9ce1dcd4e | |||
4c4e9ffe36 | |||
7fa7e44bee | |||
67ae044309 | |||
25d56d0a0f | |||
a88e4e38f8 | |||
5cdeac9b45 | |||
b3f87e414f | |||
71a86cada5 | |||
d0ce82ac2a | |||
428a1014ca | |||
1d5fe44ca4 | |||
cf586a4ef4 | |||
c4210a5c6d | |||
029ebb153a | |||
736163551c | |||
76dcd2f62e | |||
2958833012 | |||
6bea3c2154 | |||
001daeeac0 | |||
a7a23b92cb | |||
598a59927e | |||
0425379420 | |||
380727afbe | |||
69c2214bc5 | |||
af5fb7e97e | |||
22a2a223cc | |||
c08d86124a | |||
33ee047f83 | |||
49c5b2ee63 | |||
27385046ca | |||
517d6e679c | |||
420135dc85 | |||
20033709cf | |||
229d4cb982 | |||
84cf6c79c2 | |||
9d45de8b64 | |||
f5987a48b9 | |||
dafe8abe0e | |||
0d22c20d37 | |||
f9eb6577d1 | |||
c44f676224 | |||
9296534d30 | |||
f6e018f011 | |||
9a5b80e723 | |||
eaa6c2e085 | |||
64c5c19831 | |||
9befa90f41 | |||
659468ae3a | |||
001b38cf59 | |||
1ef55b1239 | |||
c748ec64e9 | |||
11ee25cfea | |||
e5410a5b65 | |||
9fbc622929 | |||
fad481d822 | |||
714502fa1f | |||
7cedc5ba5a | |||
d797af5b80 | |||
cdaf7105e0 | |||
d88d96bf55 | |||
bf38e59e49 | |||
90a554f627 | |||
d57d430ab3 | |||
be4af0d1af | |||
425aa3d3e1 | |||
21e5d906a7 | |||
815b836cdc | |||
55dcf6859a | |||
c200e8fdfd | |||
965b319d82 | |||
7fcbb64a15 | |||
5d65676659 | |||
c9a24436f3 | |||
9f85db9876 | |||
78db0b9b63 | |||
ad37a28356 | |||
474cbfa979 | |||
77f2b99a3e | |||
5cc8c2978f | |||
96dc613c71 | |||
4eacb5f4c8 | |||
a879d3c1c9 | |||
ffa73db1d2 | |||
4d52c327aa | |||
524474fdc6 | |||
ae03b3f3cf | |||
f6264f4149 | |||
2437a098ba | |||
ae3605828e | |||
2a20bb623c | |||
9a62187329 | |||
ca6991d494 | |||
95e445bd0d | |||
c97f0f1ddf | |||
4e990e0cee | |||
b0749afaf1 | |||
3ae382c933 | |||
949ea6963f | |||
b155f304e4 | |||
afbaf875b9 | |||
8905b4248c | |||
5cbb8ce1b6 | |||
b3c0d4fd44 | |||
ec5c05e8be | |||
e78950ddb7 | |||
5f335c37ab | |||
1e07d3c7fe | |||
089e7f0d2e | |||
aafb8c8e89 | |||
e225d841ed | |||
b49437529c | |||
94568d9554 | |||
2e23f4a1bf | |||
02d864f7e2 | |||
12797c60fa | |||
934c6af8b8 | |||
0383811834 | |||
ada3942576 | |||
813a4a5d26 | |||
7682d5a80a | |||
64be10447d | |||
629ca0df2e | |||
391e18dcbf | |||
e54cc3c243 | |||
66d2410999 | |||
f5932a8e47 | |||
fe19df365a | |||
14083496d7 | |||
5009673b6a | |||
833feee207 | |||
f6b9e57a9c | |||
366fb39606 | |||
8540f13bbf | |||
fa65784b47 | |||
1dc6856ef9 | |||
6a822504bc | |||
c52c32fb56 | |||
2e0aa064a2 | |||
12b724dd20 | |||
8060d3dcd7 | |||
dad15d0b38 | |||
53180e2810 | |||
8e88505a3f | |||
a79fa0242e | |||
c38dc107e7 | |||
be89ccf825 | |||
f97e66712a | |||
5a84d55129 | |||
f9745c8d21 | |||
a9ea78443f | |||
458b53240b | |||
e77aa0c180 | |||
f20392d78b | |||
f8a445b46c | |||
d532e6b1e3 | |||
3addfffc91 | |||
18143449cc | |||
b09c7fb50e | |||
352e3b68ac | |||
41c4d77b20 | |||
843c76b6ca | |||
6d2b7a8367 | |||
57fbc70015 | |||
2029aba76a | |||
6253d3f8df | |||
28d0ee925d | |||
2c208a4f10 | |||
70ad5c9b37 | |||
e1bbb87cfe | |||
6748c9c623 | |||
72e7b77dae | |||
1c5339ac40 | |||
e5754ef5fc | |||
6bbe61f319 | |||
b0841c18ec | |||
62f6cf0f8a | |||
5da8ee1a16 | |||
b1d64e358d | |||
eaa9f83997 | |||
ae02364e7c | |||
ca50617776 | |||
d9f3fa1a59 | |||
c93bbb9081 | |||
39f1033a34 | |||
90abc44968 | |||
c9fd1cc5d9 | |||
2bd6aba7ef | |||
f15ca02859 |
5
.scrutinizer.yml
Normal file
5
.scrutinizer.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
imports:
|
||||
- php
|
||||
|
||||
tools:
|
||||
external_code_coverage: true
|
29
.travis.yml
29
.travis.yml
|
@ -1,9 +1,36 @@
|
|||
language: php
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.5
|
||||
env: SYMFONY_VERSION='2.3.*'
|
||||
- php: 5.5
|
||||
env: SYMFONY_VERSION='2.5.*'
|
||||
|
||||
before_script:
|
||||
- echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
|
||||
- /usr/share/elasticsearch/bin/elasticsearch -v
|
||||
- sudo /usr/share/elasticsearch/bin/plugin -install elasticsearch/elasticsearch-mapper-attachments/2.0.0
|
||||
- sudo service elasticsearch restart
|
||||
- sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
|
||||
- sh -c 'if [ "$SYMFONY_VERSION" != "" ]; then composer require --dev --no-update symfony/symfony=$SYMFONY_VERSION; fi;'
|
||||
- composer install --dev --prefer-source
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit --coverage-clover=coverage.clover
|
||||
|
||||
services:
|
||||
- elasticsearch
|
||||
|
||||
after_script:
|
||||
- wget https://scrutinizer-ci.com/ocular.phar
|
||||
- php ocular.phar code-coverage:upload --format=php-clover coverage.clover
|
||||
|
|
16
Annotation/Search.php
Normal file
16
Annotation/Search.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Annotation;
|
||||
|
||||
/**
|
||||
* Annotation class for setting search repository.
|
||||
*
|
||||
* @author Richard Miller <info@limethinking.co.uk>
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
class Search
|
||||
{
|
||||
/** @var string */
|
||||
public $repositoryClass;
|
||||
}
|
|
@ -12,13 +12,61 @@ https://github.com/FriendsOfSymfony/FOSElasticaBundle/compare/v3.0.0...v3.0.1
|
|||
To generate a changelog summary since the last version, run
|
||||
`git log --no-merges --oneline v3.0.0...3.0.x`
|
||||
|
||||
* 3.0.0-ALPHA3 (xxxx-xx-xx)
|
||||
* 3.0.9 (2015-03-12)
|
||||
|
||||
* Fix a bug in the BC layer of the type configuration for empty configs
|
||||
* Fix the service definition for the Doctrine listener when the logger is not enabled
|
||||
|
||||
* 3.0.8 (2014-01-31)
|
||||
|
||||
* Fixed handling of empty indexes #760
|
||||
* Added support for `connectionStrategy` Elastica configuration #732
|
||||
* Allow Elastica 1.4
|
||||
|
||||
* 3.0.7 (2015-01-21)
|
||||
|
||||
* Fixed the indexing of parent/child relations, broken since 3.0 #774
|
||||
* Fixed multi_field properties not being normalised #769
|
||||
|
||||
* 3.0.6 (2015-01-04)
|
||||
|
||||
* Removed unused public image asset for the web development toolbar #742
|
||||
* Fixed is_indexable_callback BC code to support array notation #761
|
||||
* Fixed debug_logger for type providers #724
|
||||
* Clean the OM if we filter away the entire batch #737
|
||||
|
||||
* 3.0.0-ALPHA6
|
||||
|
||||
* Moved `is_indexable_callback` from the listener properties to a type property called
|
||||
`indexable_callback` which is run when both populating and listening for object
|
||||
changes.
|
||||
* AbstractProvider constructor change: Second argument is now an `IndexableInterface`
|
||||
instance.
|
||||
* Annotation @Search moved to FOS\ElasticaBundle\Annotation\Search with FOS\ElasticaBundle\Configuration\Search deprecated
|
||||
* Deprecated FOS\ElasticaBundle\Client in favour of FOS\ElasticaBundle\Elastica\Client
|
||||
* Deprecated FOS\ElasticaBundle\DynamicIndex in favour of FOS\ElasticaBundle\Elastica\Index
|
||||
* Deprecated FOS\ElasticaBundle\IndexManager in favour of FOS\ElasticaBundle\Index\IndexManager
|
||||
* Deprecated FOS\ElasticaBundle\Resetter in favour of FOS\ElasticaBundle\Index\Resetter
|
||||
|
||||
* 3.0.0-ALPHA5 (2014-05-23)
|
||||
|
||||
* Doctrine Provider speed up by disabling persistence logging while populating documents
|
||||
|
||||
* 3.0.0-ALPHA4 (2014-04-10)
|
||||
|
||||
* Indexes are now capable of logging errors with Elastica
|
||||
* Fixed deferred indexing of deleted documents
|
||||
* Resetting an index will now create it even if it doesn't exist
|
||||
* Bulk upserting of documents is now supported when populating
|
||||
|
||||
* 3.0.0-ALPHA3 (2014-04-01)
|
||||
|
||||
* a9c4c93: Logger is now only enabled in debug mode by default
|
||||
* #463: allowing hot swappable reindexing
|
||||
* #415: BC BREAK: document indexing occurs in postFlush rather than the pre* events previously.
|
||||
* 7d13823: Dropped (broken) support for Symfony <2.3
|
||||
* #496: Added support for HTTP headers
|
||||
* #528: FOSElasticaBundle will disable Doctrine logging when populating for a large increase in speed
|
||||
|
||||
* 3.0.0-ALPHA2 (2014-03-17)
|
||||
|
||||
|
|
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
|
38
Client.php
38
Client.php
|
@ -2,43 +2,11 @@
|
|||
|
||||
namespace FOS\ElasticaBundle;
|
||||
|
||||
use Elastica\Client as ElasticaClient;
|
||||
use Elastica\Request;
|
||||
use FOS\ElasticaBundle\Logger\ElasticaLogger;
|
||||
use FOS\ElasticaBundle\Elastica\Client as BaseClient;
|
||||
|
||||
/**
|
||||
* @author Gordon Franke <info@nevalon.de>
|
||||
* @deprecated Use \FOS\ElasticaBundle\Elastica\LoggingClient
|
||||
*/
|
||||
class Client extends ElasticaClient
|
||||
class Client extends BaseClient
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function request($path, $method = Request::GET, $data = array(), array $query = array())
|
||||
{
|
||||
$start = microtime(true);
|
||||
$response = parent::request($path, $method, $data, $query);
|
||||
|
||||
if (null !== $this->_logger and $this->_logger instanceof ElasticaLogger) {
|
||||
$time = microtime(true) - $start;
|
||||
|
||||
$connection = $this->getLastRequest()->getConnection();
|
||||
|
||||
$connection_array = array(
|
||||
'host' => $connection->getHost(),
|
||||
'port' => $connection->getPort(),
|
||||
'transport' => $connection->getTransport(),
|
||||
'headers' => $connection->getConfig('headers'),
|
||||
);
|
||||
|
||||
$this->_logger->logQuery($path, $method, $data, $time, $connection_array, $query);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function getIndex($name)
|
||||
{
|
||||
return new DynamicIndex($this, $name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Command;
|
||||
|
||||
use FOS\ElasticaBundle\Event\IndexPopulateEvent;
|
||||
use FOS\ElasticaBundle\Event\TypePopulateEvent;
|
||||
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
|
||||
use Symfony\Component\Console\Helper\DialogHelper;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
@ -10,18 +12,28 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||
use FOS\ElasticaBundle\IndexManager;
|
||||
use FOS\ElasticaBundle\Provider\ProviderRegistry;
|
||||
use FOS\ElasticaBundle\Resetter;
|
||||
use FOS\ElasticaBundle\Provider\ProviderInterface;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
|
||||
/**
|
||||
* Populate the search index
|
||||
* Populate the search index.
|
||||
*/
|
||||
class PopulateCommand extends ContainerAwareCommand
|
||||
{
|
||||
/**
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
/**
|
||||
* @var IndexManager
|
||||
*/
|
||||
private $indexManager;
|
||||
|
||||
/**
|
||||
* @var ProgressClosureBuilder
|
||||
*/
|
||||
private $progressClosureBuilder;
|
||||
|
||||
/**
|
||||
* @var ProviderRegistry
|
||||
*/
|
||||
|
@ -46,31 +58,46 @@ class PopulateCommand extends ContainerAwareCommand
|
|||
->addOption('sleep', null, InputOption::VALUE_REQUIRED, 'Sleep time between persisting iterations (microseconds)', 0)
|
||||
->addOption('batch-size', null, InputOption::VALUE_REQUIRED, 'Index packet size (overrides provider config option)')
|
||||
->addOption('ignore-errors', null, InputOption::VALUE_NONE, 'Do not stop on errors')
|
||||
->addOption('no-overwrite-format', null, InputOption::VALUE_NONE, 'Prevent this command from overwriting ProgressBar\'s formats')
|
||||
->setDescription('Populates search indexes from providers')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Symfony\Component\Console\Command\Command::initialize()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function initialize(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->dispatcher = $this->getContainer()->get('event_dispatcher');
|
||||
$this->indexManager = $this->getContainer()->get('fos_elastica.index_manager');
|
||||
$this->providerRegistry = $this->getContainer()->get('fos_elastica.provider_registry');
|
||||
$this->resetter = $this->getContainer()->get('fos_elastica.resetter');
|
||||
$this->progressClosureBuilder = new ProgressClosureBuilder();
|
||||
|
||||
if (!$input->getOption('no-overwrite-format') && class_exists('Symfony\\Component\\Console\\Helper\\ProgressBar')) {
|
||||
ProgressBar::setFormatDefinition('normal', " %current%/%max% [%bar%] %percent:3s%%\n%message%");
|
||||
ProgressBar::setFormatDefinition('verbose', " %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%\n%message%");
|
||||
ProgressBar::setFormatDefinition('very_verbose', " %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%\n%message%");
|
||||
ProgressBar::setFormatDefinition('debug', " %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%\n%message%");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Symfony\Component\Console\Command\Command::execute()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$index = $input->getOption('index');
|
||||
$type = $input->getOption('type');
|
||||
$reset = !$input->getOption('no-reset');
|
||||
$options = $input->getOptions();
|
||||
|
||||
$options['ignore-errors'] = $input->hasOption('ignore-errors');
|
||||
$index = $input->getOption('index');
|
||||
$type = $input->getOption('type');
|
||||
$reset = !$input->getOption('no-reset');
|
||||
$options = array(
|
||||
'ignore_errors' => $input->getOption('ignore-errors'),
|
||||
'offset' => $input->getOption('offset'),
|
||||
'sleep' => $input->getOption('sleep')
|
||||
);
|
||||
if ($input->getOption('batch-size')) {
|
||||
$options['batch_size'] = (int) $input->getOption('batch-size');
|
||||
}
|
||||
|
||||
if ($input->isInteractive() && $reset && $input->getOption('offset')) {
|
||||
/** @var DialogHelper $dialog */
|
||||
|
@ -109,25 +136,22 @@ class PopulateCommand extends ContainerAwareCommand
|
|||
*/
|
||||
private function populateIndex(OutputInterface $output, $index, $reset, $options)
|
||||
{
|
||||
if ($reset) {
|
||||
$event = new IndexPopulateEvent($index, $reset, $options);
|
||||
$this->dispatcher->dispatch(IndexPopulateEvent::PRE_INDEX_POPULATE, $event);
|
||||
|
||||
if ($event->isReset()) {
|
||||
$output->writeln(sprintf('<info>Resetting</info> <comment>%s</comment>', $index));
|
||||
$this->resetter->resetIndex($index);
|
||||
$this->resetter->resetIndex($index, true);
|
||||
}
|
||||
|
||||
/** @var $providers ProviderInterface[] */
|
||||
$providers = $this->providerRegistry->getIndexProviders($index);
|
||||
|
||||
foreach ($providers as $type => $provider) {
|
||||
$loggerClosure = function($message) use ($output, $index, $type) {
|
||||
$output->writeln(sprintf('<info>Populating</info> %s/%s, %s', $index, $type, $message));
|
||||
};
|
||||
|
||||
$provider->populate($loggerClosure, $options);
|
||||
$types = array_keys($this->providerRegistry->getIndexProviders($index));
|
||||
foreach ($types as $type) {
|
||||
$this->populateIndexType($output, $index, $type, false, $event->getOptions());
|
||||
}
|
||||
|
||||
$output->writeln(sprintf('<info>Refreshing</info> <comment>%s</comment>', $index));
|
||||
$this->resetter->postPopulate($index);
|
||||
$this->indexManager->getIndex($index)->refresh();
|
||||
$this->dispatcher->dispatch(IndexPopulateEvent::POST_INDEX_POPULATE, $event);
|
||||
|
||||
$this->refreshIndex($output, $index);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -141,17 +165,35 @@ class PopulateCommand extends ContainerAwareCommand
|
|||
*/
|
||||
private function populateIndexType(OutputInterface $output, $index, $type, $reset, $options)
|
||||
{
|
||||
if ($reset) {
|
||||
$event = new TypePopulateEvent($index, $type, $reset, $options);
|
||||
$this->dispatcher->dispatch(TypePopulateEvent::PRE_TYPE_POPULATE, $event);
|
||||
|
||||
if ($event->isReset()) {
|
||||
$output->writeln(sprintf('<info>Resetting</info> <comment>%s/%s</comment>', $index, $type));
|
||||
$this->resetter->resetIndexType($index, $type);
|
||||
}
|
||||
|
||||
$loggerClosure = function($message) use ($output, $index, $type) {
|
||||
$output->writeln(sprintf('<info>Populating</info> %s/%s, %s', $index, $type, $message));
|
||||
};
|
||||
|
||||
$provider = $this->providerRegistry->getProvider($index, $type);
|
||||
$provider->populate($loggerClosure, $options);
|
||||
$loggerClosure = $this->progressClosureBuilder->build($output, 'Populating', $index, $type);
|
||||
$provider->populate($loggerClosure, $event->getOptions());
|
||||
|
||||
$this->dispatcher->dispatch(TypePopulateEvent::POST_TYPE_POPULATE, $event);
|
||||
|
||||
$this->refreshIndex($output, $index, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes an index.
|
||||
*
|
||||
* @param OutputInterface $output
|
||||
* @param string $index
|
||||
* @param bool $postPopulate
|
||||
*/
|
||||
private function refreshIndex(OutputInterface $output, $index, $postPopulate = true)
|
||||
{
|
||||
if ($postPopulate) {
|
||||
$this->resetter->postPopulate($index);
|
||||
}
|
||||
|
||||
$output->writeln(sprintf('<info>Refreshing</info> <comment>%s</comment>', $index));
|
||||
$this->indexManager->getIndex($index)->refresh();
|
||||
|
|
103
Command/ProgressClosureBuilder.php
Normal file
103
Command/ProgressClosureBuilder.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Tim Nagel <tim@nagel.com.au>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ProgressClosureBuilder
|
||||
{
|
||||
/**
|
||||
* Builds a loggerClosure to be called from inside the Provider to update the command
|
||||
* line.
|
||||
*
|
||||
* @param OutputInterface $output
|
||||
* @param string $action
|
||||
* @param string $index
|
||||
* @param string $type
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public function build(OutputInterface $output, $action, $index, $type)
|
||||
{
|
||||
if (!class_exists('Symfony\Component\Console\Helper\ProgressBar') ||
|
||||
!is_callable(array('Symfony\Component\Console\Helper\ProgressBar', 'getProgress'))) {
|
||||
return $this->buildLegacy($output, $action, $index, $type);
|
||||
}
|
||||
|
||||
$progress = null;
|
||||
|
||||
return function ($increment, $totalObjects, $message = null) use (&$progress, $output, $action, $index, $type) {
|
||||
if (null === $progress) {
|
||||
$progress = new ProgressBar($output, $totalObjects);
|
||||
$progress->start();
|
||||
}
|
||||
|
||||
if (null !== $message) {
|
||||
$progress->clear();
|
||||
$output->writeln(sprintf('<info>%s</info> <error>%s</error>', $action, $message));
|
||||
$progress->display();
|
||||
}
|
||||
|
||||
$progress->setMessage(sprintf('<info>%s</info> <comment>%s/%s</comment>', $action, $index, $type));
|
||||
$progress->advance($increment);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a legacy closure that outputs lines for each step. Used in cases
|
||||
* where the ProgressBar component doesnt exist or does not have the correct
|
||||
* methods to support what we need.
|
||||
*
|
||||
* @param OutputInterface $output
|
||||
* @param string $action
|
||||
* @param string $index
|
||||
* @param string $type
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
private function buildLegacy(OutputInterface $output, $action, $index, $type)
|
||||
{
|
||||
$lastStep = null;
|
||||
$current = 0;
|
||||
|
||||
return function ($increment, $totalObjects, $message = null) use ($output, $action, $index, $type, &$lastStep, &$current) {
|
||||
if ($current + $increment > $totalObjects) {
|
||||
$increment = $totalObjects - $current;
|
||||
}
|
||||
|
||||
if (null !== $message) {
|
||||
$output->writeln(sprintf('<info>%s</info> <error>%s</error>', $action, $message));
|
||||
}
|
||||
|
||||
$currentTime = microtime(true);
|
||||
$timeDifference = $currentTime - $lastStep;
|
||||
$objectsPerSecond = $lastStep ? ($increment / $timeDifference) : $increment;
|
||||
$lastStep = $currentTime;
|
||||
$current += $increment;
|
||||
$percent = 100 * $current / $totalObjects;
|
||||
|
||||
$output->writeln(sprintf(
|
||||
'<info>%s</info> <comment>%s/%s</comment> %0.1f%% (%d/%d), %d objects/s (RAM: current=%uMo peak=%uMo)',
|
||||
$action,
|
||||
$index,
|
||||
$type,
|
||||
$percent,
|
||||
$current,
|
||||
$totalObjects,
|
||||
$objectsPerSecond,
|
||||
round(memory_get_usage() / (1024 * 1024)),
|
||||
round(memory_get_peak_usage() / (1024 * 1024))
|
||||
));
|
||||
};
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ use FOS\ElasticaBundle\IndexManager;
|
|||
use FOS\ElasticaBundle\Resetter;
|
||||
|
||||
/**
|
||||
* Reset search indexes
|
||||
* Reset search indexes.
|
||||
*/
|
||||
class ResetCommand extends ContainerAwareCommand
|
||||
{
|
||||
|
@ -33,6 +33,7 @@ class ResetCommand extends ContainerAwareCommand
|
|||
->setName('fos:elastica:reset')
|
||||
->addOption('index', null, InputOption::VALUE_OPTIONAL, 'The index 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')
|
||||
;
|
||||
}
|
||||
|
@ -51,8 +52,9 @@ class ResetCommand extends ContainerAwareCommand
|
|||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$index = $input->getOption('index');
|
||||
$type = $input->getOption('type');
|
||||
$index = $input->getOption('index');
|
||||
$type = $input->getOption('type');
|
||||
$force = (bool) $input->getOption('force');
|
||||
|
||||
if (null === $index && null !== $type) {
|
||||
throw new \InvalidArgumentException('Cannot specify type option without an index.');
|
||||
|
@ -69,7 +71,7 @@ class ResetCommand extends ContainerAwareCommand
|
|||
|
||||
foreach ($indexes as $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;
|
||||
|
||||
/**
|
||||
* Searches a type
|
||||
* Searches a type.
|
||||
*/
|
||||
class SearchCommand extends ContainerAwareCommand
|
||||
{
|
||||
|
|
64
Configuration/ConfigManager.php
Normal file
64
Configuration/ConfigManager.php
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?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\Configuration;
|
||||
|
||||
/**
|
||||
* Central manager for index and type configuration.
|
||||
*/
|
||||
class ConfigManager implements ManagerInterface
|
||||
{
|
||||
/**
|
||||
* @var IndexConfig[]
|
||||
*/
|
||||
private $indexes = array();
|
||||
|
||||
/**
|
||||
* @param Source\SourceInterface[] $sources
|
||||
*/
|
||||
public function __construct(array $sources)
|
||||
{
|
||||
foreach ($sources as $source) {
|
||||
$this->indexes = array_merge($source->getConfiguration(), $this->indexes);
|
||||
}
|
||||
}
|
||||
|
||||
public function getIndexConfiguration($indexName)
|
||||
{
|
||||
if (!$this->hasIndexConfiguration($indexName)) {
|
||||
throw new \InvalidArgumentException(sprintf('Index with name "%s" is not configured.', $indexName));
|
||||
}
|
||||
|
||||
return $this->indexes[$indexName];
|
||||
}
|
||||
|
||||
public function getIndexNames()
|
||||
{
|
||||
return array_keys($this->indexes);
|
||||
}
|
||||
|
||||
public function getTypeConfiguration($indexName, $typeName)
|
||||
{
|
||||
$index = $this->getIndexConfiguration($indexName);
|
||||
$type = $index->getType($typeName);
|
||||
|
||||
if (!$type) {
|
||||
throw new \InvalidArgumentException(sprintf('Type with name "%s" on index "%s" is not configured', $typeName, $indexName));
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
public function hasIndexConfiguration($indexName)
|
||||
{
|
||||
return isset($this->indexes[$indexName]);
|
||||
}
|
||||
}
|
124
Configuration/IndexConfig.php
Normal file
124
Configuration/IndexConfig.php
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?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\Configuration;
|
||||
|
||||
class IndexConfig
|
||||
{
|
||||
/**
|
||||
* The name of the index for ElasticSearch.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $elasticSearchName;
|
||||
|
||||
/**
|
||||
* The internal name of the index. May not be the same as the name used in ElasticSearch,
|
||||
* especially if aliases are enabled.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* An array of settings sent to ElasticSearch when creating the index.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $settings;
|
||||
|
||||
/**
|
||||
* All types that belong to this index.
|
||||
*
|
||||
* @var TypeConfig[]
|
||||
*/
|
||||
private $types;
|
||||
|
||||
/**
|
||||
* Indicates if the index should use an alias, allowing an index repopulation to occur
|
||||
* without overwriting the current index.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $useAlias = false;
|
||||
|
||||
/**
|
||||
* Constructor expects an array as generated by the Container Configuration builder.
|
||||
*
|
||||
* @param string $name
|
||||
* @param TypeConfig[] $types
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct($name, array $types, array $config)
|
||||
{
|
||||
$this->elasticSearchName = isset($config['elasticSearchName']) ? $config['elasticSearchName'] : $name;
|
||||
$this->name = $name;
|
||||
$this->settings = isset($config['settings']) ? $config['settings'] : array();
|
||||
$this->types = $types;
|
||||
$this->useAlias = isset($config['useAlias']) ? $config['useAlias'] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getElasticSearchName()
|
||||
{
|
||||
return $this->elasticSearchName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSettings()
|
||||
{
|
||||
return $this->settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $typeName
|
||||
*
|
||||
* @return TypeConfig
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getType($typeName)
|
||||
{
|
||||
if (!array_key_exists($typeName, $this->types)) {
|
||||
throw new \InvalidArgumentException(sprintf('Type "%s" does not exist on index "%s"', $typeName, $this->name));
|
||||
}
|
||||
|
||||
return $this->types[$typeName];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \FOS\ElasticaBundle\Configuration\TypeConfig[]
|
||||
*/
|
||||
public function getTypes()
|
||||
{
|
||||
return $this->types;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function isUseAlias()
|
||||
{
|
||||
return $this->useAlias;
|
||||
}
|
||||
}
|
44
Configuration/ManagerInterface.php
Normal file
44
Configuration/ManagerInterface.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?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\Configuration;
|
||||
|
||||
/**
|
||||
* Central manager for index and type configuration.
|
||||
*/
|
||||
interface ManagerInterface
|
||||
{
|
||||
/**
|
||||
* Returns configuration for an index.
|
||||
*
|
||||
* @param $index
|
||||
*
|
||||
* @return IndexConfig
|
||||
*/
|
||||
public function getIndexConfiguration($index);
|
||||
|
||||
/**
|
||||
* Returns an array of known index names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getIndexNames();
|
||||
|
||||
/**
|
||||
* Returns a type configuration.
|
||||
*
|
||||
* @param string $index
|
||||
* @param string $type
|
||||
*
|
||||
* @return TypeConfig
|
||||
*/
|
||||
public function getTypeConfiguration($index, $type);
|
||||
}
|
|
@ -1,16 +1,26 @@
|
|||
<?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\Configuration;
|
||||
|
||||
use FOS\ElasticaBundle\Annotation\Search as BaseSearch;
|
||||
|
||||
/**
|
||||
* Annotation class for setting search repository.
|
||||
*
|
||||
* @author Richard Miller <info@limethinking.co.uk>
|
||||
* @Annotation
|
||||
*
|
||||
* @deprecated Use FOS\ElasticaBundle\Annotation\Search instead
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
class Search
|
||||
class Search extends BaseSearch
|
||||
{
|
||||
/** @var string */
|
||||
public $repositoryClass;
|
||||
}
|
||||
|
|
80
Configuration/Source/ContainerSource.php
Normal file
80
Configuration/Source/ContainerSource.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?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\Configuration\Source;
|
||||
|
||||
use FOS\ElasticaBundle\Configuration\IndexConfig;
|
||||
use FOS\ElasticaBundle\Configuration\TypeConfig;
|
||||
|
||||
/**
|
||||
* Returns index and type configuration from the container.
|
||||
*/
|
||||
class ContainerSource implements SourceInterface
|
||||
{
|
||||
/**
|
||||
* The internal container representation of information.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $configArray;
|
||||
|
||||
public function __construct(array $configArray)
|
||||
{
|
||||
$this->configArray = $configArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should return all configuration available from the data source.
|
||||
*
|
||||
* @return IndexConfig[]
|
||||
*/
|
||||
public function getConfiguration()
|
||||
{
|
||||
$indexes = array();
|
||||
foreach ($this->configArray as $config) {
|
||||
$types = $this->getTypes($config);
|
||||
$index = new IndexConfig($config['name'], $types, array(
|
||||
'elasticSearchName' => $config['elasticsearch_name'],
|
||||
'settings' => $config['settings'],
|
||||
'useAlias' => $config['use_alias'],
|
||||
));
|
||||
|
||||
$indexes[$config['name']] = $index;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
26
Configuration/Source/SourceInterface.php
Normal file
26
Configuration/Source/SourceInterface.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?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\Configuration\Source;
|
||||
|
||||
/**
|
||||
* Represents a source of index and type information (ie, the Container configuration or
|
||||
* annotations).
|
||||
*/
|
||||
interface SourceInterface
|
||||
{
|
||||
/**
|
||||
* Should return all configuration available from the data source.
|
||||
*
|
||||
* @return \FOS\ElasticaBundle\Configuration\IndexConfig[]
|
||||
*/
|
||||
public function getConfiguration();
|
||||
}
|
113
Configuration/TypeConfig.php
Normal file
113
Configuration/TypeConfig.php
Normal file
|
@ -0,0 +1,113 @@
|
|||
<?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\Configuration;
|
||||
|
||||
class TypeConfig
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $mapping;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
public function __construct($name, array $mapping, array $config = array())
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->mapping = $mapping;
|
||||
$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
|
||||
*/
|
||||
public function getIndexAnalyzer()
|
||||
{
|
||||
return $this->getConfig('index_analyzer');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMapping()
|
||||
{
|
||||
return $this->mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getModel()
|
||||
{
|
||||
return isset($this->config['persistence']['model']) ?
|
||||
$this->config['persistence']['model'] :
|
||||
null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|null
|
||||
*/
|
||||
public function getNumericDetection()
|
||||
{
|
||||
return $this->getConfig('numeric_detection');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getSearchAnalyzer()
|
||||
{
|
||||
return $this->getConfig('search_analyzer');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*/
|
||||
private function getConfig($key)
|
||||
{
|
||||
return isset($this->config[$key]) ?
|
||||
$this->config[$key] :
|
||||
null;
|
||||
}
|
||||
}
|
36
DependencyInjection/Compiler/ConfigSourcePass.php
Normal file
36
DependencyInjection/Compiler/ConfigSourcePass.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?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\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
class ConfigSourcePass implements CompilerPassInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('fos_elastica.config_manager')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sources = array();
|
||||
foreach (array_keys($container->findTaggedServiceIds('fos_elastica.config_source')) as $id) {
|
||||
$sources[] = new Reference($id);
|
||||
}
|
||||
|
||||
$container->getDefinition('fos_elastica.config_manager')->replaceArgument(0, $sources);
|
||||
}
|
||||
}
|
38
DependencyInjection/Compiler/IndexPass.php
Normal file
38
DependencyInjection/Compiler/IndexPass.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\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
class IndexPass implements CompilerPassInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('fos_elastica.index_manager')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$indexes = array();
|
||||
foreach ($container->findTaggedServiceIds('fos_elastica.index') as $id => $tags) {
|
||||
foreach ($tags as $tag) {
|
||||
$indexes[$tag['name']] = new Reference($id);
|
||||
}
|
||||
}
|
||||
|
||||
$container->getDefinition('fos_elastica.index_manager')->replaceArgument(0, $indexes);
|
||||
}
|
||||
}
|
|
@ -55,6 +55,7 @@ class RegisterProvidersPass implements CompilerPassInterface
|
|||
* Returns whether the class implements ProviderInterface.
|
||||
*
|
||||
* @param string $class
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function isProviderImplementation($class)
|
||||
|
|
|
@ -31,7 +31,7 @@ class TransformerPass implements CompilerPassInterface
|
|||
throw new InvalidArgumentException('The Transformer must have both a type and an index defined.');
|
||||
}
|
||||
|
||||
$transformers[$tag['index']][$tag['type']]= new Reference($id);
|
||||
$transformers[$tag['index']][$tag['type']] = new Reference($id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,17 +15,22 @@ class Configuration implements ConfigurationInterface
|
|||
*/
|
||||
private $supportedDrivers = array('orm', 'mongodb', 'propel');
|
||||
|
||||
private $configArray = array();
|
||||
/**
|
||||
* If the kernel is running in debug mode.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $debug;
|
||||
|
||||
public function __construct($configArray)
|
||||
public function __construct($debug)
|
||||
{
|
||||
$this->configArray = $configArray;
|
||||
$this->debug = $debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the configuration tree.
|
||||
*
|
||||
* @return \Symfony\Component\Config\Definition\NodeInterface
|
||||
* @return TreeBuilder
|
||||
*/
|
||||
public function getConfigTreeBuilder()
|
||||
{
|
||||
|
@ -58,17 +63,7 @@ class Configuration implements ConfigurationInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Generates the configuration tree.
|
||||
*
|
||||
* @return \Symfony\Component\DependencyInjection\Configuration\NodeInterface
|
||||
*/
|
||||
public function getConfigTree()
|
||||
{
|
||||
return $this->getConfigTreeBuilder()->buildTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the configuration for the "clients" key
|
||||
* Adds the configuration for the "clients" key.
|
||||
*/
|
||||
private function addClientsSection(ArrayNodeDefinition $rootNode)
|
||||
{
|
||||
|
@ -79,49 +74,52 @@ class Configuration implements ConfigurationInterface
|
|||
->useAttributeAsKey('id')
|
||||
->prototype('array')
|
||||
->performNoDeepMerging()
|
||||
// BC - Renaming 'servers' node to 'connections'
|
||||
->beforeNormalization()
|
||||
->ifTrue(function($v) { return isset($v['host']) && isset($v['port']); })
|
||||
->then(function($v) {
|
||||
return array(
|
||||
'servers' => array(
|
||||
array(
|
||||
'host' => $v['host'],
|
||||
'port' => $v['port'],
|
||||
'logger' => isset($v['logger']) ? $v['logger'] : null,
|
||||
'headers' => isset($v['headers']) ? $v['headers'] : null,
|
||||
)
|
||||
)
|
||||
);
|
||||
})
|
||||
->ifTrue(function ($v) { return isset($v['servers']); })
|
||||
->then(function ($v) {
|
||||
$v['connections'] = $v['servers'];
|
||||
unset($v['servers']);
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
// Elastica names its properties with camel case, support both
|
||||
->beforeNormalization()
|
||||
->ifTrue(function($v) { return isset($v['url']); })
|
||||
->then(function($v) {
|
||||
return array(
|
||||
'servers' => array(
|
||||
array(
|
||||
'url' => $v['url'],
|
||||
'logger' => isset($v['logger']) ? $v['logger'] : null
|
||||
)
|
||||
)
|
||||
);
|
||||
})
|
||||
->ifTrue(function ($v) { return isset($v['connection_strategy']); })
|
||||
->then(function ($v) {
|
||||
$v['connectionStrategy'] = $v['connection_strategy'];
|
||||
unset($v['connection_strategy']);
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
// If there is no connections array key defined, assume a single connection.
|
||||
->beforeNormalization()
|
||||
->ifTrue(function ($v) { return is_array($v) && !array_key_exists('connections', $v); })
|
||||
->then(function ($v) {
|
||||
return array(
|
||||
'connections' => array($v),
|
||||
);
|
||||
})
|
||||
->end()
|
||||
->children()
|
||||
->arrayNode('servers')
|
||||
->arrayNode('connections')
|
||||
->requiresAtLeastOneElement()
|
||||
->prototype('array')
|
||||
->fixXmlConfig('header')
|
||||
->children()
|
||||
->scalarNode('url')
|
||||
->validate()
|
||||
->ifTrue(function($url) { return substr($url, -1) !== '/'; })
|
||||
->then(function($url) { return $url.'/'; })
|
||||
->ifTrue(function ($url) { return $url && substr($url, -1) !== '/'; })
|
||||
->then(function ($url) { return $url.'/'; })
|
||||
->end()
|
||||
->end()
|
||||
->scalarNode('host')->end()
|
||||
->scalarNode('port')->end()
|
||||
->scalarNode('proxy')->end()
|
||||
->scalarNode('logger')
|
||||
->defaultValue('%kernel.debug%')
|
||||
->defaultValue($this->debug ? 'fos_elastica.logger' : false)
|
||||
->treatNullLike('fos_elastica.logger')
|
||||
->treatTrueLike('fos_elastica.logger')
|
||||
->end()
|
||||
|
@ -129,12 +127,14 @@ class Configuration implements ConfigurationInterface
|
|||
->useAttributeAsKey('name')
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->scalarNode('transport')->end()
|
||||
->scalarNode('timeout')->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->scalarNode('timeout')->end()
|
||||
->scalarNode('headers')->end()
|
||||
->scalarNode('connectionStrategy')->defaultValue('Simple')->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
|
@ -143,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)
|
||||
{
|
||||
|
@ -167,61 +167,8 @@ class Configuration implements ConfigurationInterface
|
|||
->children()
|
||||
->scalarNode('index_analyzer')->end()
|
||||
->scalarNode('search_analyzer')->end()
|
||||
->arrayNode('persistence')
|
||||
->validate()
|
||||
->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['listener']); })
|
||||
->thenInvalid('Propel doesn\'t support listeners')
|
||||
->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['repository']); })
|
||||
->thenInvalid('Propel doesn\'t support the "repository" parameter')
|
||||
->end()
|
||||
->children()
|
||||
->scalarNode('driver')
|
||||
->validate()
|
||||
->ifNotInArray($this->supportedDrivers)
|
||||
->thenInvalid('The driver %s is not supported. Please choose one of '.json_encode($this->supportedDrivers))
|
||||
->end()
|
||||
->end()
|
||||
->scalarNode('identifier')->defaultValue('id')->end()
|
||||
->arrayNode('provider')
|
||||
->children()
|
||||
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
|
||||
->scalarNode('batch_size')->defaultValue(100)->end()
|
||||
->scalarNode('clear_object_manager')->defaultTrue()->end()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('listener')
|
||||
->children()
|
||||
->scalarNode('insert')->defaultTrue()->end()
|
||||
->scalarNode('update')->defaultTrue()->end()
|
||||
->scalarNode('delete')->defaultTrue()->end()
|
||||
->scalarNode('persist')->defaultValue('postFlush')->end()
|
||||
->scalarNode('service')->end()
|
||||
->variableNode('is_indexable_callback')->defaultNull()->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('finder')
|
||||
->children()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('elastica_to_model_transformer')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->scalarNode('hydrate')->defaultTrue()->end()
|
||||
->scalarNode('ignore_missing')->defaultFalse()->end()
|
||||
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('model_to_elastica_transformer')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->append($this->getPersistenceNode())
|
||||
->append($this->getSerializerNode())
|
||||
->end()
|
||||
->end()
|
||||
->variableNode('settings')->defaultValue(array())->end()
|
||||
|
@ -245,84 +192,75 @@ class Configuration implements ConfigurationInterface
|
|||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->treatNullLike(array())
|
||||
->beforeNormalization()
|
||||
->ifNull()
|
||||
->thenEmptyArray()
|
||||
->end()
|
||||
// BC - Renaming 'mappings' node to 'properties'
|
||||
->beforeNormalization()
|
||||
->ifTrue(function ($v) { return array_key_exists('mappings', $v); })
|
||||
->then(function ($v) {
|
||||
$v['properties'] = $v['mappings'];
|
||||
unset($v['mappings']);
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
// BC - Support the old is_indexable_callback property
|
||||
->beforeNormalization()
|
||||
->ifTrue(function ($v) {
|
||||
return isset($v['persistence']) &&
|
||||
isset($v['persistence']['listener']) &&
|
||||
isset($v['persistence']['listener']['is_indexable_callback']);
|
||||
})
|
||||
->then(function ($v) {
|
||||
$callback = $v['persistence']['listener']['is_indexable_callback'];
|
||||
|
||||
if (is_array($callback)) {
|
||||
list($class) = $callback + array(null);
|
||||
|
||||
if ($class[0] !== '@' && is_string($class) && !class_exists($class)) {
|
||||
$callback[0] = '@'.$class;
|
||||
}
|
||||
}
|
||||
|
||||
$v['indexable_callback'] = $callback;
|
||||
unset($v['persistence']['listener']['is_indexable_callback']);
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
// Support multiple dynamic_template formats to match the old bundle style
|
||||
// and the way ElasticSearch expects them
|
||||
->beforeNormalization()
|
||||
->ifTrue(function ($v) { return isset($v['dynamic_templates']); })
|
||||
->then(function ($v) {
|
||||
$dt = array();
|
||||
foreach ($v['dynamic_templates'] as $key => $type) {
|
||||
if (is_int($key)) {
|
||||
$dt[] = $type;
|
||||
} else {
|
||||
$dt[][$key] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
$v['dynamic_templates'] = $dt;
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
->children()
|
||||
->arrayNode('serializer')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->arrayNode('groups')
|
||||
->treatNullLike(array())
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->scalarNode('version')->end()
|
||||
->end()
|
||||
->end()
|
||||
->booleanNode('date_detection')->end()
|
||||
->arrayNode('dynamic_date_formats')->prototype('scalar')->end()->end()
|
||||
->scalarNode('index_analyzer')->end()
|
||||
->booleanNode('numeric_detection')->end()
|
||||
->scalarNode('search_analyzer')->end()
|
||||
->arrayNode('persistence')
|
||||
->validate()
|
||||
->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['listener']); })
|
||||
->thenInvalid('Propel doesn\'t support listeners')
|
||||
->ifTrue(function($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['repository']); })
|
||||
->thenInvalid('Propel doesn\'t support the "repository" parameter')
|
||||
->end()
|
||||
->children()
|
||||
->scalarNode('driver')
|
||||
->validate()
|
||||
->ifNotInArray($this->supportedDrivers)
|
||||
->thenInvalid('The driver %s is not supported. Please choose one of '.json_encode($this->supportedDrivers))
|
||||
->end()
|
||||
->end()
|
||||
->scalarNode('model')->end()
|
||||
->scalarNode('repository')->end()
|
||||
->scalarNode('identifier')->defaultValue('id')->end()
|
||||
->arrayNode('provider')
|
||||
->children()
|
||||
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
|
||||
->scalarNode('batch_size')->defaultValue(100)->end()
|
||||
->scalarNode('clear_object_manager')->defaultTrue()->end()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('listener')
|
||||
->children()
|
||||
->scalarNode('insert')->defaultTrue()->end()
|
||||
->scalarNode('update')->defaultTrue()->end()
|
||||
->scalarNode('delete')->defaultTrue()->end()
|
||||
->booleanNode('immediate')->defaultFalse()->end()
|
||||
->scalarNode('logger')
|
||||
->defaultFalse()
|
||||
->treatNullLike('fos_elastica.logger')
|
||||
->treatTrueLike('fos_elastica.logger')
|
||||
->end()
|
||||
->scalarNode('service')->end()
|
||||
->variableNode('is_indexable_callback')->defaultNull()->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('finder')
|
||||
->children()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('elastica_to_model_transformer')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->scalarNode('hydrate')->defaultTrue()->end()
|
||||
->scalarNode('ignore_missing')->defaultFalse()->end()
|
||||
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('model_to_elastica_transformer')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->variableNode('indexable_callback')->end()
|
||||
->append($this->getPersistenceNode())
|
||||
->append($this->getSerializerNode())
|
||||
->end()
|
||||
->append($this->getIdNode())
|
||||
->append($this->getMappingsNode())
|
||||
->append($this->getPropertiesNode())
|
||||
->append($this->getDynamicTemplateNode())
|
||||
->append($this->getSourceNode())
|
||||
->append($this->getBoostNode())
|
||||
|
@ -338,27 +276,17 @@ class Configuration implements ConfigurationInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the array node used for "mappings".
|
||||
* Returns the array node used for "properties".
|
||||
*/
|
||||
protected function getMappingsNode()
|
||||
protected function getPropertiesNode()
|
||||
{
|
||||
$builder = new TreeBuilder();
|
||||
$node = $builder->root('mappings');
|
||||
$node = $builder->root('properties');
|
||||
|
||||
$nestings = $this->getNestings();
|
||||
|
||||
$childrenNode = $node
|
||||
$node
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->validate()
|
||||
->ifTrue(function($v) { return isset($v['fields']) && empty($v['fields']); })
|
||||
->then(function($v) { unset($v['fields']); return $v; })
|
||||
->end()
|
||||
->treatNullLike(array())
|
||||
->addDefaultsIfNotSet()
|
||||
->children();
|
||||
|
||||
$this->addFieldConfig($childrenNode, $nestings);
|
||||
->prototype('variable')
|
||||
->treatNullLike(array());
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
@ -372,209 +300,26 @@ class Configuration implements ConfigurationInterface
|
|||
$node = $builder->root('dynamic_templates');
|
||||
|
||||
$node
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->children()
|
||||
->scalarNode('match')->end()
|
||||
->scalarNode('unmatch')->end()
|
||||
->scalarNode('match_mapping_type')->end()
|
||||
->scalarNode('path_match')->end()
|
||||
->scalarNode('path_unmatch')->end()
|
||||
->scalarNode('match_pattern')->end()
|
||||
->append($this->getDynamicTemplateMapping())
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the array node used for mapping in dynamic templates
|
||||
*/
|
||||
protected function getDynamicTemplateMapping()
|
||||
{
|
||||
$builder = new TreeBuilder();
|
||||
$node = $builder->root('mapping');
|
||||
|
||||
$nestings = $this->getNestingsForDynamicTemplates();
|
||||
|
||||
$this->addFieldConfig($node->children(), $nestings);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $node The node to which to attach the field config to
|
||||
* @param array $nestings the nested mappings for the current field level
|
||||
*/
|
||||
protected function addFieldConfig($node, $nestings)
|
||||
{
|
||||
$node
|
||||
->scalarNode('type')->defaultValue('string')->end()
|
||||
->scalarNode('boost')->end()
|
||||
->scalarNode('store')->end()
|
||||
->scalarNode('index')->end()
|
||||
->scalarNode('index_analyzer')->end()
|
||||
->scalarNode('search_analyzer')->end()
|
||||
->scalarNode('analyzer')->end()
|
||||
->scalarNode('term_vector')->end()
|
||||
->scalarNode('null_value')->end()
|
||||
->booleanNode('include_in_all')->defaultValue(true)->end()
|
||||
->booleanNode('enabled')->defaultValue(true)->end()
|
||||
->scalarNode('lat_lon')->end()
|
||||
->scalarNode('index_name')->end()
|
||||
->booleanNode('omit_norms')->end()
|
||||
->scalarNode('index_options')->end()
|
||||
->scalarNode('ignore_above')->end()
|
||||
->scalarNode('position_offset_gap')->end()
|
||||
->arrayNode('_parent')
|
||||
->treatNullLike(array())
|
||||
->children()
|
||||
->scalarNode('type')->end()
|
||||
->scalarNode('identifier')->defaultValue('id')->end()
|
||||
->end()
|
||||
->end()
|
||||
->scalarNode('format')->end()
|
||||
->scalarNode('similarity')->end();
|
||||
;
|
||||
|
||||
if (isset($nestings['fields'])) {
|
||||
$this->addNestedFieldConfig($node, $nestings, 'fields');
|
||||
}
|
||||
|
||||
if (isset($nestings['properties'])) {
|
||||
$node
|
||||
->booleanNode('include_in_parent')->end()
|
||||
->booleanNode('include_in_root')->end()
|
||||
;
|
||||
$this->addNestedFieldConfig($node, $nestings, 'properties');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $node The node to which to attach the nested config to
|
||||
* @param array $nestings The nestings for the current field level
|
||||
* @param string $property the name of the nested property ('fields' or 'properties')
|
||||
*/
|
||||
protected function addNestedFieldConfig($node, $nestings, $property)
|
||||
{
|
||||
$childrenNode = $node
|
||||
->arrayNode($property)
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->validate()
|
||||
->ifTrue(function($v) { return isset($v['fields']) && empty($v['fields']); })
|
||||
->then(function($v) { unset($v['fields']); return $v; })
|
||||
->end()
|
||||
->treatNullLike(array())
|
||||
->addDefaultsIfNotSet()
|
||||
->children();
|
||||
|
||||
$this->addFieldConfig($childrenNode, $nestings[$property]);
|
||||
|
||||
$childrenNode
|
||||
->children()
|
||||
->scalarNode('match')->end()
|
||||
->scalarNode('unmatch')->end()
|
||||
->scalarNode('match_mapping_type')->end()
|
||||
->scalarNode('path_match')->end()
|
||||
->scalarNode('path_unmatch')->end()
|
||||
->scalarNode('match_pattern')->end()
|
||||
->arrayNode('mapping')
|
||||
->prototype('variable')
|
||||
->treatNullLike(array())
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array The unique nested mappings for all types
|
||||
*/
|
||||
protected function getNestings()
|
||||
{
|
||||
if (!isset($this->configArray[0]['indexes'])) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$nestings = array();
|
||||
foreach ($this->configArray[0]['indexes'] as $index) {
|
||||
if (empty($index['types'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($index['types'] as $type) {
|
||||
if (empty($type['mappings'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$nestings = array_merge_recursive($nestings, $this->getNestingsForType($type['mappings'], $nestings));
|
||||
}
|
||||
}
|
||||
return $nestings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array The unique nested mappings for all dynamic templates
|
||||
*/
|
||||
protected function getNestingsForDynamicTemplates()
|
||||
{
|
||||
if (!isset($this->configArray[0]['indexes'])) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$nestings = array();
|
||||
foreach ($this->configArray[0]['indexes'] as $index) {
|
||||
if (empty($index['types'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($index['types'] as $type) {
|
||||
if (empty($type['dynamic_templates'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($type['dynamic_templates'] as $definition) {
|
||||
$field = $definition['mapping'];
|
||||
|
||||
if (isset($field['fields'])) {
|
||||
$this->addPropertyNesting($field, $nestings, 'fields');
|
||||
} else if (isset($field['properties'])) {
|
||||
$this->addPropertyNesting($field, $nestings, 'properties');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return $nestings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $mappings The mappings for the current type
|
||||
* @return array The nested mappings defined for this type
|
||||
*/
|
||||
protected function getNestingsForType(array $mappings = null)
|
||||
{
|
||||
if ($mappings === null) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$nestings = array();
|
||||
|
||||
foreach ($mappings as $field) {
|
||||
if (isset($field['fields'])) {
|
||||
$this->addPropertyNesting($field, $nestings, 'fields');
|
||||
} else if (isset($field['properties'])) {
|
||||
$this->addPropertyNesting($field, $nestings, 'properties');
|
||||
}
|
||||
}
|
||||
|
||||
return $nestings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $field The field mapping definition
|
||||
* @param array $nestings The nestings array
|
||||
* @param string $property The nested property name ('fields' or 'properties')
|
||||
*/
|
||||
protected function addPropertyNesting($field, &$nestings, $property)
|
||||
{
|
||||
if (!isset($nestings[$property])) {
|
||||
$nestings[$property] = array();
|
||||
}
|
||||
$nestings[$property] = array_merge_recursive($nestings[$property], $this->getNestingsForType($field[$property]));
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -614,7 +359,7 @@ class Configuration implements ConfigurationInterface
|
|||
->end()
|
||||
->scalarNode('compress')->end()
|
||||
->scalarNode('compress_threshold')->end()
|
||||
->scalarNode('enabled')->end()
|
||||
->scalarNode('enabled')->defaultTrue()->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
|
@ -677,7 +422,7 @@ class Configuration implements ConfigurationInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the array node used for "_all"
|
||||
* Returns the array node used for "_all".
|
||||
*/
|
||||
protected function getAllNode()
|
||||
{
|
||||
|
@ -687,6 +432,8 @@ class Configuration implements ConfigurationInterface
|
|||
$node
|
||||
->children()
|
||||
->scalarNode('enabled')->defaultValue(true)->end()
|
||||
->scalarNode('index_analyzer')->end()
|
||||
->scalarNode('search_analyzer')->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
|
@ -694,7 +441,7 @@ class Configuration implements ConfigurationInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the array node used for "_timestamp"
|
||||
* Returns the array node used for "_timestamp".
|
||||
*/
|
||||
protected function getTimestampNode()
|
||||
{
|
||||
|
@ -715,7 +462,7 @@ class Configuration implements ConfigurationInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the array node used for "_ttl"
|
||||
* Returns the array node used for "_ttl".
|
||||
*/
|
||||
protected function getTtlNode()
|
||||
{
|
||||
|
@ -733,4 +480,102 @@ class Configuration implements ConfigurationInterface
|
|||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayNodeDefinition|\Symfony\Component\Config\Definition\Builder\NodeDefinition
|
||||
*/
|
||||
protected function getPersistenceNode()
|
||||
{
|
||||
$builder = new TreeBuilder();
|
||||
$node = $builder->root('persistence');
|
||||
|
||||
$node
|
||||
->validate()
|
||||
->ifTrue(function ($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['listener']); })
|
||||
->thenInvalid('Propel doesn\'t support listeners')
|
||||
->ifTrue(function ($v) { return isset($v['driver']) && 'propel' === $v['driver'] && isset($v['repository']); })
|
||||
->thenInvalid('Propel doesn\'t support the "repository" parameter')
|
||||
->end()
|
||||
->children()
|
||||
->scalarNode('driver')
|
||||
->validate()
|
||||
->ifNotInArray($this->supportedDrivers)
|
||||
->thenInvalid('The driver %s is not supported. Please choose one of '.json_encode($this->supportedDrivers))
|
||||
->end()
|
||||
->end()
|
||||
->scalarNode('model')->end()
|
||||
->scalarNode('repository')->end()
|
||||
->scalarNode('identifier')->defaultValue('id')->end()
|
||||
->arrayNode('provider')
|
||||
->children()
|
||||
->scalarNode('batch_size')->defaultValue(100)->end()
|
||||
->scalarNode('clear_object_manager')->defaultTrue()->end()
|
||||
->scalarNode('debug_logging')
|
||||
->defaultValue($this->debug)
|
||||
->treatNullLike(true)
|
||||
->end()
|
||||
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('listener')
|
||||
->children()
|
||||
->scalarNode('insert')->defaultTrue()->end()
|
||||
->scalarNode('update')->defaultTrue()->end()
|
||||
->scalarNode('delete')->defaultTrue()->end()
|
||||
->scalarNode('flush')->defaultTrue()->end()
|
||||
->booleanNode('immediate')->defaultFalse()->end()
|
||||
->scalarNode('logger')
|
||||
->defaultFalse()
|
||||
->treatNullLike('fos_elastica.logger')
|
||||
->treatTrueLike('fos_elastica.logger')
|
||||
->end()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('finder')
|
||||
->children()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('elastica_to_model_transformer')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->scalarNode('hydrate')->defaultTrue()->end()
|
||||
->scalarNode('ignore_missing')->defaultFalse()->end()
|
||||
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('model_to_elastica_transformer')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->end();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayNodeDefinition|\Symfony\Component\Config\Definition\Builder\NodeDefinition
|
||||
*/
|
||||
protected function getSerializerNode()
|
||||
{
|
||||
$builder = new TreeBuilder();
|
||||
$node = $builder->root('serializer');
|
||||
|
||||
$node
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->arrayNode('groups')
|
||||
->treatNullLike(array())
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->scalarNode('version')->end()
|
||||
->end();
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,7 @@ namespace FOS\ElasticaBundle\DependencyInjection;
|
|||
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\DefinitionDecorator;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
|
@ -14,15 +12,32 @@ use InvalidArgumentException;
|
|||
|
||||
class FOSElasticaExtension extends Extension
|
||||
{
|
||||
protected $indexConfigs = array();
|
||||
protected $typeFields = array();
|
||||
protected $loadedDrivers = array();
|
||||
protected $serializerConfig = array();
|
||||
/**
|
||||
* Definition of elastica clients as configured by this extension.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $clients = array();
|
||||
|
||||
/**
|
||||
* An array of indexes as configured by the extension.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $indexConfigs = array();
|
||||
|
||||
/**
|
||||
* If we've encountered a type mapped to a specific persistence driver, it will be loaded
|
||||
* here.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $loadedDrivers = array();
|
||||
|
||||
public function load(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
$configuration = $this->getConfiguration($configs, $container);
|
||||
$config = $this->processConfiguration($configuration, $configs);
|
||||
$config = $this->processConfiguration($configuration, $configs);
|
||||
|
||||
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||
|
||||
|
@ -31,7 +46,9 @@ class FOSElasticaExtension extends Extension
|
|||
return;
|
||||
}
|
||||
|
||||
$loader->load('config.xml');
|
||||
foreach (array('config', 'index', 'persister', 'provider', 'source', 'transformer') as $basename) {
|
||||
$loader->load(sprintf('%s.xml', $basename));
|
||||
}
|
||||
|
||||
if (empty($config['default_client'])) {
|
||||
$keys = array_keys($config['clients']);
|
||||
|
@ -43,41 +60,53 @@ class FOSElasticaExtension extends Extension
|
|||
$config['default_index'] = reset($keys);
|
||||
}
|
||||
|
||||
$clientIdsByName = $this->loadClients($config['clients'], $container);
|
||||
$this->serializerConfig = isset($config['serializer']) ? $config['serializer'] : null;
|
||||
$indexIdsByName = $this->loadIndexes($config['indexes'], $container, $clientIdsByName, $config['default_client']);
|
||||
$indexRefsByName = array_map(function($id) {
|
||||
return new Reference($id);
|
||||
}, $indexIdsByName);
|
||||
if (isset($config['serializer'])) {
|
||||
$loader->load('serializer.xml');
|
||||
|
||||
$this->loadIndexManager($indexRefsByName, $container);
|
||||
$this->loadResetter($this->indexConfigs, $container);
|
||||
$this->loadSerializer($config['serializer'], $container);
|
||||
}
|
||||
|
||||
$this->loadClients($config['clients'], $container);
|
||||
$container->setAlias('fos_elastica.client', sprintf('fos_elastica.client.%s', $config['default_client']));
|
||||
|
||||
$this->loadIndexes($config['indexes'], $container);
|
||||
$container->setAlias('fos_elastica.index', sprintf('fos_elastica.index.%s', $config['default_index']));
|
||||
|
||||
$container->getDefinition('fos_elastica.config_source.container')->replaceArgument(0, $this->indexConfigs);
|
||||
|
||||
$this->loadIndexManager($container);
|
||||
|
||||
$this->createDefaultManagerAlias($config['default_manager'], $container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $config
|
||||
* @param ContainerBuilder $container
|
||||
*
|
||||
* @return Configuration
|
||||
*/
|
||||
public function getConfiguration(array $config, ContainerBuilder $container)
|
||||
{
|
||||
return new Configuration($config);
|
||||
return new Configuration($container->getParameter('kernel.debug'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the configured clients.
|
||||
*
|
||||
* @param array $clients An array of clients configurations
|
||||
* @param array $clients An array of clients configurations
|
||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function loadClients(array $clients, ContainerBuilder $container)
|
||||
private function loadClients(array $clients, ContainerBuilder $container)
|
||||
{
|
||||
$clientIds = array();
|
||||
foreach ($clients as $name => $clientConfig) {
|
||||
$clientId = sprintf('fos_elastica.client.%s', $name);
|
||||
$clientDef = new Definition('%fos_elastica.client.class%', array($clientConfig));
|
||||
$logger = $clientConfig['servers'][0]['logger'];
|
||||
|
||||
$clientDef = new DefinitionDecorator('fos_elastica.client_prototype');
|
||||
$clientDef->replaceArgument(0, $clientConfig);
|
||||
|
||||
$logger = $clientConfig['connections'][0]['logger'];
|
||||
if (false !== $logger) {
|
||||
$clientDef->addMethodCall('setLogger', array(new Reference($logger)));
|
||||
}
|
||||
|
@ -85,78 +114,75 @@ class FOSElasticaExtension extends Extension
|
|||
|
||||
$container->setDefinition($clientId, $clientDef);
|
||||
|
||||
$clientIds[$name] = $clientId;
|
||||
$this->clients[$name] = array(
|
||||
'id' => $clientId,
|
||||
'reference' => new Reference($clientId),
|
||||
);
|
||||
}
|
||||
|
||||
return $clientIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 array $clientIdsByName
|
||||
* @param $defaultClientName
|
||||
* @param $serializerConfig
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function loadIndexes(array $indexes, ContainerBuilder $container, array $clientIdsByName, $defaultClientName)
|
||||
private function loadIndexes(array $indexes, ContainerBuilder $container)
|
||||
{
|
||||
$indexIds = array();
|
||||
foreach ($indexes as $name => $index) {
|
||||
if (isset($index['client'])) {
|
||||
$clientName = $index['client'];
|
||||
if (!isset($clientIdsByName[$clientName])) {
|
||||
throw new InvalidArgumentException(sprintf('The elastica client with name "%s" is not defined', $clientName));
|
||||
}
|
||||
} else {
|
||||
$clientName = $defaultClientName;
|
||||
}
|
||||
$indexableCallbacks = array();
|
||||
|
||||
$clientId = $clientIdsByName[$clientName];
|
||||
foreach ($indexes as $name => $index) {
|
||||
$indexId = sprintf('fos_elastica.index.%s', $name);
|
||||
$indexName = isset($index['index_name']) ? $index['index_name'] : $name;
|
||||
$indexDefArgs = array($indexName);
|
||||
$indexDef = new Definition('%fos_elastica.index.class%', $indexDefArgs);
|
||||
$indexDef->setFactoryService($clientId);
|
||||
$indexDef->setFactoryMethod('getIndex');
|
||||
$container->setDefinition($indexId, $indexDef);
|
||||
$typePrototypeConfig = isset($index['type_prototype']) ? $index['type_prototype'] : array();
|
||||
$indexIds[$name] = $indexId;
|
||||
$this->indexConfigs[$name] = array(
|
||||
'index' => new Reference($indexId),
|
||||
'name_or_alias' => $indexName,
|
||||
'config' => array(
|
||||
'mappings' => array()
|
||||
)
|
||||
);
|
||||
if ($index['finder']) {
|
||||
$this->loadIndexFinder($container, $name, $indexId);
|
||||
}
|
||||
if (!empty($index['settings'])) {
|
||||
$this->indexConfigs[$name]['config']['settings'] = $index['settings'];
|
||||
}
|
||||
if ($index['use_alias']) {
|
||||
$this->indexConfigs[$name]['use_alias'] = true;
|
||||
|
||||
$indexDef = new DefinitionDecorator('fos_elastica.index_prototype');
|
||||
$indexDef->replaceArgument(0, $indexName);
|
||||
$indexDef->addTag('fos_elastica.index', array(
|
||||
'name' => $name,
|
||||
));
|
||||
|
||||
if (isset($index['client'])) {
|
||||
$client = $this->getClient($index['client']);
|
||||
$indexDef->setFactoryService($client);
|
||||
}
|
||||
|
||||
$this->loadTypes(isset($index['types']) ? $index['types'] : array(), $container, $name, $indexId, $typePrototypeConfig);
|
||||
$container->setDefinition($indexId, $indexDef);
|
||||
$reference = new Reference($indexId);
|
||||
|
||||
$this->indexConfigs[$name] = array(
|
||||
'elasticsearch_name' => $indexName,
|
||||
'reference' => $reference,
|
||||
'name' => $name,
|
||||
'settings' => $index['settings'],
|
||||
'type_prototype' => isset($index['type_prototype']) ? $index['type_prototype'] : array(),
|
||||
'use_alias' => $index['use_alias'],
|
||||
);
|
||||
|
||||
if ($index['finder']) {
|
||||
$this->loadIndexFinder($container, $name, $reference);
|
||||
}
|
||||
|
||||
$this->loadTypes((array) $index['types'], $container, $this->indexConfigs[$name], $indexableCallbacks);
|
||||
}
|
||||
|
||||
return $indexIds;
|
||||
$indexable = $container->getDefinition('fos_elastica.indexable');
|
||||
$indexable->replaceArgument(0, $indexableCallbacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the configured index finders.
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
||||
* @param string $name The index name
|
||||
* @param string $indexId The index service identifier
|
||||
* @param string $name The index name
|
||||
* @param Reference $index Reference to the related index
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function loadIndexFinder(ContainerBuilder $container, $name, $indexId)
|
||||
private function loadIndexFinder(ContainerBuilder $container, $name, Reference $index)
|
||||
{
|
||||
/* Note: transformer services may conflict with "collection.index", if
|
||||
* an index and type names were "collection" and an index, respectively.
|
||||
|
@ -167,166 +193,141 @@ class FOSElasticaExtension extends Extension
|
|||
|
||||
$finderId = sprintf('fos_elastica.finder.%s', $name);
|
||||
$finderDef = new DefinitionDecorator('fos_elastica.finder');
|
||||
$finderDef->replaceArgument(0, new Reference($indexId));
|
||||
$finderDef->replaceArgument(0, $index);
|
||||
$finderDef->replaceArgument(1, new Reference($transformerId));
|
||||
|
||||
$container->setDefinition($finderId, $finderDef);
|
||||
|
||||
return $finderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the configured types.
|
||||
*
|
||||
* @param array $types An array of types configurations
|
||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||
* @param $indexName
|
||||
* @param $indexId
|
||||
* @param array $typePrototypeConfig
|
||||
* @param $serializerConfig
|
||||
* @param array $types
|
||||
* @param ContainerBuilder $container
|
||||
* @param array $indexConfig
|
||||
* @param array $indexableCallbacks
|
||||
*/
|
||||
protected function loadTypes(array $types, ContainerBuilder $container, $indexName, $indexId, array $typePrototypeConfig)
|
||||
private function loadTypes(array $types, ContainerBuilder $container, array $indexConfig, array &$indexableCallbacks)
|
||||
{
|
||||
foreach ($types as $name => $type) {
|
||||
$type = self::deepArrayUnion($typePrototypeConfig, $type);
|
||||
$typeId = sprintf('%s.%s', $indexId, $name);
|
||||
$typeDefArgs = array($name);
|
||||
$typeDef = new Definition('%fos_elastica.type.class%', $typeDefArgs);
|
||||
$typeDef->setFactoryService($indexId);
|
||||
$typeDef->setFactoryMethod('getType');
|
||||
if ($this->serializerConfig) {
|
||||
$callbackDef = new Definition($this->serializerConfig['callback_class']);
|
||||
$callbackId = sprintf('%s.%s.serializer.callback', $indexId, $name);
|
||||
$indexName = $indexConfig['name'];
|
||||
|
||||
$typeDef->addMethodCall('setSerializer', array(array(new Reference($callbackId), 'serialize')));
|
||||
$callbackDef->addMethodCall('setSerializer', array(new Reference($this->serializerConfig['serializer'])));
|
||||
if (isset($type['serializer']['groups'])) {
|
||||
$callbackDef->addMethodCall('setGroups', array($type['serializer']['groups']));
|
||||
}
|
||||
if (isset($type['serializer']['version'])) {
|
||||
$callbackDef->addMethodCall('setVersion', array($type['serializer']['version']));
|
||||
}
|
||||
$callbackClassImplementedInterfaces = class_implements($this->serializerConfig['callback_class']); // PHP < 5.4 friendly
|
||||
if (isset($callbackClassImplementedInterfaces['Symfony\Component\DependencyInjection\ContainerAwareInterface'])) {
|
||||
$callbackDef->addMethodCall('setContainer', array(new Reference('service_container')));
|
||||
}
|
||||
|
||||
$container->setDefinition($callbackId, $callbackDef);
|
||||
|
||||
$typeDef->addMethodCall('setSerializer', array(array(new Reference($callbackId), 'serialize')));
|
||||
}
|
||||
$typeId = sprintf('%s.%s', $indexConfig['reference'], $name);
|
||||
$typeDef = new DefinitionDecorator('fos_elastica.type_prototype');
|
||||
$typeDef->replaceArgument(0, $name);
|
||||
$typeDef->setFactoryService($indexConfig['reference']);
|
||||
$container->setDefinition($typeId, $typeDef);
|
||||
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name] = array(
|
||||
"_source" => array("enabled" => true), // Add a default setting for empty mapping settings
|
||||
$typeConfig = array(
|
||||
'name' => $name,
|
||||
'mapping' => array(), // An array containing anything that gets sent directly to ElasticSearch
|
||||
'config' => array(),
|
||||
);
|
||||
|
||||
if (isset($type['_id'])) {
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['_id'] = $type['_id'];
|
||||
}
|
||||
if (isset($type['_source'])) {
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['_source'] = $type['_source'];
|
||||
}
|
||||
if (isset($type['_boost'])) {
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['_boost'] = $type['_boost'];
|
||||
}
|
||||
if (isset($type['_routing'])) {
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['_routing'] = $type['_routing'];
|
||||
}
|
||||
if (isset($type['mappings']) && !empty($type['mappings'])) {
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['properties'] = $type['mappings'];
|
||||
$typeName = sprintf('%s/%s', $indexName, $name);
|
||||
$this->typeFields[$typeName] = $type['mappings'];
|
||||
}
|
||||
if (isset($type['_parent'])) {
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['_parent'] = array('type' => $type['_parent']['type']);
|
||||
$typeName = sprintf('%s/%s', $indexName, $name);
|
||||
$this->typeFields[$typeName]['_parent'] = $type['_parent'];
|
||||
}
|
||||
if (isset($type['persistence'])) {
|
||||
$this->loadTypePersistenceIntegration($type['persistence'], $container, $typeDef, $indexName, $name);
|
||||
}
|
||||
if (isset($type['index_analyzer'])) {
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['index_analyzer'] = $type['index_analyzer'];
|
||||
}
|
||||
if (isset($type['search_analyzer'])) {
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['search_analyzer'] = $type['search_analyzer'];
|
||||
}
|
||||
if (isset($type['index'])) {
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['index'] = $type['index'];
|
||||
}
|
||||
if (isset($type['_all'])) {
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['_all'] = $type['_all'];
|
||||
}
|
||||
if (isset($type['_timestamp'])) {
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['_timestamp'] = $type['_timestamp'];
|
||||
}
|
||||
if (isset($type['_ttl'])) {
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['_ttl'] = $type['_ttl'];
|
||||
}
|
||||
if (!empty($type['dynamic_templates'])) {
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['dynamic_templates'] = array();
|
||||
foreach ($type['dynamic_templates'] as $templateName => $templateData) {
|
||||
$this->indexConfigs[$indexName]['config']['mappings'][$name]['dynamic_templates'][] = array($templateName => $templateData);
|
||||
foreach (array(
|
||||
'dynamic_templates',
|
||||
'properties',
|
||||
'_all',
|
||||
'_boost',
|
||||
'_id',
|
||||
'_parent',
|
||||
'_routing',
|
||||
'_source',
|
||||
'_timestamp',
|
||||
'_ttl',
|
||||
) as $field) {
|
||||
if (isset($type[$field])) {
|
||||
$typeConfig['mapping'][$field] = $type[$field];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two arrays without reindexing numeric keys.
|
||||
*
|
||||
* @param array $array1 An array to merge
|
||||
* @param array $array2 An array to merge
|
||||
*
|
||||
* @return array The merged array
|
||||
*/
|
||||
static protected function deepArrayUnion($array1, $array2)
|
||||
{
|
||||
foreach ($array2 as $key => $value) {
|
||||
if (is_array($value) && isset($array1[$key]) && is_array($array1[$key])) {
|
||||
$array1[$key] = self::deepArrayUnion($array1[$key], $value);
|
||||
} else {
|
||||
$array1[$key] = $value;
|
||||
foreach (array(
|
||||
'persistence',
|
||||
'serializer',
|
||||
'index_analyzer',
|
||||
'search_analyzer',
|
||||
'date_detection',
|
||||
'dynamic_date_formats',
|
||||
'numeric_detection',
|
||||
) as $field) {
|
||||
$typeConfig['config'][$field] = array_key_exists($field, $type) ?
|
||||
$type[$field] :
|
||||
null;
|
||||
}
|
||||
|
||||
$this->indexConfigs[$indexName]['types'][$name] = $typeConfig;
|
||||
|
||||
if (isset($type['persistence'])) {
|
||||
$this->loadTypePersistenceIntegration($type['persistence'], $container, new Reference($typeId), $indexName, $name);
|
||||
|
||||
$typeConfig['persistence'] = $type['persistence'];
|
||||
}
|
||||
|
||||
if (isset($type['indexable_callback'])) {
|
||||
$indexableCallbacks[sprintf('%s/%s', $indexName, $name)] = $type['indexable_callback'];
|
||||
}
|
||||
|
||||
if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
|
||||
$typeSerializerId = sprintf('%s.serializer.callback', $typeId);
|
||||
$typeSerializerDef = new DefinitionDecorator('fos_elastica.serializer_callback_prototype');
|
||||
|
||||
if (isset($type['serializer']['groups'])) {
|
||||
$typeSerializerDef->addMethodCall('setGroups', array($type['serializer']['groups']));
|
||||
}
|
||||
if (isset($type['serializer']['version'])) {
|
||||
$typeSerializerDef->addMethodCall('setVersion', array($type['serializer']['version']));
|
||||
}
|
||||
|
||||
$typeDef->addMethodCall('setSerializer', array(array(new Reference($typeSerializerId), 'serialize')));
|
||||
$container->setDefinition($typeSerializerId, $typeSerializerDef);
|
||||
}
|
||||
}
|
||||
|
||||
return $array1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the optional provider and finder for a type
|
||||
* Loads the optional provider and finder for a type.
|
||||
*
|
||||
* @param array $typeConfig
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
||||
* @param \Symfony\Component\DependencyInjection\Definition $typeDef
|
||||
* @param $indexName
|
||||
* @param $typeName
|
||||
* @param array $typeConfig
|
||||
* @param ContainerBuilder $container
|
||||
* @param Reference $typeRef
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*/
|
||||
protected function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Definition $typeDef, $indexName, $typeName)
|
||||
private function loadTypePersistenceIntegration(array $typeConfig, ContainerBuilder $container, Reference $typeRef, $indexName, $typeName)
|
||||
{
|
||||
$this->loadDriver($container, $typeConfig['driver']);
|
||||
|
||||
$elasticaToModelTransformerId = $this->loadElasticaToModelTransformer($typeConfig, $container, $indexName, $typeName);
|
||||
$modelToElasticaTransformerId = $this->loadModelToElasticaTransformer($typeConfig, $container, $indexName, $typeName);
|
||||
$objectPersisterId = $this->loadObjectPersister($typeConfig, $typeDef, $container, $indexName, $typeName, $modelToElasticaTransformerId);
|
||||
$objectPersisterId = $this->loadObjectPersister($typeConfig, $typeRef, $container, $indexName, $typeName, $modelToElasticaTransformerId);
|
||||
|
||||
if (isset($typeConfig['provider'])) {
|
||||
$this->loadTypeProvider($typeConfig, $container, $objectPersisterId, $typeDef, $indexName, $typeName);
|
||||
$this->loadTypeProvider($typeConfig, $container, $objectPersisterId, $indexName, $typeName);
|
||||
}
|
||||
if (isset($typeConfig['finder'])) {
|
||||
$this->loadTypeFinder($typeConfig, $container, $elasticaToModelTransformerId, $typeDef, $indexName, $typeName);
|
||||
$this->loadTypeFinder($typeConfig, $container, $elasticaToModelTransformerId, $typeRef, $indexName, $typeName);
|
||||
}
|
||||
if (isset($typeConfig['listener'])) {
|
||||
$this->loadTypeListener($typeConfig, $container, $objectPersisterId, $typeDef, $indexName, $typeName);
|
||||
$this->loadTypeListener($typeConfig, $container, $objectPersisterId, $indexName, $typeName);
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadElasticaToModelTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
|
||||
/**
|
||||
* Creates and loads an ElasticaToModelTransformer.
|
||||
*
|
||||
* @param array $typeConfig
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function loadElasticaToModelTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
|
||||
{
|
||||
if (isset($typeConfig['elastica_to_model_transformer']['service'])) {
|
||||
return $typeConfig['elastica_to_model_transformer']['service'];
|
||||
}
|
||||
|
||||
/* Note: transformer services may conflict with "prototype.driver", if
|
||||
* the index and type names were "prototype" and a driver, respectively.
|
||||
*/
|
||||
|
@ -339,55 +340,78 @@ class FOSElasticaExtension extends Extension
|
|||
$argPos = ('propel' === $typeConfig['driver']) ? 0 : 1;
|
||||
|
||||
$serviceDef->replaceArgument($argPos, $typeConfig['model']);
|
||||
$serviceDef->replaceArgument($argPos + 1, array(
|
||||
'hydrate' => $typeConfig['elastica_to_model_transformer']['hydrate'],
|
||||
'identifier' => $typeConfig['identifier'],
|
||||
'ignore_missing' => $typeConfig['elastica_to_model_transformer']['ignore_missing'],
|
||||
'query_builder_method' => $typeConfig['elastica_to_model_transformer']['query_builder_method']
|
||||
));
|
||||
$serviceDef->replaceArgument($argPos + 1, array_merge($typeConfig['elastica_to_model_transformer'], array(
|
||||
'identifier' => $typeConfig['identifier'],
|
||||
)));
|
||||
$container->setDefinition($serviceId, $serviceDef);
|
||||
|
||||
return $serviceId;
|
||||
}
|
||||
|
||||
protected function loadModelToElasticaTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
|
||||
/**
|
||||
* Creates and loads a ModelToElasticaTransformer for an index/type.
|
||||
*
|
||||
* @param array $typeConfig
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function loadModelToElasticaTransformer(array $typeConfig, ContainerBuilder $container, $indexName, $typeName)
|
||||
{
|
||||
if (isset($typeConfig['model_to_elastica_transformer']['service'])) {
|
||||
return $typeConfig['model_to_elastica_transformer']['service'];
|
||||
}
|
||||
|
||||
if ($this->serializerConfig) {
|
||||
$abstractId = sprintf('fos_elastica.model_to_elastica_identifier_transformer');
|
||||
} else {
|
||||
$abstractId = sprintf('fos_elastica.model_to_elastica_transformer');
|
||||
}
|
||||
$abstractId = $container->hasDefinition('fos_elastica.serializer_callback_prototype') ?
|
||||
'fos_elastica.model_to_elastica_identifier_transformer' :
|
||||
'fos_elastica.model_to_elastica_transformer';
|
||||
|
||||
$serviceId = sprintf('fos_elastica.model_to_elastica_transformer.%s.%s', $indexName, $typeName);
|
||||
$serviceDef = new DefinitionDecorator($abstractId);
|
||||
$serviceDef->replaceArgument(0, array(
|
||||
'identifier' => $typeConfig['identifier']
|
||||
'identifier' => $typeConfig['identifier'],
|
||||
));
|
||||
$container->setDefinition($serviceId, $serviceDef);
|
||||
|
||||
return $serviceId;
|
||||
}
|
||||
|
||||
protected function loadObjectPersister(array $typeConfig, Definition $typeDef, ContainerBuilder $container, $indexName, $typeName, $transformerId)
|
||||
/**
|
||||
* Creates and loads an object persister for a type.
|
||||
*
|
||||
* @param array $typeConfig
|
||||
* @param Reference $typeRef
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param string $transformerId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function loadObjectPersister(array $typeConfig, Reference $typeRef, ContainerBuilder $container, $indexName, $typeName, $transformerId)
|
||||
{
|
||||
$arguments = array(
|
||||
$typeDef,
|
||||
$typeRef,
|
||||
new Reference($transformerId),
|
||||
$typeConfig['model'],
|
||||
);
|
||||
|
||||
if ($this->serializerConfig) {
|
||||
if ($container->hasDefinition('fos_elastica.serializer_callback_prototype')) {
|
||||
$abstractId = 'fos_elastica.object_serializer_persister';
|
||||
$callbackId = sprintf('%s.%s.serializer.callback', $this->indexConfigs[$indexName]['index'], $typeName);
|
||||
$callbackId = sprintf('%s.%s.serializer.callback', $this->indexConfigs[$indexName]['reference'], $typeName);
|
||||
$arguments[] = array(new Reference($callbackId), 'serialize');
|
||||
} else {
|
||||
$abstractId = 'fos_elastica.object_persister';
|
||||
$arguments[] = $this->typeFields[sprintf('%s/%s', $indexName, $typeName)];
|
||||
$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);
|
||||
$serviceDef = new DefinitionDecorator($abstractId);
|
||||
foreach ($arguments as $i => $argument) {
|
||||
|
@ -399,31 +423,58 @@ class FOSElasticaExtension extends Extension
|
|||
return $serviceId;
|
||||
}
|
||||
|
||||
protected function loadTypeProvider(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $typeDef, $indexName, $typeName)
|
||||
/**
|
||||
* Loads a provider for a type.
|
||||
*
|
||||
* @param array $typeConfig
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $objectPersisterId
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function loadTypeProvider(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
|
||||
{
|
||||
if (isset($typeConfig['provider']['service'])) {
|
||||
return $typeConfig['provider']['service'];
|
||||
}
|
||||
|
||||
/* Note: provider services may conflict with "prototype.driver", if the
|
||||
* index and type names were "prototype" and a driver, respectively.
|
||||
*/
|
||||
$providerId = sprintf('fos_elastica.provider.%s.%s', $indexName, $typeName);
|
||||
$providerDef = new DefinitionDecorator('fos_elastica.provider.prototype.' . $typeConfig['driver']);
|
||||
$providerDef = new DefinitionDecorator('fos_elastica.provider.prototype.'.$typeConfig['driver']);
|
||||
$providerDef->addTag('fos_elastica.provider', array('index' => $indexName, 'type' => $typeName));
|
||||
$providerDef->replaceArgument(0, new Reference($objectPersisterId));
|
||||
$providerDef->replaceArgument(1, $typeConfig['model']);
|
||||
$providerDef->replaceArgument(2, $typeConfig['model']);
|
||||
// Propel provider can simply ignore Doctrine-specific options
|
||||
$providerDef->replaceArgument(2, array_diff_key($typeConfig['provider'], array('service' => 1)));
|
||||
$providerDef->replaceArgument(3, array_merge(array_diff_key($typeConfig['provider'], array('service' => 1)), array(
|
||||
'indexName' => $indexName,
|
||||
'typeName' => $typeName,
|
||||
)));
|
||||
$container->setDefinition($providerId, $providerDef);
|
||||
|
||||
return $providerId;
|
||||
}
|
||||
|
||||
protected function loadTypeListener(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $typeDef, $indexName, $typeName)
|
||||
/**
|
||||
* Loads doctrine listeners to handle indexing of new or updated objects.
|
||||
*
|
||||
* @param array $typeConfig
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $objectPersisterId
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function loadTypeListener(array $typeConfig, ContainerBuilder $container, $objectPersisterId, $indexName, $typeName)
|
||||
{
|
||||
if (isset($typeConfig['listener']['service'])) {
|
||||
return $typeConfig['listener']['service'];
|
||||
}
|
||||
|
||||
/* Note: listener services may conflict with "prototype.driver", if the
|
||||
* index and type names were "prototype" and a driver, respectively.
|
||||
*/
|
||||
|
@ -431,42 +482,42 @@ class FOSElasticaExtension extends Extension
|
|||
$listenerId = sprintf('fos_elastica.listener.%s.%s', $indexName, $typeName);
|
||||
$listenerDef = new DefinitionDecorator($abstractListenerId);
|
||||
$listenerDef->replaceArgument(0, new Reference($objectPersisterId));
|
||||
$listenerDef->replaceArgument(1, $typeConfig['model']);
|
||||
$listenerDef->replaceArgument(2, $this->getDoctrineEvents($typeConfig));
|
||||
$listenerDef->replaceArgument(3, $typeConfig['identifier']);
|
||||
if ($typeConfig['listener']['logger']) {
|
||||
$listenerDef->replaceArgument(4, new Reference($typeConfig['listener']['logger']));
|
||||
}
|
||||
$listenerDef->replaceArgument(2, array(
|
||||
'identifier' => $typeConfig['identifier'],
|
||||
'indexName' => $indexName,
|
||||
'typeName' => $typeName,
|
||||
));
|
||||
$listenerDef->replaceArgument(3, $typeConfig['listener']['logger'] ?
|
||||
new Reference($typeConfig['listener']['logger']) :
|
||||
null
|
||||
);
|
||||
|
||||
$tagName = null;
|
||||
switch ($typeConfig['driver']) {
|
||||
case 'orm': $listenerDef->addTag('doctrine.event_subscriber'); break;
|
||||
case 'mongodb': $listenerDef->addTag('doctrine_mongodb.odm.event_subscriber'); break;
|
||||
case 'orm':
|
||||
$tagName = 'doctrine.event_listener';
|
||||
break;
|
||||
case 'mongodb':
|
||||
$tagName = 'doctrine_mongodb.odm.event_listener';
|
||||
break;
|
||||
}
|
||||
if (isset($typeConfig['listener']['is_indexable_callback'])) {
|
||||
$callback = $typeConfig['listener']['is_indexable_callback'];
|
||||
|
||||
if (is_array($callback)) {
|
||||
list($class) = $callback + array(null);
|
||||
if (is_string($class) && !class_exists($class)) {
|
||||
$callback[0] = new Reference($class);
|
||||
}
|
||||
if (null !== $tagName) {
|
||||
foreach ($this->getDoctrineEvents($typeConfig) as $event) {
|
||||
$listenerDef->addTag($tagName, array('event' => $event));
|
||||
}
|
||||
|
||||
$listenerDef->addMethodCall('setIsIndexableCallback', array($callback));
|
||||
}
|
||||
|
||||
$container->setDefinition($listenerId, $listenerDef);
|
||||
|
||||
return $listenerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map Elastica to Doctrine events for the current driver
|
||||
* Map Elastica to Doctrine events for the current driver.
|
||||
*/
|
||||
private function getDoctrineEvents(array $typeConfig)
|
||||
{
|
||||
// Flush always calls depending on actions scheduled in lifecycle listeners
|
||||
$typeConfig['listener']['flush'] = true;
|
||||
|
||||
switch ($typeConfig['driver']) {
|
||||
case 'orm':
|
||||
$eventsClass = '\Doctrine\ORM\Events';
|
||||
|
@ -476,7 +527,6 @@ class FOSElasticaExtension extends Extension
|
|||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException(sprintf('Cannot determine events for driver "%s"', $typeConfig['driver']));
|
||||
break;
|
||||
}
|
||||
|
||||
$events = array();
|
||||
|
@ -484,7 +534,7 @@ class FOSElasticaExtension extends Extension
|
|||
'insert' => array(constant($eventsClass.'::postPersist')),
|
||||
'update' => array(constant($eventsClass.'::postUpdate')),
|
||||
'delete' => array(constant($eventsClass.'::preRemove')),
|
||||
'flush' => array($typeConfig['listener']['immediate'] ? constant($eventsClass.'::preFlush') : constant($eventsClass.'::postFlush'))
|
||||
'flush' => array($typeConfig['listener']['immediate'] ? constant($eventsClass.'::preFlush') : constant($eventsClass.'::postFlush')),
|
||||
);
|
||||
|
||||
foreach ($eventMapping as $event => $doctrineEvents) {
|
||||
|
@ -496,14 +546,26 @@ class FOSElasticaExtension extends Extension
|
|||
return $events;
|
||||
}
|
||||
|
||||
protected function loadTypeFinder(array $typeConfig, ContainerBuilder $container, $elasticaToModelId, $typeDef, $indexName, $typeName)
|
||||
/**
|
||||
* Loads a Type specific Finder.
|
||||
*
|
||||
* @param array $typeConfig
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $elasticaToModelId
|
||||
* @param Reference $typeRef
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function loadTypeFinder(array $typeConfig, ContainerBuilder $container, $elasticaToModelId, Reference $typeRef, $indexName, $typeName)
|
||||
{
|
||||
if (isset($typeConfig['finder']['service'])) {
|
||||
$finderId = $typeConfig['finder']['service'];
|
||||
} else {
|
||||
$finderId = sprintf('fos_elastica.finder.%s.%s', $indexName, $typeName);
|
||||
$finderDef = new DefinitionDecorator('fos_elastica.finder');
|
||||
$finderDef->replaceArgument(0, $typeDef);
|
||||
$finderDef->replaceArgument(0, $typeRef);
|
||||
$finderDef->replaceArgument(1, new Reference($elasticaToModelId));
|
||||
$container->setDefinition($finderId, $finderDef);
|
||||
}
|
||||
|
@ -520,41 +582,63 @@ class FOSElasticaExtension extends Extension
|
|||
}
|
||||
|
||||
/**
|
||||
* Loads the index manager
|
||||
* Loads the index manager.
|
||||
*
|
||||
* @param array $indexRefsByName
|
||||
* @param ContainerBuilder $container
|
||||
**/
|
||||
protected function loadIndexManager(array $indexRefsByName, ContainerBuilder $container)
|
||||
private function loadIndexManager(ContainerBuilder $container)
|
||||
{
|
||||
$indexRefs = array_map(function ($index) {
|
||||
return $index['reference'];
|
||||
}, $this->indexConfigs);
|
||||
|
||||
$managerDef = $container->getDefinition('fos_elastica.index_manager');
|
||||
$managerDef->replaceArgument(0, $indexRefsByName);
|
||||
$managerDef->replaceArgument(1, new Reference('fos_elastica.index'));
|
||||
$managerDef->replaceArgument(0, $indexRefs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the resetter
|
||||
* Makes sure a specific driver has been loaded.
|
||||
*
|
||||
* @param array $indexConfigs
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $driver
|
||||
*/
|
||||
protected function loadResetter(array $indexConfigs, ContainerBuilder $container)
|
||||
{
|
||||
$resetterDef = $container->getDefinition('fos_elastica.resetter');
|
||||
$resetterDef->replaceArgument(0, $indexConfigs);
|
||||
}
|
||||
|
||||
protected function loadDriver(ContainerBuilder $container, $driver)
|
||||
private function loadDriver(ContainerBuilder $container, $driver)
|
||||
{
|
||||
if (in_array($driver, $this->loadedDrivers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||
$loader->load($driver.'.xml');
|
||||
$this->loadedDrivers[] = $driver;
|
||||
}
|
||||
|
||||
protected function createDefaultManagerAlias($defaultManager, ContainerBuilder $container)
|
||||
/**
|
||||
* Loads and configures the serializer prototype.
|
||||
*
|
||||
* @param array $config
|
||||
* @param ContainerBuilder $container
|
||||
*/
|
||||
private function loadSerializer($config, ContainerBuilder $container)
|
||||
{
|
||||
$container->setAlias('fos_elastica.serializer', $config['serializer']);
|
||||
|
||||
$serializer = $container->getDefinition('fos_elastica.serializer_callback_prototype');
|
||||
$serializer->setClass($config['callback_class']);
|
||||
|
||||
$callbackClassImplementedInterfaces = class_implements($config['callback_class']);
|
||||
if (isset($callbackClassImplementedInterfaces['Symfony\Component\DependencyInjection\ContainerAwareInterface'])) {
|
||||
$serializer->addMethodCall('setContainer', array(new Reference('service_container')));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a default manager alias for defined default manager or the first loaded driver.
|
||||
*
|
||||
* @param string $defaultManager
|
||||
* @param ContainerBuilder $container
|
||||
*/
|
||||
private function createDefaultManagerAlias($defaultManager, ContainerBuilder $container)
|
||||
{
|
||||
if (0 == count($this->loadedDrivers)) {
|
||||
return;
|
||||
|
@ -570,4 +654,22 @@ class FOSElasticaExtension extends Extension
|
|||
|
||||
$container->setAlias('fos_elastica.manager', sprintf('fos_elastica.manager.%s', $defaultManagerService));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to a client given its configured name.
|
||||
*
|
||||
* @param string $clientName
|
||||
*
|
||||
* @return Reference
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function getClient($clientName)
|
||||
{
|
||||
if (!array_key_exists($clientName, $this->clients)) {
|
||||
throw new InvalidArgumentException(sprintf('The elastica client with name "%s" is not defined', $clientName));
|
||||
}
|
||||
|
||||
return $this->clients[$clientName]['reference'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,32 +2,34 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Doctrine;
|
||||
|
||||
use Doctrine\Common\Persistence\ManagerRegistry;
|
||||
use FOS\ElasticaBundle\HybridResult;
|
||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
||||
use FOS\ElasticaBundle\Transformer\AbstractElasticaToModelTransformer as BaseTransformer;
|
||||
use FOS\ElasticaBundle\Transformer\HighlightableModelInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
/**
|
||||
* Maps Elastica documents with Doctrine objects
|
||||
* This mapper assumes an exact match between
|
||||
* elastica documents ids and doctrine object ids
|
||||
* elastica documents ids and doctrine object ids.
|
||||
*/
|
||||
abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||
abstract class AbstractElasticaToModelTransformer extends BaseTransformer
|
||||
{
|
||||
/**
|
||||
* Manager registry
|
||||
* Manager registry.
|
||||
*
|
||||
* @var ManagerRegistry
|
||||
*/
|
||||
protected $registry = null;
|
||||
|
||||
/**
|
||||
* Class of the model to map to the elastica documents
|
||||
* Class of the model to map to the elastica documents.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $objectClass = null;
|
||||
|
||||
/**
|
||||
* Optional parameters
|
||||
* Optional parameters.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
|
@ -39,20 +41,13 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
|||
);
|
||||
|
||||
/**
|
||||
* PropertyAccessor instance
|
||||
* Instantiates a new Mapper.
|
||||
*
|
||||
* @var PropertyAccessorInterface
|
||||
*/
|
||||
protected $propertyAccessor;
|
||||
|
||||
/**
|
||||
* Instantiates a new Mapper
|
||||
*
|
||||
* @param object $registry
|
||||
* @param ManagerRegistry $registry
|
||||
* @param string $objectClass
|
||||
* @param array $options
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($registry, $objectClass, array $options = array())
|
||||
public function __construct(ManagerRegistry $registry, $objectClass, array $options = array())
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->objectClass = $objectClass;
|
||||
|
@ -69,22 +64,14 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
|||
return $this->objectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PropertyAccessor
|
||||
*
|
||||
* @param PropertyAccessorInterface $propertyAccessor
|
||||
*/
|
||||
public function setPropertyAccessor(PropertyAccessorInterface $propertyAccessor)
|
||||
{
|
||||
$this->propertyAccessor = $propertyAccessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an array of elastica objects into an array of
|
||||
* model objects fetched from the doctrine repository
|
||||
* model objects fetched from the doctrine repository.
|
||||
*
|
||||
* @param array $elasticaObjects of elastica objects
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*
|
||||
* @return array
|
||||
**/
|
||||
public function transform(array $elasticaObjects)
|
||||
|
@ -109,29 +96,31 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
|||
// sort objects in the order of ids
|
||||
$idPos = array_flip($ids);
|
||||
$identifier = $this->options['identifier'];
|
||||
$propertyAccessor = $this->propertyAccessor;
|
||||
usort($objects, function($a, $b) use ($idPos, $identifier, $propertyAccessor)
|
||||
{
|
||||
return $idPos[$propertyAccessor->getValue($a, $identifier)] > $idPos[$propertyAccessor->getValue($b, $identifier)];
|
||||
});
|
||||
usort($objects, $this->getSortingClosure($idPos, $identifier));
|
||||
|
||||
return $objects;
|
||||
}
|
||||
|
||||
public function hybridTransform(array $elasticaObjects)
|
||||
{
|
||||
$indexedElasticaResults = array();
|
||||
foreach ($elasticaObjects as $elasticaObject) {
|
||||
$indexedElasticaResults[$elasticaObject->getId()] = $elasticaObject;
|
||||
}
|
||||
|
||||
$objects = $this->transform($elasticaObjects);
|
||||
|
||||
$result = array();
|
||||
for ($i = 0; $i < count($elasticaObjects); $i++) {
|
||||
$result[] = new HybridResult($elasticaObjects[$i], $objects[$i]);
|
||||
foreach ($objects as $object) {
|
||||
$id = $this->propertyAccessor->getValue($object, $this->options['identifier']);
|
||||
$result[] = new HybridResult($indexedElasticaResults[$id], $object);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getIdentifierField()
|
||||
{
|
||||
|
@ -139,11 +128,12 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
|||
}
|
||||
|
||||
/**
|
||||
* Fetches objects by theses identifier values
|
||||
* Fetches objects by theses identifier values.
|
||||
*
|
||||
* @param array $identifierValues ids values
|
||||
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
||||
*
|
||||
* @param array $identifierValues ids values
|
||||
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
||||
* @return array of objects or arrays
|
||||
*/
|
||||
protected abstract function findByIdentifiers(array $identifierValues, $hydrate);
|
||||
abstract protected function findByIdentifiers(array $identifierValues, $hydrate);
|
||||
}
|
||||
|
|
|
@ -6,84 +6,62 @@ use Doctrine\Common\Persistence\ManagerRegistry;
|
|||
use Elastica\Exception\Bulk\ResponseException as BulkResponseException;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use FOS\ElasticaBundle\Provider\AbstractProvider as BaseAbstractProvider;
|
||||
use FOS\ElasticaBundle\Provider\IndexableInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
abstract class AbstractProvider extends BaseAbstractProvider
|
||||
{
|
||||
/**
|
||||
* @var SliceFetcherInterface
|
||||
*/
|
||||
private $sliceFetcher;
|
||||
|
||||
/**
|
||||
* @var ManagerRegistry
|
||||
*/
|
||||
protected $managerRegistry;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param IndexableInterface $indexable
|
||||
* @param string $objectClass
|
||||
* @param array $options
|
||||
* @param array $baseOptions
|
||||
* @param ManagerRegistry $managerRegistry
|
||||
* @param SliceFetcherInterface $sliceFetcher
|
||||
*/
|
||||
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $options, $managerRegistry)
|
||||
{
|
||||
parent::__construct($objectPersister, $objectClass, array_merge(array(
|
||||
'clear_object_manager' => true,
|
||||
'ignore_errors' => false,
|
||||
'query_builder_method' => 'createQueryBuilder',
|
||||
), $options));
|
||||
public function __construct(
|
||||
ObjectPersisterInterface $objectPersister,
|
||||
IndexableInterface $indexable,
|
||||
$objectClass,
|
||||
array $baseOptions,
|
||||
ManagerRegistry $managerRegistry,
|
||||
SliceFetcherInterface $sliceFetcher = null
|
||||
) {
|
||||
parent::__construct($objectPersister, $indexable, $objectClass, $baseOptions);
|
||||
|
||||
$this->managerRegistry = $managerRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Provider\ProviderInterface::populate()
|
||||
*/
|
||||
public function populate(\Closure $loggerClosure = null, array $options = array())
|
||||
{
|
||||
$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'];
|
||||
|
||||
for (; $offset < $nbObjects; $offset += $batchSize) {
|
||||
if ($loggerClosure) {
|
||||
$stepStartTime = microtime(true);
|
||||
}
|
||||
$objects = $this->fetchSlice($queryBuilder, $batchSize, $offset);
|
||||
|
||||
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']) {
|
||||
$this->managerRegistry->getManagerForClass($this->objectClass)->clear();
|
||||
}
|
||||
|
||||
usleep($sleep);
|
||||
|
||||
if ($loggerClosure) {
|
||||
$stepNbObjects = count($objects);
|
||||
$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()));
|
||||
}
|
||||
}
|
||||
$this->sliceFetcher = $sliceFetcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts objects that would be indexed using the query builder.
|
||||
*
|
||||
* @param object $queryBuilder
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
protected abstract function countObjects($queryBuilder);
|
||||
abstract protected function countObjects($queryBuilder);
|
||||
|
||||
/**
|
||||
* Creates the query builder, which will be used to fetch objects to index.
|
||||
*
|
||||
* @param string $method
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
abstract protected function createQueryBuilder($method);
|
||||
|
||||
/**
|
||||
* Fetches a slice of objects using the query builder.
|
||||
|
@ -91,14 +69,102 @@ abstract class AbstractProvider extends BaseAbstractProvider
|
|||
* @param object $queryBuilder
|
||||
* @param integer $limit
|
||||
* @param integer $offset
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected abstract function fetchSlice($queryBuilder, $limit, $offset);
|
||||
abstract protected function fetchSlice($queryBuilder, $limit, $offset);
|
||||
|
||||
/**
|
||||
* Creates the query builder, which will be used to fetch objects to index.
|
||||
*
|
||||
* @return object
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected abstract function createQueryBuilder();
|
||||
protected function doPopulate($options, \Closure $loggerClosure = null)
|
||||
{
|
||||
$manager = $this->managerRegistry->getManagerForClass($this->objectClass);
|
||||
|
||||
$queryBuilder = $this->createQueryBuilder($options['query_builder_method']);
|
||||
$nbObjects = $this->countObjects($queryBuilder);
|
||||
$offset = $options['offset'];
|
||||
|
||||
$objects = array();
|
||||
for (; $offset < $nbObjects; $offset += $options['batch_size']) {
|
||||
try {
|
||||
$objects = $this->getSlice($queryBuilder, $options['batch_size'], $offset, $objects);
|
||||
$objects = $this->filterObjects($options, $objects);
|
||||
|
||||
if (!empty($objects)) {
|
||||
$this->objectPersister->insertMany($objects);
|
||||
}
|
||||
} catch (BulkResponseException $e) {
|
||||
if (!$options['ignore_errors']) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (null !== $loggerClosure) {
|
||||
$loggerClosure(
|
||||
$options['batch_size'],
|
||||
$nbObjects,
|
||||
sprintf('<error>%s</error>', $e->getMessage())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['clear_object_manager']) {
|
||||
$manager->clear();
|
||||
}
|
||||
|
||||
usleep($options['sleep']);
|
||||
|
||||
if (null !== $loggerClosure) {
|
||||
$loggerClosure($options['batch_size'], $nbObjects);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function configureOptions()
|
||||
{
|
||||
parent::configureOptions();
|
||||
|
||||
$this->resolver->setDefaults(array(
|
||||
'clear_object_manager' => true,
|
||||
'debug_logging' => false,
|
||||
'ignore_errors' => false,
|
||||
'offset' => 0,
|
||||
'query_builder_method' => 'createQueryBuilder',
|
||||
'sleep' => 0
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* If this Provider has a SliceFetcher defined, we use it instead of falling back to
|
||||
* the fetchSlice methods defined in the ORM/MongoDB subclasses.
|
||||
*
|
||||
* @param $queryBuilder
|
||||
* @param int $limit
|
||||
* @param int $offset
|
||||
* @param array $lastSlice
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getSlice($queryBuilder, $limit, $offset, $lastSlice)
|
||||
{
|
||||
if (!$this->sliceFetcher) {
|
||||
return $this->fetchSlice($queryBuilder, $limit, $offset);
|
||||
}
|
||||
|
||||
$manager = $this->managerRegistry->getManagerForClass($this->objectClass);
|
||||
$identifierFieldNames = $manager
|
||||
->getClassMetadata($this->objectClass)
|
||||
->getIdentifierFieldNames();
|
||||
|
||||
return $this->sliceFetcher->fetch(
|
||||
$queryBuilder,
|
||||
$limit,
|
||||
$offset,
|
||||
$lastSlice,
|
||||
$identifierFieldNames
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,238 +2,117 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Doctrine;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersister;
|
||||
use Symfony\Component\ExpressionLanguage\Expression;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\ExpressionLanguage\SyntaxError;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use FOS\ElasticaBundle\Provider\IndexableInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
/**
|
||||
* Automatically update ElasticSearch based on changes to the Doctrine source
|
||||
* data. One listener is generated for each Doctrine entity / ElasticSearch type.
|
||||
*/
|
||||
class Listener implements EventSubscriber
|
||||
class Listener
|
||||
{
|
||||
/**
|
||||
* Object persister
|
||||
* Object persister.
|
||||
*
|
||||
* @var ObjectPersister
|
||||
* @var ObjectPersisterInterface
|
||||
*/
|
||||
protected $objectPersister;
|
||||
|
||||
/**
|
||||
* Class of the domain model
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $objectClass;
|
||||
|
||||
/**
|
||||
* List of subscribed events
|
||||
* Configuration for the listener.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $events;
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* Name of domain model field used as the ES identifier
|
||||
* Objects scheduled for insertion.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $esIdentifierField;
|
||||
|
||||
/**
|
||||
* Callback for determining if an object should be indexed
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $isIndexableCallback;
|
||||
|
||||
/**
|
||||
* Objects scheduled for insertion and replacement
|
||||
* @var array
|
||||
*/
|
||||
public $scheduledForInsertion = array();
|
||||
|
||||
/**
|
||||
* Objects scheduled to be updated or removed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $scheduledForUpdate = array();
|
||||
|
||||
/**
|
||||
* IDs of objects scheduled for removal
|
||||
* IDs of objects scheduled for removal.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $scheduledForDeletion = array();
|
||||
|
||||
/**
|
||||
* An instance of ExpressionLanguage
|
||||
*
|
||||
* @var ExpressionLanguage
|
||||
*/
|
||||
protected $expressionLanguage;
|
||||
|
||||
/**
|
||||
* PropertyAccessor instance
|
||||
* PropertyAccessor instance.
|
||||
*
|
||||
* @var PropertyAccessorInterface
|
||||
*/
|
||||
protected $propertyAccessor;
|
||||
|
||||
/**
|
||||
* @var IndexableInterface
|
||||
*/
|
||||
private $indexable;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param string $objectClass
|
||||
* @param array $events
|
||||
* @param string $esIdentifierField
|
||||
* @param IndexableInterface $indexable
|
||||
* @param array $config
|
||||
* @param LoggerInterface $logger
|
||||
*/
|
||||
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $events, $esIdentifierField = 'id', $logger = null)
|
||||
{
|
||||
$this->objectPersister = $objectPersister;
|
||||
$this->objectClass = $objectClass;
|
||||
$this->events = $events;
|
||||
$this->esIdentifierField = $esIdentifierField;
|
||||
public function __construct(
|
||||
ObjectPersisterInterface $objectPersister,
|
||||
IndexableInterface $indexable,
|
||||
array $config = array(),
|
||||
LoggerInterface $logger = null
|
||||
) {
|
||||
$this->config = array_merge(array(
|
||||
'identifier' => 'id',
|
||||
), $config);
|
||||
$this->indexable = $indexable;
|
||||
$this->objectPersister = $objectPersister;
|
||||
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
|
||||
|
||||
if ($logger) {
|
||||
if ($logger && $this->objectPersister instanceof ObjectPersister) {
|
||||
$this->objectPersister->setLogger($logger);
|
||||
}
|
||||
|
||||
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Doctrine\Common\EventSubscriber::getSubscribedEvents()
|
||||
*/
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the callback for determining object index eligibility.
|
||||
* Looks for new objects that should be indexed.
|
||||
*
|
||||
* If callback is a string, it must be public method on the object class
|
||||
* that expects no arguments and returns a boolean. Otherwise, the callback
|
||||
* should expect the object for consideration as its only argument and
|
||||
* return a boolean.
|
||||
*
|
||||
* @param callback $callback
|
||||
* @throws \RuntimeException if the callback is not callable
|
||||
* @param LifecycleEventArgs $eventArgs
|
||||
*/
|
||||
public function setIsIndexableCallback($callback)
|
||||
public function postPersist(LifecycleEventArgs $eventArgs)
|
||||
{
|
||||
if (is_string($callback)) {
|
||||
if (!is_callable(array($this->objectClass, $callback))) {
|
||||
if (false !== ($expression = $this->getExpressionLanguage())) {
|
||||
$callback = new Expression($callback);
|
||||
try {
|
||||
$expression->compile($callback, array($this->getExpressionVar()));
|
||||
} catch (SyntaxError $e) {
|
||||
throw new \RuntimeException(sprintf('Indexable callback %s::%s() is not callable or a valid expression.', $this->objectClass, $callback), 0, $e);
|
||||
}
|
||||
} else {
|
||||
throw new \RuntimeException(sprintf('Indexable callback %s::%s() is not callable.', $this->objectClass, $callback));
|
||||
}
|
||||
}
|
||||
} elseif (!is_callable($callback)) {
|
||||
if (is_array($callback)) {
|
||||
list($class, $method) = $callback + array(null, null);
|
||||
if (is_object($class)) {
|
||||
$class = get_class($class);
|
||||
}
|
||||
$entity = $eventArgs->getObject();
|
||||
|
||||
if ($class && $method) {
|
||||
throw new \RuntimeException(sprintf('Indexable callback %s::%s() is not callable.', $class, $method));
|
||||
}
|
||||
}
|
||||
throw new \RuntimeException('Indexable callback is not callable.');
|
||||
}
|
||||
|
||||
$this->isIndexableCallback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the object is indexable with respect to the callback.
|
||||
*
|
||||
* @param object $object
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isObjectIndexable($object)
|
||||
{
|
||||
if (!$this->isIndexableCallback) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->isIndexableCallback instanceof Expression) {
|
||||
return $this->getExpressionLanguage()->evaluate($this->isIndexableCallback, array($this->getExpressionVar($object) => $object));
|
||||
}
|
||||
|
||||
return is_string($this->isIndexableCallback)
|
||||
? call_user_func(array($object, $this->isIndexableCallback))
|
||||
: call_user_func($this->isIndexableCallback, $object);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $object
|
||||
* @return string
|
||||
*/
|
||||
private function getExpressionVar($object = null)
|
||||
{
|
||||
$class = $object ?: $this->objectClass;
|
||||
$ref = new \ReflectionClass($class);
|
||||
|
||||
return strtolower($ref->getShortName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides unified method for retrieving a doctrine object from an EventArgs instance
|
||||
*
|
||||
* @param EventArgs $eventArgs
|
||||
* @return object Entity | Document
|
||||
* @throws \RuntimeException if no valid getter is found.
|
||||
*/
|
||||
private function getDoctrineObject(EventArgs $eventArgs)
|
||||
{
|
||||
if (method_exists($eventArgs, 'getObject')) {
|
||||
return $eventArgs->getObject();
|
||||
} elseif (method_exists($eventArgs, 'getEntity')) {
|
||||
return $eventArgs->getEntity();
|
||||
} elseif (method_exists($eventArgs, 'getDocument')) {
|
||||
return $eventArgs->getDocument();
|
||||
}
|
||||
|
||||
throw new \RuntimeException('Unable to retrieve object from EventArgs.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|ExpressionLanguage
|
||||
*/
|
||||
private function getExpressionLanguage()
|
||||
{
|
||||
if (null === $this->expressionLanguage) {
|
||||
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->expressionLanguage = new ExpressionLanguage();
|
||||
}
|
||||
|
||||
return $this->expressionLanguage;
|
||||
}
|
||||
|
||||
public function postPersist(EventArgs $eventArgs)
|
||||
{
|
||||
$entity = $this->getDoctrineObject($eventArgs);
|
||||
|
||||
if ($entity instanceof $this->objectClass && $this->isObjectIndexable($entity)) {
|
||||
if ($this->objectPersister->handlesObject($entity) && $this->isObjectIndexable($entity)) {
|
||||
$this->scheduledForInsertion[] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
public function postUpdate(EventArgs $eventArgs)
|
||||
/**
|
||||
* Looks for objects being updated that should be indexed or removed from the index.
|
||||
*
|
||||
* @param LifecycleEventArgs $eventArgs
|
||||
*/
|
||||
public function postUpdate(LifecycleEventArgs $eventArgs)
|
||||
{
|
||||
$entity = $this->getDoctrineObject($eventArgs);
|
||||
$entity = $eventArgs->getObject();
|
||||
|
||||
if ($entity instanceof $this->objectClass) {
|
||||
if ($this->objectPersister->handlesObject($entity)) {
|
||||
if ($this->isObjectIndexable($entity)) {
|
||||
$this->scheduledForUpdate[] = $entity;
|
||||
} else {
|
||||
|
@ -245,20 +124,22 @@ class Listener implements EventSubscriber
|
|||
|
||||
/**
|
||||
* Delete objects preRemove instead of postRemove so that we have access to the id. Because this is called
|
||||
* preRemove, first check that the entity is managed by Doctrine
|
||||
* preRemove, first check that the entity is managed by Doctrine.
|
||||
*
|
||||
* @param LifecycleEventArgs $eventArgs
|
||||
*/
|
||||
public function preRemove(EventArgs $eventArgs)
|
||||
public function preRemove(LifecycleEventArgs $eventArgs)
|
||||
{
|
||||
$entity = $this->getDoctrineObject($eventArgs);
|
||||
$entity = $eventArgs->getObject();
|
||||
|
||||
if ($entity instanceof $this->objectClass) {
|
||||
if ($this->objectPersister->handlesObject($entity)) {
|
||||
$this->scheduleForDeletion($entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
|
@ -277,32 +158,55 @@ class Listener implements EventSubscriber
|
|||
}
|
||||
|
||||
/**
|
||||
* Iterate through scheduled actions before flushing to emulate 2.x behavior. Note that the ElasticSearch index
|
||||
* will fall out of sync with the source data in the event of a crash during flush.
|
||||
* Iterate through scheduled actions before flushing to emulate 2.x behavior.
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterating through scheduled actions *after* flushing ensures that the ElasticSearch index will be affected
|
||||
* only if the query is successful
|
||||
* Iterating through scheduled actions *after* flushing ensures that the
|
||||
* ElasticSearch index will be affected only if the query is successful.
|
||||
*/
|
||||
public function postFlush(EventArgs $eventArgs)
|
||||
public function postFlush()
|
||||
{
|
||||
$this->persistScheduled();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->esIdentifierField)) {
|
||||
if ($identifierValue = $this->propertyAccessor->getValue($object, $this->config['identifier'])) {
|
||||
$this->scheduledForDeletion[] = $identifierValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the object is indexable or not.
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isObjectIndexable($object)
|
||||
{
|
||||
return $this->indexable->isObjectIndexable(
|
||||
$this->config['indexName'],
|
||||
$this->config['typeName'],
|
||||
$object
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,21 +7,23 @@ use FOS\ElasticaBundle\Doctrine\AbstractElasticaToModelTransformer;
|
|||
/**
|
||||
* Maps Elastica documents with Doctrine objects
|
||||
* This mapper assumes an exact match between
|
||||
* elastica documents ids and doctrine object ids
|
||||
* elastica documents ids and doctrine object ids.
|
||||
*/
|
||||
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
||||
{
|
||||
/**
|
||||
* Fetch objects for theses identifier values
|
||||
* Fetch objects for theses identifier values.
|
||||
*
|
||||
* @param array $identifierValues ids values
|
||||
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
||||
*
|
||||
* @param array $identifierValues ids values
|
||||
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
||||
* @return array of objects or arrays
|
||||
*/
|
||||
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
||||
{
|
||||
return $this->registry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getRepository($this->objectClass)
|
||||
->{$this->options['query_builder_method']}($this->objectClass)
|
||||
->field($this->options['identifier'])->in($identifierValues)
|
||||
->hydrate($hydrate)
|
||||
|
|
|
@ -9,7 +9,42 @@ use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
|
|||
class Provider extends AbstractProvider
|
||||
{
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
|
||||
* Disables logging and returns the logger that was previously set.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function disableLogging()
|
||||
{
|
||||
$configuration = $this->managerRegistry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getConnection()
|
||||
->getConfiguration();
|
||||
|
||||
$logger = $configuration->getLoggerCallable();
|
||||
$configuration->setLoggerCallable(null);
|
||||
|
||||
return $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reenables the logger with the previously returned logger from disableLogging();.
|
||||
*
|
||||
* @param mixed $logger
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function enableLogging($logger)
|
||||
{
|
||||
$configuration = $this->managerRegistry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getConnection()
|
||||
->getConfiguration();
|
||||
|
||||
$configuration->setLoggerCallable($logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function countObjects($queryBuilder)
|
||||
{
|
||||
|
@ -23,7 +58,7 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::fetchSlice()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function fetchSlice($queryBuilder, $limit, $offset)
|
||||
{
|
||||
|
@ -32,21 +67,21 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
return $queryBuilder
|
||||
->limit($limit)
|
||||
->skip($offset)
|
||||
->limit($limit)
|
||||
->getQuery()
|
||||
->execute()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::createQueryBuilder()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function createQueryBuilder()
|
||||
protected function createQueryBuilder($method)
|
||||
{
|
||||
return $this->managerRegistry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getRepository($this->objectClass)
|
||||
->{$this->options['query_builder_method']}();
|
||||
->{$method}();
|
||||
}
|
||||
}
|
||||
|
|
44
Doctrine/MongoDB/SliceFetcher.php
Normal file
44
Doctrine/MongoDB/SliceFetcher.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Doctrine\MongoDB;
|
||||
|
||||
use Doctrine\ODM\MongoDB\Query\Builder;
|
||||
use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
|
||||
use FOS\ElasticaBundle\Doctrine\SliceFetcherInterface;
|
||||
|
||||
/**
|
||||
* Fetches a slice of objects.
|
||||
*
|
||||
* @author Thomas Prelot <tprelot@gmail.com>
|
||||
*/
|
||||
class SliceFetcher implements SliceFetcherInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch($queryBuilder, $limit, $offset, array $previousSlice, array $identifierFieldNames)
|
||||
{
|
||||
if (!$queryBuilder instanceof Builder) {
|
||||
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ODM\MongoDB\Query\Builder');
|
||||
}
|
||||
|
||||
$lastObject = array_pop($previousSlice);
|
||||
|
||||
if ($lastObject) {
|
||||
$queryBuilder
|
||||
->field('_id')->gt($lastObject->getId())
|
||||
->skip(0)
|
||||
;
|
||||
} else {
|
||||
$queryBuilder->skip($offset);
|
||||
}
|
||||
|
||||
return $queryBuilder
|
||||
->limit($limit)
|
||||
->sort(array('_id' => 'asc'))
|
||||
->getQuery()
|
||||
->execute()
|
||||
->toArray()
|
||||
;
|
||||
}
|
||||
}
|
|
@ -8,17 +8,18 @@ use Doctrine\ORM\Query;
|
|||
/**
|
||||
* Maps Elastica documents with Doctrine objects
|
||||
* This mapper assumes an exact match between
|
||||
* elastica documents ids and doctrine object ids
|
||||
* elastica documents ids and doctrine object ids.
|
||||
*/
|
||||
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
||||
{
|
||||
const ENTITY_ALIAS = 'o';
|
||||
|
||||
/**
|
||||
* Fetch objects for theses identifier values
|
||||
* Fetch objects for theses identifier values.
|
||||
*
|
||||
* @param array $identifierValues ids values
|
||||
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
||||
*
|
||||
* @param array $identifierValues ids values
|
||||
* @param Boolean $hydrate whether or not to hydrate the objects, false returns arrays
|
||||
* @return array of objects or arrays
|
||||
*/
|
||||
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
||||
|
@ -29,14 +30,14 @@ class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
|||
$hydrationMode = $hydrate ? Query::HYDRATE_OBJECT : Query::HYDRATE_ARRAY;
|
||||
|
||||
$qb = $this->getEntityQueryBuilder();
|
||||
$qb->where($qb->expr()->in(static::ENTITY_ALIAS.'.'.$this->options['identifier'], ':values'))
|
||||
$qb->andWhere($qb->expr()->in(static::ENTITY_ALIAS.'.'.$this->options['identifier'], ':values'))
|
||||
->setParameter('values', $identifierValues);
|
||||
|
||||
return $qb->getQuery()->setHydrationMode($hydrationMode)->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
|
|
@ -11,7 +11,42 @@ class Provider extends AbstractProvider
|
|||
const ENTITY_ALIAS = 'a';
|
||||
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
|
||||
* Disables logging and returns the logger that was previously set.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function disableLogging()
|
||||
{
|
||||
$configuration = $this->managerRegistry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getConnection()
|
||||
->getConfiguration();
|
||||
|
||||
$logger = $configuration->getSQLLogger();
|
||||
$configuration->setSQLLogger(null);
|
||||
|
||||
return $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reenables the logger with the previously returned logger from disableLogging();.
|
||||
*
|
||||
* @param mixed $logger
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function enableLogging($logger)
|
||||
{
|
||||
$configuration = $this->managerRegistry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getConnection()
|
||||
->getConfiguration();
|
||||
|
||||
$configuration->setSQLLogger($logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function countObjects($queryBuilder)
|
||||
{
|
||||
|
@ -34,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)
|
||||
{
|
||||
|
@ -42,8 +79,8 @@ class Provider extends AbstractProvider
|
|||
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
|
||||
}
|
||||
|
||||
/**
|
||||
* An orderBy DQL part is required to avoid feching the same row twice.
|
||||
/*
|
||||
* An orderBy DQL part is required to avoid fetching the same row twice.
|
||||
* @see http://stackoverflow.com/questions/6314879/does-limit-offset-length-require-order-by-for-pagination
|
||||
* @see http://www.postgresql.org/docs/current/static/queries-limit.html
|
||||
* @see http://www.sqlite.org/lang_select.html#orderby
|
||||
|
@ -68,14 +105,14 @@ class Provider extends AbstractProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::createQueryBuilder()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function createQueryBuilder()
|
||||
protected function createQueryBuilder($method)
|
||||
{
|
||||
return $this->managerRegistry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getRepository($this->objectClass)
|
||||
// ORM query builders require an alias argument
|
||||
->{$this->options['query_builder_method']}(static::ENTITY_ALIAS);
|
||||
->{$method}(static::ENTITY_ALIAS);
|
||||
}
|
||||
}
|
||||
|
|
50
Doctrine/ORM/SliceFetcher.php
Normal file
50
Doctrine/ORM/SliceFetcher.php
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Doctrine\ORM;
|
||||
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
|
||||
use FOS\ElasticaBundle\Doctrine\SliceFetcherInterface;
|
||||
|
||||
/**
|
||||
* Fetches a slice of objects.
|
||||
*
|
||||
* @author Thomas Prelot <tprelot@gmail.com>
|
||||
*/
|
||||
class SliceFetcher implements SliceFetcherInterface
|
||||
{
|
||||
/**
|
||||
* This method should remain in sync with Provider::fetchSlice until that method is deprecated and
|
||||
* removed.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch($queryBuilder, $limit, $offset, array $previousSlice, array $identifierFieldNames)
|
||||
{
|
||||
if (!$queryBuilder instanceof QueryBuilder) {
|
||||
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
|
||||
}
|
||||
|
||||
/*
|
||||
* An orderBy DQL part is required to avoid feching the same row twice.
|
||||
* @see http://stackoverflow.com/questions/6314879/does-limit-offset-length-require-order-by-for-pagination
|
||||
* @see http://www.postgresql.org/docs/current/static/queries-limit.html
|
||||
* @see http://www.sqlite.org/lang_select.html#orderby
|
||||
*/
|
||||
$orderBy = $queryBuilder->getDQLPart('orderBy');
|
||||
if (empty($orderBy)) {
|
||||
$rootAliases = $queryBuilder->getRootAliases();
|
||||
|
||||
foreach ($identifierFieldNames as $fieldName) {
|
||||
$queryBuilder->addOrderBy($rootAliases[0].'.'.$fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
return $queryBuilder
|
||||
->setFirstResult($offset)
|
||||
->setMaxResults($limit)
|
||||
->getQuery()
|
||||
->getResult()
|
||||
;
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ class RepositoryManager extends BaseManager
|
|||
}
|
||||
|
||||
/**
|
||||
* Return repository for entity
|
||||
* Return repository for entity.
|
||||
*
|
||||
* Returns custom repository if one specified otherwise
|
||||
* returns a basic repository.
|
||||
|
@ -35,7 +35,7 @@ class RepositoryManager extends BaseManager
|
|||
$realEntityName = $entityName;
|
||||
if (strpos($entityName, ':') !== false) {
|
||||
list($namespaceAlias, $simpleClassName) = explode(':', $entityName);
|
||||
$realEntityName = $this->managerRegistry->getAliasNamespace($namespaceAlias) . '\\' . $simpleClassName;
|
||||
$realEntityName = $this->managerRegistry->getAliasNamespace($namespaceAlias).'\\'.$simpleClassName;
|
||||
}
|
||||
|
||||
return parent::getRepository($realEntityName);
|
||||
|
|
24
Doctrine/SliceFetcherInterface.php
Normal file
24
Doctrine/SliceFetcherInterface.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Doctrine;
|
||||
|
||||
/**
|
||||
* Fetches a slice of objects.
|
||||
*
|
||||
* @author Thomas Prelot <tprelot@gmail.com>
|
||||
*/
|
||||
interface SliceFetcherInterface
|
||||
{
|
||||
/**
|
||||
* Fetches a slice of objects using the query builder.
|
||||
*
|
||||
* @param object $queryBuilder
|
||||
* @param integer $limit
|
||||
* @param integer $offset
|
||||
* @param array $previousSlice
|
||||
* @param array $identifierFieldNames
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetch($queryBuilder, $limit, $offset, array $previousSlice, array $identifierFieldNames);
|
||||
}
|
|
@ -2,27 +2,11 @@
|
|||
|
||||
namespace FOS\ElasticaBundle;
|
||||
|
||||
use Elastica\Index;
|
||||
use FOS\ElasticaBundle\Elastica\Index;
|
||||
|
||||
/**
|
||||
* Elastica index capable of reassigning name dynamically
|
||||
*
|
||||
* @author Konstantin Tjuterev <kostik.lv@gmail.com>
|
||||
* @deprecated Use \FOS\ElasticaBundle\Elastica\TransformingIndex
|
||||
*/
|
||||
class DynamicIndex extends Index
|
||||
{
|
||||
/**
|
||||
* Reassign index name
|
||||
*
|
||||
* While it's technically a regular setter for name property, it's specifically named overrideName, but not setName
|
||||
* since it's used for a very specific case and normally should not be used
|
||||
*
|
||||
* @param string $name Index name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function overrideName($name)
|
||||
{
|
||||
$this->_name = $name;
|
||||
}
|
||||
}
|
||||
|
|
104
Elastica/Client.php
Normal file
104
Elastica/Client.php
Normal file
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Elastica;
|
||||
|
||||
use Elastica\Client as BaseClient;
|
||||
use Elastica\Request;
|
||||
use FOS\ElasticaBundle\Logger\ElasticaLogger;
|
||||
use Symfony\Component\Stopwatch\Stopwatch;
|
||||
|
||||
/**
|
||||
* Extends the default Elastica client to provide logging for errors that occur
|
||||
* during communication with ElasticSearch.
|
||||
*
|
||||
* @author Gordon Franke <info@nevalon.de>
|
||||
*/
|
||||
class Client extends BaseClient
|
||||
{
|
||||
/**
|
||||
* Stores created indexes to avoid recreation.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $indexCache = array();
|
||||
|
||||
/**
|
||||
* Symfony's debugging Stopwatch.
|
||||
*
|
||||
* @var Stopwatch|null
|
||||
*/
|
||||
private $stopwatch;
|
||||
|
||||
/**
|
||||
* @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())
|
||||
{
|
||||
if ($this->stopwatch) {
|
||||
$this->stopwatch->start('es_request', 'fos_elastica');
|
||||
}
|
||||
|
||||
$start = microtime(true);
|
||||
$response = parent::request($path, $method, $data, $query);
|
||||
|
||||
$this->logQuery($path, $method, $data, $query, $start);
|
||||
|
||||
if ($this->stopwatch) {
|
||||
$this->stopwatch->stop('es_request');
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function getIndex($name)
|
||||
{
|
||||
if (isset($this->indexCache[$name])) {
|
||||
return $this->indexCache[$name];
|
||||
}
|
||||
|
||||
return $this->indexCache[$name] = new Index($this, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a stopwatch instance for debugging purposes.
|
||||
*
|
||||
* @param Stopwatch $stopwatch
|
||||
*/
|
||||
public function setStopwatch(Stopwatch $stopwatch = null)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
}
|
59
Elastica/Index.php
Normal file
59
Elastica/Index.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Elastica;
|
||||
|
||||
use Elastica\Index as BaseIndex;
|
||||
|
||||
/**
|
||||
* Overridden Elastica Index class that provides dynamic index name changes.
|
||||
*
|
||||
* @author Konstantin Tjuterev <kostik.lv@gmail.com>
|
||||
*/
|
||||
class Index extends BaseIndex
|
||||
{
|
||||
private $originalName;
|
||||
|
||||
/**
|
||||
* Stores created types to avoid recreation.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $typeCache = array();
|
||||
|
||||
/**
|
||||
* Returns the original name of the index if the index has been renamed for reindexing
|
||||
* or realiasing purposes.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOriginalName()
|
||||
{
|
||||
return $this->originalName ?: $this->_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*/
|
||||
public function getType($type)
|
||||
{
|
||||
if (isset($this->typeCache[$type])) {
|
||||
return $this->typeCache[$type];
|
||||
}
|
||||
|
||||
return $this->typeCache[$type] = parent::getType($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reassign index name.
|
||||
*
|
||||
* While it's technically a regular setter for name property, it's specifically named overrideName, but not setName
|
||||
* since it's used for a very specific case and normally should not be used
|
||||
*
|
||||
* @param string $name Index name
|
||||
*/
|
||||
public function overrideName($name)
|
||||
{
|
||||
$this->originalName = $this->_name;
|
||||
$this->_name = $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));
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace FOS\ElasticaBundle;
|
||||
|
||||
use FOS\ElasticaBundle\DependencyInjection\Compiler\ConfigSourcePass;
|
||||
use FOS\ElasticaBundle\DependencyInjection\Compiler\IndexPass;
|
||||
use FOS\ElasticaBundle\DependencyInjection\Compiler\RegisterProvidersPass;
|
||||
use FOS\ElasticaBundle\DependencyInjection\Compiler\TransformerPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
@ -10,7 +12,6 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
|
|||
|
||||
/**
|
||||
* Bundle.
|
||||
*
|
||||
*/
|
||||
class FOSElasticaBundle extends Bundle
|
||||
{
|
||||
|
@ -21,6 +22,8 @@ class FOSElasticaBundle extends Bundle
|
|||
{
|
||||
parent::build($container);
|
||||
|
||||
$container->addCompilerPass(new ConfigSourcePass());
|
||||
$container->addCompilerPass(new IndexPass());
|
||||
$container->addCompilerPass(new RegisterProvidersPass(), PassConfig::TYPE_BEFORE_REMOVING);
|
||||
$container->addCompilerPass(new TransformerPass());
|
||||
}
|
||||
|
|
|
@ -5,12 +5,13 @@ namespace FOS\ElasticaBundle\Finder;
|
|||
interface FinderInterface
|
||||
{
|
||||
/**
|
||||
* Searches for query results within a given limit
|
||||
* Searches for query results within a given limit.
|
||||
*
|
||||
* @param mixed $query Can be a string, an array or an \Elastica\Query object
|
||||
* @param int $limit How many results to get
|
||||
* @param mixed $query Can be a string, an array or an \Elastica\Query object
|
||||
* @param int $limit How many results to get
|
||||
* @param array $options
|
||||
*
|
||||
* @return array results
|
||||
*/
|
||||
function find($query, $limit = null, $options = array());
|
||||
public function find($query, $limit = null, $options = array());
|
||||
}
|
||||
|
|
|
@ -9,20 +9,22 @@ use Elastica\Query;
|
|||
interface PaginatedFinderInterface extends FinderInterface
|
||||
{
|
||||
/**
|
||||
* Searches for query results and returns them wrapped in a paginator
|
||||
* Searches for query results and returns them wrapped in a paginator.
|
||||
*
|
||||
* @param mixed $query Can be a string, an array or an \Elastica\Query object
|
||||
* @param mixed $query Can be a string, an array or an \Elastica\Query object
|
||||
* @param array $options
|
||||
*
|
||||
* @return Pagerfanta paginated results
|
||||
*/
|
||||
function findPaginated($query, $options = array());
|
||||
public function findPaginated($query, $options = array());
|
||||
|
||||
/**
|
||||
* Creates a paginator adapter for this query
|
||||
* Creates a paginator adapter for this query.
|
||||
*
|
||||
* @param mixed $query
|
||||
* @param array $options
|
||||
*
|
||||
* @return PaginatorAdapterInterface
|
||||
*/
|
||||
function createPaginatorAdapter($query, $options = array());
|
||||
public function createPaginatorAdapter($query, $options = array());
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use Elastica\SearchableInterface;
|
|||
use Elastica\Query;
|
||||
|
||||
/**
|
||||
* Finds elastica documents and map them to persisted objects
|
||||
* Finds elastica documents and map them to persisted objects.
|
||||
*/
|
||||
class TransformedFinder implements PaginatedFinderInterface
|
||||
{
|
||||
|
@ -25,11 +25,12 @@ class TransformedFinder implements PaginatedFinderInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Search for a query string
|
||||
* Search for a query string.
|
||||
*
|
||||
* @param string $query
|
||||
* @param string $query
|
||||
* @param integer $limit
|
||||
* @param array $options
|
||||
* @param array $options
|
||||
*
|
||||
* @return array of model objects
|
||||
**/
|
||||
public function find($query, $limit = null, $options = array())
|
||||
|
@ -50,8 +51,9 @@ class TransformedFinder implements PaginatedFinderInterface
|
|||
* Find documents similar to one with passed id.
|
||||
*
|
||||
* @param integer $id
|
||||
* @param array $params
|
||||
* @param array $query
|
||||
* @param array $params
|
||||
* @param array $query
|
||||
*
|
||||
* @return array of model objects
|
||||
**/
|
||||
public function moreLikeThis($id, $params = array(), $query = array())
|
||||
|
@ -65,7 +67,8 @@ class TransformedFinder implements PaginatedFinderInterface
|
|||
/**
|
||||
* @param $query
|
||||
* @param null|int $limit
|
||||
* @param array $options
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function search($query, $limit = null, $options = array())
|
||||
|
@ -80,10 +83,11 @@ class TransformedFinder implements PaginatedFinderInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets a paginator wrapping the result of a search
|
||||
* Gets a paginator wrapping the result of a search.
|
||||
*
|
||||
* @param string $query
|
||||
* @param array $options
|
||||
* @param array $options
|
||||
*
|
||||
* @return Pagerfanta
|
||||
*/
|
||||
public function findPaginated($query, $options = array())
|
||||
|
|
|
@ -24,4 +24,4 @@ class HybridResult
|
|||
{
|
||||
return $this->result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
197
Index/AliasProcessor.php
Normal file
197
Index/AliasProcessor.php
Normal file
|
@ -0,0 +1,197 @@
|
|||
<?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\Index;
|
||||
|
||||
use Elastica\Client;
|
||||
use Elastica\Exception\ExceptionInterface;
|
||||
use Elastica\Request;
|
||||
use FOS\ElasticaBundle\Configuration\IndexConfig;
|
||||
use FOS\ElasticaBundle\Elastica\Index;
|
||||
use FOS\ElasticaBundle\Exception\AliasIsIndexException;
|
||||
|
||||
class AliasProcessor
|
||||
{
|
||||
/**
|
||||
* Sets the randomised root name for an index.
|
||||
*
|
||||
* @param IndexConfig $indexConfig
|
||||
* @param Index $index
|
||||
*/
|
||||
public function setRootName(IndexConfig $indexConfig, Index $index)
|
||||
{
|
||||
$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
|
||||
* indexes that are set to use aliases.
|
||||
*
|
||||
* $force will delete an index encountered where an alias is expected.
|
||||
*
|
||||
* @param IndexConfig $indexConfig
|
||||
* @param Index $index
|
||||
* @param bool $force
|
||||
*
|
||||
* @throws AliasIsIndexException
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function switchIndexAlias(IndexConfig $indexConfig, Index $index, $force = false)
|
||||
{
|
||||
$client = $index->getClient();
|
||||
|
||||
$aliasName = $indexConfig->getElasticSearchName();
|
||||
$oldIndexName = null;
|
||||
$newIndexName = $index->getName();
|
||||
|
||||
try {
|
||||
$oldIndexName = $this->getAliasedIndex($client, $aliasName);
|
||||
} catch (AliasIsIndexException $e) {
|
||||
if (!$force) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->deleteIndex($client, $aliasName);
|
||||
}
|
||||
|
||||
try {
|
||||
$aliasUpdateRequest = $this->buildAliasUpdateRequest($oldIndexName, $aliasName, $newIndexName);
|
||||
$client->request('_aliases', 'POST', $aliasUpdateRequest);
|
||||
} catch (ExceptionInterface $e) {
|
||||
$this->cleanupRenameFailure($client, $newIndexName, $e);
|
||||
}
|
||||
|
||||
// Delete the old index after the alias has been switched
|
||||
if (null !== $oldIndexName) {
|
||||
$this->deleteIndex($client, $oldIndexName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an ElasticSearch request to rename or create an alias.
|
||||
*
|
||||
* @param string|null $aliasedIndex
|
||||
* @param string $aliasName
|
||||
* @param string $newIndexName
|
||||
* @return array
|
||||
*/
|
||||
private function buildAliasUpdateRequest($aliasedIndex, $aliasName, $newIndexName)
|
||||
{
|
||||
$aliasUpdateRequest = array('actions' => array());
|
||||
if (null !== $aliasedIndex) {
|
||||
// if the alias is set - add an action to remove it
|
||||
$aliasUpdateRequest['actions'][] = array(
|
||||
'remove' => array('index' => $aliasedIndex, 'alias' => $aliasName),
|
||||
);
|
||||
}
|
||||
|
||||
// add an action to point the alias to the new index
|
||||
$aliasUpdateRequest['actions'][] = array(
|
||||
'add' => array('index' => $newIndexName, 'alias' => $aliasName),
|
||||
);
|
||||
|
||||
return $aliasUpdateRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up an index when we encounter a failure to rename the alias.
|
||||
*
|
||||
* @param Client $client
|
||||
* @param string $indexName
|
||||
* @param \Exception $renameAliasException
|
||||
*/
|
||||
private function cleanupRenameFailure(Client $client, $indexName, \Exception $renameAliasException)
|
||||
{
|
||||
$additionalError = '';
|
||||
try {
|
||||
$this->deleteIndex($client, $indexName);
|
||||
} catch (ExceptionInterface $deleteNewIndexException) {
|
||||
$additionalError = sprintf(
|
||||
'Tried to delete newly built index %s, but also failed: %s',
|
||||
$indexName,
|
||||
$deleteNewIndexException->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
throw new \RuntimeException(sprintf(
|
||||
'Failed to updated index alias: %s. %s',
|
||||
$renameAliasException->getMessage(),
|
||||
$additionalError ?: sprintf('Newly built index %s was deleted', $indexName)
|
||||
), 0, $renameAliasException);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an index.
|
||||
*
|
||||
* @param Client $client
|
||||
* @param string $indexName Index name to delete
|
||||
*/
|
||||
private function deleteIndex(Client $client, $indexName)
|
||||
{
|
||||
try {
|
||||
$path = sprintf("%s", $indexName);
|
||||
$client->request($path, Request::DELETE);
|
||||
} catch (ExceptionInterface $deleteOldIndexException) {
|
||||
throw new \RuntimeException(sprintf(
|
||||
'Failed to delete index %s with message: %s',
|
||||
$indexName,
|
||||
$deleteOldIndexException->getMessage()
|
||||
), 0, $deleteOldIndexException);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of a single index that an alias points to or throws
|
||||
* an exception if there is more than one.
|
||||
*
|
||||
* @param Client $client
|
||||
* @param string $aliasName Alias name
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @throws AliasIsIndexException
|
||||
*/
|
||||
private function getAliasedIndex(Client $client, $aliasName)
|
||||
{
|
||||
$aliasesInfo = $client->request('_aliases', 'GET')->getData();
|
||||
$aliasedIndexes = array();
|
||||
|
||||
foreach ($aliasesInfo as $indexName => $indexInfo) {
|
||||
if ($indexName === $aliasName) {
|
||||
throw new AliasIsIndexException($indexName);
|
||||
}
|
||||
if (!isset($indexInfo['aliases'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$aliases = array_keys($indexInfo['aliases']);
|
||||
if (in_array($aliasName, $aliases)) {
|
||||
$aliasedIndexes[] = $indexName;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
70
Index/IndexManager.php
Normal file
70
Index/IndexManager.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Index;
|
||||
|
||||
use FOS\ElasticaBundle\Elastica\Index;
|
||||
|
||||
class IndexManager
|
||||
{
|
||||
/**
|
||||
* @var Index
|
||||
*/
|
||||
private $defaultIndex;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $indexes;
|
||||
|
||||
/**
|
||||
* @param array $indexes
|
||||
* @param Index $defaultIndex
|
||||
*/
|
||||
public function __construct(array $indexes, Index $defaultIndex)
|
||||
{
|
||||
$this->defaultIndex = $defaultIndex;
|
||||
$this->indexes = $indexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all registered indexes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAllIndexes()
|
||||
{
|
||||
return $this->indexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an index by its name.
|
||||
*
|
||||
* @param string $name Index to return, or the default index if null
|
||||
*
|
||||
* @return Index
|
||||
*
|
||||
* @throws \InvalidArgumentException if no index exists for the given name
|
||||
*/
|
||||
public function getIndex($name = null)
|
||||
{
|
||||
if (null === $name) {
|
||||
return $this->defaultIndex;
|
||||
}
|
||||
|
||||
if (!isset($this->indexes[$name])) {
|
||||
throw new \InvalidArgumentException(sprintf('The index "%s" does not exist', $name));
|
||||
}
|
||||
|
||||
return $this->indexes[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default index.
|
||||
*
|
||||
* @return Index
|
||||
*/
|
||||
public function getDefaultIndex()
|
||||
{
|
||||
return $this->defaultIndex;
|
||||
}
|
||||
}
|
134
Index/MappingBuilder.php
Normal file
134
Index/MappingBuilder.php
Normal file
|
@ -0,0 +1,134 @@
|
|||
<?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\Index;
|
||||
|
||||
use FOS\ElasticaBundle\Configuration\IndexConfig;
|
||||
use FOS\ElasticaBundle\Configuration\TypeConfig;
|
||||
|
||||
class MappingBuilder
|
||||
{
|
||||
/**
|
||||
* Skip adding default information to certain fields.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $skipTypes = array('completion');
|
||||
|
||||
/**
|
||||
* Builds mappings for an entire index.
|
||||
*
|
||||
* @param IndexConfig $indexConfig
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function buildIndexMapping(IndexConfig $indexConfig)
|
||||
{
|
||||
$typeMappings = array();
|
||||
foreach ($indexConfig->getTypes() as $typeConfig) {
|
||||
$typeMappings[$typeConfig->getName()] = $this->buildTypeMapping($typeConfig);
|
||||
}
|
||||
|
||||
$mapping = array();
|
||||
if (!empty($typeMappings)) {
|
||||
$mapping['mappings'] = $typeMappings;
|
||||
}
|
||||
// 'warmers' => $indexConfig->getWarmers(),
|
||||
|
||||
$settings = $indexConfig->getSettings();
|
||||
if (!empty($settings)) {
|
||||
$mapping['settings'] = $settings;
|
||||
}
|
||||
|
||||
return $mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds mappings for a single type.
|
||||
*
|
||||
* @param TypeConfig $typeConfig
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function buildTypeMapping(TypeConfig $typeConfig)
|
||||
{
|
||||
$mapping = $typeConfig->getMapping();
|
||||
|
||||
if (null !== $typeConfig->getDynamicDateFormats()) {
|
||||
$mapping['dynamic_date_formats'] = $typeConfig->getDynamicDateFormats();
|
||||
}
|
||||
|
||||
if (null !== $typeConfig->getDateDetection()) {
|
||||
$mapping['date_detection'] = $typeConfig->getDateDetection();
|
||||
}
|
||||
|
||||
if (null !== $typeConfig->getNumericDetection()) {
|
||||
$mapping['numeric_detection'] = $typeConfig->getNumericDetection();
|
||||
}
|
||||
|
||||
if ($typeConfig->getIndexAnalyzer()) {
|
||||
$mapping['index_analyzer'] = $typeConfig->getIndexAnalyzer();
|
||||
}
|
||||
|
||||
if ($typeConfig->getSearchAnalyzer()) {
|
||||
$mapping['search_analyzer'] = $typeConfig->getSearchAnalyzer();
|
||||
}
|
||||
|
||||
if (isset($mapping['dynamic_templates']) and empty($mapping['dynamic_templates'])) {
|
||||
unset($mapping['dynamic_templates']);
|
||||
}
|
||||
|
||||
$this->fixProperties($mapping['properties']);
|
||||
if (!$mapping['properties']) {
|
||||
unset($mapping['properties']);
|
||||
}
|
||||
|
||||
if ($typeConfig->getModel()) {
|
||||
$mapping['_meta']['model'] = $typeConfig->getModel();
|
||||
}
|
||||
|
||||
if (empty($mapping)) {
|
||||
// Empty mapping, we want it encoded as a {} instead of a []
|
||||
$mapping = new \stdClass();
|
||||
}
|
||||
|
||||
return $mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes any properties and applies basic defaults for any field that does not have
|
||||
* required options.
|
||||
*
|
||||
* @param $properties
|
||||
*/
|
||||
private function fixProperties(&$properties)
|
||||
{
|
||||
foreach ($properties as $name => &$property) {
|
||||
unset($property['property_path']);
|
||||
|
||||
if (!isset($property['type'])) {
|
||||
$property['type'] = 'string';
|
||||
}
|
||||
if ($property['type'] == 'multi_field' && isset($property['fields'])) {
|
||||
$this->fixProperties($property['fields']);
|
||||
}
|
||||
if (isset($property['properties'])) {
|
||||
$this->fixProperties($property['properties']);
|
||||
}
|
||||
if (in_array($property['type'], $this->skipTypes)) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($property['store'])) {
|
||||
$property['store'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
158
Index/Resetter.php
Normal file
158
Index/Resetter.php
Normal file
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Index;
|
||||
|
||||
use Elastica\Index;
|
||||
use Elastica\Exception\ResponseException;
|
||||
use Elastica\Type\Mapping;
|
||||
use FOS\ElasticaBundle\Configuration\ConfigManager;
|
||||
use FOS\ElasticaBundle\Event\IndexResetEvent;
|
||||
use FOS\ElasticaBundle\Event\TypeResetEvent;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* Deletes and recreates indexes.
|
||||
*/
|
||||
class Resetter
|
||||
{
|
||||
/**
|
||||
* @var AliasProcessor
|
||||
*/
|
||||
private $aliasProcessor;
|
||||
|
||||
/***
|
||||
* @var ConfigManager
|
||||
*/
|
||||
private $configManager;
|
||||
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
/**
|
||||
* @var IndexManager
|
||||
*/
|
||||
private $indexManager;
|
||||
|
||||
/**
|
||||
* @var MappingBuilder
|
||||
*/
|
||||
private $mappingBuilder;
|
||||
|
||||
/**
|
||||
* @param ConfigManager $configManager
|
||||
* @param IndexManager $indexManager
|
||||
* @param AliasProcessor $aliasProcessor
|
||||
* @param MappingBuilder $mappingBuilder
|
||||
* @param EventDispatcherInterface $eventDispatcher
|
||||
*/
|
||||
public function __construct(
|
||||
ConfigManager $configManager,
|
||||
IndexManager $indexManager,
|
||||
AliasProcessor $aliasProcessor,
|
||||
MappingBuilder $mappingBuilder,
|
||||
EventDispatcherInterface $eventDispatcher
|
||||
) {
|
||||
$this->aliasProcessor = $aliasProcessor;
|
||||
$this->configManager = $configManager;
|
||||
$this->dispatcher = $eventDispatcher;
|
||||
$this->indexManager = $indexManager;
|
||||
$this->mappingBuilder = $mappingBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes and recreates all indexes.
|
||||
*
|
||||
* @param bool $populating
|
||||
* @param bool $force
|
||||
*/
|
||||
public function resetAllIndexes($populating = false, $force = false)
|
||||
{
|
||||
foreach ($this->configManager->getIndexNames() as $name) {
|
||||
$this->resetIndex($name, $populating, $force);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes and recreates the named index. If populating, creates a new index
|
||||
* with a randomised name for an alias to be set after population.
|
||||
*
|
||||
* @param string $indexName
|
||||
* @param bool $populating
|
||||
* @param bool $force If index exists with same name as alias, remove it
|
||||
*
|
||||
* @throws \InvalidArgumentException if no index exists for the given name
|
||||
*/
|
||||
public function resetIndex($indexName, $populating = false, $force = false)
|
||||
{
|
||||
$indexConfig = $this->configManager->getIndexConfiguration($indexName);
|
||||
$index = $this->indexManager->getIndex($indexName);
|
||||
|
||||
$event = new IndexResetEvent($indexName, $populating, $force);
|
||||
$this->dispatcher->dispatch(IndexResetEvent::PRE_INDEX_RESET, $event);
|
||||
|
||||
if ($indexConfig->isUseAlias()) {
|
||||
$this->aliasProcessor->setRootName($indexConfig, $index);
|
||||
}
|
||||
|
||||
$mapping = $this->mappingBuilder->buildIndexMapping($indexConfig);
|
||||
$index->create($mapping, true);
|
||||
|
||||
if (!$populating and $indexConfig->isUseAlias()) {
|
||||
$this->aliasProcessor->switchIndexAlias($indexConfig, $index, $force);
|
||||
}
|
||||
|
||||
$this->dispatcher->dispatch(IndexResetEvent::POST_INDEX_RESET, $event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes and recreates a mapping type for the named index.
|
||||
*
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*
|
||||
* @throws \InvalidArgumentException if no index or type mapping exists for the given names
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function resetIndexType($indexName, $typeName)
|
||||
{
|
||||
$typeConfig = $this->configManager->getTypeConfiguration($indexName, $typeName);
|
||||
$type = $this->indexManager->getIndex($indexName)->getType($typeName);
|
||||
|
||||
$event = new TypeResetEvent($indexName, $typeName);
|
||||
$this->dispatcher->dispatch(TypeResetEvent::PRE_TYPE_RESET, $event);
|
||||
|
||||
try {
|
||||
$type->delete();
|
||||
} catch (ResponseException $e) {
|
||||
if (strpos($e->getMessage(), 'TypeMissingException') === false) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$mapping = new Mapping();
|
||||
foreach ($this->mappingBuilder->buildTypeMapping($typeConfig) as $name => $field) {
|
||||
$mapping->setParam($name, $field);
|
||||
}
|
||||
|
||||
$type->setMapping($mapping);
|
||||
|
||||
$this->dispatcher->dispatch(TypeResetEvent::POST_TYPE_RESET, $event);
|
||||
}
|
||||
|
||||
/**
|
||||
* A command run when a population has finished.
|
||||
*
|
||||
* @param string $indexName
|
||||
*/
|
||||
public function postPopulate($indexName)
|
||||
{
|
||||
$indexConfig = $this->configManager->getIndexConfiguration($indexName);
|
||||
|
||||
if ($indexConfig->isUseAlias()) {
|
||||
$index = $this->indexManager->getIndex($indexName);
|
||||
$this->aliasProcessor->switchIndexAlias($indexConfig, $index);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,62 +2,11 @@
|
|||
|
||||
namespace FOS\ElasticaBundle;
|
||||
|
||||
use Elastica\Index;
|
||||
use FOS\ElasticaBundle\Index\IndexManager as BaseIndexManager;
|
||||
|
||||
class IndexManager
|
||||
/**
|
||||
* @deprecated Use \FOS\ElasticaBundle\Index\IndexManager
|
||||
*/
|
||||
class IndexManager extends BaseIndexManager
|
||||
{
|
||||
protected $indexesByName;
|
||||
protected $defaultIndexName;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $indexesByName
|
||||
* @param Index $defaultIndex
|
||||
*/
|
||||
public function __construct(array $indexesByName, Index $defaultIndex)
|
||||
{
|
||||
$this->indexesByName = $indexesByName;
|
||||
$this->defaultIndexName = $defaultIndex->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all registered indexes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAllIndexes()
|
||||
{
|
||||
return $this->indexesByName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an index by its name
|
||||
*
|
||||
* @param string $name Index to return, or the default index if null
|
||||
* @return Index
|
||||
* @throws \InvalidArgumentException if no index exists for the given name
|
||||
*/
|
||||
public function getIndex($name = null)
|
||||
{
|
||||
if (null === $name) {
|
||||
$name = $this->defaultIndexName;
|
||||
}
|
||||
|
||||
if (!isset($this->indexesByName[$name])) {
|
||||
throw new \InvalidArgumentException(sprintf('The index "%s" does not exist', $name));
|
||||
}
|
||||
|
||||
return $this->indexesByName[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default index
|
||||
*
|
||||
* @return Index
|
||||
*/
|
||||
public function getDefaultIndex()
|
||||
{
|
||||
return $this->getIndex($this->defaultIndexName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,13 +25,13 @@ class RepositoryManager implements RepositoryManagerInterface
|
|||
|
||||
public function addEntity($entityName, FinderInterface $finder, $repositoryName = null)
|
||||
{
|
||||
$this->entities[$entityName]= array();
|
||||
$this->entities[$entityName] = array();
|
||||
$this->entities[$entityName]['finder'] = $finder;
|
||||
$this->entities[$entityName]['repositoryName'] = $repositoryName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return repository for entity
|
||||
* Return repository for entity.
|
||||
*
|
||||
* Returns custom repository if one specified otherwise
|
||||
* returns a basic repository.
|
||||
|
@ -59,16 +59,20 @@ class RepositoryManager implements RepositoryManagerInterface
|
|||
}
|
||||
|
||||
$refClass = new \ReflectionClass($entityName);
|
||||
$annotation = $this->reader->getClassAnnotation($refClass, 'FOS\\ElasticaBundle\\Configuration\\Search');
|
||||
$annotation = $this->reader->getClassAnnotation($refClass, 'FOS\\ElasticaBundle\\Annotation\\Search');
|
||||
if ($annotation) {
|
||||
$this->entities[$entityName]['repositoryName']
|
||||
= $annotation->repositoryClass;
|
||||
|
||||
return $annotation->repositoryClass;
|
||||
}
|
||||
|
||||
return 'FOS\ElasticaBundle\Repository';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entityName
|
||||
*/
|
||||
private function createRepository($entityName)
|
||||
{
|
||||
if (!class_exists($repositoryName = $this->getRepositoryName($entityName))) {
|
||||
|
|
|
@ -12,7 +12,6 @@ use FOS\ElasticaBundle\Finder\FinderInterface;
|
|||
*/
|
||||
interface RepositoryManagerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Adds entity name and its finder.
|
||||
* Custom repository class name can also be added.
|
||||
|
@ -24,7 +23,7 @@ interface RepositoryManagerInterface
|
|||
public function addEntity($entityName, FinderInterface $finder, $repositoryName = null);
|
||||
|
||||
/**
|
||||
* Return repository for entity
|
||||
* Return repository for entity.
|
||||
*
|
||||
* Returns custom repository if one specified otherwise
|
||||
* returns a basic repository.
|
||||
|
|
|
@ -20,8 +20,6 @@ class FantaPaginatorAdapter implements AdapterInterface
|
|||
* Returns the number of results.
|
||||
*
|
||||
* @return integer The number of results.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getNbResults()
|
||||
{
|
||||
|
@ -29,15 +27,25 @@ class FantaPaginatorAdapter implements AdapterInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns Facets
|
||||
* Returns Facets.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFacets()
|
||||
{
|
||||
return $this->adapter->getFacets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Aggregations.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getFacets()
|
||||
public function getAggregations()
|
||||
{
|
||||
return $this->adapter->getFacets();
|
||||
return $this->adapter->getAggregations();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,8 +55,6 @@ class FantaPaginatorAdapter implements AdapterInterface
|
|||
* @param integer $length The length.
|
||||
*
|
||||
* @return array|\Traversable The slice.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getSlice($offset, $length)
|
||||
{
|
||||
|
|
|
@ -8,10 +8,8 @@ interface PaginatorAdapterInterface
|
|||
* Returns the number of results.
|
||||
*
|
||||
* @return integer The number of results.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
function getTotalHits();
|
||||
public function getTotalHits();
|
||||
|
||||
/**
|
||||
* Returns an slice of the results.
|
||||
|
@ -20,15 +18,20 @@ interface PaginatorAdapterInterface
|
|||
* @param integer $length The length.
|
||||
*
|
||||
* @return PartialResultsInterface
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
function getResults($offset, $length);
|
||||
public function getResults($offset, $length);
|
||||
|
||||
/**
|
||||
* Returns Facets
|
||||
* Returns Facets.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function getFacets();
|
||||
public function getFacets();
|
||||
|
||||
/**
|
||||
* Returns Aggregations.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAggregations();
|
||||
}
|
||||
|
|
|
@ -8,24 +8,27 @@ interface PartialResultsInterface
|
|||
* Returns the paginated results.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
function toArray();
|
||||
public function toArray();
|
||||
|
||||
/**
|
||||
* Returns the number of results.
|
||||
*
|
||||
* @return integer The number of results.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
function getTotalHits();
|
||||
public function getTotalHits();
|
||||
|
||||
/**
|
||||
* Returns the facets
|
||||
* Returns the facets.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getFacets();
|
||||
}
|
||||
public function getFacets();
|
||||
|
||||
/**
|
||||
* Returns the aggregations.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAggregations();
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use Elastica\ResultSet;
|
|||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Allows pagination of Elastica\Query. Does not map results
|
||||
* Allows pagination of Elastica\Query. Does not map results.
|
||||
*/
|
||||
class RawPaginatorAdapter implements PaginatorAdapterInterface
|
||||
{
|
||||
|
@ -37,11 +37,16 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
*/
|
||||
private $facets;
|
||||
|
||||
/**
|
||||
* @var array for the aggregations
|
||||
*/
|
||||
private $aggregations;
|
||||
|
||||
/**
|
||||
* @see PaginatorAdapterInterface::__construct
|
||||
*
|
||||
* @param SearchableInterface $searchable the object to search in
|
||||
* @param Query $query the query to search
|
||||
* @param Query $query the query to search
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(SearchableInterface $searchable, Query $query, array $options = array())
|
||||
|
@ -54,9 +59,11 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
/**
|
||||
* Returns the paginated results.
|
||||
*
|
||||
* @param $offset
|
||||
* @param $itemCountPerPage
|
||||
* @param integer $offset
|
||||
* @param integer $itemCountPerPage
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return ResultSet
|
||||
*/
|
||||
protected function getElasticaResults($offset, $itemCountPerPage)
|
||||
|
@ -67,7 +74,7 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
? (integer) $this->query->getParam('size')
|
||||
: null;
|
||||
|
||||
if ($size && $size < $offset + $itemCountPerPage) {
|
||||
if (null !== $size && $size < $offset + $itemCountPerPage) {
|
||||
$itemCountPerPage = $size - $offset;
|
||||
}
|
||||
|
||||
|
@ -82,6 +89,8 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
$resultSet = $this->searchable->search($query, $this->options);
|
||||
$this->totalHits = $resultSet->getTotalHits();
|
||||
$this->facets = $resultSet->getFacets();
|
||||
$this->aggregations = $resultSet->getAggregations();
|
||||
|
||||
return $resultSet;
|
||||
}
|
||||
|
||||
|
@ -90,6 +99,7 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
*
|
||||
* @param int $offset
|
||||
* @param int $itemCountPerPage
|
||||
*
|
||||
* @return PartialResultsInterface
|
||||
*/
|
||||
public function getResults($offset, $itemCountPerPage)
|
||||
|
@ -100,27 +110,33 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
/**
|
||||
* Returns the number of results.
|
||||
*
|
||||
* If genuineTotal is provided as true, total hits is returned from the
|
||||
* hits.total value from the search results instead of just returning
|
||||
* the requested size.
|
||||
*
|
||||
* @param boolean $genuineTotal
|
||||
*
|
||||
* @return integer The number of results.
|
||||
*/
|
||||
public function getTotalHits()
|
||||
public function getTotalHits($genuineTotal = false)
|
||||
{
|
||||
if ( ! isset($this->totalHits)) {
|
||||
$this->totalHits = $this->searchable->search($this->query)->getTotalHits();
|
||||
if (! isset($this->totalHits)) {
|
||||
$this->totalHits = $this->searchable->count($this->query);
|
||||
}
|
||||
|
||||
return $this->query->hasParam('size')
|
||||
return $this->query->hasParam('size') && !$genuineTotal
|
||||
? min($this->totalHits, (integer) $this->query->getParam('size'))
|
||||
: $this->totalHits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Facets
|
||||
* Returns Facets.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFacets()
|
||||
{
|
||||
if ( ! isset($this->facets)) {
|
||||
if (! isset($this->facets)) {
|
||||
$this->facets = $this->searchable->search($this->query)->getFacets();
|
||||
}
|
||||
|
||||
|
@ -128,7 +144,21 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the Query
|
||||
* Returns Aggregations.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAggregations()
|
||||
{
|
||||
if (!isset($this->aggregations)) {
|
||||
$this->aggregations = $this->searchable->search($this->query)->getAggregations();
|
||||
}
|
||||
|
||||
return $this->aggregations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Query.
|
||||
*
|
||||
* @return Query the search query
|
||||
*/
|
||||
|
|
|
@ -6,7 +6,7 @@ use Elastica\ResultSet;
|
|||
use Elastica\Result;
|
||||
|
||||
/**
|
||||
* Raw partial results transforms to a simple array
|
||||
* Raw partial results transforms to a simple array.
|
||||
*/
|
||||
class RawPartialResults implements PartialResultsInterface
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ class RawPartialResults implements PartialResultsInterface
|
|||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return array_map(function(Result $result) {
|
||||
return array_map(function (Result $result) {
|
||||
return $result->getSource();
|
||||
}, $this->resultSet->getResults());
|
||||
}
|
||||
|
@ -47,6 +47,18 @@ class RawPartialResults implements PartialResultsInterface
|
|||
return $this->resultSet->getFacets();
|
||||
}
|
||||
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getAggregations()
|
||||
{
|
||||
if ($this->resultSet->hasAggregations()) {
|
||||
return $this->resultSet->getAggregations();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,15 +7,15 @@ use Elastica\SearchableInterface;
|
|||
use Elastica\Query;
|
||||
|
||||
/**
|
||||
* Allows pagination of \Elastica\Query
|
||||
* Allows pagination of \Elastica\Query.
|
||||
*/
|
||||
class TransformedPaginatorAdapter extends RawPaginatorAdapter
|
||||
{
|
||||
private $transformer;
|
||||
|
||||
/**
|
||||
* @param SearchableInterface $searchable the object to search in
|
||||
* @param Query $query the query to search
|
||||
* @param SearchableInterface $searchable the object to search in
|
||||
* @param Query $query the query to search
|
||||
* @param array $options
|
||||
* @param ElasticaToModelTransformerInterface $transformer the transformer for fetching the results
|
||||
*/
|
||||
|
|
|
@ -6,14 +6,14 @@ use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
|||
use Elastica\ResultSet;
|
||||
|
||||
/**
|
||||
* Partial transformed result set
|
||||
* Partial transformed result set.
|
||||
*/
|
||||
class TransformedPartialResults extends RawPartialResults
|
||||
{
|
||||
protected $transformer;
|
||||
|
||||
/**
|
||||
* @param ResultSet $resultSet
|
||||
* @param ResultSet $resultSet
|
||||
* @param \FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface $transformer
|
||||
*/
|
||||
public function __construct(ResultSet $resultSet, ElasticaToModelTransformerInterface $transformer)
|
||||
|
@ -30,4 +30,4 @@ class TransformedPartialResults extends RawPartialResults
|
|||
{
|
||||
return $this->transformer->transform($this->resultSet->getResults());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,13 @@ namespace FOS\ElasticaBundle\Persister;
|
|||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Elastica\Exception\BulkException;
|
||||
use Elastica\Exception\NotFoundException;
|
||||
use FOS\ElasticaBundle\Transformer\ModelToElasticaTransformerInterface;
|
||||
use Elastica\Type;
|
||||
use Elastica\Document;
|
||||
|
||||
/**
|
||||
* Inserts, replaces and deletes single documents in an elastica type
|
||||
* Accepts domain model objects and converts them to elastica documents
|
||||
* Accepts domain model objects and converts them to elastica documents.
|
||||
*
|
||||
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
|
||||
*/
|
||||
|
@ -31,17 +30,32 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
$this->fields = $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the ObjectPersister handles a given object.
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function handlesObject($object)
|
||||
{
|
||||
return $object instanceof $this->objectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LoggerInterface $logger
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log exception if logger defined for persister belonging to the current listener, otherwise re-throw
|
||||
* Log exception if logger defined for persister belonging to the current listener, otherwise re-throw.
|
||||
*
|
||||
* @param BulkException $e
|
||||
*
|
||||
* @throws BulkException
|
||||
* @return null
|
||||
*/
|
||||
private function log(BulkException $e)
|
||||
{
|
||||
|
@ -54,61 +68,47 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
|
||||
/**
|
||||
* Insert one object into the type
|
||||
* The object will be transformed to an elastica document
|
||||
* The object will be transformed to an elastica document.
|
||||
*
|
||||
* @param object $object
|
||||
*/
|
||||
public function insertOne($object)
|
||||
{
|
||||
$document = $this->transformToElasticaDocument($object);
|
||||
$this->type->addDocument($document);
|
||||
$this->insertMany(array($object));
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces one object in the type
|
||||
* Replaces one object in the type.
|
||||
*
|
||||
* @param object $object
|
||||
* @return null
|
||||
**/
|
||||
public function replaceOne($object)
|
||||
{
|
||||
$document = $this->transformToElasticaDocument($object);
|
||||
try {
|
||||
$this->type->deleteById($document->getId());
|
||||
} catch (NotFoundException $e) {}
|
||||
$this->type->addDocument($document);
|
||||
$this->replaceMany(array($object));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes one object in the type
|
||||
* Deletes one object in the type.
|
||||
*
|
||||
* @param object $object
|
||||
* @return null
|
||||
**/
|
||||
public function deleteOne($object)
|
||||
{
|
||||
$document = $this->transformToElasticaDocument($object);
|
||||
try {
|
||||
$this->type->deleteById($document->getId());
|
||||
} catch (NotFoundException $e) {}
|
||||
$this->deleteMany(array($object));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes one object in the type by id
|
||||
* Deletes one object in the type by id.
|
||||
*
|
||||
* @param mixed $id
|
||||
*
|
||||
* @return null
|
||||
**/
|
||||
public function deleteById($id)
|
||||
{
|
||||
try {
|
||||
$this->type->deleteById($id);
|
||||
} catch (NotFoundException $e) {}
|
||||
$this->deleteManyByIdentifiers(array($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk insert an array of objects in the type for the given method
|
||||
* Bulk insert an array of objects in the type for the given method.
|
||||
*
|
||||
* @param array $objects array of domain model objects
|
||||
* @param string Method to call
|
||||
|
@ -148,7 +148,7 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Bulk deletes an array of objects in the type
|
||||
* Bulk deletes an array of objects in the type.
|
||||
*
|
||||
* @param array $objects array of domain model objects
|
||||
*/
|
||||
|
@ -166,7 +166,7 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Bulk deletes records from an array of identifiers
|
||||
* Bulk deletes records from an array of identifiers.
|
||||
*
|
||||
* @param array $identifiers array of domain model object identifiers
|
||||
*/
|
||||
|
@ -180,9 +180,10 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Transforms an object to an elastica document
|
||||
* Transforms an object to an elastica document.
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @return Document the elastica document
|
||||
*/
|
||||
public function transformToElasticaDocument($object)
|
||||
|
|
|
@ -4,66 +4,73 @@ namespace FOS\ElasticaBundle\Persister;
|
|||
|
||||
/**
|
||||
* Inserts, replaces and deletes single documents in an elastica type
|
||||
* Accepts domain model objects and converts them to elastica documents
|
||||
* Accepts domain model objects and converts them to elastica documents.
|
||||
*
|
||||
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
|
||||
*/
|
||||
interface ObjectPersisterInterface
|
||||
{
|
||||
/**
|
||||
* Checks if this persister can handle the given object or not.
|
||||
*
|
||||
* @param mixed $object
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function handlesObject($object);
|
||||
|
||||
/**
|
||||
* Insert one object into the type
|
||||
* The object will be transformed to an elastica document
|
||||
* The object will be transformed to an elastica document.
|
||||
*
|
||||
* @param object $object
|
||||
*/
|
||||
function insertOne($object);
|
||||
public function insertOne($object);
|
||||
|
||||
/**
|
||||
* Replaces one object in the type
|
||||
* Replaces one object in the type.
|
||||
*
|
||||
* @param object $object
|
||||
**/
|
||||
function replaceOne($object);
|
||||
public function replaceOne($object);
|
||||
|
||||
/**
|
||||
* Deletes one object in the type
|
||||
* Deletes one object in the type.
|
||||
*
|
||||
* @param object $object
|
||||
**/
|
||||
function deleteOne($object);
|
||||
public function deleteOne($object);
|
||||
|
||||
/**
|
||||
* Deletes one object in the type by id
|
||||
* Deletes one object in the type by id.
|
||||
*
|
||||
* @param mixed $id
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
function deleteById($id);
|
||||
public function deleteById($id);
|
||||
|
||||
/**
|
||||
* Bulk inserts an array of objects in the type
|
||||
* Bulk inserts an array of objects in the type.
|
||||
*
|
||||
* @param array $objects array of domain model objects
|
||||
*/
|
||||
function insertMany(array $objects);
|
||||
public function insertMany(array $objects);
|
||||
|
||||
/**
|
||||
* Bulk updates an array of objects in the type
|
||||
* Bulk updates an array of objects in the type.
|
||||
*
|
||||
* @param array $objects array of domain model objects
|
||||
*/
|
||||
function replaceMany(array $objects);
|
||||
public function replaceMany(array $objects);
|
||||
|
||||
/**
|
||||
* Bulk deletes an array of objects in the type
|
||||
* Bulk deletes an array of objects in the type.
|
||||
*
|
||||
* @param array $objects array of domain model objects
|
||||
*/
|
||||
function deleteMany(array $objects);
|
||||
public function deleteMany(array $objects);
|
||||
|
||||
/**
|
||||
* Bulk deletes records from an array of identifiers
|
||||
* Bulk deletes records from an array of identifiers.
|
||||
*
|
||||
* @param array $identifiers array of domain model object identifiers
|
||||
*/
|
||||
|
|
|
@ -9,7 +9,7 @@ use FOS\ElasticaBundle\Transformer\ModelToElasticaTransformerInterface;
|
|||
/**
|
||||
* Inserts, replaces and deletes single objects in an elastica type, making use
|
||||
* of elastica's serializer support to convert objects in to elastica documents.
|
||||
* Accepts domain model objects and passes them directly to elastica
|
||||
* Accepts domain model objects and passes them directly to elastica.
|
||||
*
|
||||
* @author Lea Haensenberber <lea.haensenberger@gmail.com>
|
||||
*/
|
||||
|
@ -17,17 +17,25 @@ class ObjectSerializerPersister extends ObjectPersister
|
|||
{
|
||||
protected $serializer;
|
||||
|
||||
/**
|
||||
* @param Type $type
|
||||
* @param ModelToElasticaTransformerInterface $transformer
|
||||
* @param string $objectClass
|
||||
* @param callable $serializer
|
||||
*/
|
||||
public function __construct(Type $type, ModelToElasticaTransformerInterface $transformer, $objectClass, $serializer)
|
||||
{
|
||||
parent::__construct($type, $transformer, $objectClass, array());
|
||||
$this->serializer = $serializer;
|
||||
|
||||
$this->serializer = $serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an object to an elastica document
|
||||
* with just the identifier set
|
||||
* with just the identifier set.
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @return Document the elastica document
|
||||
*/
|
||||
public function transformToElasticaDocument($object)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace FOS\ElasticaBundle\Propel;
|
||||
|
||||
use FOS\ElasticaBundle\HybridResult;
|
||||
use FOS\ElasticaBundle\Transformer\AbstractElasticaToModelTransformer;
|
||||
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
|
@ -14,7 +15,7 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
|||
*
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
*/
|
||||
class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
||||
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
||||
{
|
||||
/**
|
||||
* Propel model class to map to Elastica documents.
|
||||
|
@ -33,18 +34,11 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
'identifier' => 'id',
|
||||
);
|
||||
|
||||
/**
|
||||
* PropertyAccessor instance.
|
||||
*
|
||||
* @var PropertyAccessorInterface
|
||||
*/
|
||||
protected $propertyAccessor;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $objectClass
|
||||
* @param array $options
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($objectClass, array $options = array())
|
||||
{
|
||||
|
@ -52,21 +46,12 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PropertyAccessor instance.
|
||||
*
|
||||
* @param PropertyAccessorInterface $propertyAccessor
|
||||
*/
|
||||
public function setPropertyAccessor(PropertyAccessorInterface $propertyAccessor)
|
||||
{
|
||||
$this->propertyAccessor = $propertyAccessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an array of Elastica document into an array of Propel entities
|
||||
* fetched from the database.
|
||||
*
|
||||
* @param array $elasticaObjects
|
||||
*
|
||||
* @return array|\ArrayObject
|
||||
*/
|
||||
public function transform(array $elasticaObjects)
|
||||
|
@ -81,11 +66,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
// Sort objects in the order of their IDs
|
||||
$idPos = array_flip($ids);
|
||||
$identifier = $this->options['identifier'];
|
||||
$propertyAccessor = $this->propertyAccessor;
|
||||
|
||||
$sortCallback = function($a, $b) use ($idPos, $identifier, $propertyAccessor) {
|
||||
return $idPos[$propertyAccessor->getValue($a, $identifier)] > $idPos[$propertyAccessor->getValue($b, $identifier)];
|
||||
};
|
||||
$sortCallback = $this->getSortingClosure($idPos, $identifier);
|
||||
|
||||
if (is_object($objects)) {
|
||||
$objects->uasort($sortCallback);
|
||||
|
@ -104,7 +85,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
$objects = $this->transform($elasticaObjects);
|
||||
|
||||
$result = array();
|
||||
for ($i = 0; $i < count($elasticaObjects); $i++) {
|
||||
for ($i = 0, $j = count($elasticaObjects); $i < $j; $i++) {
|
||||
$result[] = new HybridResult($elasticaObjects[$i], $objects[$i]);
|
||||
}
|
||||
|
||||
|
@ -135,6 +116,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
*
|
||||
* @param array $identifierValues Identifier values
|
||||
* @param boolean $hydrate Whether or not to hydrate the results
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function findByIdentifiers(array $identifierValues, $hydrate)
|
||||
|
@ -145,7 +127,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
|
||||
$query = $this->createQuery($this->objectClass, $this->options['identifier'], $identifierValues);
|
||||
|
||||
if ( ! $hydrate) {
|
||||
if (! $hydrate) {
|
||||
return $query->toArray();
|
||||
}
|
||||
|
||||
|
@ -158,6 +140,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
* @param string $class Propel model class
|
||||
* @param string $identifierField Identifier field name (e.g. "id")
|
||||
* @param array $identifierValues Identifier values
|
||||
*
|
||||
* @return \ModelCriteria
|
||||
*/
|
||||
protected function createQuery($class, $identifierField, array $identifierValues)
|
||||
|
@ -170,6 +153,8 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
|
||||
/**
|
||||
* @see https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Util/Inflector.php
|
||||
*
|
||||
* @param string $str
|
||||
*/
|
||||
private function camelize($str)
|
||||
{
|
||||
|
|
|
@ -5,44 +5,69 @@ namespace FOS\ElasticaBundle\Propel;
|
|||
use FOS\ElasticaBundle\Provider\AbstractProvider;
|
||||
|
||||
/**
|
||||
* Propel provider
|
||||
* Propel provider.
|
||||
*
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
*/
|
||||
class Provider extends AbstractProvider
|
||||
{
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Provider\ProviderInterface::populate()
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function populate(\Closure $loggerClosure = null, array $options = array())
|
||||
public function doPopulate($options, \Closure $loggerClosure = null)
|
||||
{
|
||||
$queryClass = $this->objectClass . 'Query';
|
||||
$queryClass = $this->objectClass.'Query';
|
||||
$nbObjects = $queryClass::create()->count();
|
||||
$offset = isset($options['offset']) ? intval($options['offset']) : 0;
|
||||
$sleep = isset($options['sleep']) ? intval($options['sleep']) : 0;
|
||||
$batchSize = isset($options['batch-size']) ? intval($options['batch-size']) : $this->options['batch_size'];
|
||||
|
||||
for (; $offset < $nbObjects; $offset += $batchSize) {
|
||||
if ($loggerClosure) {
|
||||
$stepStartTime = microtime(true);
|
||||
$offset = $options['offset'];
|
||||
|
||||
for (; $offset < $nbObjects; $offset += $options['batch_size']) {
|
||||
$objects = $queryClass::create()
|
||||
->limit($options['batch_size'])
|
||||
->offset($offset)
|
||||
->find()
|
||||
->getArrayCopy();
|
||||
$objects = $this->filterObjects($options, $objects);
|
||||
if (!empty($objects)) {
|
||||
$this->objectPersister->insertMany($objects);
|
||||
}
|
||||
|
||||
$objects = $queryClass::create()
|
||||
->limit($batchSize)
|
||||
->offset($offset)
|
||||
->find();
|
||||
|
||||
$this->objectPersister->insertMany($objects->getArrayCopy());
|
||||
|
||||
usleep($sleep);
|
||||
usleep($options['sleep']);
|
||||
|
||||
if ($loggerClosure) {
|
||||
$stepNbObjects = count($objects);
|
||||
$stepCount = $stepNbObjects + $offset;
|
||||
$percentComplete = 100 * $stepCount / $nbObjects;
|
||||
$objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
|
||||
$loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s %s', $percentComplete, $stepCount, $nbObjects, $objectsPerSecond, $this->getMemoryUsage()));
|
||||
$loggerClosure($options['batch_size'], $nbObjects);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function configureOptions()
|
||||
{
|
||||
parent::configureOptions();
|
||||
|
||||
$this->resolver->setDefaults(array(
|
||||
'clear_object_manager' => true,
|
||||
'debug_logging' => false,
|
||||
'ignore_errors' => false,
|
||||
'offset' => 0,
|
||||
'query_builder_method' => null,
|
||||
'sleep' => 0
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function disableLogging()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function enableLogging($logger)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,16 +3,17 @@
|
|||
namespace FOS\ElasticaBundle\Provider;
|
||||
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* AbstractProvider
|
||||
* AbstractProvider.
|
||||
*/
|
||||
abstract class AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var ObjectPersisterInterface
|
||||
* @var array
|
||||
*/
|
||||
protected $objectPersister;
|
||||
protected $baseOptions;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
@ -20,29 +21,154 @@ abstract class AbstractProvider implements ProviderInterface
|
|||
protected $objectClass;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @var ObjectPersisterInterface
|
||||
*/
|
||||
protected $options;
|
||||
protected $objectPersister;
|
||||
|
||||
/**
|
||||
* @var OptionsResolver
|
||||
*/
|
||||
protected $resolver;
|
||||
|
||||
/**
|
||||
* @var IndexableInterface
|
||||
*/
|
||||
private $indexable;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param IndexableInterface $indexable
|
||||
* @param string $objectClass
|
||||
* @param array $options
|
||||
* @param array $baseOptions
|
||||
*/
|
||||
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $options = array())
|
||||
{
|
||||
$this->objectPersister = $objectPersister;
|
||||
public function __construct(
|
||||
ObjectPersisterInterface $objectPersister,
|
||||
IndexableInterface $indexable,
|
||||
$objectClass,
|
||||
array $baseOptions = array()
|
||||
) {
|
||||
$this->baseOptions = $baseOptions;
|
||||
$this->indexable = $indexable;
|
||||
$this->objectClass = $objectClass;
|
||||
|
||||
$this->options = array_merge(array(
|
||||
'batch_size' => 100,
|
||||
), $options);
|
||||
$this->objectPersister = $objectPersister;
|
||||
$this->resolver = new OptionsResolver();
|
||||
$this->configureOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string with RAM usage information (current and peak)
|
||||
* {@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,
|
||||
'skip_indexable_check' => false,
|
||||
));
|
||||
$this->resolver->setAllowedTypes(array(
|
||||
'batch_size' => 'int'
|
||||
));
|
||||
|
||||
$this->resolver->setRequired(array(
|
||||
'indexName',
|
||||
'typeName',
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filters objects away if they are not indexable.
|
||||
*
|
||||
* @param array $options
|
||||
* @param array $objects
|
||||
* @return array
|
||||
*/
|
||||
protected function filterObjects(array $options, array $objects)
|
||||
{
|
||||
if ($options['skip_indexable_check']) {
|
||||
return $objects;
|
||||
}
|
||||
|
||||
$index = $options['indexName'];
|
||||
$type = $options['typeName'];
|
||||
|
||||
$return = array();
|
||||
foreach ($objects as $object) {
|
||||
if (!$this->indexable->isObjectIndexable($index, $type, $object)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$return[] = $object;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given object should be indexed or not.
|
||||
*
|
||||
* @deprecated To be removed in 4.0
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isObjectIndexable($object)
|
||||
{
|
||||
return $this->indexable->isObjectIndexable(
|
||||
$this->baseOptions['indexName'],
|
||||
$this->baseOptions['typeName'],
|
||||
$object
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string with RAM usage information (current and peak).
|
||||
*
|
||||
* @deprecated To be removed in 4.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
@ -53,4 +179,17 @@ abstract class AbstractProvider implements ProviderInterface
|
|||
|
||||
return sprintf('(RAM : current=%uMo peak=%uMo)', $memory, $memoryMax);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the base options provided by the class with options passed to the populate
|
||||
* method and runs them through the resolver.
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function resolveOptions(array $options)
|
||||
{
|
||||
return $this->resolver->resolve(array_merge($this->baseOptions, $options));
|
||||
}
|
||||
}
|
||||
|
|
240
Provider/Indexable.php
Normal file
240
Provider/Indexable.php
Normal file
|
@ -0,0 +1,240 @@
|
|||
<?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\Provider;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\ExpressionLanguage\Expression;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\ExpressionLanguage\SyntaxError;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
class Indexable implements IndexableInterface
|
||||
{
|
||||
/**
|
||||
* An array of raw configured callbacks for all types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $callbacks = array();
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* An instance of ExpressionLanguage.
|
||||
*
|
||||
* @var ExpressionLanguage
|
||||
*/
|
||||
private $expressionLanguage;
|
||||
|
||||
/**
|
||||
* An array of initialised callbacks.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $initialisedCallbacks = array();
|
||||
|
||||
/**
|
||||
* PropertyAccessor instance.
|
||||
*
|
||||
* @var PropertyAccessorInterface
|
||||
*/
|
||||
private $propertyAccessor;
|
||||
|
||||
/**
|
||||
* @param array $callbacks
|
||||
* @param ContainerInterface $container
|
||||
*/
|
||||
public function __construct(array $callbacks, ContainerInterface $container)
|
||||
{
|
||||
$this->callbacks = $callbacks;
|
||||
$this->container = $container;
|
||||
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the object is indexable with respect to the callback.
|
||||
*
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param mixed $object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isObjectIndexable($indexName, $typeName, $object)
|
||||
{
|
||||
$type = sprintf('%s/%s', $indexName, $typeName);
|
||||
$callback = $this->getCallback($type, $object);
|
||||
if (!$callback) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($callback instanceof Expression) {
|
||||
return (bool) $this->getExpressionLanguage()->evaluate($callback, array(
|
||||
'object' => $object,
|
||||
$this->getExpressionVar($object) => $object,
|
||||
));
|
||||
}
|
||||
|
||||
return is_string($callback)
|
||||
? call_user_func(array($object, $callback))
|
||||
: call_user_func($callback, $object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and initialises a callback.
|
||||
*
|
||||
* @param string $type
|
||||
* @param object $object
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function buildCallback($type, $object)
|
||||
{
|
||||
if (!array_key_exists($type, $this->callbacks)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$callback = $this->callbacks[$type];
|
||||
|
||||
if (is_callable($callback) or is_callable(array($object, $callback))) {
|
||||
return $callback;
|
||||
}
|
||||
|
||||
if (is_array($callback) && !is_object($callback[0])) {
|
||||
return $this->processArrayToCallback($type, $callback);
|
||||
}
|
||||
|
||||
if (is_string($callback)) {
|
||||
return $this->buildExpressionCallback($type, $object, $callback);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('Callback for type "%s" is not a valid callback.', $type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a string expression into an Expression.
|
||||
*
|
||||
* @param string $type
|
||||
* @param mixed $object
|
||||
* @param string $callback
|
||||
*
|
||||
* @return Expression
|
||||
*/
|
||||
private function buildExpressionCallback($type, $object, $callback)
|
||||
{
|
||||
$expression = $this->getExpressionLanguage();
|
||||
if (!$expression) {
|
||||
throw new \RuntimeException('Unable to process an expression without the ExpressionLanguage component.');
|
||||
}
|
||||
|
||||
try {
|
||||
$callback = new Expression($callback);
|
||||
$expression->compile($callback, array(
|
||||
'object', $this->getExpressionVar($object)
|
||||
));
|
||||
|
||||
return $callback;
|
||||
} catch (SyntaxError $e) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Callback for type "%s" is an invalid expression',
|
||||
$type
|
||||
), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreives a cached callback, or creates a new callback if one is not found.
|
||||
*
|
||||
* @param string $type
|
||||
* @param object $object
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function getCallback($type, $object)
|
||||
{
|
||||
if (!array_key_exists($type, $this->initialisedCallbacks)) {
|
||||
$this->initialisedCallbacks[$type] = $this->buildCallback($type, $object);
|
||||
}
|
||||
|
||||
return $this->initialisedCallbacks[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ExpressionLanguage class if it is available.
|
||||
*
|
||||
* @return ExpressionLanguage|null
|
||||
*/
|
||||
private function getExpressionLanguage()
|
||||
{
|
||||
if (null === $this->expressionLanguage && class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
||||
$this->expressionLanguage = new ExpressionLanguage();
|
||||
}
|
||||
|
||||
return $this->expressionLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the variable name to be used to access the object when using the ExpressionLanguage
|
||||
* component to parse and evaluate an expression.
|
||||
*
|
||||
* @param mixed $object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getExpressionVar($object = null)
|
||||
{
|
||||
if (!is_object($object)) {
|
||||
return 'object';
|
||||
}
|
||||
|
||||
$ref = new \ReflectionClass($object);
|
||||
|
||||
return strtolower($ref->getShortName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an array into a callback. Replaces the first element with a service if
|
||||
* it begins with an @.
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $callback
|
||||
* @return array
|
||||
*/
|
||||
private function processArrayToCallback($type, array $callback)
|
||||
{
|
||||
list($class, $method) = $callback + array(null, '__invoke');
|
||||
|
||||
if (strpos($class, '@') === 0) {
|
||||
$service = $this->container->get(substr($class, 1));
|
||||
$callback = array($service, $method);
|
||||
|
||||
if (!is_callable($callback)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Method "%s" on service "%s" is not callable.',
|
||||
$method,
|
||||
substr($class, 1)
|
||||
));
|
||||
}
|
||||
|
||||
return $callback;
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Unable to parse callback array for type "%s"',
|
||||
$type
|
||||
));
|
||||
}
|
||||
}
|
26
Provider/IndexableInterface.php
Normal file
26
Provider/IndexableInterface.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?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\Provider;
|
||||
|
||||
interface IndexableInterface
|
||||
{
|
||||
/**
|
||||
* Checks if an object passed should be indexable or not.
|
||||
*
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param mixed $object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isObjectIndexable($indexName, $typeName, $object);
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
namespace FOS\ElasticaBundle\Provider;
|
||||
|
||||
/**
|
||||
* Insert application domain objects into elastica types
|
||||
* Insert application domain objects into elastica types.
|
||||
*
|
||||
* @author Thibault Duplessis <thibault.duplessis@gmail.com>
|
||||
*/
|
||||
|
@ -12,9 +12,15 @@ interface ProviderInterface
|
|||
/**
|
||||
* Persists all domain objects to ElasticSearch for this provider.
|
||||
*
|
||||
* The closure can expect 2 or 3 arguments:
|
||||
* * The step size
|
||||
* * The total number of objects
|
||||
* * A message to output in error conditions (not normally provided)
|
||||
*
|
||||
* @param \Closure $loggerClosure
|
||||
* @param array $options
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
function populate(\Closure $loggerClosure = null, array $options = array());
|
||||
public function populate(\Closure $loggerClosure = null, array $options = array());
|
||||
}
|
||||
|
|
|
@ -57,8 +57,10 @@ class ProviderRegistry implements ContainerAwareInterface
|
|||
*
|
||||
* Providers will be indexed by "type" strings in the returned array.
|
||||
*
|
||||
* @param string $index
|
||||
* @return array of ProviderInterface instances
|
||||
* @param string $index
|
||||
*
|
||||
* @return ProviderInterface[]
|
||||
*
|
||||
* @throws \InvalidArgumentException if no providers were registered for the index
|
||||
*/
|
||||
public function getIndexProviders($index)
|
||||
|
@ -81,7 +83,9 @@ class ProviderRegistry implements ContainerAwareInterface
|
|||
*
|
||||
* @param string $index
|
||||
* @param string $type
|
||||
*
|
||||
* @return ProviderInterface
|
||||
*
|
||||
* @throws \InvalidArgumentException if no provider was registered for the index and type
|
||||
*/
|
||||
public function getProvider($index, $type)
|
||||
|
|
|
@ -10,16 +10,17 @@ Symfony2. Features include:
|
|||
|
||||
> **Note** Propel support is limited and contributions fixing issues are welcome!
|
||||
|
||||
[![Build Status](https://secure.travis-ci.org/FriendsOfSymfony/FOSElasticaBundle.png?branch=master)](http://travis-ci.org/FriendsOfSymfony/FOSElasticaBundle) [![Total Downloads](https://poser.pugx.org/FriendsOfSymfony/elastica-bundle/downloads.png)](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle) [![Latest Stable Version](https://poser.pugx.org/FriendsOfSymfony/elastica-bundle/v/stable.png)](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle)
|
||||
[![Build Status](https://secure.travis-ci.org/FriendsOfSymfony/FOSElasticaBundle.png?branch=master)](http://travis-ci.org/FriendsOfSymfony/FOSElasticaBundle) [![Total Downloads](https://poser.pugx.org/FriendsOfSymfony/elastica-bundle/downloads.png)](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle) [![Latest Stable Version](https://poser.pugx.org/FriendsOfSymfony/elastica-bundle/v/stable.png)](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle) [![Latest Unstable Version](https://poser.pugx.org/friendsofsymfony/elastica-bundle/v/unstable.svg)](https://packagist.org/packages/friendsofsymfony/elastica-bundle)
|
||||
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/FriendsOfSymfony/FOSElasticaBundle/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/FriendsOfSymfony/FOSElasticaBundle/?branch=master)
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Documentation for FOSElasticaBundle is in `Resources/doc/index.md`
|
||||
|
||||
[Read the documentation for 3.0.x (master)](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/Resources/doc/index.md)
|
||||
[Read the documentation for 3.1.x](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/Resources/doc/index.md)
|
||||
|
||||
[Read the documentation for 2.1.x](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/2.1.x/README.md)
|
||||
[Read the documentation for 3.0.x](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/3.0.x/Resources/doc/index.md)
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
|
|
@ -19,21 +19,47 @@ class Repository
|
|||
$this->finder = $finder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $query
|
||||
* @param integer $limit
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function find($query, $limit = null, $options = array())
|
||||
{
|
||||
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())
|
||||
{
|
||||
return $this->finder->findHybrid($query, $limit, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $query
|
||||
* @param array $options
|
||||
*
|
||||
* @return \Pagerfanta\Pagerfanta
|
||||
*/
|
||||
public function findPaginated($query, $options = array())
|
||||
{
|
||||
return $this->finder->findPaginated($query, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $options
|
||||
*
|
||||
* @return Paginator\PaginatorAdapterInterface
|
||||
*/
|
||||
public function createPaginatorAdapter($query, $options = array())
|
||||
{
|
||||
return $this->finder->createPaginatorAdapter($query, $options);
|
||||
|
|
240
Resetter.php
240
Resetter.php
|
@ -2,245 +2,11 @@
|
|||
|
||||
namespace FOS\ElasticaBundle;
|
||||
|
||||
use Elastica\Exception\ExceptionInterface;
|
||||
use Elastica\Index;
|
||||
use Elastica\Exception\ResponseException;
|
||||
use Elastica\Type\Mapping;
|
||||
use FOS\ElasticaBundle\Index\Resetter as BaseResetter;
|
||||
|
||||
/**
|
||||
* Deletes and recreates indexes
|
||||
* @deprecated Use \FOS\ElasticaBundle\Index\Resetter
|
||||
*/
|
||||
class Resetter
|
||||
class Resetter extends BaseResetter
|
||||
{
|
||||
protected $indexConfigsByName;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $indexConfigsByName
|
||||
*/
|
||||
public function __construct(array $indexConfigsByName)
|
||||
{
|
||||
$this->indexConfigsByName = $indexConfigsByName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes and recreates all indexes
|
||||
*/
|
||||
public function resetAllIndexes()
|
||||
{
|
||||
foreach (array_keys($this->indexConfigsByName) as $name) {
|
||||
$this->resetIndex($name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes and recreates the named index
|
||||
*
|
||||
* @param string $indexName
|
||||
* @throws \InvalidArgumentException if no index exists for the given name
|
||||
*/
|
||||
public function resetIndex($indexName)
|
||||
{
|
||||
$indexConfig = $this->getIndexConfig($indexName);
|
||||
$esIndex = $indexConfig['index'];
|
||||
if (isset($indexConfig['use_alias']) && $indexConfig['use_alias']) {
|
||||
$name = $indexConfig['name_or_alias'];
|
||||
$name .= uniqid();
|
||||
$esIndex->overrideName($name);
|
||||
$esIndex->create($indexConfig['config']);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$esIndex->create($indexConfig['config'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes and recreates a mapping type for the named index
|
||||
*
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @throws \InvalidArgumentException if no index or type mapping exists for the given names
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function resetIndexType($indexName, $typeName)
|
||||
{
|
||||
$indexConfig = $this->getIndexConfig($indexName);
|
||||
|
||||
if (!isset($indexConfig['config']['mappings'][$typeName]['properties'])) {
|
||||
throw new \InvalidArgumentException(sprintf('The mapping for index "%s" and type "%s" does not exist.', $indexName, $typeName));
|
||||
}
|
||||
|
||||
$type = $indexConfig['index']->getType($typeName);
|
||||
try {
|
||||
$type->delete();
|
||||
} catch (ResponseException $e) {
|
||||
if (strpos($e->getMessage(), 'TypeMissingException') === false) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
$mapping = $this->createMapping($indexConfig['config']['mappings'][$typeName]);
|
||||
$type->setMapping($mapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* create type mapping object
|
||||
*
|
||||
* @param array $indexConfig
|
||||
* @return Mapping
|
||||
*/
|
||||
protected function createMapping($indexConfig)
|
||||
{
|
||||
$mapping = Mapping::create($indexConfig['properties']);
|
||||
|
||||
$mappingSpecialFields = array('_uid', '_id', '_source', '_all', '_analyzer', '_boost', '_routing', '_index', '_size', '_timestamp', '_ttl', 'dynamic_templates');
|
||||
foreach ($mappingSpecialFields as $specialField) {
|
||||
if (isset($indexConfig[$specialField])) {
|
||||
$mapping->setParam($specialField, $indexConfig[$specialField]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($indexConfig['_parent'])) {
|
||||
$mapping->setParam('_parent', array('type' => $indexConfig['_parent']['type']));
|
||||
}
|
||||
|
||||
return $mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an index config by its name
|
||||
*
|
||||
* @param string $indexName Index name
|
||||
*
|
||||
* @param $indexName
|
||||
* @return array
|
||||
* @throws \InvalidArgumentException if no index config exists for the given name
|
||||
*/
|
||||
protected function getIndexConfig($indexName)
|
||||
{
|
||||
if (!isset($this->indexConfigsByName[$indexName])) {
|
||||
throw new \InvalidArgumentException(sprintf('The configuration for index "%s" does not exist.', $indexName));
|
||||
}
|
||||
|
||||
return $this->indexConfigsByName[$indexName];
|
||||
}
|
||||
|
||||
public function postPopulate($indexName)
|
||||
{
|
||||
$indexConfig = $this->getIndexConfig($indexName);
|
||||
if (isset($indexConfig['use_alias']) && $indexConfig['use_alias']) {
|
||||
$this->switchIndexAlias($indexName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the alias for given index (by key) to the newly populated index
|
||||
* and deletes the old index
|
||||
*
|
||||
* @param string $indexName Index name
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function switchIndexAlias($indexName)
|
||||
{
|
||||
$indexConfig = $this->getIndexConfig($indexName);
|
||||
$esIndex = $indexConfig['index'];
|
||||
$aliasName = $indexConfig['name_or_alias'];
|
||||
$oldIndexName = false;
|
||||
$newIndexName = $esIndex->getName();
|
||||
|
||||
$aliasedIndexes = $this->getAliasedIndexes($esIndex, $aliasName);
|
||||
|
||||
if (count($aliasedIndexes) > 1) {
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
'Alias %s is used for multiple indexes: [%s].
|
||||
Make sure it\'s either not used or is assigned to one index only',
|
||||
$aliasName,
|
||||
join(', ', $aliasedIndexes)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Change the alias to point to the new index
|
||||
// Elastica's addAlias can't be used directly, because in current (0.19.x) version it's not atomic
|
||||
// In 0.20.x it's atomic, but it doesn't return the old index name
|
||||
$aliasUpdateRequest = array('actions' => array());
|
||||
if (count($aliasedIndexes) == 1) {
|
||||
// if the alias is set - add an action to remove it
|
||||
$oldIndexName = $aliasedIndexes[0];
|
||||
$aliasUpdateRequest['actions'][] = array(
|
||||
'remove' => array('index' => $oldIndexName, 'alias' => $aliasName)
|
||||
);
|
||||
}
|
||||
|
||||
// add an action to point the alias to the new index
|
||||
$aliasUpdateRequest['actions'][] = array(
|
||||
'add' => array('index' => $newIndexName, 'alias' => $aliasName)
|
||||
);
|
||||
|
||||
try {
|
||||
$esIndex->getClient()->request('_aliases', 'POST', $aliasUpdateRequest);
|
||||
} catch (ExceptionInterface $renameAliasException) {
|
||||
$additionalError = '';
|
||||
// if we failed to move the alias, delete the newly built index
|
||||
try {
|
||||
$esIndex->delete();
|
||||
} catch (ExceptionInterface $deleteNewIndexException) {
|
||||
$additionalError = sprintf(
|
||||
'Tried to delete newly built index %s, but also failed: %s',
|
||||
$newIndexName,
|
||||
$deleteNewIndexException->getError()
|
||||
);
|
||||
}
|
||||
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
'Failed to updated index alias: %s. %s',
|
||||
$renameAliasException->getMessage(),
|
||||
$additionalError ?: sprintf('Newly built index %s was deleted', $newIndexName)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Delete the old index after the alias has been switched
|
||||
if ($oldIndexName) {
|
||||
$oldIndex = new Index($esIndex->getClient(), $oldIndexName);
|
||||
try {
|
||||
$oldIndex->delete();
|
||||
} catch (ExceptionInterface $deleteOldIndexException) {
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
'Failed to delete old index %s with message: %s',
|
||||
$oldIndexName,
|
||||
$deleteOldIndexException->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of indexes which are mapped to given alias
|
||||
*
|
||||
* @param Index $esIndex ES Index
|
||||
* @param string $aliasName Alias name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getAliasedIndexes(Index $esIndex, $aliasName)
|
||||
{
|
||||
$aliasesInfo = $esIndex->getClient()->request('_aliases', 'GET')->getData();
|
||||
$aliasedIndexes = array();
|
||||
|
||||
foreach ($aliasesInfo as $indexName => $indexInfo) {
|
||||
$aliases = array_keys($indexInfo['aliases']);
|
||||
if (in_array($aliasName, $aliases)) {
|
||||
$aliasedIndexes[] = $indexName;
|
||||
}
|
||||
}
|
||||
|
||||
return $aliasedIndexes;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,101 +1,51 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
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">
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="fos_elastica.client.class">FOS\ElasticaBundle\Client</parameter>
|
||||
<parameter key="fos_elastica.index.class">FOS\ElasticaBundle\DynamicIndex</parameter>
|
||||
<parameter key="fos_elastica.type.class">Elastica\Type</parameter>
|
||||
<parameter key="fos_elastica.index_manager.class">FOS\ElasticaBundle\IndexManager</parameter>
|
||||
<parameter key="fos_elastica.resetter.class">FOS\ElasticaBundle\Resetter</parameter>
|
||||
<parameter key="fos_elastica.finder.class">FOS\ElasticaBundle\Finder\TransformedFinder</parameter>
|
||||
<parameter key="fos_elastica.paginator_subscriber.class">FOS\ElasticaBundle\Subscriber\PaginateElasticaQuerySubscriber</parameter>
|
||||
<parameter key="fos_elastica.client.class">FOS\ElasticaBundle\Elastica\Client</parameter>
|
||||
<parameter key="fos_elastica.logger.class">FOS\ElasticaBundle\Logger\ElasticaLogger</parameter>
|
||||
<parameter key="fos_elastica.data_collector.class">FOS\ElasticaBundle\DataCollector\ElasticaDataCollector</parameter>
|
||||
<parameter key="fos_elastica.manager.class">FOS\ElasticaBundle\Manager\RepositoryManager</parameter>
|
||||
<parameter key="fos_elastica.elastica_to_model_transformer.collection.class">FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection</parameter>
|
||||
<parameter key="fos_elastica.provider_registry.class">FOS\ElasticaBundle\Provider\ProviderRegistry</parameter>
|
||||
<parameter key="fos_elastica.mapping_builder.class">FOS\ElasticaBundle\Index\MappingBuilder</parameter>
|
||||
<parameter key="fos_elastica.property_accessor.class">Symfony\Component\PropertyAccess\PropertyAccessor</parameter>
|
||||
<parameter key="fos_elastica.object_persister.class">FOS\ElasticaBundle\Persister\ObjectPersister</parameter>
|
||||
<parameter key="fos_elastica.object_serializer_persister.class">FOS\ElasticaBundle\Persister\ObjectSerializerPersister</parameter>
|
||||
<parameter key="fos_elastica.model_to_elastica_transformer.class">FOS\ElasticaBundle\Transformer\ModelToElasticaAutoTransformer</parameter>
|
||||
<parameter key="fos_elastica.model_to_elastica_identifier_transformer.class">FOS\ElasticaBundle\Transformer\ModelToElasticaIdentifierTransformer</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="fos_elastica.client_prototype" class="%fos_elastica.client.class%" abstract="true">
|
||||
<argument type="collection" /> <!-- configuration -->
|
||||
<argument /> <!-- callback -->
|
||||
|
||||
<service id="fos_elastica.logger" class="%fos_elastica.logger.class%">
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
<argument>%kernel.debug%</argument>
|
||||
<tag name="monolog.logger" channel="elastica" />
|
||||
<call method="setStopwatch">
|
||||
<argument type="service" id="debug.stopwatch" on-invalid="null" />
|
||||
</call>
|
||||
</service>
|
||||
<service id="fos_elastica.data_collector" class="%fos_elastica.data_collector.class%" public="true">
|
||||
|
||||
<service id="fos_elastica.config_manager" class="FOS\ElasticaBundle\Configuration\ConfigManager">
|
||||
<argument type="collection" /> <!-- collection of SourceInterface services -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.data_collector" class="%fos_elastica.data_collector.class%">
|
||||
<tag name="data_collector" template="FOSElasticaBundle:Collector:elastica" id="elastica" />
|
||||
<argument type="service" id="fos_elastica.logger" />
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.index_manager" class="%fos_elastica.index_manager.class%">
|
||||
<argument /> <!-- indexes -->
|
||||
<argument /> <!-- default index -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.resetter" class="%fos_elastica.resetter.class%">
|
||||
<argument /> <!-- index configs -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.object_persister" class="%fos_elastica.object_persister.class%" abstract="true">
|
||||
<argument /> <!-- type -->
|
||||
<argument /> <!-- model to elastica transformer -->
|
||||
<argument /> <!-- model -->
|
||||
<argument /> <!-- properties mapping -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.finder" class="%fos_elastica.finder.class%" public="true" abstract="true">
|
||||
<argument /> <!-- searchable -->
|
||||
<argument /> <!-- transformer -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.object_serializer_persister" class="%fos_elastica.object_serializer_persister.class%" abstract="true">
|
||||
<argument /> <!-- type -->
|
||||
<argument /> <!-- model to elastica transformer -->
|
||||
<argument /> <!-- model -->
|
||||
<argument /> <!-- serializer -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.model_to_elastica_transformer" class="%fos_elastica.model_to_elastica_transformer.class%" public="false" abstract="true">
|
||||
<argument /> <!-- options -->
|
||||
<call method="setPropertyAccessor">
|
||||
<argument type="service" id="fos_elastica.property_accessor" />
|
||||
</call>
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.model_to_elastica_identifier_transformer" class="%fos_elastica.model_to_elastica_identifier_transformer.class%" public="false" abstract="true">
|
||||
<argument /> <!-- options -->
|
||||
<call method="setPropertyAccessor">
|
||||
<argument type="service" id="fos_elastica.property_accessor" />
|
||||
</call>
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.elastica_to_model_transformer.collection" class="%fos_elastica.elastica_to_model_transformer.collection.class%" public="true" abstract="true">
|
||||
<argument type="collection" /> <!-- transformers -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.provider_registry" class="%fos_elastica.provider_registry.class%">
|
||||
<call method="setContainer">
|
||||
<argument type="service" id="service_container" />
|
||||
</call>
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.paginator.subscriber" class="%fos_elastica.paginator_subscriber.class%">
|
||||
<service id="fos_elastica.paginator.subscriber" class="FOS\ElasticaBundle\Subscriber\PaginateElasticaQuerySubscriber">
|
||||
<call method="setRequest">
|
||||
<argument type="service" id="request" on-invalid="null" strict="false" />
|
||||
</call>
|
||||
<tag name="knp_paginator.subscriber" />
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.logger" class="%fos_elastica.logger.class%">
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
<argument>%kernel.debug%</argument>
|
||||
<tag name="monolog.logger" channel="elastica" />
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.mapping_builder" class="%fos_elastica.mapping_builder.class%" />
|
||||
|
||||
<service id="fos_elastica.property_accessor" class="%fos_elastica.property_accessor.class%" />
|
||||
</services>
|
||||
|
||||
</container>
|
||||
|
|
53
Resources/config/index.xml
Normal file
53
Resources/config/index.xml
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="fos_elastica.alias_processor.class">FOS\ElasticaBundle\Index\AliasProcessor</parameter>
|
||||
<parameter key="fos_elastica.finder.class">FOS\ElasticaBundle\Finder\TransformedFinder</parameter>
|
||||
<parameter key="fos_elastica.index.class">FOS\ElasticaBundle\Elastica\Index</parameter>
|
||||
<parameter key="fos_elastica.indexable.class">FOS\ElasticaBundle\Provider\Indexable</parameter>
|
||||
<parameter key="fos_elastica.index_manager.class">FOS\ElasticaBundle\Index\IndexManager</parameter>
|
||||
<parameter key="fos_elastica.resetter.class">FOS\ElasticaBundle\Index\Resetter</parameter>
|
||||
<parameter key="fos_elastica.type.class">Elastica\Type</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="fos_elastica.alias_processor" class="%fos_elastica.alias_processor.class%" />
|
||||
|
||||
<service id="fos_elastica.indexable" class="%fos_elastica.indexable.class%">
|
||||
<argument type="collection" /> <!-- array of indexable callbacks keyed by type name -->
|
||||
<argument type="service" id="service_container" />
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.index_prototype" class="%fos_elastica.index.class%" factory-service="fos_elastica.client" factory-method="getIndex" abstract="true">
|
||||
<argument /> <!-- index name -->
|
||||
<!-- tagged with fos_elastica.index in the Extension -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.type_prototype" class="%fos_elastica.type.class%" factory-method="getType" abstract="true">
|
||||
<argument /> <!-- type name -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.index_manager" class="%fos_elastica.index_manager.class%">
|
||||
<argument /> <!-- indexes -->
|
||||
<argument type="service" id="fos_elastica.index" /> <!-- default index -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.resetter" class="%fos_elastica.resetter.class%">
|
||||
<argument type="service" id="fos_elastica.config_manager" />
|
||||
<argument type="service" id="fos_elastica.index_manager" />
|
||||
<argument type="service" id="fos_elastica.alias_processor" />
|
||||
<argument type="service" id="fos_elastica.mapping_builder" />
|
||||
<argument type="service" id="event_dispatcher"/>
|
||||
</service>
|
||||
|
||||
<!-- Abstract definition for all finders. -->
|
||||
<service id="fos_elastica.finder" class="%fos_elastica.finder.class%" public="true" abstract="true">
|
||||
<argument /> <!-- searchable -->
|
||||
<argument /> <!-- transformer -->
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
|
@ -4,23 +4,35 @@
|
|||
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">
|
||||
|
||||
<services>
|
||||
<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>
|
||||
|
||||
<service id="fos_elastica.provider.prototype.mongodb" class="FOS\ElasticaBundle\Doctrine\MongoDB\Provider" public="true" abstract="true">
|
||||
<services>
|
||||
<service id="fos_elastica.slice_fetcher.mongodb" class="%fos_elastica.slice_fetcher.mongodb.class%">
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.provider.prototype.mongodb" class="%fos_elastica.provider.prototype.mongodb.class%" public="true" abstract="true">
|
||||
<argument /> <!-- object persister -->
|
||||
<argument type="service" id="fos_elastica.indexable" />
|
||||
<argument /> <!-- model -->
|
||||
<argument type="collection" /> <!-- options -->
|
||||
<argument type="service" id="doctrine_mongodb" />
|
||||
<argument type="service" id="doctrine_mongodb" /> <!-- manager registry -->
|
||||
<argument type="service" id="fos_elastica.slice_fetcher.mongodb" /> <!-- slice fetcher -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.listener.prototype.mongodb" class="FOS\ElasticaBundle\Doctrine\Listener" public="false">
|
||||
<service id="fos_elastica.listener.prototype.mongodb" class="%fos_elastica.listener.prototype.mongodb.class%" public="false" abstract="true">
|
||||
<argument /> <!-- object persister -->
|
||||
<argument /> <!-- model -->
|
||||
<argument type="collection" /> <!-- events -->
|
||||
<argument/> <!-- identifier -->
|
||||
<argument type="service" id="fos_elastica.indexable" />
|
||||
<argument type="collection" /> <!-- configuration -->
|
||||
<argument>null</argument> <!-- logger -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.elastica_to_model_transformer.prototype.mongodb" class="FOS\ElasticaBundle\Doctrine\MongoDB\ElasticaToModelTransformer" public="false">
|
||||
<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 /> <!-- model -->
|
||||
<argument type="collection" /> <!-- options -->
|
||||
|
@ -29,11 +41,9 @@
|
|||
</call>
|
||||
</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="annotation_reader"/>
|
||||
</service>
|
||||
|
||||
</services>
|
||||
|
||||
</container>
|
||||
|
|
|
@ -1,27 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
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">
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="fos_elastica.slice_fetcher.orm.class">FOS\ElasticaBundle\Doctrine\ORM\SliceFetcher</parameter>
|
||||
<parameter key="fos_elastica.provider.prototype.orm.class">FOS\ElasticaBundle\Doctrine\ORM\Provider</parameter>
|
||||
<parameter key="fos_elastica.listener.prototype.orm.class">FOS\ElasticaBundle\Doctrine\Listener</parameter>
|
||||
<parameter key="fos_elastica.elastica_to_model_transformer.prototype.orm.class">FOS\ElasticaBundle\Doctrine\ORM\ElasticaToModelTransformer</parameter>
|
||||
<parameter key="fos_elastica.manager.orm.class">FOS\ElasticaBundle\Doctrine\RepositoryManager</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="fos_elastica.slice_fetcher.orm" class="%fos_elastica.slice_fetcher.orm.class%">
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.provider.prototype.orm" class="FOS\ElasticaBundle\Doctrine\ORM\Provider" public="true" abstract="true">
|
||||
<service id="fos_elastica.provider.prototype.orm" class="%fos_elastica.provider.prototype.orm.class%" public="true" abstract="true">
|
||||
<argument /> <!-- object persister -->
|
||||
<argument type="service" id="fos_elastica.indexable" />
|
||||
<argument /> <!-- model -->
|
||||
<argument type="collection" /> <!-- options -->
|
||||
<argument type="service" id="doctrine" />
|
||||
<argument type="service" id="doctrine" /> <!-- manager registry -->
|
||||
<argument type="service" id="fos_elastica.slice_fetcher.orm" /> <!-- slice fetcher -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.listener.prototype.orm" class="FOS\ElasticaBundle\Doctrine\Listener" public="false">
|
||||
<service id="fos_elastica.listener.prototype.orm" class="%fos_elastica.listener.prototype.orm.class%" public="false" abstract="true">
|
||||
<argument /> <!-- object persister -->
|
||||
<argument /> <!-- model -->
|
||||
<argument type="collection" /> <!-- events -->
|
||||
<argument/> <!-- identifier -->
|
||||
<argument /> <!-- check method -->
|
||||
<argument type="service" id="fos_elastica.indexable" />
|
||||
<argument type="collection" /> <!-- configuration -->
|
||||
<argument>null</argument> <!-- logger -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.elastica_to_model_transformer.prototype.orm" class="FOS\ElasticaBundle\Doctrine\ORM\ElasticaToModelTransformer" public="false">
|
||||
<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 /> <!-- model -->
|
||||
<argument type="collection" /> <!-- options -->
|
||||
|
@ -30,11 +41,9 @@
|
|||
</call>
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.manager.orm" class="FOS\ElasticaBundle\Doctrine\RepositoryManager">
|
||||
<argument type="service" id="doctrine"/>
|
||||
<argument type="service" id="annotation_reader"/>
|
||||
<service id="fos_elastica.manager.orm" class="%fos_elastica.manager.orm.class%">
|
||||
<argument type="service" id="doctrine" />
|
||||
<argument type="service" id="annotation_reader" />
|
||||
</service>
|
||||
|
||||
</services>
|
||||
|
||||
</container>
|
||||
|
|
27
Resources/config/persister.xml
Normal file
27
Resources/config/persister.xml
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="fos_elastica.object_persister.class">FOS\ElasticaBundle\Persister\ObjectPersister</parameter>
|
||||
<parameter key="fos_elastica.object_serializer_persister.class">FOS\ElasticaBundle\Persister\ObjectSerializerPersister</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="fos_elastica.object_persister" class="%fos_elastica.object_persister.class%" abstract="true">
|
||||
<argument /> <!-- type -->
|
||||
<argument /> <!-- model to elastica transformer -->
|
||||
<argument /> <!-- model -->
|
||||
<argument /> <!-- properties mapping -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.object_serializer_persister" class="%fos_elastica.object_serializer_persister.class%" abstract="true">
|
||||
<argument /> <!-- type -->
|
||||
<argument /> <!-- model to elastica transformer -->
|
||||
<argument /> <!-- model -->
|
||||
<argument /> <!-- serializer -->
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
|
@ -4,9 +4,9 @@
|
|||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<services>
|
||||
|
||||
<service id="fos_elastica.provider.prototype.propel" class="FOS\ElasticaBundle\Propel\Provider" public="true" abstract="true">
|
||||
<argument /> <!-- object persister -->
|
||||
<argument type="service" id="fos_elastica.indexable" />
|
||||
<argument /> <!-- model -->
|
||||
<argument type="collection" /> <!-- options -->
|
||||
</service>
|
||||
|
@ -19,10 +19,8 @@
|
|||
</call>
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.manager.propel" class="%fos_elastica.manager.class%">
|
||||
<service id="fos_elastica.manager.propel" class="FOS\ElasticaBundle\Doctrine\RepositoryManager">
|
||||
<argument type="service" id="annotation_reader"/>
|
||||
</service>
|
||||
|
||||
</services>
|
||||
|
||||
</container>
|
||||
|
|
18
Resources/config/provider.xml
Normal file
18
Resources/config/provider.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="fos_elastica.provider_registry.class">FOS\ElasticaBundle\Provider\ProviderRegistry</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="fos_elastica.provider_registry" class="%fos_elastica.provider_registry.class%">
|
||||
<call method="setContainer">
|
||||
<argument type="service" id="service_container" />
|
||||
</call>
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
14
Resources/config/serializer.xml
Normal file
14
Resources/config/serializer.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
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">
|
||||
|
||||
<services>
|
||||
<service id="fos_elastica.serializer_callback_prototype" public="false" abstract="true">
|
||||
<call method="setSerializer">
|
||||
<argument type="service" id="fos_elastica.serializer" />
|
||||
</call>
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
13
Resources/config/source.xml
Normal file
13
Resources/config/source.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
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">
|
||||
|
||||
<services>
|
||||
<service id="fos_elastica.config_source.container" class="FOS\ElasticaBundle\Configuration\Source\ContainerSource" public="false">
|
||||
<argument type="collection" /> <!-- index configs -->
|
||||
<tag name="fos_elastica.config_source" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
33
Resources/config/transformer.xml
Normal file
33
Resources/config/transformer.xml
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="fos_elastica.elastica_to_model_transformer.collection.class">FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection</parameter>
|
||||
<parameter key="fos_elastica.model_to_elastica_transformer.class">FOS\ElasticaBundle\Transformer\ModelToElasticaAutoTransformer</parameter>
|
||||
<parameter key="fos_elastica.model_to_elastica_identifier_transformer.class">FOS\ElasticaBundle\Transformer\ModelToElasticaIdentifierTransformer</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="fos_elastica.model_to_elastica_transformer" class="%fos_elastica.model_to_elastica_transformer.class%" public="false" abstract="true">
|
||||
<argument type="collection" /> <!-- options -->
|
||||
<argument type="service" id="event_dispatcher" /> <!-- options -->
|
||||
<call method="setPropertyAccessor">
|
||||
<argument type="service" id="fos_elastica.property_accessor" />
|
||||
</call>
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.model_to_elastica_identifier_transformer" class="%fos_elastica.model_to_elastica_identifier_transformer.class%" public="false" abstract="true">
|
||||
<argument type="collection" /> <!-- options -->
|
||||
<call method="setPropertyAccessor">
|
||||
<argument type="service" id="fos_elastica.property_accessor" />
|
||||
</call>
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.elastica_to_model_transformer.collection" class="%fos_elastica.elastica_to_model_transformer.collection.class%" public="true" abstract="true">
|
||||
<argument type="collection" /> <!-- transformers -->
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
45
Resources/doc/cookbook/aliased-indexes.md
Normal file
45
Resources/doc/cookbook/aliased-indexes.md
Normal file
|
@ -0,0 +1,45 @@
|
|||
Aliased Indexes
|
||||
===============
|
||||
|
||||
You can set up FOSElasticaBundle to use aliases for indexes which allows you to run an
|
||||
index population without resetting the index currently being used by the application.
|
||||
|
||||
> *Note*: When you're using an alias, resetting an individual type will still cause a
|
||||
> reset for that type.
|
||||
|
||||
To configure FOSElasticaBundle to use aliases for an index, set the use_alias option to
|
||||
true.
|
||||
|
||||
```yaml
|
||||
fos_elastica:
|
||||
indexes:
|
||||
website:
|
||||
use_alias: true
|
||||
```
|
||||
|
||||
The process for setting up aliases on an existing application is slightly more complicated
|
||||
because the bundle is not able to set an alias as the same name as an index. You have some
|
||||
options on how to handle this:
|
||||
|
||||
1) Delete the index from Elasticsearch. This option will make searching unavailable in your
|
||||
application until a population has completed itself, and an alias is created.
|
||||
|
||||
2) Change the index_name parameter for your index to something new, and manually alias the
|
||||
current index to the new index_name, which will then be replaced when you run a repopulate.
|
||||
|
||||
```yaml
|
||||
fos_elastica:
|
||||
indexes:
|
||||
website:
|
||||
use_alias: true
|
||||
index_name: website_prod
|
||||
```
|
||||
|
||||
```bash
|
||||
$ curl -XPOST 'http://localhost:9200/_aliases' -d '
|
||||
{
|
||||
"actions" : [
|
||||
{ "add" : { "index" : "website", "alias" : "website_prod" } }
|
||||
]
|
||||
}'
|
||||
```
|
33
Resources/doc/cookbook/custom-properties.md
Normal file
33
Resources/doc/cookbook/custom-properties.md
Normal file
|
@ -0,0 +1,33 @@
|
|||
##### Custom Properties
|
||||
|
||||
Since FOSElasticaBundle 3.1.0, we now dispatch an event for each transformation of an
|
||||
object into an Elastica document which allows you to set custom properties on the Elastica
|
||||
document for indexing.
|
||||
|
||||
Set up an event listener or subscriber for
|
||||
`FOS\ElasticaBundle\Event\TransformEvent::POST_TRANSFORM` to be able to inject your own
|
||||
parameters.
|
||||
|
||||
```php
|
||||
class CustomPropertyListener implements EventSubscriberInterface
|
||||
{
|
||||
private $anotherService;
|
||||
|
||||
// ...
|
||||
|
||||
public function addCustomProperty(TransformEvent $event)
|
||||
{
|
||||
$document = $event->getDocument();
|
||||
$custom = $this->anotherService->calculateCustom($event->getObject());
|
||||
|
||||
$document->set('custom', $custom);
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TransformEvent::POST_TRANSFORM => 'addCustomProperty',
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
|
@ -4,7 +4,7 @@ As well as the default repository you can create a custom repository for an enti
|
|||
methods for particular searches. These need to extend `FOS\ElasticaBundle\Repository` to have
|
||||
access to the finder:
|
||||
|
||||
```
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Acme\ElasticaBundle\SearchRepository;
|
||||
|
@ -23,42 +23,46 @@ class UserRepository extends Repository
|
|||
|
||||
To use the custom repository specify it in the mapping for the entity:
|
||||
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
indexes:
|
||||
website:
|
||||
client: default
|
||||
types:
|
||||
user:
|
||||
mappings:
|
||||
# your mappings
|
||||
persistence:
|
||||
driver: orm
|
||||
model: Application\UserBundle\Entity\User
|
||||
provider: ~
|
||||
finder: ~
|
||||
repository: Acme\ElasticaBundle\SearchRepository\UserRepository
|
||||
```yaml
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
indexes:
|
||||
website:
|
||||
client: default
|
||||
types:
|
||||
user:
|
||||
mappings:
|
||||
# your mappings
|
||||
persistence:
|
||||
driver: orm
|
||||
model: Application\UserBundle\Entity\User
|
||||
provider: ~
|
||||
finder: ~
|
||||
repository: Acme\ElasticaBundle\SearchRepository\UserRepository
|
||||
```
|
||||
|
||||
Then the custom queries will be available when using the repository returned from the manager:
|
||||
|
||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
||||
$repositoryManager = $container->get('fos_elastica.manager');
|
||||
```php
|
||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
||||
$repositoryManager = $container->get('fos_elastica.manager');
|
||||
|
||||
/** var FOS\ElasticaBundle\Repository */
|
||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
||||
/** var FOS\ElasticaBundle\Repository */
|
||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
||||
|
||||
/** var array of Acme\UserBundle\Entity\User */
|
||||
$users = $repository->findWithCustomQuery('bob');
|
||||
/** var array of Acme\UserBundle\Entity\User */
|
||||
$users = $repository->findWithCustomQuery('bob');
|
||||
```
|
||||
|
||||
Alternatively you can specify the custom repository using an annotation in the entity:
|
||||
|
||||
```
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Application\UserBundle\Entity;
|
||||
|
||||
use FOS\ElasticaBundle\Configuration\Search;
|
||||
use FOS\ElasticaBundle\Annotation\Search;
|
||||
|
||||
/**
|
||||
* @Search(repositoryClass="Acme\ElasticaBundle\SearchRepository\UserRepository")
|
||||
|
@ -69,4 +73,4 @@ class User
|
|||
//---
|
||||
|
||||
}
|
||||
```
|
||||
```
|
||||
|
|
|
@ -8,7 +8,7 @@ index and type for which the service will provide.
|
|||
# app/config/config.yml
|
||||
services:
|
||||
acme.search_provider.user:
|
||||
class: Acme\UserBundle\Search\UserProvider
|
||||
class: Acme\UserBundle\Provider\UserProvider
|
||||
arguments:
|
||||
- @fos_elastica.index.website.user
|
||||
tags:
|
||||
|
|
21
Resources/doc/cookbook/multiple-connections.md
Normal file
21
Resources/doc/cookbook/multiple-connections.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
Multiple Connections
|
||||
====================
|
||||
|
||||
You can define multiple endpoints for an Elastica client by specifying them as
|
||||
multiple connections in the client configuration:
|
||||
|
||||
```yaml
|
||||
fos_elastica:
|
||||
clients:
|
||||
default:
|
||||
connections:
|
||||
- url: http://es1.example.net:9200
|
||||
- url: http://es2.example.net:9200
|
||||
connection_strategy: RoundRobin
|
||||
```
|
||||
|
||||
Elastica allows for definition of different connection strategies and by default
|
||||
supports `RoundRobin` and `Simple`. You can see definitions for these strategies
|
||||
in the `Elastica\Connection\Strategy` namespace.
|
||||
|
||||
For more information on Elastica clustering see http://elastica.io/getting-started/installation.html#section-connect-cluster
|
|
@ -1,5 +1,5 @@
|
|||
Suppressing Server Errors
|
||||
========================
|
||||
=========================
|
||||
|
||||
By default, exceptions from the Elastica client library will propagate through
|
||||
the bundle's Client class. For instance, if the Elasticsearch server is offline,
|
||||
|
@ -12,25 +12,48 @@ container parameter with a custom class. In the following example, we override
|
|||
the `Client::request()` method and return the equivalent of an empty search
|
||||
response if an exception occurred.
|
||||
|
||||
```
|
||||
Sample client code:
|
||||
-------------------
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Acme\ElasticaBundle;
|
||||
|
||||
use FOS\ElasticaBundle\Client as BaseClient;
|
||||
|
||||
use Elastica\Exception\ExceptionInterface;
|
||||
use Elastica\Request;
|
||||
use Elastica\Response;
|
||||
use FOS\ElasticaBundle\Elastica\Client as BaseClient;
|
||||
|
||||
class Client extends BaseClient
|
||||
{
|
||||
public function request($path, $method, $data = array())
|
||||
public function request($path, $method = Request::GET, $data = array(), array $query = array())
|
||||
{
|
||||
try {
|
||||
return parent::request($path, $method, $data);
|
||||
return parent::request($path, $method, $data, $query);
|
||||
} catch (ExceptionInterface $e) {
|
||||
if ($this->_logger) {
|
||||
$this->_logger->warning('Failed to send a request to ElasticSearch', array(
|
||||
'exception' => $e->getMessage(),
|
||||
'path' => $path,
|
||||
'method' => $method,
|
||||
'data' => $data,
|
||||
'query' => $query
|
||||
));
|
||||
}
|
||||
|
||||
return new Response('{"took":0,"timed_out":false,"hits":{"total":0,"max_score":0,"hits":[]}}');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
Configuration change:
|
||||
---------------------
|
||||
|
||||
You must update a parameter in your `app/config/config.yml` file to point to your overridden client:
|
||||
|
||||
```yaml
|
||||
parameters:
|
||||
fos_elastica.client.class: Acme\ElasticaBundle\Client
|
||||
```
|
||||
|
|
|
@ -12,7 +12,11 @@ Available documentation for FOSElasticaBundle
|
|||
Cookbook Entries
|
||||
----------------
|
||||
|
||||
* [Aliased Indexes](cookbook/aliased-indexes.md)
|
||||
* [Custom Indexed Properties](cookbook/custom-properties.md)
|
||||
* [Custom Repositories](cookbook/custom-repositories.md)
|
||||
* [HTTP Headers for Elastica](cookbook/elastica-client-http-headers.md)
|
||||
* Performance - [Logging](cookbook/logging.md)
|
||||
* [Manual Providers](cookbook/manual-provider.md)
|
||||
* [Clustering - Multiple Connections](cookbook/multiple-connections.md)
|
||||
* [Suppressing server errors](cookbook/suppress-server-errors.md)
|
||||
|
|
|
@ -1,40 +1,50 @@
|
|||
Step 1: Setting up the bundle
|
||||
=============================
|
||||
|
||||
A) Install FOSElasticaBundle
|
||||
----------------------------
|
||||
A: Download the Bundle
|
||||
----------------------
|
||||
|
||||
FOSElasticaBundle is installed using [Composer](https://getcomposer.org).
|
||||
Open a command console, enter your project directory and execute the
|
||||
following command to download the latest stable version of this bundle:
|
||||
|
||||
```bash
|
||||
$ php composer.phar require friendsofsymfony/elastica-bundle "3.0.*"
|
||||
$ composer require friendsofsymfony/elastica-bundle
|
||||
```
|
||||
|
||||
This command requires you to have Composer installed globally, as explained
|
||||
in the [installation chapter](https://getcomposer.org/doc/00-intro.md)
|
||||
of the Composer documentation.
|
||||
|
||||
### Elasticsearch
|
||||
|
||||
Instructions for installing and deploying Elasticsearch may be found
|
||||
[here](http://www.elasticsearch.org/guide/reference/setup/installation/).
|
||||
Instructions for installing and deploying Elasticsearch may be found [here](https://www.elastic.co/downloads/elasticsearch).
|
||||
|
||||
Step 2: Enable the Bundle
|
||||
-------------------------
|
||||
|
||||
B) Enable FOSElasticaBundle
|
||||
---------------------------
|
||||
|
||||
Enable FOSElasticaBundle in your AppKernel:
|
||||
Then, enable the bundle by adding the following line in the `app/AppKernel.php`
|
||||
file of your project:
|
||||
|
||||
```php
|
||||
<?php
|
||||
// app/AppKernel.php
|
||||
|
||||
public function registerBundles()
|
||||
// ...
|
||||
class AppKernel extends Kernel
|
||||
{
|
||||
$bundles = array(
|
||||
public function registerBundles()
|
||||
{
|
||||
$bundles = array(
|
||||
// ...
|
||||
new FOS\ElasticaBundle\FOSElasticaBundle(),
|
||||
);
|
||||
|
||||
// ...
|
||||
new FOS\ElasticaBundle\FOSElasticaBundle(),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
C) Basic Bundle Configuration
|
||||
C: Basic Bundle Configuration
|
||||
-----------------------------
|
||||
|
||||
The basic minimal configuration for FOSElasticaBundle is one client with one Elasticsearch
|
||||
|
@ -48,27 +58,30 @@ fos_elastica:
|
|||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
indexes:
|
||||
search: ~
|
||||
app: ~
|
||||
```
|
||||
|
||||
In this example, an Elastica index (an instance of `Elastica\Index`) is available as a
|
||||
service with the key `fos_elastica.index.search`.
|
||||
service with the key `fos_elastica.index.app`.
|
||||
|
||||
If the Elasticsearch index name needs to be different to the service name in your
|
||||
application, for example, renaming the search index based on different environments.
|
||||
You may want the index `app` to be named something else on ElasticSearch depending on
|
||||
if your application is running in a different env or other conditions that suit your
|
||||
application. To set your customer index to a name that depends on the environment of your
|
||||
Symfony application, use the example below:
|
||||
|
||||
```yaml
|
||||
#app/config/config.yml
|
||||
fos_elastica:
|
||||
indexes:
|
||||
search:
|
||||
index_name: search_dev
|
||||
app:
|
||||
index_name: app_%kernel.environment%
|
||||
```
|
||||
|
||||
In this case, the service `fos_elastica.index.search` will be using an Elasticsearch
|
||||
index of search_dev.
|
||||
In this case, the service `fos_elastica.index.app` will relate to an ElasticSearch index
|
||||
that varies depending on your kernel's environment. For example, in dev it will relate to
|
||||
`app_dev`.
|
||||
|
||||
D) Defining index types
|
||||
D: Defining index types
|
||||
-----------------------
|
||||
|
||||
By default, FOSElasticaBundle requires each type that is to be indexed to be mapped.
|
||||
|
@ -81,7 +94,7 @@ will end up being indexed.
|
|||
```yaml
|
||||
fos_elastica:
|
||||
indexes:
|
||||
search:
|
||||
app:
|
||||
types:
|
||||
user:
|
||||
mappings:
|
||||
|
@ -92,7 +105,7 @@ fos_elastica:
|
|||
```
|
||||
|
||||
Each defined type is made available as a service, and in this case the service key is
|
||||
`fos_elastica.index.search.user` and is an instance of `Elastica\Type`.
|
||||
`fos_elastica.index.app.user` and is an instance of `Elastica\Type`.
|
||||
|
||||
FOSElasticaBundle requires a provider for each type that will notify when an object
|
||||
that maps to a type has been modified. The bundle ships with support for Doctrine and
|
||||
|
@ -114,15 +127,15 @@ Below is an example for the Doctrine ORM.
|
|||
driver: orm
|
||||
model: Acme\ApplicationBundle\Entity\User
|
||||
provider: ~
|
||||
listener: ~
|
||||
listener:
|
||||
immediate: ~
|
||||
finder: ~
|
||||
immediate: ~
|
||||
```
|
||||
|
||||
There are a significant number of options available for types, that can be
|
||||
[found here](types.md)
|
||||
|
||||
E) Populating the Elasticsearch index
|
||||
E: Populating the Elasticsearch index
|
||||
-------------------------------------
|
||||
|
||||
When using the providers and listeners that come with the bundle, any new or modified
|
||||
|
@ -137,7 +150,7 @@ $ php app/console fos:elastica:populate
|
|||
The command will also create all indexes and types defined if they do not already exist
|
||||
on the Elasticsearch server.
|
||||
|
||||
F) Usage
|
||||
F: Usage
|
||||
--------
|
||||
|
||||
Usage documentation for the bundle is available [here](usage.md)
|
||||
|
|
|
@ -1,6 +1,34 @@
|
|||
Type configuration
|
||||
==================
|
||||
|
||||
Custom Property Paths
|
||||
---------------------
|
||||
|
||||
Since FOSElasticaBundle 3.1.0, it is now possible to define custom property paths
|
||||
to be used for data retrieval from the underlying model.
|
||||
|
||||
```yaml
|
||||
user:
|
||||
mappings:
|
||||
username:
|
||||
property_path: indexableUsername
|
||||
firstName:
|
||||
property_path: names[first]
|
||||
```
|
||||
|
||||
This feature uses the Symfony PropertyAccessor component and supports all features
|
||||
that the component supports.
|
||||
|
||||
The above example would retrieve an indexed field `username` from the property
|
||||
`User->indexableUsername`, and the indexed field `firstName` would be populated from a
|
||||
key `first` from an array on `User->names`.
|
||||
|
||||
Setting the property path to `false` will disable transformation of that value. In this
|
||||
case the mapping will be created but no value will be populated while indexing. You can
|
||||
populate this value by listening to the `POST_TRANSFORM` event emitted by this bundle.
|
||||
See [cookbook/custom-properties.md](cookbook/custom-properties.md) for more information
|
||||
about this event.
|
||||
|
||||
Handling missing results with FOSElasticaBundle
|
||||
-----------------------------------------------
|
||||
|
||||
|
@ -149,6 +177,52 @@ analyzer, you could write:
|
|||
title: { boost: 8, analyzer: my_analyzer }
|
||||
```
|
||||
|
||||
Testing if an object should be indexed
|
||||
--------------------------------------
|
||||
|
||||
FOSElasticaBundle can be configured to automatically index changes made for
|
||||
different kinds of objects if your persistence backend supports these methods,
|
||||
but in some cases you might want to run an external service or call a property
|
||||
on the object to see if it should be indexed.
|
||||
|
||||
A property, `indexable_callback` is provided under the type configuration that
|
||||
lets you configure this behaviour which will apply for any automated watching
|
||||
for changes and for a repopulation of an index.
|
||||
|
||||
In the example below, we're checking the enabled property on the user to only
|
||||
index enabled users.
|
||||
|
||||
```yaml
|
||||
types:
|
||||
users:
|
||||
indexable_callback: 'enabled'
|
||||
```
|
||||
|
||||
The callback option supports multiple approaches:
|
||||
|
||||
* A method on the object itself provided as a string. `enabled` will call
|
||||
`Object->enabled()`. Note that this does not support chaining methods with dot notation
|
||||
like property paths. To achieve something similar use the ExpressionLanguage option
|
||||
below.
|
||||
* An array of a service id and a method which will be called with the object as the first
|
||||
and only argument. `[ @my_custom_service, 'userIndexable' ]` will call the userIndexable
|
||||
method on a service defined as my_custom_service.
|
||||
* An array of a class and a static method to call on that class which will be called with
|
||||
the object as the only argument. `[ 'Acme\DemoBundle\IndexableChecker', 'isIndexable' ]`
|
||||
will call Acme\DemoBundle\IndexableChecker::isIndexable($object)
|
||||
* A single element array with a service id can be used if the service has an __invoke
|
||||
method. Such an invoke method must accept a single parameter for the object to be indexed.
|
||||
`[ @my_custom_invokable_service ]`
|
||||
* If you have the ExpressionLanguage component installed, A valid ExpressionLanguage
|
||||
expression provided as a string. The object being indexed will be supplied as `object`
|
||||
in the expression. `object.isEnabled() or object.shouldBeIndexedAnyway()`. For more
|
||||
information on the ExpressionLanguage component and its capabilities see its
|
||||
[documentation](http://symfony.com/doc/current/components/expression_language/index.html)
|
||||
|
||||
In all cases, the callback should return a true or false, with true indicating it will be
|
||||
indexed, and a false indicating the object should not be indexed, or should be removed
|
||||
from the index if we are running an update.
|
||||
|
||||
Provider Configuration
|
||||
----------------------
|
||||
|
||||
|
@ -188,6 +262,20 @@ persistence configuration.
|
|||
identifier: searchId
|
||||
```
|
||||
|
||||
### Turning on the persistence backend logger in production
|
||||
|
||||
FOSElasticaBundle will turn of your persistence backend's logging configuration by default
|
||||
when Symfony2 is not in debug mode. You can force FOSElasticaBundle to always disable
|
||||
logging by setting debug_logging to false, to leave logging alone by setting it to true,
|
||||
or leave it set to its default value which will mirror %kernel.debug%.
|
||||
|
||||
```yaml
|
||||
user:
|
||||
persistence:
|
||||
provider:
|
||||
debug_logging: false
|
||||
```
|
||||
|
||||
Listener Configuration
|
||||
----------------------
|
||||
|
||||
|
@ -220,49 +308,6 @@ You can also choose to only listen for some of the events:
|
|||
|
||||
> **Propel** doesn't support this feature yet.
|
||||
|
||||
### Checking an entity method for listener
|
||||
|
||||
If you use listeners to update your index, you may need to validate your
|
||||
entities before you index them (e.g. only index "public" entities). Typically,
|
||||
you'll want the listener to be consistent with the provider's query criteria.
|
||||
This may be achieved by using the `is_indexable_callback` config parameter:
|
||||
|
||||
```yaml
|
||||
persistence:
|
||||
listener:
|
||||
is_indexable_callback: "isPublic"
|
||||
```
|
||||
|
||||
If `is_indexable_callback` is a string and the entity has a method with the
|
||||
specified name, the listener will only index entities for which the method
|
||||
returns `true`. Additionally, you may provide a service and method name pair:
|
||||
|
||||
```yaml
|
||||
persistence:
|
||||
listener:
|
||||
is_indexable_callback: [ "%custom_service_id%", "isIndexable" ]
|
||||
```
|
||||
|
||||
In this case, the callback_class will be the `isIndexable()` method on the specified
|
||||
service and the object being considered for indexing will be passed as the only
|
||||
argument. This allows you to do more complex validation (e.g. ACL checks).
|
||||
|
||||
If you have the [Symfony ExpressionLanguage](https://github.com/symfony/expression-language)
|
||||
component installed, you can use expressions to evaluate the callback:
|
||||
|
||||
```yaml
|
||||
persistence:
|
||||
listener:
|
||||
is_indexable_callback: "user.isActive() && user.hasRole('ROLE_USER')"
|
||||
```
|
||||
|
||||
As you might expect, new entities will only be indexed if the callback_class returns
|
||||
`true`. Additionally, modified entities will be updated or removed from the
|
||||
index depending on whether the callback_class returns `true` or `false`, respectively.
|
||||
The delete listener disregards the callback_class.
|
||||
|
||||
> **Propel** doesn't support this feature yet.
|
||||
|
||||
Flushing Method
|
||||
---------------
|
||||
|
||||
|
|
|
@ -26,7 +26,9 @@ $userPaginator = $finder->findPaginated('bob');
|
|||
$countOfResults = $userPaginator->getNbResults();
|
||||
|
||||
// Option 3b. KnpPaginator resultset
|
||||
|
||||
$paginator = $this->get('knp_paginator');
|
||||
$results = $finder->createPaginatorAdapter('bob');
|
||||
$pagination = $paginator->paginate($results, $page, 10);
|
||||
```
|
||||
|
||||
Faceted Searching
|
||||
|
@ -45,7 +47,7 @@ $companies = $finder->findPaginated($query);
|
|||
$companies->setMaxPerPage($params['limit']);
|
||||
$companies->setCurrentPage($params['page']);
|
||||
|
||||
$facets = $companies->getAdapter()->getFacets());
|
||||
$facets = $companies->getAdapter()->getFacets();
|
||||
```
|
||||
|
||||
Searching the entire index
|
||||
|
@ -65,7 +67,7 @@ You can now use the index wide finder service `fos_elastica.finder.website`:
|
|||
|
||||
```php
|
||||
/** var FOS\ElasticaBundle\Finder\MappedFinder */
|
||||
$finder = $container->get('fos_elastica.finder.website');
|
||||
$finder = $this->container->get('fos_elastica.finder.website');
|
||||
|
||||
// Returns a mixed array of any objects mapped
|
||||
$results = $finder->find('bob');
|
||||
|
@ -91,7 +93,7 @@ An example for using a repository:
|
|||
|
||||
```php
|
||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
||||
$repositoryManager = $container->get('fos_elastica.manager');
|
||||
$repositoryManager = $this->container->get('fos_elastica.manager');
|
||||
|
||||
/** var FOS\ElasticaBundle\Repository */
|
||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
||||
|
@ -160,7 +162,7 @@ fos_elastica:
|
|||
site:
|
||||
settings:
|
||||
index:
|
||||
analysis:
|
||||
analysis:
|
||||
analyzer:
|
||||
my_analyzer:
|
||||
type: snowball
|
||||
|
@ -184,7 +186,7 @@ The following code will execute a search against the Elasticsearch server:
|
|||
$finder = $this->container->get('fos_elastica.finder.site.article');
|
||||
$boolQuery = new \Elastica\Query\Bool();
|
||||
|
||||
$fieldQuery = new \Elastica\Query\Text();
|
||||
$fieldQuery = new \Elastica\Query\Match();
|
||||
$fieldQuery->setFieldQuery('title', 'I am a title string');
|
||||
$fieldQuery->setFieldParam('title', 'analyzer', 'my_analyzer');
|
||||
$boolQuery->addShould($fieldQuery);
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1 KiB |
|
@ -4,6 +4,9 @@
|
|||
{% set icon %}
|
||||
<img alt="elastica" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAcCAYAAABlL09dAAAABGdBTUEAALGPC/xhBQAAA/BpQ0NQSUNDIFByb2ZpbGUAACiRjVXdb9tUFD+Jb1ykFj+gsY4OFYuvVVNbuRsarcYGSZOl6UIauc3YKqTJdW4aU9c2ttNtVZ/2Am8M+AOAsgcekHhCGgzE9rLtAbRJU0EV1SSkPXTaQGiT9oKqcK6vU7tdxriRr38553c+79E1QMdXmuOYSRlg3vJdNZ+Rj5+YljtWIQnPQSf0QKeme066XC4CLsaFR9bDXyHB3jcH2uv/c3VWqacDJJ5CbFc9fR7xaYCUqTuuDyDeRvnwKd9B3PE84h0uJohYYXiW4yzDMxwfDzhT6ihilouk17Uq4iXE/TMx+WwM8xyCtSNPLeoausx6UXbtmmHSWLpPUP/PNW82WvF68eny5iaP4ruP1V53x9QQf65ruUnELyO+5vgZJn8V8b3GXCWNeC9A8pmae6TC+ck3FutT7yDeibhq+IWpUL5ozZQmuG1yec4+qoaca7o3ij2DFxHfqtNCkecjQJVmc6xfiHvrjbHQvzDuLUzmWn4W66Ml7kdw39PGy4h7EH/o2uoEz1lYpmZe5f6FK45fDnMQ1i2zVOQ+iUS9oMZA7tenxrgtOeDjIXJbMl0zjhRC/pJjBrOIuZHzbkOthJwbmpvLcz/kPrUqoc/UrqqWZb0dRHwYjiU0oGDDDO46WLABMqiQhwy+HXBRUwMDTJRQ1FKUGImnYQ5l7XnlgMNxxJgNrNeZNUZpz+ER7oQcm3QThezH5yApkkNkmIyATN4kb5HDJIvSEXJw07Yci89i3dn08z400CvjHYPMuZ5GXxTvrHvS0K9/9PcWa/uRnGkrn3gHwMMOtJgD8fqvLv2wK/KxQi68e7Pr6hJMPKm/qdup9dQK7quptYiR+j21hr9VSGNuZpDRPD5GkIcXyyBew2V8fNBw/wN5doy3JWLNOtcTaVgn6AelhyU42x9Jld+UP5UV5QvlvHJ3W5fbdkn4VPhW+FH4Tvhe+Blk4ZJwWfhJuCJ8I1yMndXj52Pz7IN6W9UyTbteUzCljLRbeknKSi9Ir0jFyJ/ULQ1JY9Ie1OzePLd4vHgtBpzAvdXV9rE4r4JaA04FFXhBhy04s23+Q2vSS4ZIYdvUDrNZbjHEnJgV0yCLe8URcUgcZ7iVn7gHdSO457ZMnf6YCmiMFa9zIJg6NqvMeiHQeUB9etpnF+2o7Zxxjdm6L+9TlNflNH6qqFyw9MF+WTNNOVB5sks96i7Q6iCw7yC/oh+owfctsfN6JPPfBjj0F95ZNyLZdAPgaw+g+7VI1od34rOfAVw4oDfchfDOTyR+AfBq+/fxf10ZvJtuNZsP8L7q+ARg4+Nm85/lZnPjS/S/BnDJ/BdZAHF4xCjCQAAAAAlwSFlzAAALEwAACxMBAJqcGAAABWZJREFUSIm1lU9sXFcVxr9773tv/o8n5sWeGQejTIwpoXYlUCQCUiBSRTdRpS5ihLKxiJBASBFVkViwiOpNxCYREixSdimoi0pATAQJEkJIDlWUtK5K4iKc1HatYtfO+N+8PzPvvnMOCzyWxy2BBT2bK7177u9+99N331Uigk+i9CdCBeD8L00TExOmr6/PN8Z83RjzLaXUF40xeQBzxpjfWGt/6/v+BxcvXky6a9R/s+LChQuZKIqe1Vr/SGv9FaVUopRKjTGitTZa64zW+j2l1BXP816/dOnShojIExVPTEyYUqn0nIhcBjDkuu7Dcrm8UC6XN7XWAuBQq9UaDsNwGMDLcRwXJycnfwag/URwqVT6DIApAJ/O5XKzjUbjzVqttt3f30+FQqFQ7is7YRAuz87OBvPz858noheLxeIbAG7vgZVS6sSJE4Ou646naZoS0ZtjY2PnAIx7nvePkZGRuyMjI9uFQgHZbFZXKpXC4ODgEc/z6gMDA+udTuf9hYWFUQAvAbi9l4qTJ09mtdaTzHwFwA8ymcxRpdQLRESVSuXdI0eObBeLReV5nvI8T4hoKwzDOcdxglqtdvj48eM7xpiAmZ8D9sWNiPIATouIEpE/VKtVF8CA1jqsVCrNUqkkruvC8zxorZVSKu10Oh9GUfQ2gG3f97czmUyLiHIHwUpEHBGZ1Vr/rlgspsYY7bqudV03dRwHWmtoreE4DjzPg+M4ipmb1tp3rbUrImKJCD3gbjFzGoahFZEPRMSKSCZN0ywRGWOM6kK76gFEYRgubG5u2iRJvI+AjTEiIujm+tq1a2tJkiwxcyGKouFsNlvNZDL5/aodxwEAabfbvLS0NBAEwSHP8zZ6wMxcIyKfiGg3o4jj+GqSJMna2trxxcXFwU6nUxCR/WtkZ2cHc3Nz/qNHj55SSmWLxeIvgH1XmpmnRMRn5ncAhLvg3wO4wczP37lz5wsbGxtJo9EIqtVqEIYhOp2ONz8/X7t///4zGxsbjVKp9FYul3vlILifiDqppK04iAkAbty4sXnmzJmXHccZbTabY3Ecl5eXl4/29/dvGWMkCILC2tracBzHh/L5/N9c150aHx9/vwdsrf2xiPwcgi87jjMNoA0Ap06dsq1WC4uLi6rVapWTJPlss9l0RUSladoGsGqM+SOAV9M0nTl79iz1gIno70qpx0TkEZEGgNOnTzvHjh37dqFQeMpxHCRJ8ra1dpqEjFHGYebtlZUVLwiCpa2trfcePnyYXL58GQetUAAUM+9Fr16vP9PpdL63tbXlRVG0w8yvlcvlq5wwcqWcevDgQd/CwsJPiei7InKrUqlMAVjqAQdBgFwuh/1gAD9JkqQUxzGCIHij3W7/6ubNm0F3slqtMhH9GcBRpdRXd8dlEeGeC9LNsO/76ty5c5NKqS/FcSxRFK1aa1+9e/fuh/v7V1dXw2azeVVEfikiqYiY7lwPmIhARHz+/PmjtVrtO9baviiK0G63b1trb8nHvwqamYmZsyIy1HXhoGIhIp3L5b4xOjr6ucP+YeW67rq19pV79+49/hgoAKQiMiciLWZ+qVAo9Pd4vKtYMTNmZmbeqdfrfzKOebbRaPzadd2Z/wD9NzlNZXcD0lrzR8Ddf8X169f/Wq/X79dqtemxsbG3pqenoydwDTM/LSKfAjAVRdEWsO8xHRoa8kXkdRF5rJT64crKyj8BqCcp3a2s53kvisg3ReT7aZr+RUR4T3Gr1ZJ8Pp+IyEkRueL7figiB+OH7jdrLUQEROQw89NEJCKSdvv2wMPDw6319fVbAL4G4PnuSbTWezHsWsXMXSiYGUTEAF4D8EhEuMeK/3f9C5VtKG2arhqTAAAAAElFTkSuQmCC" />
|
||||
<span class="sf-toolbar-status">{{ collector.querycount }}</span>
|
||||
{% if collector.querycount > 0 %}
|
||||
<span class="sf-toolbar-info-piece-additional-detail">in {{ '%0.2f'|format(collector.time * 1000) }} ms</span>
|
||||
{% endif %}
|
||||
{% endset %}
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
|
@ -20,10 +23,11 @@
|
|||
|
||||
{% block menu %}
|
||||
<span class="label">
|
||||
<span class="icon"><img src="{{ asset('bundles/foselastica/images/elastica.png') }}" alt="" /></span>
|
||||
<span class="icon"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAcCAQAAADPJofWAAAAAXNSR0IArs4c6QAAAAJiS0dEAP+Hj8y/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH2woEDg0xnxGaxwAAA51JREFUOMt100ts1FUUx/Hvufc/8x9mOrTTDpbysEVsqTzUqAsRgpqQGIJiIhtZSEJcqNGFJrSiZSPYkKALBUyAiLEuWKjBhdFAaEwwPNQqEAgEaA0FEWh4lE477/+9x0WLsST+Fmf1ycnJvecI/0knW4ENgZkhr5gVMtcEMmB64z1msCOCboR70tlkOuQdQREMBovF7tQt71/mXtzZQo8s89jLycFETiSqKd/v5wSY32VN18Ak3JGiR1d7an+Y3Z/Np7yVeCY/9czU4efF2iPxZ+wLXPgXP7WCTZ7MvvlnZlbqSJnGGbMeam5pk6HTIw9qs7tiH6D9vrbXW9e1Tus/sbhHZ8VOtfU1uZSExCUe1ExLtqayjcPnx4qNssjOs3TQrY9rYdVB2aFRw7HWq2kbJxCjFAsDoY+3hbkbw9ebNBsQ6EJVNlb3yQIDY8lcaAIsBisxiSXGToTNWqi/JUWXMIoabrK/95ymDNYHzmDUYAiwWOImOle+EI2AxyiKVydQGVAIoyneiBUNsAQEWIn+LA2NJqOYw3hUFQV2DFbu+FSuKTk9kRGMBoyPE7m8vza7UhMrmQNlV+NwAlDZXmXkkcHaeNamQBCEiLyeb7w631C71yxfr8/6YT8GUP6ifKlcf3xJX26kWNQyJfL+TvVs3S8rCw31hfRm2/KzL+iGQz8BnLjz8EVd5TPXF9wI/E0ZK/gr6SOLj7+Yz9RRu2bmMXm6y2/0A/rS0f6JL985+prVQAJiKEU8kLid6ezaA8Gh7iVP6BKtHad7njQr+xmSeFELLi2YovwlJ5Nbu07DNoLFoVNVrwBvh6feSMwSDcVuLvWF0y3Brd9mDlRlTsuVweGPCRzI+NOBW2rW3iYv5ZPfbrm7ji2fx1/Vkn7U/OGlivE4HA5Yh+yqUKFAYa3QfXcVf/R7Segy3wDGo4x3Tr3H3LLmqX6mZ5+jC4A5DO7jLR3SmAoYj8eTqx5uMR+UKErp78q2/e7ARNuLgE940bR1E9jpcKx+16MxJUd5+68XJp+a5DnqFxU/yYZG8XiSfJ+awkIW9WW+zLF8sh6NeqJb0cs+biIcKjW8u3T3xj+uZXZ/PbSe3km2nHWbogb3ZlSgPWz/bt71tsf432Qa64bSh5PTIFAU0pqdG3oRIiCgBBgsVTwRpYwmnWgMAlUd1aR+6m+DA4QqBlDKRDgifCqq8WO+CoFW9Ctd7dvBA+NV8DgiognuSv4bbsA/e9y1PQRZggAAAAAASUVORK5CYII=" alt="" /></span>
|
||||
<strong>Elastica</strong>
|
||||
<span class="count">
|
||||
<span>{{ collector.querycount }}</span>
|
||||
<span>{{ '%0.0f'|format(collector.time * 1000) }} ms</span>
|
||||
</span>
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
|
|
@ -8,7 +8,7 @@ use JMS\Serializer\SerializerInterface;
|
|||
class Callback
|
||||
{
|
||||
protected $serializer;
|
||||
protected $groups;
|
||||
protected $groups = array();
|
||||
protected $version;
|
||||
|
||||
public function setSerializer($serializer)
|
||||
|
@ -23,10 +23,8 @@ class Callback
|
|||
{
|
||||
$this->groups = $groups;
|
||||
|
||||
if ($this->groups) {
|
||||
if (!$this->serializer instanceof SerializerInterface) {
|
||||
throw new \RuntimeException('Setting serialization groups requires using "JMS\Serializer\Serializer".');
|
||||
}
|
||||
if (!empty($this->groups) && !$this->serializer instanceof SerializerInterface) {
|
||||
throw new \RuntimeException('Setting serialization groups requires using "JMS\Serializer\Serializer".');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,18 +32,16 @@ class Callback
|
|||
{
|
||||
$this->version = $version;
|
||||
|
||||
if ($this->version) {
|
||||
if (!$this->serializer instanceof SerializerInterface) {
|
||||
throw new \RuntimeException('Setting serialization version requires using "JMS\Serializer\Serializer".');
|
||||
}
|
||||
if ($this->version && !$this->serializer instanceof SerializerInterface) {
|
||||
throw new \RuntimeException('Setting serialization version requires using "JMS\Serializer\Serializer".');
|
||||
}
|
||||
}
|
||||
|
||||
public function serialize($object)
|
||||
{
|
||||
$context = $this->serializer instanceof SerializerInterface ? new SerializationContext() : array();
|
||||
$context = $this->serializer instanceof SerializerInterface ? SerializationContext::create()->enableMaxDepthChecks() : array();
|
||||
|
||||
if ($this->groups) {
|
||||
if (!empty($this->groups)) {
|
||||
$context->setGroups($this->groups);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,13 +32,17 @@ class PaginateElasticaQuerySubscriber implements EventSubscriberInterface
|
|||
if (null != $facets) {
|
||||
$event->setCustomPaginationParameter('facets', $facets);
|
||||
}
|
||||
$aggregations = $results->getAggregations();
|
||||
if (null != $aggregations) {
|
||||
$event->setCustomPaginationParameter('aggregations', $aggregations);
|
||||
}
|
||||
|
||||
$event->stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds knp paging sort to query
|
||||
* Adds knp paging sort to query.
|
||||
*
|
||||
* @param ItemsEvent $event
|
||||
*/
|
||||
|
@ -70,7 +74,7 @@ class PaginateElasticaQuerySubscriber implements EventSubscriberInterface
|
|||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'knp_pager.items' => array('items', 1)
|
||||
'knp_pager.items' => array('items', 1),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue