Merge remote-tracking branch 'upstream/master'
Conflicts: Paginator/RawPaginatorAdapter.php
This commit is contained in:
commit
419bf2ccf6
5
.scrutinizer.yml
Normal file
5
.scrutinizer.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
imports:
|
||||
- php
|
||||
|
||||
tools:
|
||||
external_code_coverage: true
|
27
.travis.yml
27
.travis.yml
|
@ -2,8 +2,31 @@ language: php
|
|||
|
||||
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.*'
|
||||
- php: 5.5
|
||||
env: SYMFONY_VERSION='dev-master'
|
||||
|
||||
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,12 +12,38 @@ 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.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)
|
||||
|
||||
|
|
16
CHANGELOG-3.1.md
Normal file
16
CHANGELOG-3.1.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
CHANGELOG for 3.0.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.0
|
||||
|
||||
* BC BREAK: `DoctrineListener#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.
|
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,9 +109,9 @@ class PopulateCommand extends ContainerAwareCommand
|
|||
*/
|
||||
private function populateIndex(OutputInterface $output, $index, $reset, $options)
|
||||
{
|
||||
if ($reset && $this->indexManager->getIndex($index)->exists()) {
|
||||
if ($reset) {
|
||||
$output->writeln(sprintf('<info>Resetting</info> <comment>%s</comment>', $index));
|
||||
$this->resetter->resetIndex($index);
|
||||
$this->resetter->resetIndex($index, true);
|
||||
}
|
||||
|
||||
/** @var $providers ProviderInterface[] */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
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]);
|
||||
}
|
||||
}
|
122
Configuration/IndexConfig.php
Normal file
122
Configuration/IndexConfig.php
Normal file
|
@ -0,0 +1,122 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
42
Configuration/ManagerInterface.php
Normal file
42
Configuration/ManagerInterface.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?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,25 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
||||
|
|
64
Configuration/Source/ContainerSource.php
Normal file
64
Configuration/Source/ContainerSource.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\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 = array();
|
||||
foreach ($config['types'] as $typeConfig) {
|
||||
$types[$typeConfig['name']] = new TypeConfig(
|
||||
$typeConfig['name'],
|
||||
$typeConfig['mapping'],
|
||||
$typeConfig['config']
|
||||
);
|
||||
// TODO: handle prototypes..
|
||||
}
|
||||
|
||||
$index = new IndexConfig($config['name'], $types, array(
|
||||
'elasticSearchName' => $config['elasticsearch_name'],
|
||||
'settings' => $config['settings'],
|
||||
'useAlias' => $config['use_alias'],
|
||||
));
|
||||
|
||||
$indexes[$config['name']] = $index;
|
||||
}
|
||||
|
||||
return $indexes;
|
||||
}
|
||||
}
|
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();
|
||||
}
|
89
Configuration/TypeConfig.php
Normal file
89
Configuration/TypeConfig.php
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?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 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 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);
|
||||
}
|
||||
}
|
|
@ -8,19 +8,29 @@ use Symfony\Component\Config\Definition\ConfigurationInterface;
|
|||
|
||||
class Configuration implements ConfigurationInterface
|
||||
{
|
||||
/**
|
||||
* Stores supported database drivers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
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()
|
||||
{
|
||||
|
@ -32,8 +42,12 @@ class Configuration implements ConfigurationInterface
|
|||
|
||||
$rootNode
|
||||
->children()
|
||||
->scalarNode('default_client')->end()
|
||||
->scalarNode('default_index')->end()
|
||||
->scalarNode('default_client')
|
||||
->info('Defaults to the first client defined')
|
||||
->end()
|
||||
->scalarNode('default_index')
|
||||
->info('Defaults to the first index defined')
|
||||
->end()
|
||||
->scalarNode('default_manager')->defaultValue('orm')->end()
|
||||
->arrayNode('serializer')
|
||||
->treatNullLike(array())
|
||||
|
@ -48,16 +62,6 @@ class Configuration implements ConfigurationInterface
|
|||
return $treeBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the configuration tree.
|
||||
*
|
||||
* @return \Symfony\Component\DependencyInjection\Configuration\NodeInterface
|
||||
*/
|
||||
public function getConfigTree()
|
||||
{
|
||||
return $this->getConfigTreeBuilder()->buildTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the configuration for the "clients" key
|
||||
*/
|
||||
|
@ -70,49 +74,42 @@ 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()
|
||||
// If there is no connections array key defined, assume a single connection.
|
||||
->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 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) !== '/'; })
|
||||
->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('fos_elastica.logger')
|
||||
->defaultValue($this->debug ? 'fos_elastica.logger' : false)
|
||||
->treatNullLike('fos_elastica.logger')
|
||||
->treatTrueLike('fos_elastica.logger')
|
||||
->end()
|
||||
|
@ -120,6 +117,7 @@ class Configuration implements ConfigurationInterface
|
|||
->useAttributeAsKey('name')
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->scalarNode('transport')->end()
|
||||
->scalarNode('timeout')->end()
|
||||
->end()
|
||||
->end()
|
||||
|
@ -145,7 +143,9 @@ class Configuration implements ConfigurationInterface
|
|||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->children()
|
||||
->scalarNode('index_name')->end()
|
||||
->scalarNode('index_name')
|
||||
->info('Defaults to the name of the index, but can be modified if the index name is different in ElasticSearch')
|
||||
->end()
|
||||
->booleanNode('use_alias')->defaultValue(false)->end()
|
||||
->scalarNode('client')->end()
|
||||
->scalarNode('finder')
|
||||
|
@ -156,61 +156,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()
|
||||
|
@ -234,79 +181,58 @@ class Configuration implements ConfigurationInterface
|
|||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->treatNullLike(array())
|
||||
// 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) {
|
||||
$v['indexable_callback'] = $v['persistence']['listener']['is_indexable_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()
|
||||
->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('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('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())
|
||||
|
@ -322,27 +248,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;
|
||||
}
|
||||
|
@ -356,205 +272,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')
|
||||
->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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -594,7 +331,7 @@ class Configuration implements ConfigurationInterface
|
|||
->end()
|
||||
->scalarNode('compress')->end()
|
||||
->scalarNode('compress_threshold')->end()
|
||||
->scalarNode('enabled')->end()
|
||||
->scalarNode('enabled')->defaultTrue()->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
|
@ -715,4 +452,98 @@ 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('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('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,21 +12,42 @@ 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'));
|
||||
$loader->load('config.xml');
|
||||
|
||||
if (empty($config['clients']) || empty($config['indexes'])) {
|
||||
throw new InvalidArgumentException('You must define at least one client and one index');
|
||||
// No Clients or indexes are defined
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (array('config', 'index', 'persister', 'provider', 'source', 'transformer') as $basename) {
|
||||
$loader->load(sprintf('%s.xml', $basename));
|
||||
}
|
||||
|
||||
if (empty($config['default_client'])) {
|
||||
|
@ -41,25 +60,33 @@ 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'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,13 +96,15 @@ class FOSElasticaExtension extends Extension
|
|||
* @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)));
|
||||
}
|
||||
|
@ -83,10 +112,11 @@ class FOSElasticaExtension extends Extension
|
|||
|
||||
$container->setDefinition($clientId, $clientDef);
|
||||
|
||||
$clientIds[$name] = $clientId;
|
||||
$this->clients[$name] = array(
|
||||
'id' => $clientId,
|
||||
'reference' => new Reference($clientId)
|
||||
);
|
||||
}
|
||||
|
||||
return $clientIds;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,56 +124,49 @@ class FOSElasticaExtension extends Extension
|
|||
*
|
||||
* @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();
|
||||
$indexableCallbacks = 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;
|
||||
}
|
||||
|
||||
$clientId = $clientIdsByName[$clientName];
|
||||
$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;
|
||||
$indexName = isset($index['index_name']) ? $index['index_name']: $name;
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,10 +174,10 @@ class FOSElasticaExtension extends Extension
|
|||
*
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
||||
* @param string $name The index name
|
||||
* @param string $indexId The index service identifier
|
||||
* @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.
|
||||
|
@ -165,166 +188,137 @@ 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',
|
||||
) 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
|
||||
*
|
||||
* @param array $typeConfig
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
||||
* @param \Symfony\Component\DependencyInjection\Definition $typeDef
|
||||
* @param $indexName
|
||||
* @param $typeName
|
||||
* @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.
|
||||
*/
|
||||
|
@ -337,28 +331,32 @@ 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);
|
||||
|
@ -370,22 +368,34 @@ class FOSElasticaExtension extends Extension
|
|||
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)];
|
||||
$arguments[] = $this->indexConfigs[$indexName]['types'][$typeName]['mapping']['properties'];
|
||||
}
|
||||
|
||||
$serviceId = sprintf('fos_elastica.object_persister.%s.%s', $indexName, $typeName);
|
||||
$serviceDef = new DefinitionDecorator($abstractId);
|
||||
foreach ($arguments as $i => $argument) {
|
||||
|
@ -397,11 +407,22 @@ 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.
|
||||
*/
|
||||
|
@ -409,19 +430,33 @@ class FOSElasticaExtension extends Extension
|
|||
$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.
|
||||
*/
|
||||
|
@ -429,25 +464,21 @@ 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(3, $typeConfig['identifier']);
|
||||
$listenerDef->replaceArgument(2, $this->getDoctrineEvents($typeConfig));
|
||||
$listenerDef->replaceArgument(1, $this->getDoctrineEvents($typeConfig));
|
||||
$listenerDef->replaceArgument(3, array(
|
||||
'identifier' => $typeConfig['identifier'],
|
||||
'indexName' => $indexName,
|
||||
'typeName' => $typeName,
|
||||
));
|
||||
if ($typeConfig['listener']['logger']) {
|
||||
$listenerDef->replaceArgument(4, new Reference($typeConfig['listener']['logger']));
|
||||
}
|
||||
|
||||
switch ($typeConfig['driver']) {
|
||||
case 'orm': $listenerDef->addTag('doctrine.event_subscriber'); break;
|
||||
case 'mongodb': $listenerDef->addTag('doctrine_mongodb.odm.event_subscriber'); 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);
|
||||
}
|
||||
}
|
||||
|
||||
$listenerDef->addMethodCall('setIsIndexableCallback', array($callback));
|
||||
}
|
||||
$container->setDefinition($listenerId, $listenerDef);
|
||||
|
||||
return $listenerId;
|
||||
|
@ -458,9 +489,6 @@ class FOSElasticaExtension extends Extension
|
|||
*/
|
||||
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';
|
||||
|
@ -490,14 +518,25 @@ 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);
|
||||
}
|
||||
|
@ -516,39 +555,61 @@ class FOSElasticaExtension extends Extension
|
|||
/**
|
||||
* 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;
|
||||
|
@ -564,4 +625,20 @@ 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'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,11 +120,17 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
|
|||
|
||||
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;
|
||||
|
|
|
@ -6,6 +6,7 @@ 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;
|
||||
|
||||
abstract class AbstractProvider extends BaseAbstractProvider
|
||||
{
|
||||
|
@ -15,14 +16,21 @@ abstract class AbstractProvider extends BaseAbstractProvider
|
|||
* Constructor.
|
||||
*
|
||||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param string $objectClass
|
||||
* @param array $options
|
||||
* @param ManagerRegistry $managerRegistry
|
||||
* @param IndexableInterface $indexable
|
||||
* @param string $objectClass
|
||||
* @param array $options
|
||||
* @param ManagerRegistry $managerRegistry
|
||||
*/
|
||||
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $options, $managerRegistry)
|
||||
{
|
||||
parent::__construct($objectPersister, $objectClass, array_merge(array(
|
||||
public function __construct(
|
||||
ObjectPersisterInterface $objectPersister,
|
||||
IndexableInterface $indexable,
|
||||
$objectClass,
|
||||
array $options,
|
||||
ManagerRegistry $managerRegistry
|
||||
) {
|
||||
parent::__construct($objectPersister, $indexable, $objectClass, array_merge(array(
|
||||
'clear_object_manager' => true,
|
||||
'debug_logging' => false,
|
||||
'ignore_errors' => false,
|
||||
'query_builder_method' => 'createQueryBuilder',
|
||||
), $options));
|
||||
|
@ -35,18 +43,34 @@ abstract class AbstractProvider extends BaseAbstractProvider
|
|||
*/
|
||||
public function populate(\Closure $loggerClosure = null, array $options = array())
|
||||
{
|
||||
if (!$this->options['debug_logging']) {
|
||||
$logger = $this->disableLogging();
|
||||
}
|
||||
|
||||
$queryBuilder = $this->createQueryBuilder();
|
||||
$nbObjects = $this->countObjects($queryBuilder);
|
||||
$offset = isset($options['offset']) ? intval($options['offset']) : 0;
|
||||
$sleep = isset($options['sleep']) ? intval($options['sleep']) : 0;
|
||||
$batchSize = isset($options['batch-size']) ? intval($options['batch-size']) : $this->options['batch_size'];
|
||||
$ignoreErrors = isset($options['ignore-errors']) ? $options['ignore-errors'] : $this->options['ignore_errors'];
|
||||
$manager = $this->managerRegistry->getManagerForClass($this->objectClass);
|
||||
|
||||
for (; $offset < $nbObjects; $offset += $batchSize) {
|
||||
if ($loggerClosure) {
|
||||
$stepStartTime = microtime(true);
|
||||
}
|
||||
$objects = $this->fetchSlice($queryBuilder, $batchSize, $offset);
|
||||
if ($loggerClosure) {
|
||||
$stepNbObjects = count($objects);
|
||||
}
|
||||
$objects = array_filter($objects, array($this, 'isObjectIndexable'));
|
||||
if (!$objects) {
|
||||
if ($loggerClosure) {
|
||||
$loggerClosure('<info>Entire batch was filtered away, skipping...</info>');
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$ignoreErrors) {
|
||||
$this->objectPersister->insertMany($objects);
|
||||
|
@ -61,19 +85,23 @@ abstract class AbstractProvider extends BaseAbstractProvider
|
|||
}
|
||||
|
||||
if ($this->options['clear_object_manager']) {
|
||||
$this->managerRegistry->getManagerForClass($this->objectClass)->clear();
|
||||
$manager->clear();
|
||||
}
|
||||
|
||||
usleep($sleep);
|
||||
|
||||
if ($loggerClosure) {
|
||||
$stepNbObjects = count($objects);
|
||||
$stepCount = $stepNbObjects + $offset;
|
||||
$percentComplete = 100 * $stepCount / $nbObjects;
|
||||
$objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
|
||||
$timeDifference = microtime(true) - $stepStartTime;
|
||||
$objectsPerSecond = $timeDifference ? ($stepNbObjects / $timeDifference) : $stepNbObjects;
|
||||
$loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s %s', $percentComplete, $stepCount, $nbObjects, $objectsPerSecond, $this->getMemoryUsage()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->options['debug_logging']) {
|
||||
$this->enableLogging($logger);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,6 +112,21 @@ abstract class AbstractProvider extends BaseAbstractProvider
|
|||
*/
|
||||
protected abstract function countObjects($queryBuilder);
|
||||
|
||||
/**
|
||||
* Disables logging and returns the logger that was previously set.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected abstract function disableLogging();
|
||||
|
||||
/**
|
||||
* Reenables the logger with the previously returned logger from disableLogging();
|
||||
*
|
||||
* @param mixed $logger
|
||||
* @return mixed
|
||||
*/
|
||||
protected abstract function enableLogging($logger);
|
||||
|
||||
/**
|
||||
* Fetches a slice of objects using the query builder.
|
||||
*
|
||||
|
|
|
@ -2,14 +2,18 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Doctrine;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersister;
|
||||
use Symfony\Component\ExpressionLanguage\Expression;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\ExpressionLanguage\SyntaxError;
|
||||
use FOS\ElasticaBundle\Provider\IndexableInterface;
|
||||
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
|
||||
{
|
||||
/**
|
||||
|
@ -19,13 +23,6 @@ class Listener implements EventSubscriber
|
|||
*/
|
||||
protected $objectPersister;
|
||||
|
||||
/**
|
||||
* Class of the domain model
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $objectClass;
|
||||
|
||||
/**
|
||||
* List of subscribed events
|
||||
*
|
||||
|
@ -34,47 +31,72 @@ class Listener implements EventSubscriber
|
|||
protected $events;
|
||||
|
||||
/**
|
||||
* Name of domain model field used as the ES identifier
|
||||
* Configuration for the listener
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $esIdentifierField;
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* Callback for determining if an object should be indexed
|
||||
* Objects scheduled for insertion.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $isIndexableCallback;
|
||||
|
||||
/**
|
||||
* Objects scheduled for insertion, replacement, or removal
|
||||
* @var array
|
||||
*/
|
||||
public $scheduledForInsertion = array();
|
||||
|
||||
/**
|
||||
* Objects scheduled to be updated or removed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $scheduledForUpdate = array();
|
||||
|
||||
/**
|
||||
* IDs of objects scheduled for removal
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $scheduledForDeletion = array();
|
||||
|
||||
/**
|
||||
* An instance of ExpressionLanguage
|
||||
* PropertyAccessor instance
|
||||
*
|
||||
* @var ExpressionLanguage
|
||||
* @var PropertyAccessorInterface
|
||||
*/
|
||||
protected $expressionLanguage;
|
||||
protected $propertyAccessor;
|
||||
|
||||
/**
|
||||
* @var IndexableInterface
|
||||
*/
|
||||
private $indexable;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param string $objectClass
|
||||
* @param array $events
|
||||
* @param string $esIdentifierField
|
||||
* @param array $events
|
||||
* @param IndexableInterface $indexable
|
||||
* @param array $config
|
||||
* @param null $logger
|
||||
*/
|
||||
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $events, $esIdentifierField = 'id')
|
||||
{
|
||||
$this->objectPersister = $objectPersister;
|
||||
$this->objectClass = $objectClass;
|
||||
$this->events = $events;
|
||||
$this->esIdentifierField = $esIdentifierField;
|
||||
public function __construct(
|
||||
ObjectPersisterInterface $objectPersister,
|
||||
array $events,
|
||||
IndexableInterface $indexable,
|
||||
array $config = array(),
|
||||
$logger = null
|
||||
) {
|
||||
$this->config = array_merge(array(
|
||||
'identifier' => 'id',
|
||||
), $config);
|
||||
$this->events = $events;
|
||||
$this->indexable = $indexable;
|
||||
$this->objectPersister = $objectPersister;
|
||||
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
|
||||
|
||||
if ($logger) {
|
||||
$this->objectPersister->setLogger($logger);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,160 +108,122 @@ class Listener implements EventSubscriber
|
|||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 = $eventArgs->getEntity();
|
||||
|
||||
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 = $eventArgs->getEntity();
|
||||
$entity = $eventArgs->getObject();
|
||||
|
||||
if ($entity instanceof $this->objectClass) {
|
||||
if ($this->objectPersister->handlesObject($entity)) {
|
||||
if ($this->isObjectIndexable($entity)) {
|
||||
$this->scheduledForUpdate[] = $entity;
|
||||
} else {
|
||||
// Delete if no longer indexable
|
||||
$this->scheduledForDeletion[] = $entity;
|
||||
$this->scheduleForDeletion($entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function preRemove(EventArgs $eventArgs)
|
||||
/**
|
||||
* Delete objects preRemove instead of postRemove so that we have access to the id. Because this is called
|
||||
* preRemove, first check that the entity is managed by Doctrine
|
||||
*
|
||||
* @param LifecycleEventArgs $eventArgs
|
||||
*/
|
||||
public function preRemove(LifecycleEventArgs $eventArgs)
|
||||
{
|
||||
$entity = $eventArgs->getEntity();
|
||||
$entity = $eventArgs->getObject();
|
||||
|
||||
if ($entity instanceof $this->objectClass) {
|
||||
$this->scheduledForDeletion[] = $entity;
|
||||
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
|
||||
*/
|
||||
private function persistScheduled()
|
||||
{
|
||||
if (count($this->scheduledForInsertion)) {
|
||||
$this->objectPersister->insertMany($this->scheduledForInsertion);
|
||||
$this->scheduledForInsertion = array();
|
||||
}
|
||||
if (count($this->scheduledForUpdate)) {
|
||||
$this->objectPersister->replaceMany($this->scheduledForUpdate);
|
||||
$this->scheduledForUpdate = array();
|
||||
}
|
||||
if (count($this->scheduledForDeletion)) {
|
||||
$this->objectPersister->deleteMany($this->scheduledForDeletion);
|
||||
$this->objectPersister->deleteManyByIdentifiers($this->scheduledForDeletion);
|
||||
$this->scheduledForDeletion = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 object $object
|
||||
*/
|
||||
private function scheduleForDeletion($object)
|
||||
{
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
|
|||
{
|
||||
return $this->registry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getRepository($this->objectClass)
|
||||
->{$this->options['query_builder_method']}($this->objectClass)
|
||||
->field($this->options['identifier'])->in($identifierValues)
|
||||
->hydrate($hydrate)
|
||||
|
|
|
@ -8,6 +8,40 @@ use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
|
|||
|
||||
class Provider extends AbstractProvider
|
||||
{
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
|
||||
*/
|
||||
|
|
|
@ -29,7 +29,7 @@ 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();
|
||||
|
|
|
@ -9,7 +9,41 @@ use FOS\ElasticaBundle\Exception\InvalidArgumentTypeException;
|
|||
class Provider extends AbstractProvider
|
||||
{
|
||||
const ENTITY_ALIAS = 'a';
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see FOS\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
|
||||
*/
|
||||
|
@ -50,12 +84,13 @@ class Provider extends AbstractProvider
|
|||
*/
|
||||
$orderBy = $queryBuilder->getDQLPart('orderBy');
|
||||
if (empty($orderBy)) {
|
||||
$rootAliases = $queryBuilder->getRootAliases();
|
||||
$identifierFieldNames = $this->managerRegistry
|
||||
->getManagerForClass($this->objectClass)
|
||||
->getClassMetadata($this->objectClass)
|
||||
->getIdentifierFieldNames();
|
||||
foreach ($identifierFieldNames as $fieldName) {
|
||||
$queryBuilder->addOrderBy(static::ENTITY_ALIAS.'.'.$fieldName);
|
||||
$queryBuilder->addOrderBy($rootAliases[0].'.'.$fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
103
Elastica/Client.php
Normal file
103
Elastica/Client.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
61
Elastica/Index.php
Normal file
61
Elastica/Index.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?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
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function overrideName($name)
|
||||
{
|
||||
$this->originalName = $this->_name;
|
||||
$this->_name = $name;
|
||||
}
|
||||
}
|
12
Exception/AliasIsIndexException.php
Normal file
12
Exception/AliasIsIndexException.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Exception;
|
||||
|
||||
class AliasIsIndexException extends \Exception
|
||||
{
|
||||
public function __construct($indexName)
|
||||
{
|
||||
parent::__construct(sprintf('Expected alias %s instead of 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;
|
||||
|
@ -21,6 +23,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());
|
||||
}
|
||||
|
|
178
Index/AliasProcessor.php
Normal file
178
Index/AliasProcessor.php
Normal file
|
@ -0,0 +1,178 @@
|
|||
<?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 = false;
|
||||
$newIndexName = $index->getName();
|
||||
|
||||
try {
|
||||
$aliasedIndexes = $this->getAliasedIndexes($client, $aliasName);
|
||||
} catch(AliasIsIndexException $e) {
|
||||
if (!$force) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->deleteIndex($client, $aliasName);
|
||||
$aliasedIndexes = array();
|
||||
}
|
||||
|
||||
if (count($aliasedIndexes) > 1) {
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
'Alias %s is used for multiple indexes: [%s].
|
||||
Make sure it\'s either not used or is assigned to one index only',
|
||||
$aliasName,
|
||||
join(', ', $aliasedIndexes)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$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 {
|
||||
$client->request('_aliases', 'POST', $aliasUpdateRequest);
|
||||
} catch (ExceptionInterface $renameAliasException) {
|
||||
$additionalError = '';
|
||||
// if we failed to move the alias, delete the newly built index
|
||||
try {
|
||||
$index->delete();
|
||||
} catch (ExceptionInterface $deleteNewIndexException) {
|
||||
$additionalError = sprintf(
|
||||
'Tried to delete newly built index %s, but also failed: %s',
|
||||
$newIndexName,
|
||||
$deleteNewIndexException->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
'Failed to updated index alias: %s. %s',
|
||||
$renameAliasException->getMessage(),
|
||||
$additionalError ?: sprintf('Newly built index %s was deleted', $newIndexName)
|
||||
), 0, $renameAliasException
|
||||
);
|
||||
}
|
||||
|
||||
// Delete the old index after the alias has been switched
|
||||
if ($oldIndexName) {
|
||||
$oldIndex = new Index($client, $oldIndexName);
|
||||
try {
|
||||
$oldIndex->delete();
|
||||
} catch (ExceptionInterface $deleteOldIndexException) {
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
'Failed to delete old index %s with message: %s',
|
||||
$oldIndexName,
|
||||
$deleteOldIndexException->getMessage()
|
||||
), 0, $deleteOldIndexException
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of indexes which are mapped to given alias
|
||||
*
|
||||
* @param Client $client
|
||||
* @param string $aliasName Alias name
|
||||
*
|
||||
* @return array
|
||||
* @throws AliasIsIndexException
|
||||
*/
|
||||
private function getAliasedIndexes(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;
|
||||
}
|
||||
}
|
||||
|
||||
return $aliasedIndexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an index
|
||||
*
|
||||
* @param Client $client
|
||||
* @param string $indexName Index name to delete
|
||||
*/
|
||||
private function deleteIndex(Client $client, $indexName)
|
||||
{
|
||||
$path = sprintf("%s", $indexName);
|
||||
$client->request($path, Request::DELETE);
|
||||
}
|
||||
}
|
63
Index/IndexManager.php
Normal file
63
Index/IndexManager.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Index;
|
||||
|
||||
use FOS\ElasticaBundle\Elastica\Index;
|
||||
|
||||
class IndexManager
|
||||
{
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
121
Index/MappingBuilder.php
Normal file
121
Index/MappingBuilder.php
Normal file
|
@ -0,0 +1,121 @@
|
|||
<?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 ($typeMappings) {
|
||||
$mapping['mappings'] = $typeMappings;
|
||||
}
|
||||
// 'warmers' => $indexConfig->getWarmers(),
|
||||
|
||||
$settings = $indexConfig->getSettings();
|
||||
if ($settings) {
|
||||
$mapping['settings'] = $settings;
|
||||
}
|
||||
|
||||
return $mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds mappings for a single type.
|
||||
*
|
||||
* @param TypeConfig $typeConfig
|
||||
* @return array
|
||||
*/
|
||||
public function buildTypeMapping(TypeConfig $typeConfig)
|
||||
{
|
||||
$mapping = array_merge($typeConfig->getMapping(), array(
|
||||
// 'date_detection' => true,
|
||||
// 'dynamic_date_formats' => array()
|
||||
// 'dynamic_templates' => $typeConfig->getDynamicTemplates(),
|
||||
// 'numeric_detection' => false,
|
||||
// 'properties' => array(),
|
||||
));
|
||||
|
||||
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 (!$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) {
|
||||
if (!isset($property['type'])) {
|
||||
$property['type'] = 'string';
|
||||
}
|
||||
if (isset($property['properties'])) {
|
||||
$this->fixProperties($property['properties']);
|
||||
}
|
||||
if (in_array($property['type'], $this->skipTypes)) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($property['store'])) {
|
||||
$property['store'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
122
Index/Resetter.php
Normal file
122
Index/Resetter.php
Normal file
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Index;
|
||||
|
||||
use Elastica\Index;
|
||||
use Elastica\Exception\ResponseException;
|
||||
use Elastica\Type\Mapping;
|
||||
use FOS\ElasticaBundle\Configuration\ConfigManager;
|
||||
|
||||
/**
|
||||
* Deletes and recreates indexes
|
||||
*/
|
||||
class Resetter
|
||||
{
|
||||
/**
|
||||
* @var AliasProcessor
|
||||
*/
|
||||
private $aliasProcessor;
|
||||
|
||||
/***
|
||||
* @var \FOS\ElasticaBundle\Configuration\Manager
|
||||
*/
|
||||
private $configManager;
|
||||
|
||||
/**
|
||||
* @var IndexManager
|
||||
*/
|
||||
private $indexManager;
|
||||
|
||||
/**
|
||||
* @var MappingBuilder
|
||||
*/
|
||||
private $mappingBuilder;
|
||||
|
||||
public function __construct(ConfigManager $configManager, IndexManager $indexManager, AliasProcessor $aliasProcessor, MappingBuilder $mappingBuilder)
|
||||
{
|
||||
$this->aliasProcessor = $aliasProcessor;
|
||||
$this->configManager = $configManager;
|
||||
$this->indexManager = $indexManager;
|
||||
$this->mappingBuilder = $mappingBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes and recreates all indexes
|
||||
*/
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ 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;
|
||||
|
@ -69,6 +69,9 @@ class RepositoryManager implements RepositoryManagerInterface
|
|||
return 'FOS\ElasticaBundle\Repository';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entityName
|
||||
*/
|
||||
private function createRepository($entityName)
|
||||
{
|
||||
if (!class_exists($repositoryName = $this->getRepositoryName($entityName))) {
|
||||
|
|
|
@ -59,8 +59,8 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
/**
|
||||
* Returns the paginated results.
|
||||
*
|
||||
* @param $offset
|
||||
* @param $itemCountPerPage
|
||||
* @param integer $offset
|
||||
* @param integer $itemCountPerPage
|
||||
* @throws \InvalidArgumentException
|
||||
* @return ResultSet
|
||||
*/
|
||||
|
@ -132,6 +132,7 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
|
||||
return $this->facets;
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
|
||||
/**
|
||||
* Returns Aggregations
|
||||
|
@ -146,6 +147,8 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
return $this->aggregations;
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> upstream/master
|
||||
|
||||
/**
|
||||
* Returns the Query
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Persister;
|
||||
|
||||
use Elastica\Exception\NotFoundException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Elastica\Exception\BulkException;
|
||||
use FOS\ElasticaBundle\Transformer\ModelToElasticaTransformerInterface;
|
||||
use Elastica\Type;
|
||||
use Elastica\Document;
|
||||
|
@ -19,6 +20,7 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
protected $transformer;
|
||||
protected $objectClass;
|
||||
protected $fields;
|
||||
protected $logger;
|
||||
|
||||
public function __construct(Type $type, ModelToElasticaTransformerInterface $transformer, $objectClass, array $fields)
|
||||
{
|
||||
|
@ -28,6 +30,38 @@ 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;
|
||||
}
|
||||
|
||||
public function setLogger(LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
if (! $this->logger) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->logger->error($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert one object into the type
|
||||
* The object will be transformed to an elastica document
|
||||
|
@ -36,8 +70,7 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
*/
|
||||
public function insertOne($object)
|
||||
{
|
||||
$document = $this->transformToElasticaDocument($object);
|
||||
$this->type->addDocument($document);
|
||||
$this->insertMany(array($object));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,11 +81,7 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
**/
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,10 +92,7 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
**/
|
||||
public function deleteOne($object)
|
||||
{
|
||||
$document = $this->transformToElasticaDocument($object);
|
||||
try {
|
||||
$this->type->deleteById($document->getId());
|
||||
} catch (NotFoundException $e) {}
|
||||
$this->deleteMany(array($object));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -78,9 +104,7 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
**/
|
||||
public function deleteById($id)
|
||||
{
|
||||
try {
|
||||
$this->type->deleteById($id);
|
||||
} catch (NotFoundException $e) {}
|
||||
$this->deleteManyByIdentifiers(array($id));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -95,11 +119,15 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
foreach ($objects as $object) {
|
||||
$documents[] = $this->transformToElasticaDocument($object);
|
||||
}
|
||||
$this->type->addDocuments($documents);
|
||||
try {
|
||||
$this->type->addDocuments($documents);
|
||||
} catch (BulkException $e) {
|
||||
$this->log($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk updates an array of objects in the type
|
||||
* Bulk update an array of objects in the type. Create document if it does not already exist.
|
||||
*
|
||||
* @param array $objects array of domain model objects
|
||||
*/
|
||||
|
@ -107,9 +135,16 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
{
|
||||
$documents = array();
|
||||
foreach ($objects as $object) {
|
||||
$documents[] = $this->transformToElasticaDocument($object);
|
||||
$document = $this->transformToElasticaDocument($object);
|
||||
$document->setDocAsUpsert(true);
|
||||
$documents[] = $document;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->type->updateDocuments($documents);
|
||||
} catch (BulkException $e) {
|
||||
$this->log($e);
|
||||
}
|
||||
$this->type->updateDocuments($documents);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,7 +158,25 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
foreach ($objects as $object) {
|
||||
$documents[] = $this->transformToElasticaDocument($object);
|
||||
}
|
||||
$this->type->deleteDocuments($documents);
|
||||
try {
|
||||
$this->type->deleteDocuments($documents);
|
||||
} catch (BulkException $e) {
|
||||
$this->log($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk deletes records from an array of identifiers
|
||||
*
|
||||
* @param array $identifiers array of domain model object identifiers
|
||||
*/
|
||||
public function deleteManyByIdentifiers(array $identifiers)
|
||||
{
|
||||
try {
|
||||
$this->type->getIndex()->getClient()->deleteIds($identifiers, $this->type->getIndex(), $this->type);
|
||||
} catch (BulkException $e) {
|
||||
$this->log($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,4 +189,4 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
{
|
||||
return $this->transformer->transform($object, $this->fields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,4 +61,19 @@ interface ObjectPersisterInterface
|
|||
* @param array $objects array of domain model objects
|
||||
*/
|
||||
function deleteMany(array $objects);
|
||||
|
||||
/**
|
||||
* Bulk deletes records from an array of identifiers
|
||||
*
|
||||
* @param array $identifiers array of domain model object identifiers
|
||||
*/
|
||||
public function deleteManyByIdentifiers(array $identifiers);
|
||||
|
||||
/**
|
||||
* If the object persister handles the given object.
|
||||
*
|
||||
* @param object $object
|
||||
* @return bool
|
||||
*/
|
||||
public function handlesObject($object);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ class ObjectSerializerPersister extends ObjectPersister
|
|||
{
|
||||
protected $serializer;
|
||||
|
||||
/**
|
||||
* @param string $objectClass
|
||||
*/
|
||||
public function __construct(Type $type, ModelToElasticaTransformerInterface $transformer, $objectClass, $serializer)
|
||||
{
|
||||
parent::__construct($type, $transformer, $objectClass, array());
|
||||
|
|
|
@ -170,6 +170,7 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
|
|||
|
||||
/**
|
||||
* @see https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Util/Inflector.php
|
||||
* @param string $str
|
||||
*/
|
||||
private function camelize($str)
|
||||
{
|
||||
|
|
|
@ -30,14 +30,23 @@ class Provider extends AbstractProvider
|
|||
$objects = $queryClass::create()
|
||||
->limit($batchSize)
|
||||
->offset($offset)
|
||||
->find();
|
||||
->find()
|
||||
->getArrayCopy();
|
||||
if ($loggerClosure) {
|
||||
$stepNbObjects = count($objects);
|
||||
}
|
||||
$objects = array_filter($objects, array($this, 'isObjectIndexable'));
|
||||
if (!$objects) {
|
||||
$loggerClosure('<info>Entire batch was filtered away, skipping...</info>');
|
||||
|
||||
$this->objectPersister->insertMany($objects->getArrayCopy());
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->objectPersister->insertMany($objects);
|
||||
|
||||
usleep($sleep);
|
||||
|
||||
if ($loggerClosure) {
|
||||
$stepNbObjects = count($objects);
|
||||
$stepCount = $stepNbObjects + $offset;
|
||||
$percentComplete = 100 * $stepCount / $nbObjects;
|
||||
$objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
|
||||
|
|
|
@ -24,23 +24,49 @@ abstract class AbstractProvider implements ProviderInterface
|
|||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* @var Indexable
|
||||
*/
|
||||
private $indexable;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param string $objectClass
|
||||
* @param array $options
|
||||
* @param IndexableInterface $indexable
|
||||
* @param string $objectClass
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $options = array())
|
||||
{
|
||||
$this->objectPersister = $objectPersister;
|
||||
public function __construct(
|
||||
ObjectPersisterInterface $objectPersister,
|
||||
IndexableInterface $indexable,
|
||||
$objectClass,
|
||||
array $options = array()
|
||||
) {
|
||||
$this->indexable = $indexable;
|
||||
$this->objectClass = $objectClass;
|
||||
$this->objectPersister = $objectPersister;
|
||||
|
||||
$this->options = array_merge(array(
|
||||
'batch_size' => 100,
|
||||
), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given object should be indexed or not.
|
||||
*
|
||||
* @param object $object
|
||||
* @return bool
|
||||
*/
|
||||
protected function isObjectIndexable($object)
|
||||
{
|
||||
return $this->indexable->isObjectIndexable(
|
||||
$this->options['indexName'],
|
||||
$this->options['typeName'],
|
||||
$object
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string with RAM usage information (current and peak)
|
||||
*
|
||||
|
|
188
Provider/Indexable.php
Normal file
188
Provider/Indexable.php
Normal file
|
@ -0,0 +1,188 @@
|
|||
<?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
|
||||
*/
|
||||
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 $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 null;
|
||||
}
|
||||
|
||||
$callback = $this->callbacks[$type];
|
||||
|
||||
if (is_callable($callback) or is_callable(array($object, $callback))) {
|
||||
return $callback;
|
||||
}
|
||||
|
||||
if (is_array($callback)) {
|
||||
list($class, $method) = $callback + array(null, null);
|
||||
|
||||
if (is_object($class)) {
|
||||
$class = get_class($class);
|
||||
}
|
||||
|
||||
if (strpos($class, '@') === 0) {
|
||||
$service = $this->container->get(substr($class, 1));
|
||||
|
||||
return array($service, $method);
|
||||
}
|
||||
|
||||
if ($class && $method) {
|
||||
throw new \InvalidArgumentException(sprintf('Callback for type "%s", "%s::%s()", is not callable.', $type, $class, $method));
|
||||
}
|
||||
}
|
||||
|
||||
if (is_string($callback) && $expression = $this->getExpressionLanguage()) {
|
||||
$callback = new Expression($callback);
|
||||
|
||||
try {
|
||||
$expression->compile($callback, array('object', $this->getExpressionVar($object)));
|
||||
|
||||
return $callback;
|
||||
} catch (SyntaxError $e) {
|
||||
throw new \InvalidArgumentException(sprintf('Callback for type "%s" is an invalid expression', $type), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('Callback for type "%s" is not a valid callback.', $type));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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];
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $object
|
||||
* @return string
|
||||
*/
|
||||
private function getExpressionVar($object = null)
|
||||
{
|
||||
$ref = new \ReflectionClass($object);
|
||||
|
||||
return strtolower($ref->getShortName());
|
||||
}
|
||||
}
|
25
Provider/IndexableInterface.php
Normal file
25
Provider/IndexableInterface.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?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);
|
||||
}
|
906
README.md
906
README.md
|
@ -1,897 +1,35 @@
|
|||
[Elastica](https://github.com/ruflin/Elastica) integration in Symfony2
|
||||
FOSElasticaBundle
|
||||
=================
|
||||
|
||||
### Installation
|
||||
This bundle provides integration with [ElasticSearch](http://www.elasticsearch.org) and [Elastica](https://github.com/ruflin/Elastica) with
|
||||
Symfony2. Features include:
|
||||
|
||||
#### Bundle and Dependencies
|
||||
- Integrates the Elastica library into a Symfony2 environment
|
||||
- Automatically generate mappings using a serializer
|
||||
- Listeners for Doctrine events for automatic indexing
|
||||
|
||||
For Symfony 2.0.x projects, you must use a 1.x release of this bundle. Please
|
||||
check the bundle
|
||||
[tags](https://github.com/FriendsOfSymfony/FOSElasticaBundle/tags) or the
|
||||
[Packagist](https://packagist.org/packages/friendsofsymfony/elastica-bundle)
|
||||
page for information on Symfony and Elastica compatibility.
|
||||
> **Note** Propel support is limited and contributions fixing issues are welcome!
|
||||
|
||||
Add FOSElasticaBundle to your application's `composer.json` file:
|
||||
[![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)
|
||||
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"friendsofsymfony/elastica-bundle": "3.0.*@dev"
|
||||
}
|
||||
}
|
||||
```
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Install the bundle and its dependencies with the following command:
|
||||
Documentation for FOSElasticaBundle is in `Resources/doc/index.md`
|
||||
|
||||
```bash
|
||||
$ php composer.phar update friendsofsymfony/elastica-bundle
|
||||
```
|
||||
[Read the documentation for 3.0.x (master)](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/Resources/doc/index.md)
|
||||
|
||||
You may rely on Composer to fetch the appropriate version of Elastica. Lastly,
|
||||
enable the bundle in your application kernel:
|
||||
[Read the documentation for 2.1.x](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/2.1.x/README.md)
|
||||
|
||||
```php
|
||||
// app/AppKernel.php
|
||||
Installation
|
||||
------------
|
||||
|
||||
public function registerBundles()
|
||||
{
|
||||
$bundles = array(
|
||||
// ...
|
||||
new FOS\ElasticaBundle\FOSElasticaBundle(),
|
||||
);
|
||||
}
|
||||
```
|
||||
Installation instructions can be found in the [documentation](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/Resources/doc/setup.md)
|
||||
|
||||
#### Elasticsearch
|
||||
License
|
||||
-------
|
||||
|
||||
Instructions for installing and deploying Elasticsearch may be found
|
||||
[here](http://www.elasticsearch.org/guide/reference/setup/installation/).
|
||||
This bundle is under the MIT license. See the complete license in the bundle:
|
||||
|
||||
### Basic configuration
|
||||
|
||||
#### Declare a client
|
||||
|
||||
Elasticsearch client is comparable to a database connection.
|
||||
Most of the time, you will need only one.
|
||||
|
||||
#app/config/config.yml
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
|
||||
If your client requires Basic HTTP Authentication, you can specify an Authorization Header to
|
||||
include in HTTP requests. The Authorization Header value is a ``base64`` encoded string that
|
||||
includes the authentication username and password, and can be obtained by running the following
|
||||
command in your terminal:
|
||||
|
||||
php -r "Print 'Basic ' . base64_encode('your_auth_username' . ':' . 'your_auth_password');"
|
||||
|
||||
A sample configuration with Basic HTTP Authentication is:
|
||||
|
||||
#app/config/config.yml
|
||||
fos_elastica:
|
||||
clients:
|
||||
default:
|
||||
host: example.com
|
||||
port: 80
|
||||
headers:
|
||||
Authorization: "Basic jdumrGK7rY9TMuQOPng7GZycmxyMHNoir=="
|
||||
|
||||
A client configuration can also override the Elastica logger to change the used class ```logger: <your logger class>``` or to simply disable it ```logger: false```. Disabling the logger should be done on production because it can cause a memory leak.
|
||||
|
||||
#### Declare a serializer
|
||||
|
||||
Elastica can handle objects instead of data arrays if a serializer callable is configured
|
||||
|
||||
#app/config/config.yml
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
serializer:
|
||||
callback_class: callback_class
|
||||
serializer: serializer
|
||||
|
||||
``callback_class`` is the name of a class having a public method serialize($object) and should
|
||||
extends from ``FOS\ElasticaBundle\Serializer\Callback``.
|
||||
|
||||
``serializer`` is the service id for the actual serializer, e.g. ``serializer`` if you're using
|
||||
JMSSerializerBundle. If this is configured you can use ``\Elastica\Type::addObject`` instead of
|
||||
``\Elastica\Type::addDocument`` to add data to the index. The bundle provides a default implementation
|
||||
with a serializer service id 'serializer' that can be turned on by adding the following line to your config.
|
||||
|
||||
#app/config/config.yml
|
||||
fos_elastica:
|
||||
serializer: ~
|
||||
|
||||
#### Declare an index
|
||||
|
||||
Elasticsearch index is comparable to Doctrine entity manager.
|
||||
Most of the time, you will need only one.
|
||||
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
serializer:
|
||||
callback_class: FOS\ElasticaBundle\Serializer\Callback
|
||||
serializer: serializer
|
||||
indexes:
|
||||
website:
|
||||
client: default
|
||||
|
||||
Here we created a "website" index, that uses our "default" client.
|
||||
|
||||
Our index is now available as a service: `fos_elastica.index.website`. It is an instance of `\Elastica\Index`.
|
||||
|
||||
If you need to have different index name from the service name, for example,
|
||||
in order to have different indexes for different environments then you can
|
||||
use the ```index_name``` key to change the index name. The service name will
|
||||
remain the same across the environments:
|
||||
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
indexes:
|
||||
website:
|
||||
client: default
|
||||
index_name: website_qa
|
||||
|
||||
The service id will be `fos_elastica.index.website` but the underlying index name is website_qa.
|
||||
|
||||
#### Declare a type
|
||||
|
||||
Elasticsearch type is comparable to Doctrine entity repository.
|
||||
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
serializer:
|
||||
callback_class: FOS\ElasticaBundle\Serializer\Callback
|
||||
serializer: serializer
|
||||
indexes:
|
||||
website:
|
||||
client: default
|
||||
types:
|
||||
user:
|
||||
mappings:
|
||||
username: { boost: 5 }
|
||||
firstName: { boost: 3 }
|
||||
lastName: { boost: 3 }
|
||||
aboutMe: ~
|
||||
|
||||
Our type is now available as a service: `fos_elastica.index.website.user`. It is an instance of `\Elastica\Type`.
|
||||
|
||||
### Declaring serializer groups
|
||||
|
||||
If you are using the JMSSerializerBundle for serializing objects passed to elastica you can define serializer groups
|
||||
per type.
|
||||
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
serializer:
|
||||
callback_class: %classname%
|
||||
serializer: serializer
|
||||
indexes:
|
||||
website:
|
||||
client: default
|
||||
types:
|
||||
user:
|
||||
mappings:
|
||||
username: { boost: 5 }
|
||||
firstName: { boost: 3 }
|
||||
lastName: { boost: 3 }
|
||||
aboutMe:
|
||||
serializer:
|
||||
groups: [elastica, Default]
|
||||
|
||||
### Declaring parent field
|
||||
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
serializer:
|
||||
callback_class: FOS\ElasticaBundle\Serializer\Callback
|
||||
serializer: serializer
|
||||
indexes:
|
||||
website:
|
||||
client: default
|
||||
types:
|
||||
comment:
|
||||
mappings:
|
||||
date: { boost: 5 }
|
||||
content: ~
|
||||
_parent: { type: "post", property: "post", identifier: "id" }
|
||||
|
||||
The parent field declaration has the following values:
|
||||
|
||||
* `type`: The parent type.
|
||||
* `property`: The property in the child entity where to look for the parent entity. It may be ignored if is equal to the parent type.
|
||||
* `identifier`: The property in the parent entity which has the parent identifier. Defaults to `id`.
|
||||
|
||||
Note that to create a document with a parent, you need to call `setParent` on the document rather than setting a _parent field.
|
||||
If you do this wrong, you will see a `RoutingMissingException` as elasticsearch does not know where to store a document that should have a parent but does not specify it.
|
||||
|
||||
### Declaring `nested` or `object`
|
||||
|
||||
Note that object can autodetect properties
|
||||
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
serializer:
|
||||
callback_class: FOS\ElasticaBundle\Serializer\Callback
|
||||
serializer: serializer
|
||||
indexes:
|
||||
website:
|
||||
client: default
|
||||
types:
|
||||
post:
|
||||
mappings:
|
||||
date: { boost: 5 }
|
||||
title: { boost: 3 }
|
||||
content: ~
|
||||
comments:
|
||||
type: "nested"
|
||||
properties:
|
||||
date: { boost: 5 }
|
||||
content: ~
|
||||
user:
|
||||
type: "object"
|
||||
approver:
|
||||
type: "object"
|
||||
properties:
|
||||
date: { boost: 5 }
|
||||
|
||||
#### Doctrine ORM and `object` mappings
|
||||
|
||||
Objects operate in the same way as the nested results but they need to have associations set up in Doctrine ORM so that they can be referenced correctly when indexing.
|
||||
|
||||
If an "Entity was not found" error occurs while indexing, a null association has been discovered in the database. A custom Doctrine query must be used to utilize left joins instead of the default inner join.
|
||||
|
||||
### Populate the types
|
||||
|
||||
php app/console fos:elastica:populate
|
||||
|
||||
This command deletes and creates the declared indexes and types.
|
||||
It applies the configured mappings to the types.
|
||||
|
||||
This command needs providers to insert new documents in the elasticsearch types.
|
||||
There are 2 ways to create providers.
|
||||
If your elasticsearch type matches a Doctrine repository or a Propel query, go for the persistence automatic provider.
|
||||
Or, for complete flexibility, go for a manual provider.
|
||||
|
||||
#### Persistence automatic provider
|
||||
|
||||
If we want to index the entities from a Doctrine repository or a Propel query,
|
||||
some configuration will let ElasticaBundle do it for us.
|
||||
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
serializer:
|
||||
callback_class: FOS\ElasticaBundle\Serializer\Callback
|
||||
serializer: serializer
|
||||
indexes:
|
||||
website:
|
||||
client: default
|
||||
types:
|
||||
user:
|
||||
mappings:
|
||||
username: { boost: 5 }
|
||||
firstName: { boost: 3 }
|
||||
# more mappings...
|
||||
persistence:
|
||||
driver: orm # orm, mongodb, propel are available
|
||||
model: Application\UserBundle\Entity\User
|
||||
provider: ~
|
||||
|
||||
Three drivers are actually supported: orm, mongodb, and propel.
|
||||
|
||||
##### Use a custom Doctrine query builder
|
||||
|
||||
You can control which entities will be indexed by specifying a custom query builder method.
|
||||
|
||||
persistence:
|
||||
driver: orm
|
||||
model: Application\UserBundle\Entity\User
|
||||
provider:
|
||||
query_builder_method: createIsActiveQueryBuilder
|
||||
|
||||
Your repository must implement this method and return a Doctrine query builder.
|
||||
|
||||
> **Propel** doesn't support this feature yet.
|
||||
|
||||
##### Change the batch size
|
||||
|
||||
By default, ElasticaBundle will index documents by packets of 100.
|
||||
You can change this value in the provider configuration.
|
||||
|
||||
persistence:
|
||||
driver: orm
|
||||
model: Application\UserBundle\Entity\User
|
||||
provider:
|
||||
batch_size: 100
|
||||
|
||||
##### Change the document identifier field
|
||||
|
||||
By default, ElasticaBundle will use the `id` field of your entities as the elasticsearch document identifier.
|
||||
You can change this value in the persistence configuration.
|
||||
|
||||
persistence:
|
||||
driver: orm
|
||||
model: Application\UserBundle\Entity\User
|
||||
identifier: id
|
||||
|
||||
#### Manual provider
|
||||
|
||||
Create a service with the tag "fos_elastica.provider" and attributes for the
|
||||
index and type for which the service will provide.
|
||||
|
||||
<service id="acme.search_provider.user" class="Acme\UserBundle\Search\UserProvider">
|
||||
<tag name="fos_elastica.provider" index="website" type="user" />
|
||||
<argument type="service" id="fos_elastica.index.website.user" />
|
||||
</service>
|
||||
|
||||
Its class must implement `FOS\ElasticaBundle\Provider\ProviderInterface`.
|
||||
|
||||
<?php
|
||||
|
||||
namespace Acme\UserBundle\Provider;
|
||||
|
||||
use FOS\ElasticaBundle\Provider\ProviderInterface;
|
||||
use Elastica\Type;
|
||||
use Elastica\Document;
|
||||
|
||||
class UserProvider implements ProviderInterface
|
||||
{
|
||||
protected $userType;
|
||||
|
||||
public function __construct(Type $userType)
|
||||
{
|
||||
$this->userType = $userType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the repository objects in the type index
|
||||
*
|
||||
* @param \Closure $loggerClosure
|
||||
* @param array $options
|
||||
*/
|
||||
public function populate(\Closure $loggerClosure = null, array $options = array())
|
||||
{
|
||||
if ($loggerClosure) {
|
||||
$loggerClosure('Indexing users');
|
||||
}
|
||||
|
||||
$document = new Document();
|
||||
$document->setData(array('username' => 'Bob'));
|
||||
$this->userType->addDocuments(array($document));
|
||||
}
|
||||
}
|
||||
|
||||
You will find a more complete implementation example in `src/FOS/ElasticaBundle/Doctrine/AbstractProvider.php`.
|
||||
|
||||
### Search
|
||||
|
||||
You can just use the index and type Elastica objects, provided as services, to perform searches.
|
||||
|
||||
/** var Elastica\Type */
|
||||
$userType = $this->container->get('fos_elastica.index.website.user');
|
||||
|
||||
/** var Elastica\ResultSet */
|
||||
$resultSet = $userType->search('bob');
|
||||
|
||||
#### Doctrine/Propel finder
|
||||
|
||||
If your elasticsearch type is bound to a Doctrine entity repository or a Propel query,
|
||||
you can get your entities instead of Elastica results when you perform a search.
|
||||
Declare that you want a Doctrine/Propel finder in your configuration:
|
||||
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
serializer:
|
||||
callback_class: FOS\ElasticaBundle\Serializer\Callback
|
||||
serializer: serializer
|
||||
indexes:
|
||||
website:
|
||||
client: default
|
||||
types:
|
||||
user:
|
||||
mappings:
|
||||
# your mappings
|
||||
persistence:
|
||||
driver: orm
|
||||
model: Application\UserBundle\Entity\User
|
||||
provider: ~
|
||||
finder: ~
|
||||
|
||||
You can now use the `fos_elastica.finder.website.user` service:
|
||||
|
||||
/** var FOS\ElasticaBundle\Finder\TransformedFinder */
|
||||
$finder = $container->get('fos_elastica.finder.website.user');
|
||||
|
||||
/** var array of Acme\UserBundle\Entity\User */
|
||||
$users = $finder->find('bob');
|
||||
|
||||
/** var array of Acme\UserBundle\Entity\User limited to 10 results */
|
||||
$users = $finder->find('bob', 10);
|
||||
|
||||
You can even get paginated results!
|
||||
|
||||
Pagerfanta:
|
||||
|
||||
/** var Pagerfanta\Pagerfanta */
|
||||
$userPaginator = $finder->findPaginated('bob');
|
||||
|
||||
/** Number of results to be used for paging the results */
|
||||
$countOfResults = $userPaginator->getNbResults();
|
||||
|
||||
Knp paginator:
|
||||
|
||||
$paginator = $this->get('knp_paginator');
|
||||
$userPaginator = $paginator->paginate($finder->createPaginatorAdapter('bob'));
|
||||
|
||||
You can also get both the Elastica results and the entities together from the finder.
|
||||
You can then access the score, highlights etc. from the Elastica\Result whilst
|
||||
still also getting the entity.
|
||||
|
||||
/** var array of FOS\ElasticaBundle\HybridResult */
|
||||
$hybridResults = $finder->findHybrid('bob');
|
||||
foreach ($hybridResults as $hybridResult) {
|
||||
|
||||
/** var Acme\UserBundle\Entity\User */
|
||||
$user = $hybridResult->getTransformed();
|
||||
|
||||
/** var Elastica\Result */
|
||||
$result = $hybridResult->getResult();
|
||||
}
|
||||
|
||||
If you would like to access facets while using Pagerfanta they can be accessed through
|
||||
the Adapter seen in the example below.
|
||||
|
||||
```php
|
||||
$query = new \Elastica\Query();
|
||||
$facet = new \Elastica\Facet\Terms('tags');
|
||||
$facet->setField('companyGroup');
|
||||
$query->addFacet($facet);
|
||||
|
||||
$companies = $finder->findPaginated($query);
|
||||
$companies->setMaxPerPage($params['limit']);
|
||||
$companies->setCurrentPage($params['page']);
|
||||
|
||||
$facets = $companies->getAdapter()->getFacets());
|
||||
```
|
||||
|
||||
##### Index wide finder
|
||||
|
||||
You can also define a finder that will work on the entire index. Adjust your index
|
||||
configuration as per below:
|
||||
|
||||
fos_elastica:
|
||||
indexes:
|
||||
website:
|
||||
client: default
|
||||
finder: ~
|
||||
|
||||
You can now use the index wide finder service `fos_elastica.finder.website`:
|
||||
|
||||
/** var FOS\ElasticaBundle\Finder\MappedFinder */
|
||||
$finder = $container->get('fos_elastica.finder.website');
|
||||
|
||||
// Returns a mixed array of any objects mapped
|
||||
$results = $finder->find('bob');
|
||||
|
||||
#### Repositories
|
||||
|
||||
As well as using the finder service for a particular Doctrine/Propel entity you
|
||||
can use a manager service for each driver and get a repository for an entity to search
|
||||
against. This allows you to use the same service rather than the particular finder. For
|
||||
example:
|
||||
|
||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
||||
$repositoryManager = $container->get('fos_elastica.manager.orm');
|
||||
|
||||
/** var FOS\ElasticaBundle\Repository */
|
||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
||||
|
||||
/** var array of Acme\UserBundle\Entity\User */
|
||||
$users = $repository->find('bob');
|
||||
|
||||
You can also specify the full name of the entity instead of the shortcut syntax:
|
||||
|
||||
/** var FOS\ElasticaBundle\Repository */
|
||||
$repository = $repositoryManager->getRepository('Application\UserBundle\Entity\User');
|
||||
|
||||
> The **2.0** branch doesn't support using `UserBundle:User` style syntax and you must use the full name of the entity. .
|
||||
|
||||
##### Default Manager
|
||||
|
||||
If you are only using one driver then its manager service is automatically aliased
|
||||
to `fos_elastica.manager`. So the above example could be simplified to:
|
||||
|
||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
||||
$repositoryManager = $container->get('fos_elastica.manager');
|
||||
|
||||
/** var FOS\ElasticaBundle\Repository */
|
||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
||||
|
||||
/** var array of Acme\UserBundle\Entity\User */
|
||||
$users = $repository->find('bob');
|
||||
|
||||
If you use multiple drivers then you can choose which one is aliased to `fos_elastica.manager`
|
||||
using the `default_manager` parameter:
|
||||
|
||||
fos_elastica:
|
||||
default_manager: mongodb #defaults to orm
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
#--
|
||||
|
||||
##### Custom Repositories
|
||||
|
||||
As well as the default repository you can create a custom repository for an entity and add
|
||||
methods for particular searches. These need to extend `FOS\ElasticaBundle\Repository` to have
|
||||
access to the finder:
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
namespace Acme\ElasticaBundle\SearchRepository;
|
||||
|
||||
use FOS\ElasticaBundle\Repository;
|
||||
|
||||
class UserRepository extends Repository
|
||||
{
|
||||
public function findWithCustomQuery($searchText)
|
||||
{
|
||||
// build $query with Elastica objects
|
||||
return $this->find($query);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
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');
|
||||
|
||||
/** var FOS\ElasticaBundle\Repository */
|
||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
||||
|
||||
/** 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
|
||||
|
||||
namespace Application\UserBundle\Entity;
|
||||
|
||||
use FOS\ElasticaBundle\Configuration\Search;
|
||||
|
||||
/**
|
||||
* @Search(repositoryClass="Acme\ElasticaBundle\SearchRepository\UserRepository")
|
||||
*/
|
||||
class User
|
||||
{
|
||||
|
||||
//---
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### Realtime, selective index update
|
||||
|
||||
If you use the Doctrine integration, you can let ElasticaBundle update the indexes automatically
|
||||
when an object is added, updated or removed. It uses Doctrine lifecycle events to schedule updates
|
||||
and then synchronizes changes either before or after flush.
|
||||
|
||||
> **Propel** doesn't support this feature yet.
|
||||
|
||||
Declare that you want to update the index in real time:
|
||||
|
||||
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
|
||||
listener: ~ # by default, listens to "insert", "update" and "delete" and updates `postFlush`
|
||||
|
||||
Now the index is automatically updated each time the state of the bound Doctrine repository changes.
|
||||
No need to repopulate the whole "user" index when a new `User` is created.
|
||||
|
||||
You can also choose to only listen for some of the events:
|
||||
|
||||
persistence:
|
||||
listener:
|
||||
insert: true
|
||||
update: false
|
||||
delete: true
|
||||
|
||||
By default, the ElasticSearch index will be updated after flush. To update before flushing, set `immediate`
|
||||
to `true`:
|
||||
|
||||
persistence:
|
||||
listener:
|
||||
insert: true
|
||||
update: false
|
||||
delete: true
|
||||
immediate: true
|
||||
|
||||
> Using `immediate` to update ElasticSearch before flush completes may cause the ElasticSearch index to fall out of
|
||||
> sync with the source database in the event of a crash during the flush itself, such as in the case of a bad query.
|
||||
|
||||
|
||||
### 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:
|
||||
|
||||
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:
|
||||
|
||||
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:
|
||||
|
||||
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.
|
||||
|
||||
### Ignoring missing index results
|
||||
|
||||
By default, FOSElasticaBundle will throw an exception if the results returned from
|
||||
Elasticsearch are different from the results it finds from the chosen persistence
|
||||
provider. This may pose problems for a large index where updates do not occur instantly
|
||||
or another process has removed the results from your persistence provider without
|
||||
updating Elasticsearch.
|
||||
|
||||
The error you're likely to see is something like:
|
||||
'Cannot find corresponding Doctrine objects for all Elastica results.'
|
||||
|
||||
To solve this issue, each mapped object can be configured to ignore the missing results:
|
||||
|
||||
persistence:
|
||||
elastica_to_model_transformer:
|
||||
ignore_missing: true
|
||||
|
||||
### Advanced elasticsearch configuration
|
||||
|
||||
Any setting can be specified when declaring a type. For example, to enable a custom analyzer, you could write:
|
||||
|
||||
fos_elastica:
|
||||
indexes:
|
||||
doc:
|
||||
settings:
|
||||
index:
|
||||
analysis:
|
||||
analyzer:
|
||||
my_analyzer:
|
||||
type: custom
|
||||
tokenizer: lowercase
|
||||
filter : [my_ngram]
|
||||
filter:
|
||||
my_ngram:
|
||||
type: "nGram"
|
||||
min_gram: 3
|
||||
max_gram: 5
|
||||
types:
|
||||
blog:
|
||||
mappings:
|
||||
title: { boost: 8, analyzer: my_analyzer }
|
||||
|
||||
### Overriding the Client class to suppress exceptions
|
||||
|
||||
By default, exceptions from the Elastica client library will propagate through
|
||||
the bundle's Client class. For instance, if the elasticsearch server is offline,
|
||||
issuing a request will result in an `Elastica\Exception\Connection` being thrown.
|
||||
Depending on your needs, it may be desirable to suppress these exceptions and
|
||||
allow searches to fail silently.
|
||||
|
||||
One way to achieve this is to override the `fos_elastica.client.class` service
|
||||
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.
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
namespace Acme\ElasticaBundle;
|
||||
|
||||
use FOS\ElasticaBundle\Client as BaseClient;
|
||||
|
||||
use Elastica\Exception\ExceptionInterface;
|
||||
use Elastica\Response;
|
||||
|
||||
class Client extends BaseClient
|
||||
{
|
||||
public function request($path, $method, $data = array())
|
||||
{
|
||||
try {
|
||||
return parent::request($path, $method, $data);
|
||||
} catch (ExceptionInterface $e) {
|
||||
return new Response('{"took":0,"timed_out":false,"hits":{"total":0,"max_score":0,"hits":[]}}');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Clients as Tagged Services
|
||||
|
||||
Clients will be tagged as `fos_elastica.client`, which makes it possible to
|
||||
retrieve all clients from the service container and interact with them via a
|
||||
compiler pass. See
|
||||
[Working with Tagged Services](http://symfony.com/doc/current/components/dependency_injection/tags.html)
|
||||
for more information.
|
||||
|
||||
### Example of Advanced Query
|
||||
|
||||
If you would like to perform more advanced queries, here is one example using
|
||||
the snowball stemming algorithm.
|
||||
|
||||
It searches for Article entities using `title`, `tags`, and `categoryIds`.
|
||||
Results must match at least one specified `categoryIds`, and should match the
|
||||
`title` or `tags` criteria. Additionally, we define a snowball analyzer to
|
||||
apply to queries against the `title` field.
|
||||
|
||||
```php
|
||||
$finder = $this->container->get('fos_elastica.finder.website.article');
|
||||
$boolQuery = new \Elastica\Query\Bool();
|
||||
|
||||
$fieldQuery = new \Elastica\Query\Text();
|
||||
$fieldQuery->setFieldQuery('title', 'I am a title string');
|
||||
$fieldQuery->setFieldParam('title', 'analyzer', 'my_analyzer');
|
||||
$boolQuery->addShould($fieldQuery);
|
||||
|
||||
$tagsQuery = new \Elastica\Query\Terms();
|
||||
$tagsQuery->setTerms('tags', array('tag1', 'tag2'));
|
||||
$boolQuery->addShould($tagsQuery);
|
||||
|
||||
$categoryQuery = new \Elastica\Query\Terms();
|
||||
$categoryQuery->setTerms('categoryIds', array('1', '2', '3'));
|
||||
$boolQuery->addMust($categoryQuery);
|
||||
|
||||
$data = $finder->find($boolQuery);
|
||||
```
|
||||
|
||||
Configuration:
|
||||
|
||||
```yaml
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
indexes:
|
||||
site:
|
||||
settings:
|
||||
index:
|
||||
analysis:
|
||||
analyzer:
|
||||
my_analyzer:
|
||||
type: snowball
|
||||
language: English
|
||||
types:
|
||||
article:
|
||||
mappings:
|
||||
title: { boost: 10, analyzer: my_analyzer }
|
||||
tags:
|
||||
categoryIds:
|
||||
persistence:
|
||||
driver: orm
|
||||
model: Acme\DemoBundle\Entity\Article
|
||||
provider:
|
||||
finder:
|
||||
```
|
||||
|
||||
### Filtering Results and Executing a Default Query
|
||||
|
||||
If may want to omit certain results from a query, filtering can be more
|
||||
performant than a basic query because the filter results can be cached. In turn,
|
||||
the query is run against only a subset of the results. A common use case for
|
||||
filtering would be if your data has fields that indicate whether records are
|
||||
"active" or "inactive". The following example illustrates how to issue such a
|
||||
query with Elastica:
|
||||
|
||||
```php
|
||||
$query = new \Elastica\Query\QueryString($queryString);
|
||||
$term = new \Elastica\Filter\Term(array('active' => true));
|
||||
|
||||
$filteredQuery = new \Elastica\Query\Filtered($query, $term);
|
||||
$results = $this->container->get('fos_elastica.finder.index.type')->find($filteredQuery);
|
||||
```
|
||||
|
||||
### Date format example
|
||||
|
||||
If you want to specify a [date format](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-date-format.html):
|
||||
|
||||
```yaml
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
indexes:
|
||||
site:
|
||||
types:
|
||||
user:
|
||||
mappings:
|
||||
username: { type: string }
|
||||
lastlogin: { type: date, format: basic_date_time }
|
||||
birthday: { type: date, format: "yyyy-MM-dd" }
|
||||
```
|
||||
|
||||
#### Dynamic templates
|
||||
|
||||
Dynamic templates allow to define mapping templates that will be
|
||||
applied when dynamic introduction of fields / objects happens.
|
||||
|
||||
[Documentation](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-root-object-type.html#_dynamic_templates)
|
||||
|
||||
```yaml
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
indexes:
|
||||
site:
|
||||
types:
|
||||
user:
|
||||
dynamic_templates:
|
||||
my_template_1:
|
||||
match: apples_*
|
||||
mapping:
|
||||
type: float
|
||||
my_template_2:
|
||||
match: *
|
||||
match_mapping_type: string
|
||||
mapping:
|
||||
type: string
|
||||
index: not_analyzed
|
||||
mappings:
|
||||
username: { type: string }
|
||||
```
|
||||
Resources/meta/LICENSE
|
||||
|
|
|
@ -19,21 +19,43 @@ 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,94 +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.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 -->
|
||||
|
||||
<call method="setStopwatch">
|
||||
<argument type="service" id="debug.stopwatch" on-invalid="null" />
|
||||
</call>
|
||||
</service>
|
||||
|
||||
<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.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.data_collector" class="%fos_elastica.data_collector.class%" public="true">
|
||||
<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\ElasticaBundle\IndexManager">
|
||||
<argument /> <!-- indexes -->
|
||||
<argument /> <!-- default index -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.resetter" class="FOS\ElasticaBundle\Resetter">
|
||||
<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\ElasticaBundle\Finder\TransformedFinder" 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\ElasticaBundle\Subscriber\PaginateElasticaQuerySubscriber">
|
||||
<tag name="knp_paginator.subscriber" />
|
||||
</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>
|
||||
|
|
52
Resources/config/index.xml
Normal file
52
Resources/config/index.xml
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?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" />
|
||||
</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,32 @@
|
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<services>
|
||||
<parameters>
|
||||
<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.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" />
|
||||
</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 /> <!-- 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 +38,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,34 @@
|
|||
<?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.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.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" />
|
||||
</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 on-invalid="ignore" /> <!-- 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 +37,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>
|
32
Resources/config/transformer.xml
Normal file
32
Resources/config/transformer.xml
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?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 -->
|
||||
<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" } }
|
||||
]
|
||||
}'
|
||||
```
|
72
Resources/doc/cookbook/custom-repositories.md
Normal file
72
Resources/doc/cookbook/custom-repositories.md
Normal file
|
@ -0,0 +1,72 @@
|
|||
##### Custom Repositories
|
||||
|
||||
As well as the default repository you can create a custom repository for an entity and add
|
||||
methods for particular searches. These need to extend `FOS\ElasticaBundle\Repository` to have
|
||||
access to the finder:
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
namespace Acme\ElasticaBundle\SearchRepository;
|
||||
|
||||
use FOS\ElasticaBundle\Repository;
|
||||
|
||||
class UserRepository extends Repository
|
||||
{
|
||||
public function findWithCustomQuery($searchText)
|
||||
{
|
||||
// build $query with Elastica objects
|
||||
$this->find($query);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
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');
|
||||
|
||||
/** var FOS\ElasticaBundle\Repository */
|
||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
||||
|
||||
/** 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
|
||||
|
||||
namespace Application\UserBundle\Entity;
|
||||
|
||||
use FOS\ElasticaBundle\Annotation\Search;
|
||||
|
||||
/**
|
||||
* @Search(repositoryClass="Acme\ElasticaBundle\SearchRepository\UserRepository")
|
||||
*/
|
||||
class User
|
||||
{
|
||||
|
||||
//---
|
||||
|
||||
}
|
||||
```
|
18
Resources/doc/cookbook/elastica-client-http-headers.md
Normal file
18
Resources/doc/cookbook/elastica-client-http-headers.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
Setting HTTP Headers on the Elastica Client
|
||||
===========================================
|
||||
|
||||
It may be necessary to set HTTP headers on the Elastica client, for example an
|
||||
Authorization header.
|
||||
|
||||
They can be set using the headers configuration key:
|
||||
|
||||
```yaml
|
||||
# app/config/config.yml
|
||||
fos_elastica:
|
||||
clients:
|
||||
default:
|
||||
host: example.com
|
||||
port: 80
|
||||
headers:
|
||||
Authorization: "Basic jdumrGK7rY9TMuQOPng7GZycmxyMHNoir=="
|
||||
```
|
35
Resources/doc/cookbook/logging.md
Normal file
35
Resources/doc/cookbook/logging.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
Logging and its performance considerations
|
||||
==========================================
|
||||
|
||||
By default, FOSElasticaBundle sets a logger against each Elastica client configured and
|
||||
logs all information sent to and received from Elasticsearch. This can lead to large
|
||||
memory usage during population or reindexing of an index.
|
||||
|
||||
By default FOSElasticaBundle will only enable a logger when debug mode is enabled, meaning
|
||||
in a production environment there wont be a logger enabled. To enable a logger anyway, you
|
||||
can set the logger property of a client configuration to true or a service id of a logging
|
||||
service you wish to use.
|
||||
|
||||
```yaml
|
||||
# app/config/config.yml
|
||||
fos_elastica:
|
||||
clients:
|
||||
default:
|
||||
host: example.com
|
||||
logger: true
|
||||
```
|
||||
|
||||
Custom Logger Service
|
||||
---------------------
|
||||
|
||||
It is also possible to specify a custom logger instance to be injected into each client by
|
||||
specifying the service id of the logger you wish to use.
|
||||
|
||||
```yaml
|
||||
# app/config/config.yml
|
||||
fos_elastica:
|
||||
clients:
|
||||
default:
|
||||
host: example.com
|
||||
logger: 'acme.custom.logger'
|
||||
```
|
56
Resources/doc/cookbook/manual-provider.md
Normal file
56
Resources/doc/cookbook/manual-provider.md
Normal file
|
@ -0,0 +1,56 @@
|
|||
Manual provider
|
||||
===============
|
||||
|
||||
Create a service with the tag "fos_elastica.provider" and attributes for the
|
||||
index and type for which the service will provide.
|
||||
|
||||
```yaml
|
||||
# app/config/config.yml
|
||||
services:
|
||||
acme.search_provider.user:
|
||||
class: Acme\UserBundle\Search\UserProvider
|
||||
arguments:
|
||||
- @fos_elastica.index.website.user
|
||||
tags:
|
||||
- { name: fos_elastica.provider, index: website, type: user }
|
||||
```
|
||||
|
||||
Its class must implement `FOS\ElasticaBundle\Provider\ProviderInterface`.
|
||||
|
||||
```php
|
||||
|
||||
namespace Acme\UserBundle\Provider;
|
||||
|
||||
use FOS\ElasticaBundle\Provider\ProviderInterface;
|
||||
use Elastica\Type;
|
||||
use Elastica\Document;
|
||||
|
||||
class UserProvider implements ProviderInterface
|
||||
{
|
||||
protected $userType;
|
||||
|
||||
public function __construct(Type $userType)
|
||||
{
|
||||
$this->userType = $userType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the repository objects in the type index
|
||||
*
|
||||
* @param \Closure $loggerClosure
|
||||
* @param array $options
|
||||
*/
|
||||
public function populate(\Closure $loggerClosure = null, array $options = array())
|
||||
{
|
||||
if ($loggerClosure) {
|
||||
$loggerClosure('Indexing users');
|
||||
}
|
||||
|
||||
$document = new Document();
|
||||
$document->setData(array('username' => 'Bob'));
|
||||
$this->userType->addDocuments(array($document));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You will find a more complete implementation example in `src/FOS/ElasticaBundle/Doctrine/AbstractProvider.php`.
|
16
Resources/doc/cookbook/multiple-connections.md
Normal file
16
Resources/doc/cookbook/multiple-connections.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
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
|
||||
```
|
||||
|
||||
For more information on Elastica clustering see http://elastica.io/getting-started/installation.html#section-connect-cluster
|
59
Resources/doc/cookbook/suppress-server-errors.md
Normal file
59
Resources/doc/cookbook/suppress-server-errors.md
Normal file
|
@ -0,0 +1,59 @@
|
|||
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,
|
||||
issuing a request will result in an `Elastica\Exception\Connection` being thrown.
|
||||
Depending on your needs, it may be desirable to suppress these exceptions and
|
||||
allow searches to fail silently.
|
||||
|
||||
One way to achieve this is to override the `fos_elastica.client.class` service
|
||||
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 Elastica\Exception\ExceptionInterface;
|
||||
use Elastica\Request;
|
||||
use Elastica\Response;
|
||||
use FOS\ElasticaBundle\Client as BaseClient;
|
||||
|
||||
class Client extends BaseClient
|
||||
{
|
||||
public function request($path, $method = Request::GET, $data = array(), array $query = array())
|
||||
{
|
||||
try {
|
||||
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
|
||||
```
|
21
Resources/doc/index.md
Normal file
21
Resources/doc/index.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
FOSElasticaBundle Documentation
|
||||
===============================
|
||||
|
||||
Available documentation for FOSElasticaBundle
|
||||
---------------------------------------------
|
||||
|
||||
* [Setup](setup.md)
|
||||
* [Usage](usage.md)
|
||||
* [Using a Serializer](serializer.md)
|
||||
* [Types](types.md)
|
||||
|
||||
Cookbook Entries
|
||||
----------------
|
||||
|
||||
* [Aliased Indexes](cookbook/aliased-indexes.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)
|
39
Resources/doc/serializer.md
Normal file
39
Resources/doc/serializer.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
Using a Serializer in FOSElasticaBundle
|
||||
=======================================
|
||||
|
||||
FOSElasticaBundle supports using a Serializer component to serialize your objects to JSON
|
||||
which will be sent directly to the Elasticsearch server. Combined with automatic mapping
|
||||
it means types do not have to be mapped.
|
||||
|
||||
A) Install and declare the serializer
|
||||
-------------------------
|
||||
|
||||
Follow the installation instructions for [JMSSerializerBundle](http://jmsyst.com/bundles/JMSSerializerBundle).
|
||||
|
||||
Enable the serializer configuration for the bundle:
|
||||
|
||||
```yaml
|
||||
#app/config/config.yml
|
||||
fos_elastica:
|
||||
serializer: ~
|
||||
```
|
||||
|
||||
The default configuration that comes with FOSElasticaBundle supports both the JMS Serializer
|
||||
and the Symfony Serializer. If JMSSerializerBundle is installed, additional support for
|
||||
serialization groups and versions are added to the bundle.
|
||||
|
||||
B) Set up each defined type to support serialization
|
||||
----------------------------------------------------
|
||||
|
||||
A type does not need to have mappings defined when using a serializer. An example configuration
|
||||
for a type in this case:
|
||||
|
||||
```yaml
|
||||
fos_elastica:
|
||||
indexes:
|
||||
search:
|
||||
types:
|
||||
user:
|
||||
serializer:
|
||||
groups: [elastica, Default]
|
||||
```
|
143
Resources/doc/setup.md
Normal file
143
Resources/doc/setup.md
Normal file
|
@ -0,0 +1,143 @@
|
|||
Step 1: Setting up the bundle
|
||||
=============================
|
||||
|
||||
A) Install FOSElasticaBundle
|
||||
----------------------------
|
||||
|
||||
FOSElasticaBundle is installed using [Composer](https://getcomposer.org).
|
||||
|
||||
```bash
|
||||
$ php composer.phar require friendsofsymfony/elastica-bundle "~3.0"
|
||||
```
|
||||
|
||||
### Elasticsearch
|
||||
|
||||
Instructions for installing and deploying Elasticsearch may be found
|
||||
[here](http://www.elasticsearch.org/guide/reference/setup/installation/).
|
||||
|
||||
|
||||
B) Enable FOSElasticaBundle
|
||||
---------------------------
|
||||
|
||||
Enable FOSElasticaBundle in your AppKernel:
|
||||
|
||||
```php
|
||||
<?php
|
||||
// app/AppKernel.php
|
||||
|
||||
public function registerBundles()
|
||||
{
|
||||
$bundles = array(
|
||||
// ...
|
||||
new FOS\ElasticaBundle\FOSElasticaBundle(),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
C) Basic Bundle Configuration
|
||||
-----------------------------
|
||||
|
||||
The basic minimal configuration for FOSElasticaBundle is one client with one Elasticsearch
|
||||
index. In almost all cases, an application will only need a single index. An index can
|
||||
be considered comparable to a Doctrine Entity Manager, where the index will hold multiple
|
||||
type definitions.
|
||||
|
||||
```yaml
|
||||
#app/config/config.yml
|
||||
fos_elastica:
|
||||
clients:
|
||||
default: { host: localhost, port: 9200 }
|
||||
indexes:
|
||||
search: ~
|
||||
```
|
||||
|
||||
In this example, an Elastica index (an instance of `Elastica\Index`) is available as a
|
||||
service with the key `fos_elastica.index.search`.
|
||||
|
||||
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.
|
||||
|
||||
```yaml
|
||||
#app/config/config.yml
|
||||
fos_elastica:
|
||||
indexes:
|
||||
search:
|
||||
index_name: search_dev
|
||||
```
|
||||
|
||||
In this case, the service `fos_elastica.index.search` will be using an Elasticsearch
|
||||
index of search_dev.
|
||||
|
||||
D) Defining index types
|
||||
-----------------------
|
||||
|
||||
By default, FOSElasticaBundle requires each type that is to be indexed to be mapped.
|
||||
It is possible to use a serializer to avoid this requirement. To use a serializer, see
|
||||
the [serializer documentation](serializer.md)
|
||||
|
||||
An Elasticsearch type needs to be defined with each field of a related PHP object that
|
||||
will end up being indexed.
|
||||
|
||||
```yaml
|
||||
fos_elastica:
|
||||
indexes:
|
||||
search:
|
||||
types:
|
||||
user:
|
||||
mappings:
|
||||
username: ~
|
||||
firstName: ~
|
||||
lastName: ~
|
||||
email: ~
|
||||
```
|
||||
|
||||
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`.
|
||||
|
||||
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
|
||||
Propel objects.
|
||||
|
||||
Below is an example for the Doctrine ORM.
|
||||
|
||||
```yaml
|
||||
user:
|
||||
mappings:
|
||||
username: ~
|
||||
firstName: ~
|
||||
lastName: ~
|
||||
email: ~
|
||||
persistence:
|
||||
# the driver can be orm, mongodb or propel
|
||||
# listener and finder are not supported by
|
||||
# propel and should be removed
|
||||
driver: orm
|
||||
model: Acme\ApplicationBundle\Entity\User
|
||||
provider: ~
|
||||
listener:
|
||||
immediate: ~
|
||||
finder: ~
|
||||
```
|
||||
|
||||
There are a significant number of options available for types, that can be
|
||||
[found here](types.md)
|
||||
|
||||
E) Populating the Elasticsearch index
|
||||
-------------------------------------
|
||||
|
||||
When using the providers and listeners that come with the bundle, any new or modified
|
||||
object will be indexed automatically. In some cases, where the database is modified
|
||||
externally, the Elasticsearch index must be updated manually. This can be achieved by
|
||||
running the console command:
|
||||
|
||||
```bash
|
||||
$ 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
|
||||
--------
|
||||
|
||||
Usage documentation for the bundle is available [here](usage.md)
|
306
Resources/doc/types.md
Normal file
306
Resources/doc/types.md
Normal file
|
@ -0,0 +1,306 @@
|
|||
Type configuration
|
||||
==================
|
||||
|
||||
Handling missing results with FOSElasticaBundle
|
||||
-----------------------------------------------
|
||||
|
||||
By default, FOSElasticaBundle will throw an exception if the results returned from
|
||||
Elasticsearch are different from the results it finds from the chosen persistence
|
||||
provider. This may pose problems for a large index where updates do not occur instantly
|
||||
or another process has removed the results from your persistence provider without
|
||||
updating Elasticsearch.
|
||||
|
||||
The error you're likely to see is something like:
|
||||
'Cannot find corresponding Doctrine objects for all Elastica results.'
|
||||
|
||||
To solve this issue, each type can be configured to ignore the missing results:
|
||||
|
||||
```yaml
|
||||
user:
|
||||
persistence:
|
||||
elastica_to_model_transformer:
|
||||
ignore_missing: true
|
||||
```
|
||||
|
||||
Dynamic templates
|
||||
-----------------
|
||||
|
||||
Dynamic templates allow to define mapping templates that will be
|
||||
applied when dynamic introduction of fields / objects happens.
|
||||
|
||||
[Documentation](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-root-object-type.html#_dynamic_templates)
|
||||
|
||||
```yaml
|
||||
fos_elastica:
|
||||
indexes:
|
||||
site:
|
||||
types:
|
||||
user:
|
||||
dynamic_templates:
|
||||
my_template_1:
|
||||
match: apples_*
|
||||
mapping:
|
||||
type: float
|
||||
my_template_2:
|
||||
match: *
|
||||
match_mapping_type: string
|
||||
mapping:
|
||||
type: string
|
||||
index: not_analyzed
|
||||
mappings:
|
||||
username: { type: string }
|
||||
```
|
||||
|
||||
Nested objects in FOSElasticaBundle
|
||||
-----------------------------------
|
||||
|
||||
Note that object can autodetect properties
|
||||
|
||||
```yaml
|
||||
fos_elastica:
|
||||
indexes:
|
||||
website:
|
||||
types:
|
||||
post:
|
||||
mappings:
|
||||
date: { boost: 5 }
|
||||
title: { boost: 3 }
|
||||
content: ~
|
||||
comments:
|
||||
type: "nested"
|
||||
properties:
|
||||
date: { boost: 5 }
|
||||
content: ~
|
||||
user:
|
||||
type: "object"
|
||||
approver:
|
||||
type: "object"
|
||||
properties:
|
||||
date: { boost: 5 }
|
||||
```
|
||||
|
||||
Parent fields
|
||||
-------------
|
||||
|
||||
```yaml
|
||||
fos_elastica:
|
||||
indexes:
|
||||
website:
|
||||
types:
|
||||
comment:
|
||||
mappings:
|
||||
date: { boost: 5 }
|
||||
content: ~
|
||||
_parent:
|
||||
type: "post"
|
||||
property: "post"
|
||||
identifier: "id"
|
||||
```
|
||||
|
||||
The parent field declaration has the following values:
|
||||
|
||||
* `type`: The parent type.
|
||||
* `property`: The property in the child entity where to look for the parent entity. It may be ignored if is equal to
|
||||
the parent type.
|
||||
* `identifier`: The property in the parent entity which has the parent identifier. Defaults to `id`.
|
||||
|
||||
Note that to create a document with a parent, you need to call `setParent` on the document rather than setting a
|
||||
_parent field. If you do this wrong, you will see a `RoutingMissingException` as Elasticsearch does not know where
|
||||
to store a document that should have a parent but does not specify it.
|
||||
|
||||
Date format example
|
||||
-------------------
|
||||
|
||||
If you want to specify a [date format](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-date-format.html):
|
||||
|
||||
```yaml
|
||||
user:
|
||||
mappings:
|
||||
username: { type: string }
|
||||
lastlogin: { type: date, format: basic_date_time }
|
||||
birthday: { type: date, format: "yyyy-MM-dd" }
|
||||
```
|
||||
|
||||
Custom settings
|
||||
---------------
|
||||
|
||||
Any setting can be specified when declaring a type. For example, to enable a custom
|
||||
analyzer, you could write:
|
||||
|
||||
```yaml
|
||||
indexes:
|
||||
search:
|
||||
settings:
|
||||
index:
|
||||
analysis:
|
||||
analyzer:
|
||||
my_analyzer:
|
||||
type: custom
|
||||
tokenizer: lowercase
|
||||
filter : [my_ngram]
|
||||
filter:
|
||||
my_ngram:
|
||||
type: "nGram"
|
||||
min_gram: 3
|
||||
max_gram: 5
|
||||
types:
|
||||
blog:
|
||||
mappings:
|
||||
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()`
|
||||
* 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)
|
||||
* 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
|
||||
----------------------
|
||||
|
||||
### Specifying a custom query builder for populating indexes
|
||||
|
||||
When populating an index, it may be required to use a different query builder method
|
||||
to define which entities should be queried.
|
||||
|
||||
```yaml
|
||||
user:
|
||||
persistence:
|
||||
provider:
|
||||
query_builder_method: createIsActiveQueryBuilder
|
||||
```
|
||||
|
||||
### Populating batch size
|
||||
|
||||
By default, ElasticaBundle will index documents by packets of 100.
|
||||
You can change this value in the provider configuration.
|
||||
|
||||
```yaml
|
||||
user:
|
||||
persistence:
|
||||
provider:
|
||||
batch_size: 10
|
||||
```
|
||||
|
||||
### Changing the document identifier
|
||||
|
||||
By default, ElasticaBundle will use the `id` field of your entities as
|
||||
the Elasticsearch document identifier. You can change this value in the
|
||||
persistence configuration.
|
||||
|
||||
```yaml
|
||||
user:
|
||||
persistence:
|
||||
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
|
||||
----------------------
|
||||
|
||||
### Realtime, selective index update
|
||||
|
||||
If you use the Doctrine integration, you can let ElasticaBundle update the indexes automatically
|
||||
when an object is added, updated or removed. It uses Doctrine lifecycle events.
|
||||
Declare that you want to update the index in real time:
|
||||
|
||||
```yaml
|
||||
user:
|
||||
persistence:
|
||||
driver: orm
|
||||
model: Application\UserBundle\Entity\User
|
||||
listener: ~ # by default, listens to "insert", "update" and "delete"
|
||||
```
|
||||
|
||||
Now the index is automatically updated each time the state of the bound Doctrine repository changes.
|
||||
No need to repopulate the whole "user" index when a new `User` is created.
|
||||
|
||||
You can also choose to only listen for some of the events:
|
||||
|
||||
```yaml
|
||||
persistence:
|
||||
listener:
|
||||
insert: true
|
||||
update: false
|
||||
delete: true
|
||||
```
|
||||
|
||||
> **Propel** doesn't support this feature yet.
|
||||
|
||||
Flushing Method
|
||||
---------------
|
||||
|
||||
FOSElasticaBundle, since 3.0.0 performs its indexing in the postFlush Doctrine event
|
||||
instead of prePersist and preUpdate which means that indexing will only occur when there
|
||||
has been a successful flush. This new default makes more sense but in the instance where
|
||||
you want to perform indexing before the flush is confirmed you may set the `immediate`
|
||||
option on a type persistence configuration to `true`.
|
||||
|
||||
```yaml
|
||||
persistence:
|
||||
listener:
|
||||
immediate: true
|
||||
```
|
||||
|
||||
Logging Errors
|
||||
--------------
|
||||
|
||||
By default FOSElasticaBundle will not catch errors thrown by Elastica/ElasticSearch.
|
||||
Configure a logger per listener if you would rather catch and log these.
|
||||
|
||||
```yaml
|
||||
persistence:
|
||||
listener:
|
||||
logger: true
|
||||
```
|
||||
|
||||
Specifying `true` will use the default Elastica logger. Alternatively define your own
|
||||
logger service id.
|
201
Resources/doc/usage.md
Normal file
201
Resources/doc/usage.md
Normal file
|
@ -0,0 +1,201 @@
|
|||
FOSElasticaBundle Usage
|
||||
=======================
|
||||
|
||||
Basic Searching with a Finder
|
||||
-----------------------------
|
||||
|
||||
The most useful searching method is to use a finder defined by the type configuration.
|
||||
A finder will return results that have been hydrated by the configured persistence backend,
|
||||
allowing you to use relationships of returned entities. For more information about
|
||||
configuration options for this kind of searching, please see the [types](types.md)
|
||||
documentation.
|
||||
|
||||
```php
|
||||
$finder = $this->container->get('fos_elastica.finder.search.user');
|
||||
|
||||
// Option 1. Returns all users who have example.net in any of their mapped fields
|
||||
$results = $finder->find('example.net');
|
||||
|
||||
// Option 2. Returns a set of hybrid results that contain all Elasticsearch results
|
||||
// and their transformed counterparts. Each result is an instance of a HybridResult
|
||||
$results = $finder->findHybrid('example.net');
|
||||
|
||||
// Option 3a. Pagerfanta'd resultset
|
||||
/** var Pagerfanta\Pagerfanta */
|
||||
$userPaginator = $finder->findPaginated('bob');
|
||||
$countOfResults = $userPaginator->getNbResults();
|
||||
|
||||
// Option 3b. KnpPaginator resultset
|
||||
|
||||
```
|
||||
|
||||
Faceted Searching
|
||||
-----------------
|
||||
|
||||
When searching with facets, the facets can be retrieved when using the paginated
|
||||
methods on the finder.
|
||||
|
||||
```php
|
||||
$query = new \Elastica\Query();
|
||||
$facet = new \Elastica\Facet\Terms('tags');
|
||||
$facet->setField('companyGroup');
|
||||
$query->addFacet($facet);
|
||||
|
||||
$companies = $finder->findPaginated($query);
|
||||
$companies->setMaxPerPage($params['limit']);
|
||||
$companies->setCurrentPage($params['page']);
|
||||
|
||||
$facets = $companies->getAdapter()->getFacets();
|
||||
```
|
||||
|
||||
Searching the entire index
|
||||
--------------------------
|
||||
|
||||
You can also define a finder that will work on the entire index. Adjust your index
|
||||
configuration as per below:
|
||||
|
||||
```yaml
|
||||
fos_elastica:
|
||||
indexes:
|
||||
website:
|
||||
finder: ~
|
||||
```
|
||||
|
||||
You can now use the index wide finder service `fos_elastica.finder.website`:
|
||||
|
||||
```php
|
||||
/** var FOS\ElasticaBundle\Finder\MappedFinder */
|
||||
$finder = $this->container->get('fos_elastica.finder.website');
|
||||
|
||||
// Returns a mixed array of any objects mapped
|
||||
$results = $finder->find('bob');
|
||||
```
|
||||
|
||||
Type Repositories
|
||||
-----------------
|
||||
|
||||
In the case where you need many different methods for different searching terms, it
|
||||
may be better to separate methods for each type into their own dedicated repository
|
||||
classes, just like Doctrine ORM's EntityRepository classes.
|
||||
|
||||
The manager class that handles repositories has a service key of `fos_elastica.manager`.
|
||||
The manager will default to handling ORM entities, and the configuration must be changed
|
||||
for MongoDB users.
|
||||
|
||||
```yaml
|
||||
fos_elastica:
|
||||
default_manager: mongodb
|
||||
```
|
||||
|
||||
An example for using a repository:
|
||||
|
||||
```php
|
||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
||||
$repositoryManager = $this->container->get('fos_elastica.manager');
|
||||
|
||||
/** var FOS\ElasticaBundle\Repository */
|
||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
||||
|
||||
/** var array of Acme\UserBundle\Entity\User */
|
||||
$users = $repository->find('bob');
|
||||
```
|
||||
|
||||
For more information about customising repositories, see the cookbook entry
|
||||
[Custom Repositories](cookbook/custom-repositories.md).
|
||||
|
||||
Using a custom query builder method for transforming results
|
||||
------------------------------------------------------------
|
||||
|
||||
When returning results from Elasticsearch to be transformed by the bundle, the default
|
||||
`createQueryBuilder` method on each objects Repository class will be called. In many
|
||||
circumstances this is not ideal and you'd prefer to use a different method to join in
|
||||
any entity relations that are required on the page that will be displaying the results.
|
||||
|
||||
```yaml
|
||||
user:
|
||||
persistence:
|
||||
elastica_to_model_transformer:
|
||||
query_builder_method: createSearchQueryBuilder
|
||||
```
|
||||
|
||||
An example for using a custom query builder method:
|
||||
|
||||
```php
|
||||
class UserRepository extends EntityRepository
|
||||
{
|
||||
/**
|
||||
* Used by Elastica to transform results to model
|
||||
*
|
||||
* @param string $entityAlias
|
||||
* @return Doctrine\ORM\QueryBuilder
|
||||
*/
|
||||
public function createSearchQueryBuilder($entityAlias)
|
||||
{
|
||||
$qb = $this->createQueryBuilder($entityAlias);
|
||||
|
||||
$qb->select($entityAlias, 'g')
|
||||
->innerJoin($entityAlias.'.groups', 'g');
|
||||
|
||||
return $qb;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Advanced Searching Example
|
||||
--------------------------
|
||||
|
||||
If you would like to perform more advanced queries, here is one example using
|
||||
the snowball stemming algorithm.
|
||||
|
||||
It searches for Article entities using `title`, `tags`, and `categoryIds`.
|
||||
Results must match at least one specified `categoryIds`, and should match the
|
||||
`title` or `tags` criteria. Additionally, we define a snowball analyzer to
|
||||
apply to queries against the `title` field.
|
||||
|
||||
Assuming a type is configured as follows:
|
||||
|
||||
```yaml
|
||||
fos_elastica:
|
||||
indexes:
|
||||
site:
|
||||
settings:
|
||||
index:
|
||||
analysis:
|
||||
analyzer:
|
||||
my_analyzer:
|
||||
type: snowball
|
||||
language: English
|
||||
types:
|
||||
article:
|
||||
mappings:
|
||||
title: { boost: 10, analyzer: my_analyzer }
|
||||
tags:
|
||||
categoryIds:
|
||||
persistence:
|
||||
driver: orm
|
||||
model: Acme\DemoBundle\Entity\Article
|
||||
provider: ~
|
||||
finder: ~
|
||||
```
|
||||
|
||||
The following code will execute a search against the Elasticsearch server:
|
||||
|
||||
```php
|
||||
$finder = $this->container->get('fos_elastica.finder.site.article');
|
||||
$boolQuery = new \Elastica\Query\Bool();
|
||||
|
||||
$fieldQuery = new \Elastica\Query\Match();
|
||||
$fieldQuery->setFieldQuery('title', 'I am a title string');
|
||||
$fieldQuery->setFieldParam('title', 'analyzer', 'my_analyzer');
|
||||
$boolQuery->addShould($fieldQuery);
|
||||
|
||||
$tagsQuery = new \Elastica\Query\Terms();
|
||||
$tagsQuery->setTerms('tags', array('tag1', 'tag2'));
|
||||
$boolQuery->addShould($tagsQuery);
|
||||
|
||||
$categoryQuery = new \Elastica\Query\Terms();
|
||||
$categoryQuery->setTerms('categoryIds', array('1', '2', '3'));
|
||||
$boolQuery->addMust($categoryQuery);
|
||||
|
||||
$data = $finder->find($boolQuery);
|
||||
```
|
|
@ -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">
|
||||
|
@ -24,6 +27,7 @@
|
|||
<strong>Elastica</strong>
|
||||
<span class="count">
|
||||
<span>{{ collector.querycount }}</span>
|
||||
<span>{{ '%0.0f'|format(collector.time * 1000) }} ms</span>
|
||||
</span>
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
|
|
@ -43,7 +43,7 @@ class Callback
|
|||
|
||||
public function serialize($object)
|
||||
{
|
||||
$context = $this->serializer instanceof SerializerInterface ? new SerializationContext() : array();
|
||||
$context = $this->serializer instanceof SerializerInterface ? SerializationContext::create()->enableMaxDepthChecks() : array();
|
||||
|
||||
if ($this->groups) {
|
||||
$context->setGroups($this->groups);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Subscriber;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Knp\Component\Pager\Event\ItemsEvent;
|
||||
use FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface;
|
||||
|
@ -9,9 +10,19 @@ use FOS\ElasticaBundle\Paginator\PartialResultsInterface;
|
|||
|
||||
class PaginateElasticaQuerySubscriber implements EventSubscriberInterface
|
||||
{
|
||||
private $request;
|
||||
|
||||
public function setRequest(Request $request = null)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
public function items(ItemsEvent $event)
|
||||
{
|
||||
if ($event->target instanceof PaginatorAdapterInterface) {
|
||||
// Add sort to query
|
||||
$this->setSorting($event);
|
||||
|
||||
/** @var $results PartialResultsInterface */
|
||||
$results = $event->target->getResults($event->getOffset(), $event->getLimit());
|
||||
|
||||
|
@ -30,6 +41,36 @@ class PaginateElasticaQuerySubscriber implements EventSubscriberInterface
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds knp paging sort to query
|
||||
*
|
||||
* @param ItemsEvent $event
|
||||
*/
|
||||
protected function setSorting(ItemsEvent $event)
|
||||
{
|
||||
$options = $event->options;
|
||||
$sortField = $this->request->get($options['sortFieldParameterName']);
|
||||
|
||||
if (!empty($sortField)) {
|
||||
// determine sort direction
|
||||
$dir = 'asc';
|
||||
$sortDirection = $this->request->get($options['sortDirectionParameterName']);
|
||||
if ('desc' === strtolower($sortDirection)) {
|
||||
$dir = 'desc';
|
||||
}
|
||||
|
||||
// check if the requested sort field is in the sort whitelist
|
||||
if (isset($options['sortFieldWhitelist']) && !in_array($sortField, $options['sortFieldWhitelist'])) {
|
||||
throw new \UnexpectedValueException(sprintf('Cannot sort by: [%s] this field is not in whitelist', $sortField));
|
||||
}
|
||||
|
||||
// set sort on active query
|
||||
$event->target->getQuery()->setSort(array(
|
||||
$sortField => array('order' => $dir),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
|
|
|
@ -11,126 +11,237 @@ use Symfony\Component\Config\Definition\Processor;
|
|||
class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var Configuration
|
||||
* @var Processor
|
||||
*/
|
||||
private $configuration;
|
||||
private $processor;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->configuration = new Configuration(array());
|
||||
$this->processor = new Processor();
|
||||
}
|
||||
|
||||
public function testEmptyConfigContainsFormatMappingOptionNode()
|
||||
private function getConfigs(array $configArray)
|
||||
{
|
||||
$tree = $this->configuration->getConfigTree();
|
||||
$children = $tree->getChildren();
|
||||
$children = $children['indexes']->getPrototype()->getChildren();
|
||||
$typeNodes = $children['types']->getPrototype()->getChildren();
|
||||
$mappings = $typeNodes['mappings']->getPrototype()->getChildren();
|
||||
$configuration = new Configuration(true);
|
||||
|
||||
$this->assertArrayHasKey('format', $mappings);
|
||||
$this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $mappings['format']);
|
||||
$this->assertNull($mappings['format']->getDefaultValue());
|
||||
return $this->processor->processConfiguration($configuration, array($configArray));
|
||||
}
|
||||
|
||||
public function testDynamicTemplateNodes()
|
||||
public function testUnconfiguredConfiguration()
|
||||
{
|
||||
$tree = $this->configuration->getConfigTree();
|
||||
$children = $tree->getChildren();
|
||||
$children = $children['indexes']->getPrototype()->getChildren();
|
||||
$typeNodes = $children['types']->getPrototype()->getChildren();
|
||||
$dynamicTemplates = $typeNodes['dynamic_templates']->getPrototype()->getChildren();
|
||||
$configuration = $this->getConfigs(array());
|
||||
|
||||
$this->assertArrayHasKey('match', $dynamicTemplates);
|
||||
$this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $dynamicTemplates['match']);
|
||||
$this->assertNull($dynamicTemplates['match']->getDefaultValue());
|
||||
|
||||
$this->assertArrayHasKey('match_mapping_type', $dynamicTemplates);
|
||||
$this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $dynamicTemplates['match_mapping_type']);
|
||||
$this->assertNull($dynamicTemplates['match_mapping_type']->getDefaultValue());
|
||||
|
||||
$this->assertArrayHasKey('unmatch', $dynamicTemplates);
|
||||
$this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $dynamicTemplates['unmatch']);
|
||||
$this->assertNull($dynamicTemplates['unmatch']->getDefaultValue());
|
||||
|
||||
$this->assertArrayHasKey('path_match', $dynamicTemplates);
|
||||
$this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $dynamicTemplates['path_match']);
|
||||
$this->assertNull($dynamicTemplates['path_match']->getDefaultValue());
|
||||
|
||||
$this->assertArrayHasKey('path_unmatch', $dynamicTemplates);
|
||||
$this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $dynamicTemplates['path_unmatch']);
|
||||
$this->assertNull($dynamicTemplates['path_unmatch']->getDefaultValue());
|
||||
|
||||
$this->assertArrayHasKey('match_pattern', $dynamicTemplates);
|
||||
$this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $dynamicTemplates['match_pattern']);
|
||||
$this->assertNull($dynamicTemplates['match_pattern']->getDefaultValue());
|
||||
|
||||
$this->assertArrayHasKey('mapping', $dynamicTemplates);
|
||||
$this->assertInstanceOf('Symfony\Component\Config\Definition\ArrayNode', $dynamicTemplates['mapping']);
|
||||
$this->assertSame(array(
|
||||
'clients' => array(),
|
||||
'indexes' => array(),
|
||||
'default_manager' => 'orm'
|
||||
), $configuration);
|
||||
}
|
||||
|
||||
public function testDynamicTemplateMappingNodes()
|
||||
public function testClientConfiguration()
|
||||
{
|
||||
$tree = $this->configuration->getConfigTree();
|
||||
$children = $tree->getChildren();
|
||||
$children = $children['indexes']->getPrototype()->getChildren();
|
||||
$typeNodes = $children['types']->getPrototype()->getChildren();
|
||||
$dynamicTemplates = $typeNodes['dynamic_templates']->getPrototype()->getChildren();
|
||||
$mapping = $dynamicTemplates['mapping']->getChildren();
|
||||
$configuration = $this->getConfigs(array(
|
||||
'clients' => array(
|
||||
'default' => array(
|
||||
'url' => 'http://localhost:9200',
|
||||
),
|
||||
'clustered' => array(
|
||||
'connections' => array(
|
||||
array(
|
||||
'url' => 'http://es1:9200',
|
||||
'headers' => array(
|
||||
'Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
|
||||
)
|
||||
),
|
||||
array(
|
||||
'url' => 'http://es2:9200',
|
||||
'headers' => array(
|
||||
'Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
|
||||
)
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
));
|
||||
|
||||
$this->assertArrayHasKey('type', $mapping);
|
||||
$this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $mapping['type']);
|
||||
$this->assertSame('string', $mapping['type']->getDefaultValue());
|
||||
$this->assertCount(2, $configuration['clients']);
|
||||
$this->assertCount(1, $configuration['clients']['default']['connections']);
|
||||
$this->assertCount(0, $configuration['clients']['default']['connections'][0]['headers']);
|
||||
|
||||
$this->assertArrayHasKey('index', $mapping);
|
||||
$this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $mapping['index']);
|
||||
$this->assertNull($mapping['index']->getDefaultValue());
|
||||
$this->assertCount(2, $configuration['clients']['clustered']['connections']);
|
||||
$this->assertEquals('http://es2:9200/', $configuration['clients']['clustered']['connections'][1]['url']);
|
||||
$this->assertCount(1, $configuration['clients']['clustered']['connections'][1]['headers']);
|
||||
$this->assertEquals('Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', $configuration['clients']['clustered']['connections'][0]['headers'][0]);
|
||||
}
|
||||
|
||||
public function testLogging()
|
||||
{
|
||||
$configuration = $this->getConfigs(array(
|
||||
'clients' => array(
|
||||
'logging_enabled' => array(
|
||||
'url' => 'http://localhost:9200',
|
||||
'logger' => true,
|
||||
),
|
||||
'logging_disabled' => array(
|
||||
'url' => 'http://localhost:9200',
|
||||
'logger' => false,
|
||||
),
|
||||
'logging_not_mentioned' => array(
|
||||
'url' => 'http://localhost:9200',
|
||||
),
|
||||
'logging_custom' => array(
|
||||
'url' => 'http://localhost:9200',
|
||||
'logger' => 'custom.service'
|
||||
),
|
||||
)
|
||||
));
|
||||
|
||||
$this->assertCount(4, $configuration['clients']);
|
||||
|
||||
$this->assertEquals('fos_elastica.logger', $configuration['clients']['logging_enabled']['connections'][0]['logger']);
|
||||
$this->assertFalse($configuration['clients']['logging_disabled']['connections'][0]['logger']);
|
||||
$this->assertEquals('fos_elastica.logger', $configuration['clients']['logging_not_mentioned']['connections'][0]['logger']);
|
||||
$this->assertEquals('custom.service', $configuration['clients']['logging_custom']['connections'][0]['logger']);
|
||||
}
|
||||
|
||||
public function testSlashIsAddedAtTheEndOfServerUrl()
|
||||
{
|
||||
$config = array(
|
||||
'clients' => array(
|
||||
'default' => array(
|
||||
'url' => 'http://www.github.com',
|
||||
),
|
||||
'default' => array('url' => 'http://www.github.com'),
|
||||
),
|
||||
);
|
||||
|
||||
$processor = new Processor();
|
||||
);
|
||||
$configuration = $this->getConfigs($config);
|
||||
|
||||
$configuration = $processor->processConfiguration($this->configuration, array($config));
|
||||
|
||||
$this->assertEquals('http://www.github.com/', $configuration['clients']['default']['servers'][0]['url']);
|
||||
$this->assertEquals('http://www.github.com/', $configuration['clients']['default']['connections'][0]['url']);
|
||||
}
|
||||
|
||||
public function testEmptyFieldsIndexIsUnset()
|
||||
public function testTypeConfig()
|
||||
{
|
||||
$config = array(
|
||||
$this->getConfigs(array(
|
||||
'clients' => array(
|
||||
'default' => array('url' => 'http://localhost:9200'),
|
||||
),
|
||||
'indexes' => array(
|
||||
'test' => array(
|
||||
'type_prototype' => array(
|
||||
'index_analyzer' => 'custom_analyzer',
|
||||
'persistence' => array(
|
||||
'identifier' => 'ID',
|
||||
),
|
||||
'serializer' => array(
|
||||
'groups' => array('Search'),
|
||||
'version' => 1
|
||||
)
|
||||
),
|
||||
'types' => array(
|
||||
'test' => array(
|
||||
'mappings' => array(
|
||||
'title' => array(
|
||||
'type' => 'string',
|
||||
'fields' => array(
|
||||
'autocomplete' => null
|
||||
)
|
||||
),
|
||||
'content' => null
|
||||
'title' => array(),
|
||||
'published' => array('type' => 'datetime'),
|
||||
'body' => null,
|
||||
),
|
||||
'persistence' => array(
|
||||
'listener' => array(
|
||||
'logger' => true,
|
||||
)
|
||||
)
|
||||
),
|
||||
'test2' => array(
|
||||
'mappings' => array(
|
||||
'title' => null,
|
||||
'children' => array(
|
||||
'type' => 'nested',
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
$processor = new Processor();
|
||||
public function testClientConfigurationNoUrl()
|
||||
{
|
||||
$configuration = $this->getConfigs(array(
|
||||
'clients' => array(
|
||||
'default' => array(
|
||||
'host' => 'localhost',
|
||||
'port' => 9200,
|
||||
),
|
||||
)
|
||||
));
|
||||
|
||||
$configuration = $processor->processConfiguration(new Configuration(array($config)), array($config));
|
||||
$this->assertTrue(empty($configuration['clients']['default']['connections'][0]['url']));
|
||||
}
|
||||
|
||||
$this->assertArrayNotHasKey('fields', $configuration['indexes']['test']['types']['test']['mappings']['content']);
|
||||
$this->assertArrayHasKey('fields', $configuration['indexes']['test']['types']['test']['mappings']['title']);
|
||||
public function testMappingsRenamedToProperties()
|
||||
{
|
||||
$configuration = $this->getConfigs(array(
|
||||
'clients' => array(
|
||||
'default' => array('url' => 'http://localhost:9200'),
|
||||
),
|
||||
'indexes' => array(
|
||||
'test' => array(
|
||||
'types' => array(
|
||||
'test' => array(
|
||||
'mappings' => array(
|
||||
'title' => array(),
|
||||
'published' => array('type' => 'datetime'),
|
||||
'body' => null,
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
));
|
||||
|
||||
$this->assertCount(3, $configuration['indexes']['test']['types']['test']['properties']);
|
||||
}
|
||||
|
||||
public function testNestedProperties()
|
||||
{
|
||||
$this->getConfigs(array(
|
||||
'clients' => array(
|
||||
'default' => array('url' => 'http://localhost:9200'),
|
||||
),
|
||||
'indexes' => array(
|
||||
'test' => array(
|
||||
'types' => array(
|
||||
'user' => array(
|
||||
'properties' => array(
|
||||
'field1' => array(),
|
||||
),
|
||||
'persistence' => array(),
|
||||
),
|
||||
'user_profile' => array(
|
||||
'_parent' => array(
|
||||
'type' => 'user',
|
||||
'property' => 'owner',
|
||||
),
|
||||
'properties' => array(
|
||||
'field1' => array(),
|
||||
'field2' => array(
|
||||
'type' => 'nested',
|
||||
'properties' => array(
|
||||
'nested_field1' => array(
|
||||
'type' => 'integer'
|
||||
),
|
||||
'nested_field2' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'type' => 'integer'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
66
Tests/Doctrine/AbstractElasticaToModelTransformerTest.php
Normal file
66
Tests/Doctrine/AbstractElasticaToModelTransformerTest.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\Doctrine;
|
||||
|
||||
use Elastica\Result;
|
||||
use FOS\ElasticaBundle\Doctrine\ORM\ElasticaToModelTransformer;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
|
||||
class AbstractElasticaToModelTransformerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \Doctrine\Common\Persistence\ManagerRegistry|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $registry;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $objectClass = 'stdClass';
|
||||
|
||||
/**
|
||||
* Tests if ignore_missing option is properly handled in transformHybrid() method
|
||||
*/
|
||||
public function testIgnoreMissingOptionDuringTransformHybrid()
|
||||
{
|
||||
$transformer = $this->getMock(
|
||||
'FOS\ElasticaBundle\Doctrine\ORM\ElasticaToModelTransformer',
|
||||
array('findByIdentifiers'),
|
||||
array($this->registry, $this->objectClass, array('ignore_missing' => true))
|
||||
);
|
||||
|
||||
$transformer->setPropertyAccessor(PropertyAccess::createPropertyAccessor());
|
||||
|
||||
$firstOrmResult = new \stdClass();
|
||||
$firstOrmResult->id = 1;
|
||||
$secondOrmResult = new \stdClass();
|
||||
$secondOrmResult->id = 3;
|
||||
$transformer->expects($this->once())
|
||||
->method('findByIdentifiers')
|
||||
->with(array(1, 2, 3))
|
||||
->willReturn(array($firstOrmResult, $secondOrmResult));
|
||||
|
||||
$firstElasticaResult = new Result(array('_id' => 1));
|
||||
$secondElasticaResult = new Result(array('_id' => 2));
|
||||
$thirdElasticaResult = new Result(array('_id' => 3));
|
||||
|
||||
$hybridResults = $transformer->hybridTransform(array($firstElasticaResult, $secondElasticaResult, $thirdElasticaResult));
|
||||
|
||||
$this->assertCount(2, $hybridResults);
|
||||
$this->assertEquals($firstOrmResult, $hybridResults[0]->getTransformed());
|
||||
$this->assertEquals($firstElasticaResult, $hybridResults[0]->getResult());
|
||||
$this->assertEquals($secondOrmResult, $hybridResults[1]->getTransformed());
|
||||
$this->assertEquals($thirdElasticaResult, $hybridResults[1]->getResult());
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
if (!interface_exists('Doctrine\Common\Persistence\ManagerRegistry')) {
|
||||
$this->markTestSkipped('Doctrine Common is not present');
|
||||
}
|
||||
|
||||
$this->registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
}
|
||||
}
|
|
@ -11,12 +11,12 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
public function testObjectInsertedOnPersist()
|
||||
{
|
||||
$persister = $this->getMockPersister();
|
||||
|
||||
$entity = new Listener\Entity(1);
|
||||
$persister = $this->getMockPersister($entity, 'index', 'type');
|
||||
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
||||
$indexable = $this->getMockIndexable('index', 'type', $entity, true);
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener = $this->createListener($persister, array(), $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener->postPersist($eventArgs);
|
||||
|
||||
$this->assertEquals($entity, current($listener->scheduledForInsertion));
|
||||
|
@ -28,18 +28,14 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
$listener->postFlush($eventArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideIsIndexableCallbacks
|
||||
*/
|
||||
public function testNonIndexableObjectNotInsertedOnPersist($isIndexableCallback)
|
||||
public function testNonIndexableObjectNotInsertedOnPersist()
|
||||
{
|
||||
$persister = $this->getMockPersister();
|
||||
|
||||
$entity = new Listener\Entity(1, false);
|
||||
$entity = new Listener\Entity(1);
|
||||
$persister = $this->getMockPersister($entity, 'index', 'type');
|
||||
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
||||
$indexable = $this->getMockIndexable('index', 'type', $entity, false);
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener->setIsIndexableCallback($isIndexableCallback);
|
||||
$listener = $this->createListener($persister, array(), $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener->postPersist($eventArgs);
|
||||
|
||||
$this->assertEmpty($listener->scheduledForInsertion);
|
||||
|
@ -54,12 +50,12 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
public function testObjectReplacedOnUpdate()
|
||||
{
|
||||
$persister = $this->getMockPersister();
|
||||
|
||||
$entity = new Listener\Entity(1);
|
||||
$persister = $this->getMockPersister($entity, 'index', 'type');
|
||||
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
||||
$indexable = $this->getMockIndexable('index', 'type', $entity, true);
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener = $this->createListener($persister, array(), $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener->postUpdate($eventArgs);
|
||||
|
||||
$this->assertEquals($entity, current($listener->scheduledForUpdate));
|
||||
|
@ -73,17 +69,15 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
$listener->postFlush($eventArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideIsIndexableCallbacks
|
||||
*/
|
||||
public function testNonIndexableObjectRemovedOnUpdate($isIndexableCallback)
|
||||
public function testNonIndexableObjectRemovedOnUpdate()
|
||||
{
|
||||
$classMetadata = $this->getMockClassMetadata();
|
||||
$objectManager = $this->getMockObjectManager();
|
||||
$persister = $this->getMockPersister();
|
||||
|
||||
$entity = new Listener\Entity(1, false);
|
||||
$entity = new Listener\Entity(1);
|
||||
$persister = $this->getMockPersister($entity, 'index', 'type');
|
||||
$eventArgs = $this->createLifecycleEventArgs($entity, $objectManager);
|
||||
$indexable = $this->getMockIndexable('index', 'type', $entity, false);
|
||||
|
||||
$objectManager->expects($this->any())
|
||||
->method('getClassMetadata')
|
||||
|
@ -95,18 +89,17 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
->with($entity, 'id')
|
||||
->will($this->returnValue($entity->getId()));
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener->setIsIndexableCallback($isIndexableCallback);
|
||||
$listener = $this->createListener($persister, array(), $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener->postUpdate($eventArgs);
|
||||
|
||||
$this->assertEmpty($listener->scheduledForUpdate);
|
||||
$this->assertEquals($entity, current($listener->scheduledForDeletion));
|
||||
$this->assertEquals($entity->getId(), current($listener->scheduledForDeletion));
|
||||
|
||||
$persister->expects($this->never())
|
||||
->method('replaceOne');
|
||||
$persister->expects($this->once())
|
||||
->method('deleteMany')
|
||||
->with(array($entity));
|
||||
->method('deleteManyByIdentifiers')
|
||||
->with(array($entity->getId()));
|
||||
|
||||
$listener->postFlush($eventArgs);
|
||||
}
|
||||
|
@ -115,10 +108,11 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
$classMetadata = $this->getMockClassMetadata();
|
||||
$objectManager = $this->getMockObjectManager();
|
||||
$persister = $this->getMockPersister();
|
||||
|
||||
$entity = new Listener\Entity(1);
|
||||
$persister = $this->getMockPersister($entity, 'index', 'type');
|
||||
$eventArgs = $this->createLifecycleEventArgs($entity, $objectManager);
|
||||
$indexable = $this->getMockIndexable('index', 'type', $entity);
|
||||
|
||||
$objectManager->expects($this->any())
|
||||
->method('getClassMetadata')
|
||||
|
@ -130,14 +124,14 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
->with($entity, 'id')
|
||||
->will($this->returnValue($entity->getId()));
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener = $this->createListener($persister, array(), $indexable, array('indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener->preRemove($eventArgs);
|
||||
|
||||
$this->assertEquals($entity, current($listener->scheduledForDeletion));
|
||||
$this->assertEquals($entity->getId(), current($listener->scheduledForDeletion));
|
||||
|
||||
$persister->expects($this->once())
|
||||
->method('deleteMany')
|
||||
->with(array($entity));
|
||||
->method('deleteManyByIdentifiers')
|
||||
->with(array($entity->getId()));
|
||||
|
||||
$listener->postFlush($eventArgs);
|
||||
}
|
||||
|
@ -146,10 +140,12 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
$classMetadata = $this->getMockClassMetadata();
|
||||
$objectManager = $this->getMockObjectManager();
|
||||
$persister = $this->getMockPersister();
|
||||
|
||||
$entity = new Listener\Entity(1);
|
||||
$entity->identifier = 'foo';
|
||||
$persister = $this->getMockPersister($entity, 'index', 'type');
|
||||
$eventArgs = $this->createLifecycleEventArgs($entity, $objectManager);
|
||||
$indexable = $this->getMockIndexable('index', 'type', $entity);
|
||||
|
||||
$objectManager->expects($this->any())
|
||||
->method('getClassMetadata')
|
||||
|
@ -161,54 +157,30 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
->with($entity, 'identifier')
|
||||
->will($this->returnValue($entity->getId()));
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array(), 'identifier');
|
||||
$listener = $this->createListener($persister, array(), $indexable, array('identifier' => 'identifier', 'indexName' => 'index', 'typeName' => 'type'));
|
||||
$listener->preRemove($eventArgs);
|
||||
|
||||
$this->assertEquals($entity, current($listener->scheduledForDeletion));
|
||||
$this->assertEquals($entity->identifier, current($listener->scheduledForDeletion));
|
||||
|
||||
$persister->expects($this->once())
|
||||
->method('deleteMany')
|
||||
->with(array($entity));
|
||||
->method('deleteManyByIdentifiers')
|
||||
->with(array($entity->identifier));
|
||||
|
||||
$listener->postFlush($eventArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideInvalidIsIndexableCallbacks
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testInvalidIsIndexableCallbacks($isIndexableCallback)
|
||||
{
|
||||
$listener = $this->createListener($this->getMockPersister(), 'FOS\ElasticaBundle\Tests\Doctrine\Listener\Entity', array());
|
||||
$listener->setIsIndexableCallback($isIndexableCallback);
|
||||
}
|
||||
|
||||
public function provideInvalidIsIndexableCallbacks()
|
||||
{
|
||||
return array(
|
||||
array('nonexistentEntityMethod'),
|
||||
array(array(new Listener\IndexableDecider(), 'internalMethod')),
|
||||
array(42),
|
||||
array('entity.getIsIndexable() && nonexistentEntityFunction()'),
|
||||
);
|
||||
}
|
||||
|
||||
public function provideIsIndexableCallbacks()
|
||||
{
|
||||
return array(
|
||||
array('getIsIndexable'),
|
||||
array(array(new Listener\IndexableDecider(), 'isIndexable')),
|
||||
array(function(Listener\Entity $entity) { return $entity->getIsIndexable(); }),
|
||||
array('entity.getIsIndexable()')
|
||||
);
|
||||
}
|
||||
|
||||
abstract protected function getLifecycleEventArgsClass();
|
||||
|
||||
abstract protected function getListenerClass();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getObjectManagerClass();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getClassMetadataClass();
|
||||
|
||||
private function createLifecycleEventArgs()
|
||||
|
@ -239,9 +211,59 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
->getMock();
|
||||
}
|
||||
|
||||
private function getMockPersister()
|
||||
/**
|
||||
* @param Listener\Entity $object
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
*/
|
||||
private function getMockPersister($object, $indexName, $typeName)
|
||||
{
|
||||
return $this->getMock('FOS\ElasticaBundle\Persister\ObjectPersisterInterface');
|
||||
$mock = $this->getMockBuilder('FOS\ElasticaBundle\Persister\ObjectPersister')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$mock->expects($this->any())
|
||||
->method('handlesObject')
|
||||
->with($object)
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$index = $this->getMockBuilder('Elastica\Index')->disableOriginalConstructor()->getMock();
|
||||
$index->expects($this->any())
|
||||
->method('getName')
|
||||
->will($this->returnValue($indexName));
|
||||
$type = $this->getMockBuilder('Elastica\Type')->disableOriginalConstructor()->getMock();
|
||||
$type->expects($this->any())
|
||||
->method('getName')
|
||||
->will($this->returnValue($typeName));
|
||||
$type->expects($this->any())
|
||||
->method('getIndex')
|
||||
->will($this->returnValue($index));
|
||||
|
||||
$mock->expects($this->any())
|
||||
->method('getType')
|
||||
->will($this->returnValue($type));
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param Listener\Entity $object
|
||||
* @param boolean $return
|
||||
*/
|
||||
private function getMockIndexable($indexName, $typeName, $object, $return = null)
|
||||
{
|
||||
$mock = $this->getMock('FOS\ElasticaBundle\Provider\IndexableInterface');
|
||||
|
||||
if (null !== $return) {
|
||||
$mock->expects($this->once())
|
||||
->method('isObjectIndexable')
|
||||
->with($indexName, $typeName, $object)
|
||||
->will($this->returnValue($return));
|
||||
}
|
||||
|
||||
return $mock;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,33 +272,18 @@ namespace FOS\ElasticaBundle\Tests\Doctrine\Listener;
|
|||
class Entity
|
||||
{
|
||||
private $id;
|
||||
private $isIndexable;
|
||||
|
||||
public function __construct($id, $isIndexable = true)
|
||||
/**
|
||||
* @param integer $id
|
||||
*/
|
||||
public function __construct($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->isIndexable = $isIndexable;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getIsIndexable()
|
||||
{
|
||||
return $this->isIndexable;
|
||||
}
|
||||
}
|
||||
|
||||
class IndexableDecider
|
||||
{
|
||||
public function isIndexable(Entity $entity)
|
||||
{
|
||||
return $entity->getIsIndexable();
|
||||
}
|
||||
|
||||
protected function internalMethod()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Tests\Doctrine;
|
||||
|
||||
use Elastica\Bulk\ResponseSet;
|
||||
use Elastica\Response;
|
||||
|
||||
class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $objectClass;
|
||||
|
@ -9,24 +12,26 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
private $objectPersister;
|
||||
private $options;
|
||||
private $managerRegistry;
|
||||
private $indexable;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
if (!interface_exists('Doctrine\Common\Persistence\ManagerRegistry')) {
|
||||
$this->markTestSkipped('Doctrine Common is not available.');
|
||||
}
|
||||
if (!interface_exists('Doctrine\Common\Persistence\ManagerRegistry')) {
|
||||
$this->markTestSkipped('Doctrine Common is not available.');
|
||||
}
|
||||
|
||||
$this->objectClass = 'objectClass';
|
||||
$this->options = array();
|
||||
$this->objectClass = 'objectClass';
|
||||
$this->options = array('debug_logging' => true, 'indexName' => 'index', 'typeName' => 'type');
|
||||
|
||||
$this->objectPersister = $this->getMockObjectPersister();
|
||||
$this->managerRegistry = $this->getMockManagerRegistry();
|
||||
$this->objectManager = $this->getMockObjectManager();
|
||||
$this->objectPersister = $this->getMockObjectPersister();
|
||||
$this->managerRegistry = $this->getMockManagerRegistry();
|
||||
$this->objectManager = $this->getMockObjectManager();
|
||||
$this->indexable = $this->getMockIndexable();
|
||||
|
||||
$this->managerRegistry->expects($this->any())
|
||||
->method('getManagerForClass')
|
||||
->with($this->objectClass)
|
||||
->will($this->returnValue($this->objectManager));
|
||||
$this->managerRegistry->expects($this->any())
|
||||
->method('getManagerForClass')
|
||||
->with($this->objectClass)
|
||||
->will($this->returnValue($this->objectManager));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,6 +54,11 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
->with($queryBuilder)
|
||||
->will($this->returnValue($nbObjects));
|
||||
|
||||
$this->indexable->expects($this->any())
|
||||
->method('isObjectIndexable')
|
||||
->with('index', 'type', $this->anything())
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$providerInvocationOffset = 2;
|
||||
|
||||
foreach ($objectsByIteration as $i => $objects) {
|
||||
|
@ -59,14 +69,13 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
->with($queryBuilder, $batchSize, $offset)
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->objectPersister->expects($this->at($i))
|
||||
->method('insertMany')
|
||||
->with($objects);
|
||||
|
||||
$this->objectManager->expects($this->at($i))
|
||||
->method('clear');
|
||||
}
|
||||
|
||||
$this->objectPersister->expects($this->exactly(count($objectsByIteration)))
|
||||
->method('insertMany');
|
||||
|
||||
$provider->populate();
|
||||
}
|
||||
|
||||
|
@ -102,6 +111,11 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
->method('fetchSlice')
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->indexable->expects($this->any())
|
||||
->method('isObjectIndexable')
|
||||
->with('index', 'type', $this->anything())
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$this->objectManager->expects($this->never())
|
||||
->method('clear');
|
||||
|
||||
|
@ -123,6 +137,11 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
->method('fetchSlice')
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->indexable->expects($this->any())
|
||||
->method('isObjectIndexable')
|
||||
->with('index', 'type', $this->anything())
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$loggerClosureInvoked = false;
|
||||
$loggerClosure = function () use (&$loggerClosureInvoked) {
|
||||
$loggerClosureInvoked = true;
|
||||
|
@ -150,6 +169,11 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
->method('fetchSlice')
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->indexable->expects($this->any())
|
||||
->method('isObjectIndexable')
|
||||
->with('index', 'type', $this->anything())
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$this->objectPersister->expects($this->any())
|
||||
->method('insertMany')
|
||||
->will($this->throwException($this->getMockBulkResponseException()));
|
||||
|
@ -159,6 +183,36 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
$provider->populate(null, array('ignore-errors' => false));
|
||||
}
|
||||
|
||||
public function testPopulateRunsIndexCallable()
|
||||
{
|
||||
$nbObjects = 2;
|
||||
$objects = array(1, 2);
|
||||
|
||||
$provider = $this->getMockAbstractProvider();
|
||||
$provider->expects($this->any())
|
||||
->method('countObjects')
|
||||
->will($this->returnValue($nbObjects));
|
||||
$provider->expects($this->any())
|
||||
->method('fetchSlice')
|
||||
->will($this->returnValue($objects));
|
||||
|
||||
$this->indexable->expects($this->at(0))
|
||||
->method('isObjectIndexable')
|
||||
->with('index', 'type', 1)
|
||||
->will($this->returnValue(false));
|
||||
$this->indexable->expects($this->at(1))
|
||||
->method('isObjectIndexable')
|
||||
->with('index', 'type', 2)
|
||||
->will($this->returnValue(true));
|
||||
|
||||
|
||||
$this->objectPersister->expects($this->once())
|
||||
->method('insertMany')
|
||||
->with(array(1 => 2));
|
||||
|
||||
$provider->populate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \FOS\ElasticaBundle\Doctrine\AbstractProvider|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
|
@ -166,6 +220,7 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
return $this->getMockForAbstractClass('FOS\ElasticaBundle\Doctrine\AbstractProvider', array(
|
||||
$this->objectPersister,
|
||||
$this->indexable,
|
||||
$this->objectClass,
|
||||
$this->options,
|
||||
$this->managerRegistry,
|
||||
|
@ -177,9 +232,9 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
private function getMockBulkResponseException()
|
||||
{
|
||||
return $this->getMockBuilder('Elastica\Exception\Bulk\ResponseException')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
return $this->getMock('Elastica\Exception\Bulk\ResponseException', null, array(
|
||||
new ResponseSet(new Response(array()), array())
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,6 +260,14 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
return $this->getMock('FOS\ElasticaBundle\Persister\ObjectPersisterInterface');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \FOS\ElasticaBundle\Provider\IndexableInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private function getMockIndexable()
|
||||
{
|
||||
return $this->getMock('FOS\ElasticaBundle\Provider\IndexableInterface');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\Resetter;
|
||||
namespace FOS\ElasticaBundle\Tests\Client;
|
||||
|
||||
use Elastica\Request;
|
||||
use Elastica\Transport\Null as NullTransport;
|
||||
|
||||
class ClientTest extends \PHPUnit_Framework_TestCase
|
||||
class LoggingClientTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testRequestsAreLogged()
|
||||
{
|
||||
|
@ -28,7 +28,7 @@ class ClientTest extends \PHPUnit_Framework_TestCase
|
|||
$this->isType('array')
|
||||
);
|
||||
|
||||
$client = $this->getMockBuilder('FOS\ElasticaBundle\Client')
|
||||
$client = $this->getMockBuilder('FOS\ElasticaBundle\Elastica\Client')
|
||||
->setMethods(array('getConnection'))
|
||||
->getMock();
|
||||
|
|
@ -3,37 +3,21 @@
|
|||
namespace FOS\ElasticaBundle\Tests\Resetter;
|
||||
|
||||
use FOS\ElasticaBundle\FOSElasticaBundle;
|
||||
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
|
||||
|
||||
class FOSElasticaBundleTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testCompilerPassesAreRegistered()
|
||||
{
|
||||
$passes = array(
|
||||
array (
|
||||
'FOS\ElasticaBundle\DependencyInjection\Compiler\RegisterProvidersPass',
|
||||
PassConfig::TYPE_BEFORE_REMOVING
|
||||
),
|
||||
array (
|
||||
'FOS\ElasticaBundle\DependencyInjection\Compiler\TransformerPass'
|
||||
),
|
||||
);
|
||||
|
||||
$container = $this
|
||||
->getMock('Symfony\Component\DependencyInjection\ContainerBuilder');
|
||||
|
||||
$container
|
||||
->expects($this->at(0))
|
||||
->expects($this->atLeastOnce())
|
||||
->method('addCompilerPass')
|
||||
->with($this->isInstanceOf($passes[0][0]), $passes[0][1]);
|
||||
->with($this->isInstanceOf('Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface'));
|
||||
|
||||
$container
|
||||
->expects($this->at(1))
|
||||
->method('addCompilerPass')
|
||||
->with($this->isInstanceOf($passes[1][0]));
|
||||
|
||||
$bundle = new FOSElasticaBundle();
|
||||
|
||||
$bundle->build($container);
|
||||
}
|
||||
}
|
||||
|
|
58
Tests/Functional/ConfigurationManagerTest.php
Normal file
58
Tests/Functional/ConfigurationManagerTest.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Tim Nagel <tim@nagel.com.au>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\Functional;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Client;
|
||||
|
||||
/**
|
||||
* @group functional
|
||||
*/
|
||||
class ConfigurationManagerTest extends WebTestCase
|
||||
{
|
||||
public function testContainerSource()
|
||||
{
|
||||
$client = $this->createClient(array('test_case' => 'Basic'));
|
||||
$manager = $this->getManager($client);
|
||||
|
||||
$index = $manager->getIndexConfiguration('index');
|
||||
|
||||
$this->assertEquals('index', $index->getName());
|
||||
$this->assertGreaterThanOrEqual(2, count($index->getTypes()));
|
||||
$this->assertInstanceOf('FOS\\ElasticaBundle\\Configuration\\TypeConfig', $index->getType('type'));
|
||||
$this->assertInstanceOf('FOS\\ElasticaBundle\\Configuration\\TypeConfig', $index->getType('parent'));
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->deleteTmpDir('Basic');
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
$this->deleteTmpDir('Basic');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @return \FOS\ElasticaBundle\Configuration\ConfigManager
|
||||
*/
|
||||
private function getManager(Client $client)
|
||||
{
|
||||
$manager = $client->getContainer()->get('fos_elastica.config_manager');
|
||||
|
||||
return $manager;
|
||||
}
|
||||
}
|
52
Tests/Functional/IndexableCallbackTest.php
Normal file
52
Tests/Functional/IndexableCallbackTest.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Tim Nagel <tim@nagel.com.au>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\Functional;
|
||||
|
||||
/**
|
||||
* @group functional
|
||||
*/
|
||||
class IndexableCallbackTest extends WebTestCase
|
||||
{
|
||||
/**
|
||||
* 2 reasons for this test:
|
||||
*
|
||||
* 1) To test that the configuration rename from is_indexable_callback under the listener
|
||||
* key is respected, and
|
||||
* 2) To test the Extension's set up of the Indexable service.
|
||||
*/
|
||||
public function testIndexableCallback()
|
||||
{
|
||||
$client = $this->createClient(array('test_case' => 'ORM'));
|
||||
|
||||
/** @var \FOS\ElasticaBundle\Provider\Indexable $in */
|
||||
$in = $client->getContainer()->get('fos_elastica.indexable');
|
||||
|
||||
$this->assertTrue($in->isObjectIndexable('index', 'type', new TypeObj()));
|
||||
$this->assertTrue($in->isObjectIndexable('index', 'type2', new TypeObj()));
|
||||
$this->assertFalse($in->isObjectIndexable('index', 'type3', new TypeObj()));
|
||||
$this->assertFalse($in->isObjectIndexable('index', 'type4', new TypeObj()));
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->deleteTmpDir('ORM');
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
$this->deleteTmpDir('ORM');
|
||||
}
|
||||
}
|
130
Tests/Functional/MappingToElasticaTest.php
Normal file
130
Tests/Functional/MappingToElasticaTest.php
Normal file
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Tim Nagel <tim@nagel.com.au>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\Functional;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Client;
|
||||
|
||||
/**
|
||||
* @group functional
|
||||
*/
|
||||
class MappingToElasticaTest extends WebTestCase
|
||||
{
|
||||
public function testResetIndexAddsMappings()
|
||||
{
|
||||
$client = $this->createClient(array('test_case' => 'Basic'));
|
||||
$resetter = $this->getResetter($client);
|
||||
$resetter->resetIndex('index');
|
||||
|
||||
$type = $this->getType($client);
|
||||
$mapping = $type->getMapping();
|
||||
|
||||
$this->assertNotEmpty($mapping, 'Mapping was populated');
|
||||
$this->assertArrayHasKey('store', $mapping['type']['properties']['field1']);
|
||||
$this->assertTrue($mapping['type']['properties']['field1']['store']);
|
||||
$this->assertArrayNotHasKey('store', $mapping['type']['properties']['field2']);
|
||||
|
||||
$parent = $this->getType($client, 'parent');
|
||||
$mapping = $parent->getMapping();
|
||||
|
||||
$this->assertEquals('my_analyzer', $mapping['parent']['index_analyzer']);
|
||||
$this->assertEquals('whitespace', $mapping['parent']['search_analyzer']);
|
||||
}
|
||||
|
||||
public function testResetType()
|
||||
{
|
||||
$client = $this->createClient(array('test_case' => 'Basic'));
|
||||
$resetter = $this->getResetter($client);
|
||||
$resetter->resetIndexType('index', 'type');
|
||||
|
||||
$type = $this->getType($client);
|
||||
$mapping = $type->getMapping();
|
||||
|
||||
$this->assertNotEmpty($mapping, 'Mapping was populated');
|
||||
$this->assertArrayHasKey('store', $mapping['type']['properties']['field1']);
|
||||
$this->assertTrue($mapping['type']['properties']['field1']['store']);
|
||||
$this->assertArrayNotHasKey('store', $mapping['type']['properties']['field2']);
|
||||
}
|
||||
|
||||
public function testORMResetIndexAddsMappings()
|
||||
{
|
||||
$client = $this->createClient(array('test_case' => 'ORM'));
|
||||
$resetter = $this->getResetter($client);
|
||||
$resetter->resetIndex('index');
|
||||
|
||||
$type = $this->getType($client);
|
||||
$mapping = $type->getMapping();
|
||||
|
||||
$this->assertNotEmpty($mapping, 'Mapping was populated');
|
||||
}
|
||||
|
||||
public function testORMResetType()
|
||||
{
|
||||
$client = $this->createClient(array('test_case' => 'ORM'));
|
||||
$resetter = $this->getResetter($client);
|
||||
$resetter->resetIndexType('index', 'type');
|
||||
|
||||
$type = $this->getType($client);
|
||||
$mapping = $type->getMapping();
|
||||
|
||||
$this->assertNotEmpty($mapping, 'Mapping was populated');
|
||||
}
|
||||
|
||||
public function testMappingIteratorToArrayField()
|
||||
{
|
||||
$client = $this->createClient(array('test_case' => 'ORM'));
|
||||
$persister = $client->getContainer()->get('fos_elastica.object_persister.index.type');
|
||||
|
||||
$object = new TypeObj();
|
||||
$object->id = 1;
|
||||
$object->coll = new \ArrayIterator(array('foo', 'bar'));
|
||||
$persister->insertOne($object);
|
||||
|
||||
$object->coll = new \ArrayIterator(array('foo', 'bar', 'bazz'));
|
||||
$object->coll->offsetUnset(1);
|
||||
|
||||
$persister->replaceOne($object);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @return \FOS\ElasticaBundle\Resetter $resetter
|
||||
*/
|
||||
private function getResetter(Client $client)
|
||||
{
|
||||
return $client->getContainer()->get('fos_elastica.resetter');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @return \Elastica\Type
|
||||
*/
|
||||
private function getType(Client $client, $type = 'type')
|
||||
{
|
||||
return $client->getContainer()->get('fos_elastica.index.index.' . $type);
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->deleteTmpDir('Basic');
|
||||
$this->deleteTmpDir('ORM');
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
$this->deleteTmpDir('Basic');
|
||||
$this->deleteTmpDir('ORM');
|
||||
}
|
||||
}
|
55
Tests/Functional/SerializerTest.php
Normal file
55
Tests/Functional/SerializerTest.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Tim Nagel <tim@nagel.com.au>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\Functional;
|
||||
|
||||
/**
|
||||
* @group functional
|
||||
*/
|
||||
class SerializerTest extends WebTestCase
|
||||
{
|
||||
public function testMappingIteratorToArrayField()
|
||||
{
|
||||
$client = $this->createClient(array('test_case' => 'Serializer'));
|
||||
$persister = $client->getContainer()->get('fos_elastica.object_persister.index.type');
|
||||
|
||||
$object = new TypeObj();
|
||||
$object->id = 1;
|
||||
$object->coll = new \ArrayIterator(array('foo', 'bar'));
|
||||
$persister->insertOne($object);
|
||||
|
||||
$object->coll = new \ArrayIterator(array('foo', 'bar', 'bazz'));
|
||||
$object->coll->offsetUnset(1);
|
||||
|
||||
$persister->replaceOne($object);
|
||||
}
|
||||
|
||||
public function testUnmappedType()
|
||||
{
|
||||
$client = $this->createClient(array('test_case' => 'Serializer'));
|
||||
$resetter = $client->getContainer()->get('fos_elastica.resetter');
|
||||
$resetter->resetIndex('index');
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->deleteTmpDir('Serializer');
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
$this->deleteTmpDir('Serializer');
|
||||
}
|
||||
}
|
33
Tests/Functional/TypeObj.php
Normal file
33
Tests/Functional/TypeObj.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?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\Tests\Functional;
|
||||
|
||||
class TypeObj
|
||||
{
|
||||
public $coll;
|
||||
public $field1;
|
||||
|
||||
public function isIndexable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isntIndexable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getSerializableColl()
|
||||
{
|
||||
return iterator_to_array($this->coll, false);
|
||||
}
|
||||
}
|
40
Tests/Functional/WebTestCase.php
Normal file
40
Tests/Functional/WebTestCase.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Tim Nagel <tim@nagel.com.au>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\Functional;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Tests\Functional\WebTestCase as BaseWebTestCase;
|
||||
|
||||
class WebTestCase extends BaseWebTestCase
|
||||
{
|
||||
protected static function getKernelClass()
|
||||
{
|
||||
require_once __DIR__.'/app/AppKernel.php';
|
||||
|
||||
return 'FOS\ElasticaBundle\Tests\Functional\app\AppKernel';
|
||||
}
|
||||
|
||||
protected static function createKernel(array $options = array())
|
||||
{
|
||||
$class = self::getKernelClass();
|
||||
|
||||
if (!isset($options['test_case'])) {
|
||||
throw new \InvalidArgumentException('The option "test_case" must be set.');
|
||||
}
|
||||
|
||||
return new $class(
|
||||
$options['test_case'],
|
||||
isset($options['root_config']) ? $options['root_config'] : 'config.yml',
|
||||
isset($options['environment']) ? $options['environment'] : 'foselasticabundle'.strtolower($options['test_case']),
|
||||
isset($options['debug']) ? $options['debug'] : true
|
||||
);
|
||||
}
|
||||
}
|
118
Tests/Functional/app/AppKernel.php
Normal file
118
Tests/Functional/app/AppKernel.php
Normal file
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\Functional\app;
|
||||
|
||||
// get the autoload file
|
||||
$dir = __DIR__;
|
||||
$lastDir = null;
|
||||
while ($dir !== $lastDir) {
|
||||
$lastDir = $dir;
|
||||
|
||||
if (file_exists($dir.'/autoload.php')) {
|
||||
require_once $dir.'/autoload.php';
|
||||
break;
|
||||
}
|
||||
|
||||
if (file_exists($dir.'/autoload.php.dist')) {
|
||||
require_once $dir.'/autoload.php.dist';
|
||||
break;
|
||||
}
|
||||
|
||||
if (file_exists($dir.'/vendor/autoload.php')) {
|
||||
require_once $dir.'/vendor/autoload.php';
|
||||
break;
|
||||
}
|
||||
|
||||
$dir = dirname($dir);
|
||||
}
|
||||
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
|
||||
/**
|
||||
* App Test Kernel for functional tests.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class AppKernel extends Kernel
|
||||
{
|
||||
private $testCase;
|
||||
private $rootConfig;
|
||||
|
||||
public function __construct($testCase, $rootConfig, $environment, $debug)
|
||||
{
|
||||
if (!is_dir(__DIR__.'/'.$testCase)) {
|
||||
throw new \InvalidArgumentException(sprintf('The test case "%s" does not exist.', $testCase));
|
||||
}
|
||||
$this->testCase = $testCase;
|
||||
|
||||
$fs = new Filesystem();
|
||||
if (!$fs->isAbsolutePath($rootConfig) && !file_exists($rootConfig = __DIR__.'/'.$testCase.'/'.$rootConfig)) {
|
||||
throw new \InvalidArgumentException(sprintf('The root config "%s" does not exist.', $rootConfig));
|
||||
}
|
||||
$this->rootConfig = $rootConfig;
|
||||
|
||||
parent::__construct($environment, $debug);
|
||||
}
|
||||
|
||||
public function registerBundles()
|
||||
{
|
||||
if (!file_exists($filename = $this->getRootDir().'/'.$this->testCase.'/bundles.php')) {
|
||||
throw new \RuntimeException(sprintf('The bundles file "%s" does not exist.', $filename));
|
||||
}
|
||||
|
||||
return include $filename;
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
}
|
||||
|
||||
public function getRootDir()
|
||||
{
|
||||
return __DIR__;
|
||||
}
|
||||
|
||||
public function getCacheDir()
|
||||
{
|
||||
return sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$this->testCase.'/cache/'.$this->environment;
|
||||
}
|
||||
|
||||
public function getLogDir()
|
||||
{
|
||||
return sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$this->testCase.'/logs';
|
||||
}
|
||||
|
||||
public function registerContainerConfiguration(LoaderInterface $loader)
|
||||
{
|
||||
$loader->load($this->rootConfig);
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return serialize(array($this->testCase, $this->rootConfig, $this->getEnvironment(), $this->isDebug()));
|
||||
}
|
||||
|
||||
public function unserialize($str)
|
||||
{
|
||||
call_user_func_array(array($this, '__construct'), unserialize($str));
|
||||
}
|
||||
|
||||
protected function getKernelParameters()
|
||||
{
|
||||
$parameters = parent::getKernelParameters();
|
||||
$parameters['kernel.test_case'] = $this->testCase;
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
}
|
13
Tests/Functional/app/Basic/bundles.php
Normal file
13
Tests/Functional/app/Basic/bundles.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
use FOS\ElasticaBundle\FOSElasticaBundle;
|
||||
use Knp\Bundle\PaginatorBundle\KnpPaginatorBundle;
|
||||
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
|
||||
use Symfony\Bundle\TwigBundle\TwigBundle;
|
||||
|
||||
return array(
|
||||
new FrameworkBundle(),
|
||||
new FOSElasticaBundle(),
|
||||
new KnpPaginatorBundle(),
|
||||
new TwigBundle(),
|
||||
);
|
89
Tests/Functional/app/Basic/config.yml
Normal file
89
Tests/Functional/app/Basic/config.yml
Normal file
|
@ -0,0 +1,89 @@
|
|||
imports:
|
||||
- { resource: ./../config/config.yml }
|
||||
|
||||
framework:
|
||||
templating:
|
||||
engines: ['twig']
|
||||
|
||||
twig:
|
||||
debug: %kernel.debug%
|
||||
|
||||
fos_elastica:
|
||||
clients:
|
||||
default:
|
||||
connections:
|
||||
- url: http://localhost:9200
|
||||
- host: localhost
|
||||
port: 9200
|
||||
second_server:
|
||||
url: http://localhost:9200
|
||||
indexes:
|
||||
index:
|
||||
index_name: foselastica_basic_test_%kernel.environment%
|
||||
settings:
|
||||
analysis:
|
||||
analyzer:
|
||||
my_analyzer:
|
||||
type: custom
|
||||
tokenizer: lowercase
|
||||
filter: [my_ngram]
|
||||
filter:
|
||||
my_ngram:
|
||||
type: "nGram"
|
||||
min_gram: 3
|
||||
max_gram: 5
|
||||
types:
|
||||
parent:
|
||||
dynamic_templates:
|
||||
dates:
|
||||
match: "date_*"
|
||||
mapping:
|
||||
type: date
|
||||
mappings:
|
||||
field1: ~
|
||||
field2: ~
|
||||
search_analyzer: whitespace
|
||||
index_analyzer: my_analyzer
|
||||
type:
|
||||
search_analyzer: my_analyzer
|
||||
dynamic_templates:
|
||||
- dates:
|
||||
match: "date_*"
|
||||
mapping:
|
||||
type: date
|
||||
- strings:
|
||||
match: "*"
|
||||
mapping:
|
||||
analyzer: english
|
||||
type: string
|
||||
properties:
|
||||
field1: ~
|
||||
field2:
|
||||
type: integer
|
||||
store: false
|
||||
date: { boost: 5 }
|
||||
completion:
|
||||
type: completion
|
||||
title: { boost: 8, analyzer: my_analyzer }
|
||||
content: ~
|
||||
comments:
|
||||
type: "nested"
|
||||
properties:
|
||||
date: { boost: 5 }
|
||||
content: ~
|
||||
user:
|
||||
type: "object"
|
||||
approver:
|
||||
type: "object"
|
||||
properties:
|
||||
date: { boost: 5 }
|
||||
agreement:
|
||||
type: "attachment"
|
||||
lastlogin: { type: date, format: basic_date_time }
|
||||
birthday: { type: date, format: "yyyy-MM-dd" }
|
||||
_parent:
|
||||
type: "parent"
|
||||
property: "parent"
|
||||
identifier: "id"
|
||||
null_mappings:
|
||||
mappings: ~
|
25
Tests/Functional/app/ORM/IndexableService.php
Normal file
25
Tests/Functional/app/ORM/IndexableService.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?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\Tests\Functional\app\ORM;
|
||||
|
||||
class IndexableService
|
||||
{
|
||||
public function isIndexable($object)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function isntIndexable($object)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
13
Tests/Functional/app/ORM/bundles.php
Normal file
13
Tests/Functional/app/ORM/bundles.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
|
||||
use FOS\ElasticaBundle\FOSElasticaBundle;
|
||||
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
|
||||
use JMS\SerializerBundle\JMSSerializerBundle;
|
||||
|
||||
return array(
|
||||
new FrameworkBundle(),
|
||||
new FOSElasticaBundle(),
|
||||
new DoctrineBundle(),
|
||||
new JMSSerializerBundle(),
|
||||
);
|
77
Tests/Functional/app/ORM/config.yml
Normal file
77
Tests/Functional/app/ORM/config.yml
Normal file
|
@ -0,0 +1,77 @@
|
|||
imports:
|
||||
- { resource: ./../config/config.yml }
|
||||
|
||||
doctrine:
|
||||
dbal:
|
||||
path: %kernel.cache_dir%/db.sqlite
|
||||
charset: UTF8
|
||||
orm:
|
||||
auto_generate_proxy_classes: false
|
||||
auto_mapping: false
|
||||
|
||||
services:
|
||||
indexableService:
|
||||
class: FOS\ElasticaBundle\Tests\Functional\app\ORM\IndexableService
|
||||
|
||||
fos_elastica:
|
||||
clients:
|
||||
default:
|
||||
url: http://localhost:9200
|
||||
indexes:
|
||||
fos_elastica_orm_test:
|
||||
types:
|
||||
type:
|
||||
properties:
|
||||
field1: ~
|
||||
index:
|
||||
index_name: foselastica_orm_test_%kernel.environment%
|
||||
types:
|
||||
type:
|
||||
properties:
|
||||
field1: ~
|
||||
coll: ~
|
||||
persistence:
|
||||
driver: orm
|
||||
model: FOS\ElasticaBundle\Tests\Functional\TypeObj
|
||||
listener:
|
||||
is_indexable_callback: 'object.isIndexable() && !object.isntIndexable()'
|
||||
type2:
|
||||
properties:
|
||||
field1: ~
|
||||
persistence:
|
||||
driver: orm
|
||||
model: FOS\ElasticaBundle\Tests\Functional\TypeObj
|
||||
listener:
|
||||
is_indexable_callback: [ @indexableService, 'isIndexable' ]
|
||||
type3:
|
||||
mappings:
|
||||
field1: ~
|
||||
persistence:
|
||||
driver: orm
|
||||
model: FOS\ElasticaBundle\Tests\Functional\TypeObj
|
||||
finder: ~
|
||||
provider: ~
|
||||
listener:
|
||||
is_indexable_callback: 'isntIndexable'
|
||||
type4:
|
||||
mappings:
|
||||
field1: ~
|
||||
persistence:
|
||||
driver: orm
|
||||
model: FOS\ElasticaBundle\Tests\Functional\TypeObj
|
||||
finder: ~
|
||||
provider: ~
|
||||
listener:
|
||||
is_indexable_callback: [ 'FOS\ElasticaBundle\Tests\Functional\app\ORM\IndexableService', 'isntIndexable' ]
|
||||
second_index:
|
||||
index_name: foselastica_orm_test_second_%kernel.environment%
|
||||
types:
|
||||
type:
|
||||
properties:
|
||||
field1: ~
|
||||
coll: ~
|
||||
persistence:
|
||||
driver: orm
|
||||
model: FOS\ElasticaBundle\Tests\Functional\TypeObj
|
||||
listener:
|
||||
is_indexable_callback: 'object.isIndexable() && !object.isntIndexable()'
|
8
Tests/Functional/app/Serializer/TypeObj.yml
Normal file
8
Tests/Functional/app/Serializer/TypeObj.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
FOS\ElasticaBundle\Tests\Functional\TypeObj:
|
||||
properties:
|
||||
field1:
|
||||
type: string
|
||||
virtualProperties:
|
||||
getSerializableColl:
|
||||
serializedName: coll
|
||||
type: array
|
13
Tests/Functional/app/Serializer/bundles.php
Normal file
13
Tests/Functional/app/Serializer/bundles.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
|
||||
use FOS\ElasticaBundle\FOSElasticaBundle;
|
||||
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
|
||||
use JMS\SerializerBundle\JMSSerializerBundle;
|
||||
|
||||
return array(
|
||||
new FrameworkBundle(),
|
||||
new FOSElasticaBundle(),
|
||||
new DoctrineBundle(),
|
||||
new JMSSerializerBundle(),
|
||||
);
|
50
Tests/Functional/app/Serializer/config.yml
Normal file
50
Tests/Functional/app/Serializer/config.yml
Normal file
|
@ -0,0 +1,50 @@
|
|||
imports:
|
||||
- { resource: ./../config/config.yml }
|
||||
|
||||
doctrine:
|
||||
dbal:
|
||||
path: %kernel.cache_dir%/db.sqlite
|
||||
charset: UTF8
|
||||
orm:
|
||||
auto_generate_proxy_classes: false
|
||||
auto_mapping: false
|
||||
|
||||
services:
|
||||
indexableService:
|
||||
class: FOS\ElasticaBundle\Tests\Functional\app\ORM\IndexableService
|
||||
|
||||
jms_serializer:
|
||||
metadata:
|
||||
auto_detection: true
|
||||
directories:
|
||||
type_obj:
|
||||
namespace_prefix: "FOS\\ElasticaBundle\\Tests\\Functional"
|
||||
path: "%kernel.root_dir%/Serializer"
|
||||
|
||||
fos_elastica:
|
||||
clients:
|
||||
default:
|
||||
url: http://localhost:9200
|
||||
serializer: ~
|
||||
indexes:
|
||||
index:
|
||||
index_name: foselastica_test_%kernel.environment%
|
||||
types:
|
||||
type:
|
||||
properties:
|
||||
coll: ~
|
||||
field1: ~
|
||||
persistence:
|
||||
driver: orm
|
||||
model: FOS\ElasticaBundle\Tests\Functional\TypeObj
|
||||
serializer:
|
||||
groups: ['search', 'Default']
|
||||
version: 1.1
|
||||
unmapped:
|
||||
persistence:
|
||||
driver: orm
|
||||
model: FOS\ElasticaBundle\Tests\Functional\TypeObj
|
||||
serializer:
|
||||
groups: ['search', 'Default']
|
||||
version: 1.1
|
||||
|
8
Tests/Functional/app/config/config.yml
Normal file
8
Tests/Functional/app/config/config.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
framework:
|
||||
secret: secret
|
||||
router: { resource: "%kernel.root_dir%/%kernel.test_case%/routing.yml" }
|
||||
test: ~
|
||||
default_locale: en
|
||||
|
||||
services:
|
||||
logger: { class: Symfony\Component\HttpKernel\Log\NullLogger }
|
59
Tests/Index/IndexManagerTest.php
Normal file
59
Tests/Index/IndexManagerTest.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\Index;
|
||||
|
||||
use FOS\ElasticaBundle\Index\IndexManager;
|
||||
|
||||
class IndexManagerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $indexes = array();
|
||||
|
||||
/**
|
||||
* @var IndexManager
|
||||
*/
|
||||
private $indexManager;
|
||||
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
foreach (array('index1', 'index2', 'index3') as $indexName) {
|
||||
$index = $this->getMockBuilder('FOS\\ElasticaBundle\\Elastica\\Index')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$index->expects($this->any())
|
||||
->method('getName')
|
||||
->will($this->returnValue($indexName));
|
||||
|
||||
$this->indexes[$indexName] = $index;
|
||||
}
|
||||
|
||||
$this->indexManager = new IndexManager($this->indexes, $this->indexes['index2']);
|
||||
}
|
||||
|
||||
public function testGetAllIndexes()
|
||||
{
|
||||
$this->assertEquals($this->indexes, $this->indexManager->getAllIndexes());
|
||||
}
|
||||
|
||||
public function testGetIndex()
|
||||
{
|
||||
$this->assertEquals($this->indexes['index1'], $this->indexManager->getIndex('index1'));
|
||||
$this->assertEquals($this->indexes['index2'], $this->indexManager->getIndex('index2'));
|
||||
$this->assertEquals($this->indexes['index3'], $this->indexManager->getIndex('index3'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testGetIndexShouldThrowExceptionForInvalidName()
|
||||
{
|
||||
$this->indexManager->getIndex('index4');
|
||||
}
|
||||
|
||||
public function testGetDefaultIndex()
|
||||
{
|
||||
$this->assertEquals('index2', $this->indexManager->getIndex()->getName());
|
||||
$this->assertEquals('index2', $this->indexManager->getDefaultIndex()->getName());
|
||||
}
|
||||
}
|
|
@ -1,24 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\Resetter;
|
||||
namespace FOS\ElasticaBundle\Tests\Index;
|
||||
|
||||
use Elastica\Exception\ResponseException;
|
||||
use Elastica\Request;
|
||||
use Elastica\Response;
|
||||
use FOS\ElasticaBundle\Resetter;
|
||||
use Elastica\Type\Mapping;
|
||||
use FOS\ElasticaBundle\Configuration\IndexConfig;
|
||||
use FOS\ElasticaBundle\Index\Resetter;
|
||||
|
||||
class ResetterTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $indexConfigsByName;
|
||||
/**
|
||||
* @var Resetter
|
||||
*/
|
||||
private $resetter;
|
||||
|
||||
private $configManager;
|
||||
private $indexManager;
|
||||
private $aliasProcessor;
|
||||
private $mappingBuilder;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->indexConfigsByName = array(
|
||||
$this->markTestIncomplete('To be rewritten');
|
||||
$this->configManager = $this->getMockBuilder('FOS\\ElasticaBundle\\Configuration\\ConfigManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->indexManager = $this->getMockBuilder('FOS\\ElasticaBundle\\Index\\IndexManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->aliasProcessor = $this->getMockBuilder('FOS\\ElasticaBundle\\Index\\AliasProcessor')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->mappingBuilder = $this->getMockBuilder('FOS\\ElasticaBundle\\Index\\MappingBuilder')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->resetter = new Resetter($this->configManager, $this->indexManager, $this->aliasProcessor, $this->mappingBuilder);
|
||||
|
||||
/*$this->indexConfigsByName = array(
|
||||
'foo' => array(
|
||||
'index' => $this->getMockElasticaIndex(),
|
||||
'config' => array(
|
||||
'mappings' => array(
|
||||
'properties' => array(
|
||||
'a' => array(
|
||||
'dynamic_templates' => array(),
|
||||
'properties' => array(),
|
||||
|
@ -30,7 +55,7 @@ class ResetterTest extends \PHPUnit_Framework_TestCase
|
|||
'bar' => array(
|
||||
'index' => $this->getMockElasticaIndex(),
|
||||
'config' => array(
|
||||
'mappings' => array(
|
||||
'properties' => array(
|
||||
'a' => array('properties' => array()),
|
||||
'b' => array('properties' => array()),
|
||||
),
|
||||
|
@ -39,7 +64,7 @@ class ResetterTest extends \PHPUnit_Framework_TestCase
|
|||
'parent' => array(
|
||||
'index' => $this->getMockElasticaIndex(),
|
||||
'config' => array(
|
||||
'mappings' => array(
|
||||
'properties' => array(
|
||||
'a' => array(
|
||||
'properties' => array(
|
||||
'field_2' => array()
|
||||
|
@ -54,12 +79,26 @@ class ResetterTest extends \PHPUnit_Framework_TestCase
|
|||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
);*/
|
||||
}
|
||||
|
||||
public function testResetAllIndexes()
|
||||
{
|
||||
$this->indexConfigsByName['foo']['index']->expects($this->once())
|
||||
$this->configManager->expects($this->once())
|
||||
->method('getIndexNames')
|
||||
->will($this->returnValue(array('index1')));
|
||||
|
||||
$this->configManager->expects($this->once())
|
||||
->method('getIndexConfiguration')
|
||||
->with('index1')
|
||||
->will($this->returnValue(new IndexConfig('index1', array(), array())));
|
||||
|
||||
$this->indexManager->expects($this->once())
|
||||
->method('getIndex')
|
||||
->with('index1')
|
||||
->will($this->returnValue());
|
||||
|
||||
/*$this->indexConfigsByName['foo']['index']->expects($this->once())
|
||||
->method('create')
|
||||
->with($this->indexConfigsByName['foo']['config'], true);
|
||||
|
||||
|
@ -67,8 +106,8 @@ class ResetterTest extends \PHPUnit_Framework_TestCase
|
|||
->method('create')
|
||||
->with($this->indexConfigsByName['bar']['config'], true);
|
||||
|
||||
$resetter = new Resetter($this->indexConfigsByName);
|
||||
$resetter->resetAllIndexes();
|
||||
$resetter = new Resetter($this->indexConfigsByName);*/
|
||||
$this->resetter->resetAllIndexes();
|
||||
}
|
||||
|
||||
public function testResetIndex()
|
||||
|
@ -105,8 +144,8 @@ class ResetterTest extends \PHPUnit_Framework_TestCase
|
|||
$type->expects($this->once())
|
||||
->method('delete');
|
||||
|
||||
$mapping = Mapping::create($this->indexConfigsByName['foo']['config']['mappings']['a']['properties']);
|
||||
$mapping->setParam('dynamic_templates', $this->indexConfigsByName['foo']['config']['mappings']['a']['dynamic_templates']);
|
||||
$mapping = Mapping::create($this->indexConfigsByName['foo']['config']['properties']['a']['properties']);
|
||||
$mapping->setParam('dynamic_templates', $this->indexConfigsByName['foo']['config']['properties']['a']['dynamic_templates']);
|
||||
$type->expects($this->once())
|
||||
->method('setMapping')
|
||||
->with($mapping);
|
||||
|
@ -149,8 +188,8 @@ class ResetterTest extends \PHPUnit_Framework_TestCase
|
|||
new Response(array('error' => 'TypeMissingException[[de_20131022] type[bla] missing]', 'status' => 404)))
|
||||
));
|
||||
|
||||
$mapping = Mapping::create($this->indexConfigsByName['foo']['config']['mappings']['a']['properties']);
|
||||
$mapping->setParam('dynamic_templates', $this->indexConfigsByName['foo']['config']['mappings']['a']['dynamic_templates']);
|
||||
$mapping = Mapping::create($this->indexConfigsByName['foo']['config']['properties']['a']['properties']);
|
||||
$mapping->setParam('dynamic_templates', $this->indexConfigsByName['foo']['config']['properties']['a']['dynamic_templates']);
|
||||
$type->expects($this->once())
|
||||
->method('setMapping')
|
||||
->with($mapping);
|
||||
|
@ -171,7 +210,7 @@ class ResetterTest extends \PHPUnit_Framework_TestCase
|
|||
$type->expects($this->once())
|
||||
->method('delete');
|
||||
|
||||
$mapping = Mapping::create($this->indexConfigsByName['parent']['config']['mappings']['a']['properties']);
|
||||
$mapping = Mapping::create($this->indexConfigsByName['parent']['config']['properties']['a']['properties']);
|
||||
$mapping->setParam('_parent', array('type' => 'b'));
|
||||
$type->expects($this->once())
|
||||
->method('setMapping')
|
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\IndexManager;
|
||||
|
||||
use FOS\ElasticaBundle\IndexManager;
|
||||
|
||||
class IndexManagerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $defaultIndexName;
|
||||
private $indexesByName;
|
||||
/** @var IndexManager */
|
||||
private $indexManager;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->defaultIndexName = 'index2';
|
||||
$this->indexesByName = array(
|
||||
'index1' => 'test1',
|
||||
'index2' => 'test2',
|
||||
);
|
||||
|
||||
/** @var $defaultIndex \PHPUnit_Framework_MockObject_MockObject|\Elastica\Index */
|
||||
$defaultIndex = $this->getMockBuilder('Elastica\Index')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$defaultIndex->expects($this->any())
|
||||
->method('getName')
|
||||
->will($this->returnValue($this->defaultIndexName));
|
||||
|
||||
$this->indexManager = new IndexManager($this->indexesByName, $defaultIndex);
|
||||
}
|
||||
|
||||
public function testGetAllIndexes()
|
||||
{
|
||||
$this->assertEquals($this->indexesByName, $this->indexManager->getAllIndexes());
|
||||
}
|
||||
|
||||
public function testGetIndex()
|
||||
{
|
||||
$this->assertEquals($this->indexesByName['index1'], $this->indexManager->getIndex('index1'));
|
||||
$this->assertEquals($this->indexesByName['index2'], $this->indexManager->getIndex('index2'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testGetIndexShouldThrowExceptionForInvalidName()
|
||||
{
|
||||
$this->indexManager->getIndex('index3');
|
||||
}
|
||||
|
||||
public function testGetDefaultIndex()
|
||||
{
|
||||
$this->assertEquals('test2', $this->indexManager->getIndex());
|
||||
$this->assertEquals('test2', $this->indexManager->getDefaultIndex());
|
||||
}
|
||||
}
|
17
Tests/Integration/MappingTest.php
Normal file
17
Tests/Integration/MappingTest.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Tim Nagel <tim@nagel.com.au>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\Integration;
|
||||
|
||||
|
||||
class MappingTest {
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue