Completed
Pull Request — develop (#261)
by Wachter
27:08 queued 12:01
created

ReindexCommand::clearIndex()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 10
ccs 0
cts 0
cp 0
rs 9.4285
cc 2
eloc 6
nc 2
nop 3
crap 6
1
<?php
2
3
/*
4
 * This file is part of Sulu.
5
 *
6
 * (c) MASSIVE ART WebServices GmbH
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Sulu\Bundle\ArticleBundle\Command;
13
14
use PHPCR\Query\QueryResultInterface;
15
use Sulu\Bundle\ArticleBundle\Document\Index\IndexerInterface;
16
use Sulu\Component\HttpKernel\SuluKernel;
17
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
18
use Symfony\Component\Console\Helper\ProgressBar;
19
use Symfony\Component\Console\Helper\QuestionHelper;
20
use Symfony\Component\Console\Input\InputInterface;
21
use Symfony\Component\Console\Input\InputOption;
22
use Symfony\Component\Console\Output\OutputInterface;
23
use Symfony\Component\Console\Question\ConfirmationQuestion;
24
25
/**
26
 * Reindixes articles.
27
 */
28
class ReindexCommand extends ContainerAwareCommand
29
{
30
    /**
31
     * {@inheritdoc}
32
     */
33
    public function configure()
34
    {
35
        $this->setName('sulu:article:reindex');
36
        $this->setDescription('Rebuild elastic-search index for articles');
37
        $this->setHelp('This command will load all articles and index them to elastic-search indexes.');
38
        $this->addOption('drop', null, InputOption::VALUE_NONE, 'Drop and recreate index before reindex');
39
        $this->addOption('clear', null, InputOption::VALUE_NONE, 'Clear all articles of index before reindex');
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45
    protected function execute(InputInterface $input, OutputInterface $output)
46
    {
47
        $context = $this->getContainer()->getParameter('sulu.context');
48
        $startTime = microtime(true);
49
50
        $id = 'sulu_article.elastic_search.article_indexer';
51
        if ($context === SuluKernel::CONTEXT_WEBSITE) {
52
            $id = 'sulu_article.elastic_search.article_live_indexer';
53
        }
54
55
        /** @var IndexerInterface $indexer */
56
        $indexer = $this->getContainer()->get($id);
57
58
        $output->writeln(sprintf('Reindex articles for the <comment>`%s`</comment> context' . PHP_EOL, $context));
59
60
        if (!$this->dropIndex($indexer, $input, $output)) {
61
            // Drop was canceled by user.
62
63
            return;
64
        }
65
66
        $indexer->createIndex();
67
        $this->clearIndex($indexer, $input, $output);
68
69
        $webspaceManager = $this->getContainer()->get('sulu_core.webspace.webspace_manager');
70
        $locales = $webspaceManager->getAllLocalizations();
71
72
        foreach ($locales as $locale) {
73
            $output->writeln(sprintf('<info>Locale "</info>%s<info>"</info>' . PHP_EOL, $locale->getLocale()));
74
75
            $this->indexDocuments($locale->getLocale(), $indexer, $output);
76
77
            $output->writeln(PHP_EOL);
78
        }
79
80
        $output->writeln(
81
            sprintf(
82
                '<info>Index rebuild completed (</info>%ss %s</info><info>)</info>',
83
                number_format(microtime(true) - $startTime, 2),
84
                $this->humanBytes(memory_get_peak_usage())
85
            )
86
        );
87
    }
88
89
    /**
90
     * Drop index if requested.
91
     *
92
     * @param IndexerInterface $indexer
93
     * @param InputInterface $input
94
     * @param OutputInterface $output
95
     *
96
     * @return bool
97
     */
98
    protected function dropIndex(IndexerInterface $indexer, InputInterface $input, OutputInterface $output)
99
    {
100
        if (!$input->getOption('drop')) {
101
            return true;
102
        }
103
104
        if (!$input->getOption('no-interaction')) {
105
            $output->writeln(
106
                '<comment>ATTENTION</comment>: This operation drops and recreates the whole index and deletes the complete data.'
107
            );
108
109
            $question = new ConfirmationQuestion('           Are you sure you want to drop the index? ');
110
111
            /** @var QuestionHelper $questionHelper */
112
            $questionHelper = $this->getHelper('question');
113
            if (!$questionHelper->ask($input, $output, $question)) {
114
                return false;
115
            }
116
117
            $output->writeln('');
118
        }
119
120
        $indexer->dropIndex();
121
122
        $context = $this->getContainer()->getParameter('sulu.context');
123
        $output->writeln(
124
            sprintf('Dropped and recreated index for the <comment>`%s`</comment> context' . PHP_EOL, $context)
125
        );
126
127
        return true;
128
    }
129
130
    /**
131
     * Clear article-content of index.
132
     *
133
     * @param IndexerInterface $indexer
134
     * @param InputInterface $input
135
     * @param OutputInterface $output
136
     */
137
    protected function clearIndex(IndexerInterface $indexer, InputInterface $input, OutputInterface $output)
138
    {
139
        if (!$input->getOption('clear')) {
140
            return;
141
        }
142
143
        $context = $this->getContainer()->getParameter('sulu.context');
144
        $output->writeln(sprintf('Cleared index for the <comment>`%s`</comment> context', $context));
145
        $indexer->clear();
146
    }
147
148
    /**
149
     * Index documents for given locale.
150
     *
151
     * @param string $locale
152
     * @param IndexerInterface $indexer
153
     * @param OutputInterface $output
154
     */
155
    protected function indexDocuments($locale, IndexerInterface $indexer, OutputInterface $output)
156
    {
157
        $documents = $this->getDocuments($locale);
158
        $count = count($documents);
159
        if (0 === $count) {
160
            $output->writeln('  No documents found');
161
162
            return;
163
        }
164
165
        $progessBar = new ProgressBar($output, $count);
166
        $progessBar->setFormat(' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%');
167
        $progessBar->start();
168
169
        foreach ($documents as $document) {
170
            $indexer->index($document);
171
            $progessBar->advance();
172
        }
173
174
        $indexer->flush();
175
        $progessBar->finish();
176
    }
177
178
    /**
179
     * Query for documents with given locale.
180
     *
181
     * @param string $locale
182
     *
183
     * @return QueryResultInterface
184
     */
185
    protected function getDocuments($locale)
186
    {
187
        $propertyEncoder = $this->getContainer()->get('sulu_document_manager.property_encoder');
188
        $documentManager = $this->getContainer()->get('sulu_document_manager.document_manager');
189
190
        $sql2 = sprintf(
191
            'SELECT * FROM [nt:unstructured] AS a WHERE [jcr:mixinTypes] = "sulu:article" AND [%s] IS NOT NULL',
192
            $propertyEncoder->localizedSystemName('template', $locale)
193
        );
194
195
        return $documentManager->createQuery($sql2, $locale, ['load_ghost_content' => false])->execute();
196
    }
197
198
    /**
199
     * Converts bytes into human readable.
200
     *
201
     * Inspired by http://jeffreysambells.com/2012/10/25/human-readable-filesize-php
202
     *
203
     * @param int $bytes
204
     * @param int $dec
205
     *
206
     * @return string
207
     */
208
    protected function humanBytes($bytes, $dec = 2)
209
    {
210
        $size = ['b', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
211
        $factor = (int) floor((strlen($bytes) - 1) / 3);
212
213
        return sprintf("%.{$dec}f", $bytes / pow(1024, $factor)) . $size[$factor];
214
    }
215
}
216