Skip to content

Commit

Permalink
started work on Migration support for elasticsearch
Browse files Browse the repository at this point in the history
issue #6712
  • Loading branch information
cebe committed Jan 24, 2015
1 parent 7c609ad commit b9a8c22
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 20 deletions.
7 changes: 3 additions & 4 deletions extensions/elasticsearch/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -237,15 +237,14 @@ public function update($index, $type, $id, $data, $options = [])

/**
* creates an index
* @param $index
* @param array $configuration
* @return mixed
* @param string $index index name.
* @param array $configuration index configuration.
* @return mixed the request result.
* @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html
*/
public function createIndex($index, $configuration = null)
{
$body = $configuration !== null ? Json::encode($configuration) : null;

return $this->db->put([$index], [], $body);
}

Expand Down
198 changes: 198 additions & 0 deletions extensions/elasticsearch/MigrateController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/

namespace yii\elasticsearch;

use Yii;
use yii\console\controllers\BaseMigrateController;
use yii\console\Exception;
use yii\di\Instance;
use yii\helpers\ArrayHelper;
use yii\helpers\Console;

/**
* Manages application migrations using Elasticsearch.
*
* This is an analog of [[\yii\console\controllers\MigrateController]] but for Elasticsearch.
*
* This command provides support for tracking the migration history, upgrading
* or downloading with migrations, and creating new migration skeletons.
*
* The migration history is stored in a Elasticsearch index/type specified by [[index]] and [[type]].
* This index will be automatically created the first time this command is executed, if it does not exist.
*
* In order to enable this command you should adjust the configuration of your console application:
*
* ~~~
* return [
* // ...
* 'controllerMap' => [
* 'es-migrate' => 'yii\elasticsearch\MigrateController',
* 'index' => 'myindex', // set this to the index you want to use to store migration state
* 'type' => 'migrations',
* ],
* ];
* ~~~
*
* Below are some common usages of this command:
*
* ~~~
* # creates a new migration named 'create_user_index'
* yii es-migrate/create create_user_index
*
* # applies ALL new migrations
* yii es-migrate
*
* # reverts the last applied migration
* yii es-migrate/down
* ~~~
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0.3
*/
class MigrateController extends BaseMigrateController
{
/**
* @var Connection|array|string the DB connection object or the application component ID of the DB connection.
* Starting from version 2.0.2, this can also be a configuration array for creating the object.
*/
public $db = 'elasticsearch';
/**
* @var string the name of the index that is used to store migration state.
* Defaults to `yii2`.
* @see type
*/
public $index = 'yii2';
/**
* @var string the name of the type that is used to store migration state.
* Defaults to `migrations`.
* @see index
*/
public $type = 'migrations';
/**
* @inheritdoc
*/
public $templateFile = '@yii/mongodb/views/migration.php'; //TODO


/**
* @inheritdoc
*/
public function options($actionID)
{
return array_merge(
parent::options($actionID),
['index', 'type', 'db'] // global for all actions
);
}

/**
* @inheritdoc
*/
public function beforeAction($action)
{
if (parent::beforeAction($action)) {
if ($action->id !== 'create') {
$this->db = Instance::ensure($this->db, Connection::className());
}
return true;
} else {
return false;
}
}

/**
* Creates a new migration instance.
* @param string $class the migration class name
* @return \yii\elasticsearch\Migration the migration instance
*/
protected function createMigration($class)
{
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php';
require_once($file);

return new $class(['db' => $this->db]);
}

/**
* @inheritdoc
*/
protected function getMigrationHistory($limit)
{
$this->ensureBaseMigrationHistory();

$query = new Query;
$rows = $query->source(['version', 'apply_time'])
->from($this->index, $this->type)
->orderBy(['version' => SORT_DESC])
->limit($limit === null ? 10000 : $limit)
->all($this->db);
$history = ArrayHelper::map(
$rows,
function($row) { return $row['_source']['version']; },
function($row) { return $row['_source']['apply_time']; }
);
unset($history[self::BASE_MIGRATION]);
return $history;
}

/**
* Ensures migration history contains at least base migration entry.
*/
protected function ensureBaseMigrationHistory()
{
$command = $this->db->createCommand();
if (!$command->typeExists($this->index, $this->type)) {
$this->stdout("Creating migration history index and type \"{$this->index}/{$this->type}\"...");

if (!$command->indexExists($this->index)) {
$command->createIndex($this->index);
}
$command->setMapping($this->index, $this->type, [
$this->migrationTable => [
"_id" => [
"index" => "not_analyzed",
"store" => "yes",
"path" => "version"
],
"properties" => [
"version" => ["type" => "string", "index" => "not_analyzed"],
"apply_time" => ["type" => "integer"],
],
]
]);
$this->stdout("done.\n", Console::FG_GREEN);
}

$baseMigration = $command->get($this->index, $this->type, self::BASE_MIGRATION);
if ($baseMigration['found'] === false) {
$this->addMigrationHistory(self::BASE_MIGRATION);
}
}

/**
* @inheritdoc
*/
protected function addMigrationHistory($version)
{
$this->db->createCommand()->insert($this->index, $this->type, [
'version' => $version,
'apply_time' => time(),
], $version, [
'op_type' => 'create',
'refresh' => 'true'
]);
}

/**
* @inheritdoc
*/
protected function removeMigrationHistory($version)
{
$this->db->createCommand()->delete($this->index, $this->type, $version);
}
}
82 changes: 82 additions & 0 deletions extensions/elasticsearch/Migration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/

