diff --git a/Client/Connection.php b/Client/Connection.php
index 1fbb5a10..a462e6b9 100644
--- a/Client/Connection.php
+++ b/Client/Connection.php
@@ -221,7 +221,10 @@ public function scroll($scrollId, $scrollDuration)
*/
public function createIndex($putWarmers = false)
{
- $this->client->indices()->create($this->settings);
+ $settings = $this->settings;
+ unset($settings['body']['mappings']);
+
+ $this->client->indices()->create($settings);
if ($putWarmers) {
// Sometimes Elasticsearch gives service unavailable.
@@ -238,6 +241,85 @@ public function dropIndex()
$this->client->indices()->delete(['index' => $this->getIndexName()]);
}
+ /**
+ * Puts mapping into elasticsearch client.
+ *
+ * @param array $types Specific types to put.
+ *
+ * @return int
+ */
+ public function createTypes(array $types = [])
+ {
+ $mapping = $this->getMapping($types);
+ if (empty($mapping)) {
+ return 0;
+ }
+
+ $mapping = array_diff_key($mapping, $this->getMappingFromIndex($types));
+ if (empty($mapping)) {
+ return -1;
+ }
+
+ $this->loadMappingArray($mapping);
+
+ return 1;
+ }
+
+ /**
+ * Drops mapping from elasticsearch client.
+ *
+ * @param array $types Specific types to drop.
+ *
+ * @return int
+ */
+ public function dropTypes(array $types = [])
+ {
+ $mapping = $this->getMapping($types);
+
+ if (empty($mapping)) {
+ return 0;
+ }
+
+ $this->unloadMappingArray(array_keys($mapping));
+
+ return 1;
+ }
+
+ /**
+ * Updates elasticsearch client mapping.
+ *
+ * @param array $types Specific types to update.
+ *
+ * @return int
+ */
+ public function updateTypes(array $types = [])
+ {
+ if (!$this->getMapping($types)) {
+ return -1;
+ }
+
+ $tempSettings = $this->settings;
+ $tempSettings['index'] = uniqid('mapping_check_');
+ $mappingCheckConnection = new Connection($this->client, $tempSettings);
+ $mappingCheckConnection->createIndex();
+ $mappingCheckConnection->createTypes($types);
+
+ $newMapping = $mappingCheckConnection->getMappingFromIndex($types);
+ $oldMapping = $this->getMappingFromIndex($types);
+
+ $mappingCheckConnection->dropIndex();
+
+ $tool = new MappingTool();
+ $updated = (int)$tool->checkMapping($oldMapping, $newMapping);
+
+ if ($updated) {
+ $this->unloadMappingArray($tool->getRemovedTypes());
+ $this->loadMappingArray($tool->getUpdatedTypes());
+ }
+
+ return $updated;
+ }
+
/**
* Tries to drop and create fresh elasticsearch index.
*
@@ -285,18 +367,16 @@ public function setIndexName($name)
}
/**
- * Returns mapping by type.
+ * Returns mapping by type if defined.
*
- * @param string $type Type name.
+ * @param string|array $type Type names.
*
* @return array|null
*/
- public function getMapping($type)
+ public function getMapping($type = [])
{
- if (isset($this->settings['body']['mappings'])
- && array_key_exists($type, $this->settings['body']['mappings'])
- ) {
- return $this->settings['body']['mappings'][$type];
+ if (isset($this->settings['body']['mappings'])) {
+ return $this->filterMapping($type, $this->settings['body']['mappings']);
}
return null;
@@ -343,49 +423,17 @@ public function setMultipleMapping(array $mapping, $cleanUp = false)
/**
* Mapping is compared with loaded, if needed updates it and returns true.
*
+ * @param array $types Types to update.
+ *
* @return bool
+ *
* @throws \LogicException
+ *
+ * @deprecated Will be removed in 1.0. Please now use Connection#updateTypes()
*/
- public function updateMapping()
+ public function updateMapping(array $types = [])
{
- if (!isset($this->settings['body']['mappings']) || empty($this->settings['body']['mappings'])) {
- throw new \LogicException('Connection does not have any mapping loaded.');
- }
-
- $tempSettings = $this->settings;
- $tempSettings['index'] = uniqid('mapping_check_');
- $mappingCheckConnection = new Connection($this->client, $tempSettings);
- $mappingCheckConnection->createIndex();
-
- $newMapping = $mappingCheckConnection->getMappingFromIndex();
- $oldMapping = $this->getMappingFromIndex();
-
- $mappingCheckConnection->dropIndex();
-
- $tool = new MappingTool();
- $updated = $tool->checkMapping($oldMapping, $newMapping);
-
- if ($updated) {
- foreach ($tool->getRemovedTypes() as $type) {
- $this->client->indices()->deleteMapping(
- [
- 'index' => $this->getIndexName(),
- 'type' => $type,
- ]
- );
- }
- foreach ($tool->getUpdatedTypes() as $type => $properties) {
- $this->client->indices()->putMapping(
- [
- 'index' => $this->getIndexName(),
- 'type' => $type,
- 'body' => [$type => $properties],
- ]
- );
- }
- }
-
- return $updated;
+ return $this->updateTypes($types);
}
/**
@@ -423,9 +471,11 @@ public function open()
/**
* Returns mapping from index.
*
+ * @param array|string $types Returns only certain set of types if set.
+ *
* @return array
*/
- public function getMappingFromIndex()
+ public function getMappingFromIndex($types = [])
{
$mapping = $this
->client
@@ -433,7 +483,7 @@ public function getMappingFromIndex()
->getMapping(['index' => $this->getIndexName()]);
if (array_key_exists($this->getIndexName(), $mapping)) {
- return $mapping[$this->getIndexName()]['mappings'];
+ return $this->filterMapping($types, $mapping[$this->getIndexName()]['mappings']);
}
return [];
@@ -465,7 +515,7 @@ public function updateSettings(array $settings, $force = false)
}
/**
- * Clears elasticsearch cache.
+ * Clears elasticsearch client cache.
*/
public function clearCache()
{
@@ -577,4 +627,62 @@ private function validateWarmers($names, $warmerNames = [])
);
}
}
+
+ /**
+ * Puts mapping into elasticsearch.
+ *
+ * @param array $mapping Mapping to put into client.
+ */
+ private function loadMappingArray(array $mapping)
+ {
+ foreach ($mapping as $type => $properties) {
+ $this->client->indices()->putMapping(
+ [
+ 'index' => $this->getIndexName(),
+ 'type' => $type,
+ 'body' => [
+ $type => $properties,
+ ],
+ ]
+ );
+ }
+ }
+
+ /**
+ * Drops mapping from elasticsearch client.
+ *
+ * @param array $mapping Mapping to drop from client.
+ */
+ private function unloadMappingArray(array $mapping)
+ {
+ foreach ($mapping as $type) {
+ $this->client->indices()->deleteMapping(
+ [
+ 'index' => $this->getIndexName(),
+ 'type' => $type,
+ ]
+ );
+ }
+ }
+
+ /**
+ * Filters out mapping from given type.
+ *
+ * @param string|array $type Types to filter from mapping.
+ * @param array $mapping Mapping array.
+ *
+ * @return array
+ */
+ private function filterMapping($type, $mapping)
+ {
+ if (empty($type)) {
+ return $mapping;
+ } elseif (is_string($type) && array_key_exists($type, $mapping)) {
+ return $mapping[$type];
+ } elseif (is_array($type)) {
+ return array_intersect_key($mapping, array_flip($type));
+ }
+
+ return [];
+ }
}
diff --git a/Command/AbstractManagerAwareCommand.php b/Command/AbstractManagerAwareCommand.php
index b28f1653..e19437a7 100644
--- a/Command/AbstractManagerAwareCommand.php
+++ b/Command/AbstractManagerAwareCommand.php
@@ -36,13 +36,13 @@ protected function configure()
}
/**
- * Returns elasticsearch manager by name with latest mappings.
+ * Returns elasticsearch manager by name from service container.
*
- * @param string $name
+ * @param string $name Manager name defined in configuration.
*
* @return Manager
*
- * @throws \RuntimeException
+ * @throws \RuntimeException If manager was not found.
*/
protected function getManager($name)
{
@@ -53,21 +53,23 @@ protected function getManager($name)
}
throw new \RuntimeException(
- sprintf('Manager named `%s` not found. Check your configuration.', $name)
+ sprintf(
+ 'Manager named `%s` not found. Available: `%s`.',
+ $name,
+ implode('`, `', array_keys($this->getContainer()->getParameter('es.managers')))
+ )
);
}
/**
- * Returns connection service id.
+ * Formats manager service id from its name.
*
- * @param string $name
+ * @param string $name Manager name.
*
- * @return string
+ * @return string Service id.
*/
private function getManagerId($name)
{
- $manager = $name == 'default' || empty($name) ? 'es.manager' : sprintf('es.manager.%s', $name);
-
- return $manager;
+ return sprintf('es.manager.%s', $name);
}
}
diff --git a/Command/IndexDropCommand.php b/Command/IndexDropCommand.php
index 1fae4d3b..27136c8e 100644
--- a/Command/IndexDropCommand.php
+++ b/Command/IndexDropCommand.php
@@ -33,7 +33,7 @@ protected function configure()
->setDescription('Drops elasticsearch index.')
->addOption(
'force',
- null,
+ 'f',
InputOption::VALUE_NONE,
'Set this parameter to execute this command'
);
@@ -54,7 +54,14 @@ protected function execute(InputInterface $input, OutputInterface $output)
)
);
} else {
- $output->writeln('Parameter --force has to be used to drop the index.');
+ $output->writeln(
+ 'ATTENTION: This action should not be used in production environment.'
+ . "\n\nOption --force has to be used to drop type(s)."
+ );
+
+ return 1;
}
+
+ return 0;
}
}
diff --git a/Command/TypeCreateCommand.php b/Command/TypeCreateCommand.php
new file mode 100644
index 00000000..1242c760
--- /dev/null
+++ b/Command/TypeCreateCommand.php
@@ -0,0 +1,84 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace ONGR\ElasticsearchBundle\Command;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Command for putting mapping into elasticsearch client.
+ */
+class TypeCreateCommand extends AbstractManagerAwareCommand
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ parent::configure();
+
+ $this
+ ->setName('es:type:create')
+ ->setDescription('Puts mappings into elasticsearch client for specific manager.')
+ ->addOption(
+ 'type',
+ 't',
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
+ 'Specific types to load.',
+ []
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $manager = $input->getOption('manager');
+ $type = $input->getOption('type');
+
+ $connection = $this->getManager($manager)->getConnection();
+ $status = $connection->createTypes($type);
+
+ switch ($status) {
+ case 0:
+ $message = sprintf(
+ 'Manager `%s` does not contain%s type(s) information.',
+ $manager,
+ empty($type) ? '' : ' `' . implode('`, `', $type) . '`'
+ );
+ break;
+ case 1:
+ $message = sprintf(
+ 'Created `%s` type(s) for manager named '
+ . '`%s`.',
+ empty($type) ? 'all' : implode('`, `', $type),
+ $manager
+ );
+ break;
+ case -1:
+ $message = sprintf(
+ 'ATTENTION: type(s) already loaded into `%s` manager.',
+ $manager
+ );
+ break;
+ default:
+ $message = 'Message not found.';
+ break;
+ }
+
+ $output->writeln($message);
+
+ return 0;
+ }
+}
diff --git a/Command/TypeDropCommand.php b/Command/TypeDropCommand.php
new file mode 100644
index 00000000..29596d5a
--- /dev/null
+++ b/Command/TypeDropCommand.php
@@ -0,0 +1,88 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace ONGR\ElasticsearchBundle\Command;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Elasticsearch command used for dropping types.
+ */
+class TypeDropCommand extends AbstractManagerAwareCommand
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ parent::configure();
+
+ $this
+ ->setName('es:type:drop')
+ ->setDescription('Updates elasticsearch index mappings.')
+ ->addOption(
+ 'type',
+ 't',
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
+ 'Specific types to drop.',
+ []
+ )
+ ->addOption(
+ 'force',
+ 'f',
+ InputOption::VALUE_NONE,
+ 'Set this parameter to execute this command'
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ if (!$input->getOption('force')) {
+ $output->writeln(
+ 'ATTENTION: This action should not be used in production environment.'
+ . "\n\nOption --force has to be used to drop type(s)."
+ );
+
+ return 1;
+ }
+
+ $manager = $input->getOption('manager');
+ $type = $input->getOption('type');
+
+ $connection = $this->getManager($manager)->getConnection();
+ $status = $connection->dropTypes($type);
+
+ if ($status) {
+ $message = sprintf(
+ 'Dropped `%s` type(s) for manager named '
+ . '`%s`.',
+ empty($type) ? 'all' : implode('`, `', $type),
+ $manager
+ );
+ } else {
+ $message = sprintf(
+ 'Manager `%s` does not contain%s type(s) information.',
+ $manager,
+ empty($type) ? '' : ' `'
+ . implode('`, `', $type) . '`'
+ );
+ }
+
+ $output->writeln($message);
+
+ return 0;
+ }
+}
diff --git a/Command/TypeUpdateCommand.php b/Command/TypeUpdateCommand.php
index d3e1ec35..35281e6c 100644
--- a/Command/TypeUpdateCommand.php
+++ b/Command/TypeUpdateCommand.php
@@ -34,15 +34,16 @@ protected function configure()
->setDescription('Updates elasticsearch index mappings.')
->addOption(
'force',
- null,
+ 'f',
InputOption::VALUE_NONE,
'Set this parameter to execute this command'
)
->addOption(
'type',
- null,
- InputOption::VALUE_REQUIRED,
- 'Set this parameter to update only a specific type'
+ 't',
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
+ 'Set this parameter to update only a specific types',
+ []
);
}
@@ -52,68 +53,55 @@ protected function configure()
protected function execute(InputInterface $input, OutputInterface $output)
{
if (!$input->getOption('force')) {
- $output->writeln('Option --force has to be used to update mapping.');
+ $output->writeln(
+ 'ATTENTION: This action should not be used in production environment.'
+ . "\n\nOption --force has to be used to drop type(s)."
+ );
return 1;
}
- try {
- $manager = $this->clearMappingCache($input->getOption('manager'), $input->getOption('type'));
- } catch (\UnderflowException $ex) {
- $output->writeln("Undefined document type {$input->getOption('type')}");
-
- return 2;
+ $managerName = $input->getOption('manager');
+ $types = $input->getOption('type');
+
+ $result = $this
+ ->getManager($input->getOption('manager'))
+ ->getConnection()
+ ->updateTypes($types);
+
+ $typesOutput = empty($types) ? 'all' : implode('`, `', $types);
+
+ switch($result) {
+ case 1:
+ $message = sprintf(
+ '`%s` type(s) have been updated for manager'
+ . ' named `%s`.',
+ $typesOutput,
+ $managerName
+ );
+ break;
+ case 0:
+ $message = sprintf(
+ '`%s` type(s) are already up to date for manager'
+ . ' named `%s`.',
+ $typesOutput,
+ $managerName
+ );
+ break;
+ case -1:
+ $message = sprintf(
+ 'No mapping was found%s in `%s` manager.',
+ empty($types) ? '' : sprintf(' for `%s` types', $typesOutput),
+ $managerName
+ );
+ break;
+ default:
+ $message = 'Message not found.';
+ break;
}
- $result = $manager->getConnection()->updateMapping();
- if ($result === true) {
- $output->writeln('Types updated.');
- } elseif ($result === false) {
- $output->writeln('Types are already up to date.');
- } else {
- throw new \UnexpectedValueException('Expected boolean value from Connection::updateMapping()');
- }
+ $output->writeln($message);
return 0;
}
-
- /**
- * Updates mapping information for the given manager.
- *
- * @param string $managerName
- * @param string $type
- *
- * @return Manager
- * @throws \UnderflowException
- */
- protected function clearMappingCache($managerName, $type = '')
- {
- $manager = $this->getManager($managerName);
- /** @var MetadataCollector $collector */
- $collector = $this->getContainer()->get('es.metadata_collector');
-
- $mappings = [];
-
- foreach ($this->getContainer()->getParameter('es.managers') as $name => $setting) {
- if (!$managerName || $managerName == 'default' || $name == $managerName) {
- foreach ($setting['mappings'] as $bundle) {
- $mappings = array_replace_recursive(
- $mappings,
- $collector->getClientMapping($bundle, true)
- );
- }
- }
- }
-
- if (!empty($type)) {
- if (!isset($mappings[$type])) {
- throw new \UnderflowException("Document type {$type} not found");
- }
- $manager->getConnection()->setMapping($type, $mappings[$type]);
- } else {
- $manager->getConnection()->forceMapping($mappings);
- }
-
- return $manager;
- }
}
diff --git a/Test/AbstractElasticsearchTestCase.php b/Test/AbstractElasticsearchTestCase.php
index 9b6a186f..a7dd0c0c 100644
--- a/Test/AbstractElasticsearchTestCase.php
+++ b/Test/AbstractElasticsearchTestCase.php
@@ -174,6 +174,8 @@ protected function populateElasticsearchWithData($manager, array $data)
*/
protected function tearDown()
{
+ parent::tearDown();
+
foreach ($this->managers as $name => $manager) {
try {
$manager->getConnection()->dropIndex();
@@ -184,16 +186,20 @@ protected function tearDown()
}
/**
+ * Returns service container.
+ *
+ * @param bool $reinitialize Force kernel reinitialization.
+ * @param array $kernelOptions Options used passed to kernel if it needs to be initialized.
+ *
* @return ContainerInterface
*/
- protected function getContainer()
+ protected function getContainer($reinitialize = false, $kernelOptions = [])
{
- if ($this->container) {
- return $this->container;
+ if (!$this->container || $reinitialize) {
+ static::bootKernel($kernelOptions);
+ $this->container = static::$kernel->getContainer();
}
- $this->container = self::createClient()->getContainer();
-
return $this->container;
}
@@ -236,7 +242,10 @@ protected function getManager($name = 'default', $createIndex = true, array $cus
}
// Drops and creates index.
- $createIndex && $connection->dropAndCreateIndex();
+ if ($createIndex) {
+ $connection->dropAndCreateIndex();
+ $connection->createTypes();
+ }
// Populates elasticsearch index with data.
$data = $this->getDataArray();
diff --git a/Tests/Functional/Client/ConnectionTest.php b/Tests/Functional/Client/ConnectionTest.php
index 2606feff..663346a3 100644
--- a/Tests/Functional/Client/ConnectionTest.php
+++ b/Tests/Functional/Client/ConnectionTest.php
@@ -26,29 +26,17 @@ public function testUpdateMapping()
$manager = $this->getManager('bar');
$connection = $manager->getConnection();
- // Using phpunit setExpectedException does not continue after exception is thrown.
- $thrown = false;
- try {
- $connection->updateMapping();
- } catch (\LogicException $e) {
- $thrown = true;
- // Continue.
- }
- $this->assertTrue($thrown, '\LogicException should be thrown');
+ $this->assertEquals(-1, $connection->updateMapping(), 'Connection does not have any mapping loaded.');
$connection = $this->getManager(
'bar',
false,
$this->getTestMapping()
)->getConnection();
-
- $status = $connection->updateMapping();
- $this->assertTrue($status, 'Mapping should be updated');
+ $this->assertEquals(1, $connection->updateMapping(), 'Mapping should be updated');
$connection->forceMapping($this->getTestLessMapping());
-
- $status = $connection->updateMapping();
- $this->assertTrue($status, 'Mapping should be updated');
+ $this->assertEquals(1, $connection->updateMapping(), 'Mapping should be updated');
$clientMapping = $connection->getClient()->indices()->getMapping(
[
diff --git a/Tests/Functional/Command/TypeCreateCommandTest.php b/Tests/Functional/Command/TypeCreateCommandTest.php
new file mode 100644
index 00000000..99f56d94
--- /dev/null
+++ b/Tests/Functional/Command/TypeCreateCommandTest.php
@@ -0,0 +1,187 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace ONGR\ElasticsearchBundle\Tests\Functional\Command;
+
+use ONGR\ElasticsearchBundle\Command\TypeCreateCommand;
+use ONGR\ElasticsearchBundle\Test\AbstractElasticsearchTestCase;
+use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Tester\CommandTester;
+
+/**
+ * Tests type create command execution.
+ */
+class TypeCreateCommandTest extends AbstractElasticsearchTestCase
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp()
+ {
+ // Do nothing.
+ }
+
+ /**
+ * Data provider for testing type create command.
+ *
+ * @return array
+ */
+ public function getTestExecuteData()
+ {
+ return [
+ [
+ [],
+ "Created `all` type(s) for manager named `default`.\n",
+ ],
+ [
+ ['--type' => ['product', 'content']],
+ "Created `product`, `content` type(s) for manager named `default`.\n",
+ ],
+ [
+ ['--manager' => 'bar'],
+ "Manager `bar` does not contain type(s) information.\n",
+ false,
+ ],
+ [
+ ['--manager' => 'bar', '--type' => ['category']],
+ "Manager `bar` does not contain `category` type(s) information.\n",
+ false,
+ ],
+ ];
+ }
+
+ /**
+ * Test execute method on type create command.
+ *
+ * @param array $arguments Arguments pass to command.
+ * @param string $message Output message to assert.
+ * @param bool $checkMapping Set to false if mapping should not be checked.
+ *
+ * @dataProvider getTestExecuteData
+ */
+ public function testExecute($arguments, $message, $checkMapping = true)
+ {
+ $manager = array_key_exists('--manager', $arguments) ? $arguments['--manager'] : 'default';
+ $connection = $this
+ ->getContainer()
+ ->get('es.manager.' . $manager)
+ ->getConnection();
+ $connection->dropAndCreateIndex();
+
+ $this->assertEquals($message, $this->runCreateCommand($arguments));
+
+ if ($checkMapping) {
+ $this->assertTypes(
+ array_key_exists('--type', $arguments) ? $arguments['--type'] : [],
+ $manager
+ );
+ }
+
+ $connection->dropIndex();
+ }
+
+ /**
+ * Data provider for executing type create on existing types.
+ *
+ * @return array
+ */
+ public function getTestExecuteOnExistingTypesData()
+ {
+ return [
+ [
+ ['--type' => ['product', 'comment']],
+ "Created `product`, `comment` type(s) for manager named `default`.\n",
+ ['product', 'foocontent'],
+ ],
+ [
+ ['--type' => ['product']],
+ "ATTENTION: type(s) already loaded into `default` manager.\n",
+ ['product', 'category'],
+ ],
+ [
+ [],
+ "ATTENTION: type(s) already loaded into `default` manager.\n",
+ ],
+ [
+ ['--type' => ['product', 'comment']],
+ "Created `product`, `comment` type(s) for manager named `default`.\n",
+ ['product'],
+ ],
+ ];
+ }
+
+ /**
+ * Tests executing create types command on existing types.
+ *
+ * @param array $arguments
+ * @param string $message
+ * @param array $createTypes
+ *
+ * @dataProvider getTestExecuteOnExistingTypesData
+ */
+ public function testExecuteOnExistingTypes($arguments, $message, $createTypes = [])
+ {
+ $manager = array_key_exists('--manager', $arguments) ? $arguments['--manager'] : 'default';
+ $connection = $this
+ ->getContainer()
+ ->get('es.manager.' . $manager)
+ ->getConnection();
+ $connection->dropAndCreateIndex();
+ $connection->createTypes($createTypes);
+
+ $this->assertEquals($message, $this->runCreateCommand($arguments));
+
+ $connection->dropIndex();
+ }
+
+ /**
+ * Checks if types was created on client.
+ *
+ * @param array $types Types to check.
+ * @param string $manager Manager name.
+ */
+ private function assertTypes($types, $manager = 'default')
+ {
+ $this->assertNotEmpty(
+ $this->getManager($manager, false)->getConnection()->getMappingFromIndex($types),
+ 'Mapping should be created.'
+ );
+ }
+
+ /**
+ * Runs type create command.
+ *
+ * @param array $arguments Arguments or options pass to command.
+ *
+ * @return string Command output.
+ */
+ private function runCreateCommand(array $arguments = [])
+ {
+ $app = new Application();
+ $command = new TypeCreateCommand();
+ $command->setContainer($this->getContainer());
+ $app->add($command);
+ $cmd = $app->find('es:type:create');
+ $tester = new CommandTester($cmd);
+ $tester->execute(
+ array_filter(
+ array_replace(
+ [
+ 'command' => $cmd->getName(),
+ ],
+ $arguments
+ )
+ )
+ );
+
+ return $tester->getDisplay();
+ }
+}
diff --git a/Tests/Functional/Command/TypeDropCommandTest.php b/Tests/Functional/Command/TypeDropCommandTest.php
new file mode 100644
index 00000000..e4f6cd26
--- /dev/null
+++ b/Tests/Functional/Command/TypeDropCommandTest.php
@@ -0,0 +1,123 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace ONGR\ElasticsearchBundle\Tests\Functional\Command;
+
+use ONGR\ElasticsearchBundle\Command\TypeDropCommand;
+use ONGR\ElasticsearchBundle\Test\AbstractElasticsearchTestCase;
+use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Tester\CommandTester;
+
+/**
+ * Tests type drop command execution.
+ */
+class TypeDropCommandTest extends AbstractElasticsearchTestCase
+{
+ /**
+ * Data provider for test execute method.
+ *
+ * @return array
+ */
+ public function getTestExecuteData()
+ {
+ return [
+ [
+ [],
+ "Dropped `all` type(s) for manager named `default`.\n",
+ ],
+ [
+ ['--force' => false],
+ 'ATTENTION: This action should not be used in production environment.'
+ . "\n\nOption --force has to be used to drop type(s).\n",
+ false,
+ ],
+ [
+ ['--type' => ['product', 'category']],
+ "Dropped `product`, `category` type(s) for manager named `default`.\n",
+ ],
+ [
+ ['--type' => ['content'], '--manager' => 'bar'],
+ "Manager `bar` does not contain `content` type(s) information.\n",
+ false,
+ ],
+ [
+ ['--manager' => 'bar'],
+ "Manager `bar` does not contain type(s) information.\n",
+ false,
+ ],
+ ];
+ }
+
+ /**
+ * Tests command execute method.
+ *
+ * @param array $arguments Arguments pass to command.
+ * @param string $message Message outputed by command.
+ * @param bool $checkMapping Set to false if mapping should not be checked.
+ *
+ * @dataProvider getTestExecuteData
+ */
+ public function testExecute($arguments, $message, $checkMapping = true)
+ {
+ $this->assertEquals($this->runDropCommand($arguments), $message);
+
+ if ($checkMapping) {
+ $this->assertTypes(
+ array_key_exists('--type', $arguments) ? $arguments['--type'] : [],
+ array_key_exists('--manager', $arguments) ? $arguments['--manager'] : 'default'
+ );
+ }
+ }
+
+ /**
+ * Checks if types was created on client.
+ *
+ * @param array $types Types to check.
+ * @param string $manager Manager name.
+ */
+ private function assertTypes($types, $manager = 'default')
+ {
+ $this->assertEmpty(
+ $this->getManager($manager, false)->getConnection()->getMappingFromIndex($types),
+ 'Mappings should be deleted.'
+ );
+ }
+
+ /**
+ * Runs type drop command.
+ *
+ * @param array $arguments Arguments / options pass to command.
+ *
+ * @return string Command output.
+ */
+ private function runDropCommand($arguments)
+ {
+ $app = new Application();
+ $command = new TypeDropCommand();
+ $command->setContainer($this->getContainer());
+ $app->add($command);
+ $cmd = $app->find('es:type:drop');
+ $tester = new CommandTester($cmd);
+ $tester->execute(
+ array_filter(
+ array_replace(
+ [
+ 'command' => $cmd->getName(),
+ '--force' => true,
+ ],
+ $arguments
+ )
+ )
+ );
+
+ return $tester->getDisplay();
+ }
+}
diff --git a/Tests/Functional/Command/TypeUpdateCommandTest.php b/Tests/Functional/Command/TypeUpdateCommandTest.php
index 16128a9f..fb9585ed 100644
--- a/Tests/Functional/Command/TypeUpdateCommandTest.php
+++ b/Tests/Functional/Command/TypeUpdateCommandTest.php
@@ -11,61 +11,36 @@
namespace ONGR\ElasticsearchBundle\Tests\Functional\Command;
-use ONGR\ElasticsearchBundle\Command\IndexCreateCommand;
use ONGR\ElasticsearchBundle\Command\TypeUpdateCommand;
-use ONGR\ElasticsearchBundle\ORM\Manager;
-use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
-use Symfony\Component\Config\Definition\Exception\Exception;
+use ONGR\ElasticsearchBundle\Test\AbstractElasticsearchTestCase;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
-use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Functional tests for type update command.
*/
-class TypeUpdateCommandTest extends WebTestCase
+class TypeUpdateCommandTest extends AbstractElasticsearchTestCase
{
/**
* @var string
*/
private $documentDir;
- /**
- * @var Manager
- */
- private $manager;
-
- /**
- * @var Application
- */
- private $app;
-
/**
* @var string
*/
private $file;
- /**
- * @var ContainerInterface
- */
- private $container;
-
/**
* {@inheritdoc}
*/
public function setUp()
{
- // Only a single instance of container should be used on all commands and throughout the test.
- $this->container = self::createClient()->getContainer();
- $this->manager = $this->container->get('es.manager');
+ parent::setUp();
// Set up custom document to test mapping with.
- $this->documentDir = $this->container->get('kernel')->locateResource('@AcmeTestBundle/Document/');
+ $this->documentDir = $this->getContainer()->get('kernel')->locateResource('@AcmeTestBundle/Document/');
$this->file = $this->documentDir . 'Article.php';
-
- // Create index for testing.
- $this->app = new Application();
- $this->createIndexCommand();
}
/**
@@ -74,10 +49,15 @@ public function setUp()
public function testExecute()
{
$this->assertMappingNotSet("Article mapping shouldn't be defined yet.");
-
copy($this->documentDir . 'documentSample.txt', $this->file);
- $this->runUpdateCommand();
+ // Reinitialize container.
+ $this->getContainer(true, ['environment' => 'test2']);
+
+ $this->assertEquals(
+ $this->runUpdateCommand(),
+ "`all` type(s) have been updated for manager named `default`.\n"
+ );
$this->assertMappingSet('Article mapping should be defined after update.');
}
@@ -87,13 +67,21 @@ public function testExecute()
public function testExecuteType()
{
$this->assertMappingNotSet("Article mapping shouldn't be defined yet.");
-
copy($this->documentDir . 'documentSample.txt', $this->file);
- $this->runUpdateCommand('product');
+ // Reinitialize container.
+ $this->getContainer(true, ['environment' => 'test2']);
+
+ $this->assertEquals(
+ $this->runUpdateCommand(['--type' => ['product']]),
+ "`product` type(s) are already up to date for manager named `default`.\n"
+ );
$this->assertMappingNotSet("Article mapping shouldn't be defined, type selected was `product`.");
- $this->runUpdateCommand('article');
+ $this->assertEquals(
+ $this->runUpdateCommand(['--type' => ['article']]),
+ "`article` type(s) have been updated for manager named `default`.\n"
+ );
$this->assertMappingSet('Article mapping should be defined after update, type selected was `article`.');
}
@@ -102,19 +90,64 @@ public function testExecuteType()
*/
public function testExecuteUpdated()
{
- $this->assertStringStartsWith('Types are already up to date.', $this->runUpdateCommand());
+ $this->assertEquals(
+ "`all` type(s) are already up to date for manager named `default`.\n",
+ $this->runUpdateCommand()
+ );
$this->assertMappingNotSet("Article was never added, type shouldn't be added.");
}
+ /**
+ * Data provider for testing update command with manager w/o mapping.
+ *
+ * @return array
+ */
+ public function getTestUpdateCommandMessagesData()
+ {
+ return [
+ [
+ ['--manager' => 'bar'],
+ "No mapping was found in `bar` manager.\n",
+ ],
+ [
+ ['--manager' => 'bar', '--type' => ['product']],
+ "No mapping was found for `product` types in `bar` manager.\n",
+ ],
+ [
+ ['--force' => false],
+ 'ATTENTION: This action should not be used in production environment.'
+ . "\n\nOption --force has to be used to drop type(s).\n",
+ ],
+ ];
+ }
+
+ /**
+ * Tests update command output.
+ *
+ * @param array $arguments Arguments to pass to command.
+ * @param string $message Output message.
+ *
+ * @dataProvider getTestUpdateCommandMessagesData
+ */
+ public function testUpdateCommandMessages($arguments, $message)
+ {
+ $this->assertEquals($this->runUpdateCommand($arguments), $message);
+ }
+
/**
* Asserts mapping is set and correct.
*
* @param string $message
*/
- protected function assertMappingSet($message)
+ private function assertMappingSet($message)
{
- $mapping = $this->manager->getConnection()->getMapping('article');
- $this->assertNotNull($mapping, $message);
+ $mapping = $this
+ ->getContainer()
+ ->get('es.manager.default')
+ ->getConnection()
+ ->getMappingFromIndex('article');
+
+ $this->assertNotEmpty($mapping, $message);
$expectedMapping = [
'properties' => [
'title' => ['type' => 'string'],
@@ -126,73 +159,55 @@ protected function assertMappingSet($message)
/**
* Asserts mapping isn't set.
*
- * @param string $message
+ * @param string $message Assert message.
*/
- protected function assertMappingNotSet($message)
+ private function assertMappingNotSet($message)
{
- $this->assertNull($this->manager->getConnection()->getMapping('article'), $message);
+ $this->assertEmpty(
+ $this->getContainer()->get('es.manager.default')->getConnection()->getMappingFromIndex('article'),
+ $message
+ );
}
/**
* Runs update command.
*
- * @param string $type
+ * @param array $arguments Arguments/options to pass to command.
*
- * @return string
+ * @return string Command display.
*/
- protected function runUpdateCommand($type = '')
+ private function runUpdateCommand($arguments = [])
{
$command = new TypeUpdateCommand();
- $command->setContainer($this->container);
+ $command->setContainer($this->getContainer());
+
+ $app = new Application();
+ $app->add($command);
- $this->app->add($command);
- $commandToTest = $this->app->find('es:type:update');
+ $commandToTest = $app->find('es:type:update');
$commandTester = new CommandTester($commandToTest);
$result = $commandTester->execute(
- [
- 'command' => $commandToTest->getName(),
- '--force' => true,
- '--type' => $type,
- ]
+ array_filter(
+ array_replace(
+ [
+ 'command' => $commandToTest->getName(),
+ '--force' => true,
+ ],
+ $arguments
+ )
+ )
);
- $this->assertEquals(0, $result, "Mapping update wasn't executed successfully.");
-
return $commandTester->getDisplay();
}
- /**
- * Creates index for testing.
- *
- * @param string $manager
- */
- protected function createIndexCommand($manager = 'default')
- {
- $command = new IndexCreateCommand();
- $command->setContainer($this->container);
-
- $this->app->add($command);
- $command = $this->app->find('es:index:create');
- $commandTester = new CommandTester($command);
- $commandTester->execute(
- [
- 'command' => $command->getName(),
- '--manager' => $manager,
- ]
- );
- }
-
/**
* {@inheritdoc}
*/
public function tearDown()
{
- try {
- $this->manager->getConnection()->dropIndex();
- } catch (Exception $ex) {
- // Index wasn't actually created.
- }
+ parent::tearDown();
@unlink($this->file);
}
}
diff --git a/Tests/Functional/DataCollector/ElasticsearchDataCollectorTest.php b/Tests/Functional/DataCollector/ElasticsearchDataCollectorTest.php
index da8e8904..0de81108 100644
--- a/Tests/Functional/DataCollector/ElasticsearchDataCollectorTest.php
+++ b/Tests/Functional/DataCollector/ElasticsearchDataCollectorTest.php
@@ -20,8 +20,6 @@
class ElasticsearchDataCollectorTest extends ElasticsearchTestCase
{
- const START_QUERY_COUNT = 8;
-
/**
* {@inheritdoc}
*/
@@ -62,7 +60,7 @@ public function testGetQueryCount()
$manager->commit();
// Four queries executed while index was being created.
- $this->assertEquals(4, $this->getCollector()->getQueryCount() - self::START_QUERY_COUNT);
+ $this->greaterThanOrEqual(4, $this->getCollector()->getQueryCount());
}
/**
diff --git a/Tests/Unit/Client/ConnectionTest.php b/Tests/Unit/Client/ConnectionTest.php
index 9ec1601a..9193e1b2 100644
--- a/Tests/Unit/Client/ConnectionTest.php
+++ b/Tests/Unit/Client/ConnectionTest.php
@@ -23,13 +23,6 @@ public function testGetters()
{
$config = [
'index' => 'index_name',
- 'body' => [
- 'mappings' => [
- 'test_mapping' => [
- 'properties' => [],
- ],
- ],
- ],
];
$connection = new Connection($this->getClient(), $config);
@@ -39,12 +32,20 @@ public function testGetters()
$connection->getIndexName(),
'Recieved wrong index name'
);
+
$this->assertNull(
+ $connection->getMapping(),
+ 'should return null because no mapping is loaded into connection'
+ );
+
+ $connection->setMapping('test_mapping', ['properties' => []]);
+
+ $this->assertEmpty(
$connection->getMapping('product'),
- 'should not contain product mapping'
+ 'should not contain product mapping and return empty array'
);
- $this->assertArrayHasKey(
- 'properties',
+
+ $this->assertNotEmpty(
$connection->getMapping('test_mapping'),
'should contain test mapping'
);
diff --git a/Tests/Unit/Command/UpdateTypeCommandTest.php b/Tests/Unit/Command/UpdateTypeCommandTest.php
deleted file mode 100644
index 06ba0003..00000000
--- a/Tests/Unit/Command/UpdateTypeCommandTest.php
+++ /dev/null
@@ -1,111 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace ONGR\ElasticsearchBundle\Tests\Unit\Command;
-
-use ONGR\ElasticsearchBundle\Command\TypeUpdateCommand;
-use Symfony\Component\Console\Application;
-use Symfony\Component\Console\Tester\CommandTester;
-use Symfony\Component\DependencyInjection\Container;
-
-/**
- * Unit tests for UpdateTypeCommand.
- */
-class UpdateTypeCommandTest extends \PHPUnit_Framework_TestCase
-{
- /**
- * Check if exception is thrown when manager return unknown result.
- *
- * @expectedException \UnexpectedValueException
- * @expectedExceptionMessage Expected boolean value from Connection::updateMapping()
- */
- public function testExecuteUnexpectedValue()
- {
- $managerMock = $this
- ->getMockBuilder('ONGR\ElasticsearchBundle\ORM\Manager')
- ->disableOriginalConstructor()
- ->setMethods(['getConnection', 'updateMapping'])
- ->getMock();
-
- $managerMock->expects($this->once())->method('getConnection')->willReturnSelf();
- $managerMock->expects($this->once())->method('updateMapping')->willReturn(3);
-
- /** @var TypeUpdateCommand|\PHPUnit_Framework_MockObject_MockObject $command */
- $command = $this->getMockBuilder('ONGR\ElasticsearchBundle\Command\TypeUpdateCommand')
- ->setMethods(['clearMappingCache'])
- ->getMock();
- $command->expects($this->once())->method('clearMappingCache')->willReturn($managerMock);
-
- $app = new Application();
- $app->add($command);
-
- $commandToTest = $app->find('es:type:update');
- $commandTester = new CommandTester($commandToTest);
- $commandTester->execute(
- [
- 'command' => $command->getName(),
- '--force' => true,
- ]
- );
- }
-
- /**
- * Check if correct value is returned when force flag isn't set.
- */
- public function testForceDisabled()
- {
- $command = new TypeUpdateCommand();
- $app = new Application();
- $app->add($command);
-
- $commandToTest = $app->find('es:type:update');
- $commandTester = new CommandTester($commandToTest);
- $result = $commandTester->execute(
- [
- 'command' => $command->getName(),
- ]
- );
-
- $this->assertEquals(1, $result);
- }
-
- /**
- * Check if correct value is returned when undefined type is specified.
- */
- public function testUndefinedType()
- {
- /** @var Container|\PHPUnit_Framework_MockObject_MockObject $containerMock */
- $containerMock = $this->getMockBuilder('Symfony\Component\DependencyInjection\Container')
- ->setMethods(['getParameter', 'get', 'has'])
- ->disableOriginalConstructor()
- ->getMock();
- $containerMock->expects($this->any())->method('getParameter')->willReturn([]);
- $containerMock->expects($this->any())->method('get')->willReturnSelf();
- $containerMock->expects($this->any())->method('has')->will($this->returnValue(true));
- $command = new TypeUpdateCommand();
- $command->setContainer($containerMock);
-
- $app = new Application();
- $app->add($command);
-
- $commandToTest = $app->find('es:type:update');
- $commandTester = new CommandTester($commandToTest);
- $result = $commandTester->execute(
- [
- 'command' => $command->getName(),
- '--force' => true,
- '--type' => 'unkown',
- ]
- );
-
- $this->assertEquals(2, $result);
- }
-}
diff --git a/Tests/Unit/Test/ElasticsearchTestCaseDummy.php b/Tests/Unit/Test/ElasticsearchTestCaseDummy.php
index 41c6a863..3e674014 100644
--- a/Tests/Unit/Test/ElasticsearchTestCaseDummy.php
+++ b/Tests/Unit/Test/ElasticsearchTestCaseDummy.php
@@ -21,8 +21,8 @@ class ElasticsearchTestCaseDummy extends ElasticsearchTestCase
/**
* {@inheritdoc}
*/
- public function getContainer()
+ protected function getContainer($rebuild = false, $kernelOptions = [])
{
- return parent::getContainer();
+ return parent::getContainer($rebuild, $kernelOptions);
}
}
diff --git a/Tests/app/config/config_test2.yml b/Tests/app/config/config_test2.yml
new file mode 100644
index 00000000..3cc6d1aa
--- /dev/null
+++ b/Tests/app/config/config_test2.yml
@@ -0,0 +1,2 @@
+imports:
+ - { resource: config_test.yml }
diff --git a/Tests/app/fixture/Acme/TestBundle/Document/documentSample.txt b/Tests/app/fixture/Acme/TestBundle/Document/documentSample.txt
index 8f4fdc53..c107ef5f 100644
--- a/Tests/app/fixture/Acme/TestBundle/Document/documentSample.txt
+++ b/Tests/app/fixture/Acme/TestBundle/Document/documentSample.txt
@@ -3,16 +3,13 @@
namespace ONGR\ElasticsearchBundle\Tests\app\fixture\Acme\TestBundle\Document;
use ONGR\ElasticsearchBundle\Annotation as ES;
-use ONGR\ElasticsearchBundle\Document\DocumentInterface;
-use ONGR\ElasticsearchBundle\Document\DocumentTrait;
+use ONGR\ElasticsearchBundle\Document\AbstractDocument;
/**
* @ES\Document
*/
-class Article implements DocumentInterface
+class Article extends AbstractDocument
{
- use DocumentTrait;
-
/**
* @var string
*