Skip to content

Commit b29dec2

Browse files
author
Fabian Schmengler /
authored
Merge pull request #51 from integer-net/partial-index-command
"Clear" and "Swap" commands
2 parents f87cc79 + 0e65068 commit b29dec2

File tree

11 files changed

+470
-26
lines changed

11 files changed

+470
-26
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
namespace IntegerNet\Solr\Console\Command;
4+
5+
use IntegerNet\Solr\Indexer\Slice;
6+
use IntegerNet\Solr\Model\Indexer;
7+
use Magento\Framework\App;
8+
use Symfony\Component\Console\Command\Command;
9+
use Symfony\Component\Console\Helper\ProgressBar;
10+
use Symfony\Component\Console\Input\InputInterface;
11+
use Symfony\Component\Console\Input\InputOption;
12+
use Symfony\Component\Console\Output\OutputInterface;
13+
use Symfony\Component\Console\Style\SymfonyStyle;
14+
15+
/**
16+
* solr:clear command
17+
*/
18+
class ClearCommand extends Command
19+
{
20+
const INPUT_STORES = 'stores';
21+
const INPUT_USESWAPCORE = 'useswapcore';
22+
/**
23+
* @var Indexer\Console
24+
*/
25+
private $indexer;
26+
/**
27+
* @var App\State
28+
*/
29+
private $appState;
30+
31+
public function __construct(Indexer\Console $indexer, App\State $appState, $name = null)
32+
{
33+
parent::__construct($name);
34+
$this->indexer = $indexer;
35+
$this->appState = $appState;
36+
}
37+
38+
protected function configure()
39+
{
40+
$options = [
41+
new InputOption(
42+
self::INPUT_STORES,
43+
null,
44+
InputOption::VALUE_OPTIONAL,
45+
'Clear solr product index for given stores (can be store id, store code, comma seperated. Or "all".) '
46+
. 'If not set, clear all stores.'
47+
),
48+
new InputOption(
49+
self::INPUT_USESWAPCORE,
50+
null,
51+
InputOption::VALUE_NONE,
52+
'Use swap core for clearing instead of live solr core (only if configured correctly).'
53+
),
54+
];
55+
$this->setName('solr:clear');
56+
$this->setHelp('Clear Solr index for given stores (see "stores" param).');
57+
$this->setDescription('Clear Solr index');
58+
$this->setDefinition($options);
59+
}
60+
61+
protected function execute(InputInterface $input, OutputInterface $output)
62+
{
63+
$styledOutput = new StyledOutput(
64+
$output,
65+
class_exists(SymfonyStyle::class) ? new SymfonyStyle($input, $output) : null
66+
);
67+
$startTime = microtime(true);
68+
$this->appState->setAreaCode(App\Area::AREA_GLOBAL);
69+
if (!$input->getOption(self::INPUT_STORES) || $input->getOption(self::INPUT_STORES) === 'all') {
70+
$stores = null;
71+
$styledOutput->title('Clearing full Solr product index...');
72+
} else {
73+
$stores = \explode(',', $input->getOption(self::INPUT_STORES));
74+
$styledOutput->title('Clearing Solr product index for stores ' . \implode(', ', $stores) . '...');
75+
}
76+
try {
77+
$this->indexer->addProgressHandler(
78+
new ProgressInConsole($output)
79+
);
80+
if ($input->getOption(self::INPUT_USESWAPCORE)) {
81+
$this->indexer->clearStoresOnSwappedCore($stores);
82+
} else {
83+
$this->indexer->clearStores($stores);
84+
}
85+
$totalTime = number_format(microtime(true) - $startTime, 2);
86+
$styledOutput->success("Clearing finished in $totalTime seconds.");
87+
} catch (\Exception $e) {
88+
$styledOutput->error($e->getMessage());
89+
}
90+
}
91+
}

