diff --git a/Client.php b/Client.php
index 7719095..679317c 100644
--- a/Client.php
+++ b/Client.php
@@ -32,4 +32,9 @@ class Client extends ElasticaClient
return $response;
}
+
+ public function getIndex($name)
+ {
+ return new DynamicIndex($this, $name);
+ }
}
diff --git a/Command/PopulateCommand.php b/Command/PopulateCommand.php
index a67df94..98834c7 100644
--- a/Command/PopulateCommand.php
+++ b/Command/PopulateCommand.php
@@ -126,6 +126,7 @@ class PopulateCommand extends ContainerAwareCommand
}
$output->writeln(sprintf('Refreshing %s', $index));
+ $this->resetter->postPopulate($index);
$this->indexManager->getIndex($index)->refresh();
}
diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
index 0f868af..e72d85c 100644
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -140,6 +140,7 @@ class Configuration implements ConfigurationInterface
->prototype('array')
->children()
->scalarNode('index_name')->end()
+ ->booleanNode('use_alias')->defaultValue(false)->end()
->scalarNode('client')->end()
->scalarNode('finder')
->treatNullLike(true)
diff --git a/DependencyInjection/FOSElasticaExtension.php b/DependencyInjection/FOSElasticaExtension.php
index 41c8c1f..553af14 100644
--- a/DependencyInjection/FOSElasticaExtension.php
+++ b/DependencyInjection/FOSElasticaExtension.php
@@ -125,6 +125,7 @@ class FOSElasticaExtension extends Extension
$indexIds[$name] = $indexId;
$this->indexConfigs[$name] = array(
'index' => new Reference($indexId),
+ 'name_or_alias' => $indexName,
'config' => array(
'mappings' => array()
)
@@ -135,6 +136,10 @@ class FOSElasticaExtension extends Extension
if (!empty($index['settings'])) {
$this->indexConfigs[$name]['config']['settings'] = $index['settings'];
}
+ if ($index['use_alias']) {
+ $this->indexConfigs[$name]['use_alias'] = true;
+ }
+
$this->loadTypes(isset($index['types']) ? $index['types'] : array(), $container, $name, $indexId, $typePrototypeConfig);
}
diff --git a/DynamicIndex.php b/DynamicIndex.php
new file mode 100644
index 0000000..cbec8e9
--- /dev/null
+++ b/DynamicIndex.php
@@ -0,0 +1,28 @@
+
+ */
+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;
+ }
+}
diff --git a/Resetter.php b/Resetter.php
index 863524b..c7f4638 100644
--- a/Resetter.php
+++ b/Resetter.php
@@ -2,6 +2,8 @@
namespace FOS\ElasticaBundle;
+use Elastica\Exception\ExceptionInterface;
+use Elastica\Index;
use Elastica\Exception\ResponseException;
use Elastica\Type\Mapping;
@@ -27,8 +29,8 @@ class Resetter
*/
public function resetAllIndexes()
{
- foreach ($this->indexConfigsByName as $indexConfig) {
- $indexConfig['index']->create($indexConfig['config'], true);
+ foreach (array_keys($this->indexConfigsByName) as $name) {
+ $this->resetIndex($name);
}
}
@@ -41,7 +43,17 @@ class Resetter
public function resetIndex($indexName)
{
$indexConfig = $this->getIndexConfig($indexName);
- $indexConfig['index']->create($indexConfig['config'], true);
+ $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);
}
/**
@@ -112,4 +124,122 @@ class Resetter
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;
+ }
}
diff --git a/Resources/config/config.xml b/Resources/config/config.xml
index 85cfbed..4419b4a 100644
--- a/Resources/config/config.xml
+++ b/Resources/config/config.xml
@@ -6,7 +6,7 @@
FOS\ElasticaBundle\Client
- Elastica\Index
+ FOS\ElasticaBundle\DynamicIndex
Elastica\Type
FOS\ElasticaBundle\Logger\ElasticaLogger
FOS\ElasticaBundle\DataCollector\ElasticaDataCollector