namespace yii\elasticsearch;

use yii\base\Component;
use yii\db\MigrationInterface;
use yii\di\Instance;
use yii\helpers\Json;

/**
* Migration is the base class for representing an Elasticsearch migration.
*
* Each child class of Migration represents an individual migration which
* is identified by the child class name.
*
* Within each migration, the [[up()]] method should be overridden to contain the logic
* for "upgrading" the database; while the [[down()]] method for the "downgrading"
* logic.
*
* Migration provides a set of convenient methods for manipulating Elasticsearch data and schema.
* For example, the [[createIndex()]] method can be used to create an index.
* Compared with the same methods in [[Command]], these methods will display extra
* information showing the method parameters and execution time, which may be useful when
* applying migrations.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0.3
*/
abstract class Migration extends Component implements MigrationInterface
{
/**
* @var Connection|array|string the DB connection object or the application component ID of the DB connection.
* This can also be a configuration array for creating the object.
*/
public $db = 'elasticsearch';


// TODO pass index and type of the command here
// TODO use reference to the command for output

/**
* Initializes the migration.
* This method will set [[db]] to be the 'db' application component, if it is null.
*/
public function init()
{
parent::init();
$this->db = Instance::ensure($this->db, Connection::className());
}

/**
* Creates a command for execution.
* @param array $config the configuration for the Command class
* @return Command the DB command
*/
protected function createCommand($config = [])
{
return $this->db->createCommand($config);
}

/**
* creates an index
* @param string $index index name.
* @param array $configuration index configuration.
* @return mixed the request result.
* @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html
*/
public function createIndex($index, $configuration = null)
{
echo " > create index \"{$index}\" ...";
$time = microtime(true);
$this->createCommand()->createIndex($index, $configuration);
echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
}

// TODO add more shortcut functions
}
13 changes: 5 additions & 8 deletions extensions/mongodb/console/controllers/MigrateController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Yii;
use yii\console\controllers\BaseMigrateController;
use yii\console\Exception;
use yii\di\Instance;
use yii\mongodb\Connection;
use yii\mongodb\Query;
use yii\helpers\ArrayHelper;
Expand Down Expand Up @@ -64,8 +65,9 @@ class MigrateController extends BaseMigrateController
*/
public $templateFile = '@yii/mongodb/views/migration.php';
/**
* @var Connection|string the DB connection object or the application
* component ID of the DB connection.
* @var Connection|array|string the MongoDB connection object or the application component ID of the MongoDB connection
* that this migration should work with.
* Starting from version 2.0.3, this can also be a configuration array for creating the object.
*/
public $db = 'mongodb';

Expand All @@ -92,12 +94,7 @@ public function beforeAction($action)
{
if (parent::beforeAction($action)) {
if ($action->id !== 'create') {
if (is_string($this->db)) {
$this->db = Yii::$app->get($this->db);
}
if (!$this->db instanceof Connection) {
throw new Exception("The 'db' option must refer to the application component ID of a MongoDB connection.");
}
$this->db = Instance::ensure($this->db, Connection::className());
}
return true;
} else {
Expand Down
13 changes: 5 additions & 8 deletions framework/console/controllers/MigrateController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use yii\console\Exception;
use yii\db\Connection;
use yii\db\Query;
use yii\di\Instance;
use yii\helpers\ArrayHelper;
use yii\helpers\Console;

Expand Down Expand Up @@ -64,8 +65,9 @@ class MigrateController extends BaseMigrateController
*/
public $templateFile = '@yii/views/migration.php';
/**
* @var Connection|string the DB connection object or the application
* component ID of the DB connection.
* @var Connection|array|string the DB connection object or the application component ID of the DB connection
* that this migration should work with.
* Starting from version 2.0.3, this can also be a configuration array for creating the object.
*/
public $db = 'db';

Expand All @@ -92,12 +94,7 @@ public function beforeAction($action)
{
if (parent::beforeAction($action)) {
if ($action->id !== 'create') {
if (is_string($this->db)) {
$this->db = Yii::$app->get($this->db);
}
if (!$this->db instanceof Connection) {
throw new Exception("The 'db' option must refer to the application component ID of a DB connection.");
}
$this->db = Instance::ensure($this->db, Connection::className());
}
return true;
} else {
Expand Down

0 comments on commit b9a8c22

Please sign in to comment.