main/src/Console/Command/ReindexCommand.php

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
*/
1818
class ReindexCommand extends Command
1919
{
20-
const INPUT_STORES = 'stores';
20+
const INPUT_STORES = 'stores';
21+
const INPUT_EMPTYINDEX = 'emptyindex';
22+
const INPUT_NOEMPTYINDEX = 'noemptyindex';
23+
const INPUT_PROGRESS = 'progress';
2124
/**
2225
* @var Indexer\Console
2326
*/
@@ -38,26 +41,26 @@ protected function configure()
3841
{
3942
$options = [
4043
new InputOption(
41-
'stores',
44+
self::INPUT_STORES,
4245
null,
4346
InputOption::VALUE_OPTIONAL,
4447
'Reindex given stores (can be store id, store code, comma seperated. Or "all".) '
4548
. 'If not set, reindex all stores.'
4649
),
4750
new InputOption(
48-
'emptyindex',
51+
self::INPUT_EMPTYINDEX,
4952
null,
5053
InputOption::VALUE_NONE,
5154
'Force emptying the solr index for the given store(s). If not set, configured value is used.'
5255
),
5356
new InputOption(
54-
'noemptyindex',
57+
self::INPUT_NOEMPTYINDEX,
5558
null,
5659
InputOption::VALUE_NONE,
5760
'Force not emptying the solr index for the given store(s). If not set, configured value is used.'
5861
),
5962
new InputOption(
60-
'progress',
63+
self::INPUT_PROGRESS,
6164
null,
6265
InputOption::VALUE_NONE,
6366
'Show progress bar.'
@@ -88,13 +91,13 @@ class_exists(SymfonyStyle::class) ? new SymfonyStyle($input, $output) : null
8891
$this->indexer->addProgressHandler(
8992
new ProgressInConsole(
9093
$output,
91-
$input->getOption('progress') ? ProgressInConsole::USE_PROGRESS_BAR : false
94+
$input->getOption(self::INPUT_PROGRESS) ? ProgressInConsole::USE_PROGRESS_BAR : false
9295
)
9396
);
94-
if ($input->getOption('emptyindex')) {
97+
if ($input->getOption(self::INPUT_EMPTYINDEX)) {
9598
$styledOutput->note('Forcing empty index.');
9699
$this->indexer->executeStoresForceEmpty($stores);
97-
} elseif ($input->getOption('noemptyindex')) {
100+
} elseif ($input->getOption(self::INPUT_NOEMPTYINDEX)) {
98101
$styledOutput->note('Forcing non-empty index.');
99102
$this->indexer->executeStoresForceNotEmpty($stores);
100103
} else {

main/src/Console/Command/ReindexSliceCommand.php

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
*/
1818
class ReindexSliceCommand extends Command
1919
{
20-
const INPUT_STORES = 'stores';
20+
const INPUT_STORES = 'stores';
21+
const INPUT_SLICE = 'slice';
22+
const INPUT_USESWAPCORE = 'useswapcore';
23+
const INPUT_PROGRESS = 'progress';
2124
/**
2225
* @var Indexer\Console
2326
*/
@@ -38,33 +41,36 @@ protected function configure()
3841
{
3942
$options = [
4043
new InputOption(
41-
'stores',
44+
self::INPUT_STORES,
4245
null,
4346
InputOption::VALUE_OPTIONAL,
4447
'Reindex given stores (can be store id, store code, comma seperated. Or "all".) '
4548
. 'If not set, reindex all stores.'
4649
),
4750
new InputOption(
48-
'slice',
51+
self::INPUT_SLICE,
4952
null,
5053
InputOption::VALUE_REQUIRED,
5154
'<number>/<total_number>, i.e. "1/5" or "2/5". '
5255
),
5356
new InputOption(
54-
'useswapcore',
57+
self::INPUT_USESWAPCORE,
5558
null,
5659
InputOption::VALUE_NONE,
5760
'Use swap core for indexing instead of live solr core (only if configured correctly).'
5861
),
5962
new InputOption(
60-
'progress',
63+
self::INPUT_PROGRESS,
6164
null,
6265
InputOption::VALUE_NONE,
6366
'Show progress bar.'
6467
)
6568
];
6669
$this->setName('solr:reindex:slice');
67-
$this->setHelp('Partially reindex Solr for given stores (see "stores" param). Can be used for letting indexing run in parallel.');
70+
$this->setHelp(
71+
'Partially reindex Solr for given stores (see "stores" param). '
72+
. 'Can be used for letting indexing run in parallel.'
73+
);
6874
$this->setDescription('Partially reindex Solr');
6975
$this->setDefinition($options);
7076
}
@@ -88,17 +94,20 @@ class_exists(SymfonyStyle::class) ? new SymfonyStyle($input, $output) : null
8894
$this->indexer->addProgressHandler(
8995
new ProgressInConsole(
9096
$output,
91-
$input->getOption('progress') ? ProgressInConsole::USE_PROGRESS_BAR : false
97+
$input->getOption(self::INPUT_PROGRESS) ? ProgressInConsole::USE_PROGRESS_BAR : false
9298
)
9399
);
94-
$styledOutput->note('Processing slice ' . $input->getOption('slice') . '...');
95-
if ($input->getOption('useswapcore')) {
100+
$styledOutput->note('Processing slice ' . $input->getOption(self::INPUT_SLICE) . '...');
101+
if ($input->getOption(self::INPUT_USESWAPCORE)) {
96102
$this->indexer->executeStoresSliceOnSwappedCore(
97-
Slice::fromExpression($input->getOption('slice')),
103+
Slice::fromExpression($input->getOption(self::INPUT_SLICE)),
98104
$stores
99105
);
100106
} else {
101-
$this->indexer->executeStoresSlice(Slice::fromExpression($input->getOption('slice')), $stores);
107+
$this->indexer->executeStoresSlice(
108+
Slice::fromExpression($input->getOption(self::INPUT_SLICE)),
109+
$stores
110+
);
102111
}
103112
$totalTime = number_format(microtime(true) - $startTime, 2);
104113
$styledOutput->success("Reindex finished in $totalTime seconds.");
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
namespace IntegerNet\Solr\Console\Command;
4+
5+
use IntegerNet\Solr\Indexer\Slice;
6+
use IntegerNet\Solr\Model\Indexer;
7+
use Magento\Framework\App;
8+
use Symfony\Component\Console\Command\Command;
9+
use Symfony\Component\Console\Helper\ProgressBar;
10+
use Symfony\Component\Console\Input\InputInterface;
11+
use Symfony\Component\Console\Input\InputOption;
12+
use Symfony\Component\Console\Output\OutputInterface;
13+
use Symfony\Component\Console\Style\SymfonyStyle;
14+
15+
/**
16+
* solr:swap command
17+
*/
18+
class SwapCommand extends Command
19+
{
20+
const INPUT_STORES = 'stores';
21+
22+
/**
23+
* @var Indexer\Console
24+
*/
25+
private $indexer;
26+
/**
27+
* @var App\State
28+
*/
29+
private $appState;
30+
31+
public function __construct(Indexer\Console $indexer, App\State $appState, $name = null)
32+
{
33+
parent::__construct($name);
34+
$this->indexer = $indexer;
35+
$this->appState = $appState;
36+
}
37+
38+
protected function configure()
39+
{
40+
$options = [
41+
new InputOption(
42+
self::INPUT_STORES,
43+
null,
44+
InputOption::VALUE_OPTIONAL,
45+
'Swap cores for given stores (can be store id, store code, comma seperated. Or "all".) '
46+
. 'If not set, swap cores for all stores.'
47+
),
48+
];
49+
$this->setName('solr:swap');
50+
$this->setHelp(
51+
'Swap cores. This is useful if using slices (see solr:reindex:slice) '
52+
. 'after indexing with the "--use_swap_core" param; it\'s not needed otherwise.'
53+
);
54+
$this->setDescription('Swap cores');
55+
$this->setDefinition($options);
56+
}
57+
58+
protected function execute(InputInterface $input, OutputInterface $output)
59+
{
60+
$styledOutput = new StyledOutput(
61+
$output,
62+
class_exists(SymfonyStyle::class) ? new SymfonyStyle($input, $output) : null
63+
);
64+
$startTime = microtime(true);
65+
$this->appState->setAreaCode(App\Area::AREA_GLOBAL);
66+
if (!$input->getOption(self::INPUT_STORES) || $input->getOption(self::INPUT_STORES) === 'all') {
67+
$stores = null;
68+
$styledOutput->title('Swap all cores...');
69+
} else {
70+
$stores = \explode(',', $input->getOption(self::INPUT_STORES));
71+
$styledOutput->title('Swap cores for stores ' . \implode(', ', $stores) . '...');
72+
}
73+
try {
74+
$this->indexer->addProgressHandler(
75+
new ProgressInConsole($output)
76+
);
77+
$this->indexer->swapCores($stores);
78+
$totalTime = number_format(microtime(true) - $startTime, 2);
79+
$styledOutput->success("Core swap finished in $totalTime seconds.");
80+
} catch (\Exception $e) {
81+
$styledOutput->error($e->getMessage());
82+
}
83+
}
84+
}

main/src/Model/Indexer/Console.php

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use IntegerNet\Solr\Indexer\ProductIndexer;
1414
use IntegerNet\Solr\Indexer\Progress\ProgressHandler;
1515
use IntegerNet\Solr\Indexer\Slice;
16+
use Magento\Store\Api\Data\StoreInterface;
1617
use Magento\Store\Model\StoreManagerInterface;
1718

1819
class Console
@@ -64,15 +65,42 @@ public function executeStoresSliceOnSwappedCore($slice, $storeIds)
6465

6566
public function clearStores(array $storeIds = null)
6667
{
67-
//TODO fetch all store ids if NULL
6868
if (empty($storeIds)) {
69-
throw new \BadMethodCallException("Command for 'clear all stores' not implemented yet");
69+
$storeIds = array_map(
70+
function (StoreInterface $store) {
71+
return $store->getId();
72+
},
73+
$this->storeManager->getStores()
74+
);
7075
}
7176
foreach ($this->getStoreIds($storeIds) as $storeId) {
7277
$this->solrIndexer->clearIndex($storeId);
7378
}
7479
}
7580

81+
public function clearStoresOnSwappedCore(array $storeIds = null)
82+
{
83+
if (empty($storeIds)) {
84+
$storeIds = array_map(
85+
function (StoreInterface $store) {
86+
return $store->getId();
87+
},
88+
$this->storeManager->getStores()
89+
);
90+
}
91+
$this->solrIndexer->activateSwapCore();
92+
foreach ($this->getStoreIds($storeIds) as $storeId) {
93+
$this->solrIndexer->clearIndex($storeId);
94+
}
95+
$this->solrIndexer->deactivateSwapCore();
96+
}
97+
98+
public function swapCores(array $storeIds = null)
99+
{
100+
$this->solrIndexer->checkSwapCoresConfiguration($storeIds);
101+
$this->solrIndexer->swapCores($storeIds);
102+
}
103+
76104
public function addProgressHandler(ProgressHandler $handler)
77105
{
78106
$this->solrIndexer->addProgressHandler($handler);
@@ -122,7 +150,7 @@ function ($storeId) use ($storesByCode) {
122150
if (isset($storesByCode[$storeId])) {
123151
return $storesByCode[$storeId]->getId();
124152
}
125-
throw new \InvalidArgumentException("'$storeId' is neither a numric ID nor an existing store code");
153+
throw new \InvalidArgumentException("'$storeId' is neither a numeric ID nor an existing store code");
126154
},
127155
$storeIds
128156
);

0 commit comments

Comments
 (0)