Passed
Push — sheepy/introspection ( 17b395...901708 )
by Simon
08:33 queued 05:46
created

SolrIndexTask::run()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 26
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 3.0021

Importance

Changes 22
Bugs 1 Features 0
Metric Value
eloc 15
c 22
b 1
f 0
dl 0
loc 26
ccs 15
cts 16
cp 0.9375
rs 9.7666
cc 3
nc 3
nop 1
crap 3.0021
1
<?php
2
3
4
namespace Firesphere\SolrSearch\Tasks;
5
6
use Exception;
7
use Firesphere\SolrSearch\Factories\DocumentFactory;
8
use Firesphere\SolrSearch\Indexes\BaseIndex;
9
use Firesphere\SolrSearch\Services\SolrCoreService;
10
use Firesphere\SolrSearch\Traits\LoggerTrait;
11
use GuzzleHttp\Exception\RequestException;
12
use Psr\Log\LoggerInterface;
13
use SilverStripe\Control\Director;
14
use SilverStripe\Control\HTTPRequest;
15
use SilverStripe\Core\Injector\Injector;
16
use SilverStripe\Dev\BuildTask;
17
use SilverStripe\ORM\ArrayList;
18
use SilverStripe\ORM\DataList;
19
use SilverStripe\ORM\DataObject;
20
use SilverStripe\Versioned\Versioned;
21
22
class SolrIndexTask extends BuildTask
23
{
24
    use LoggerTrait;
25
    /**
26
     * @var string
27
     */
28
    private static $segment = 'SolrIndexTask';
29
30
    /**
31
     * @var string
32
     */
33
    protected $title = 'Solr Index update';
34
35
    /**
36
     * @var string
37
     */
38
    protected $description = 'Add or update documents to an existing Solr core.';
39
40
    /**
41
     * @var bool
42
     */
43
    protected $debug = false;
44
45
    /**
46
     * @var SolrCoreService
47
     */
48
    protected $service;
49
50
    /**
51
     * SolrIndexTask constructor. Sets up the document factory
52
     */
53 12
    public function __construct()
54
    {
55 12
        parent::__construct();
56
        // Only index live items.
57
        // The old FTS module also indexed Draft items. This is unnecessary
58 12
        Versioned::set_reading_mode(Versioned::DRAFT . '.' . Versioned::LIVE);
59 12
        $this->setService(Injector::inst()->get(SolrCoreService::class));
60 12
        $this->setLogger(Injector::inst()->get(LoggerInterface::class));
61 12
        $this->setDebug(Director::isDev() || Director::is_cli());
62 12
    }
63
64
    /**
65
     * @param SolrCoreService $service
66
     * @return SolrIndexTask
67
     */
68 12
    public function setService(SolrCoreService $service): SolrIndexTask
69
    {
70 12
        $this->service = $service;
71
72 12
        return $this;
73
    }
74
75
    /**
76
     * @param bool $debug
77
     * @return SolrIndexTask
78
     */
79 12
    public function setDebug(bool $debug): SolrIndexTask
80
    {
81 12
        $this->debug = $debug;
82
83 12
        return $this;
84
    }
85
86
    /**
87
     * Implement this method in the task subclass to
88
     * execute via the TaskRunner
89
     *
90
     * @param HTTPRequest $request
91
     * @return int|bool
92
     * @throws Exception
93
     * @todo defer to background because it may run out of memory
94
     */
95 11
    public function run($request)
96
    {
97 11
        $startTime = time();
98 11
        [$vars, $group, $isGroup] = $this->taskSetup($request);
99 11
        $groups = 0;
100 11
        $indexes = $this->service->getValidIndexes($request->getVar('index'));
101
102 11
        foreach ($indexes as $indexName) {
103
            /** @var BaseIndex $index */
104 11
            $index = Injector::inst()->get($indexName, false);
105
106 11
            $indexClasses = $index->getClasses();
107 11
            $classes = $this->getClasses($vars, $indexClasses);
108 11
            if (!count($classes)) {
109
                continue;
110
            }
111
112 11
            $vars = $this->clearIndex($vars, $indexName, $index);
113
114 11
            $groups = $this->indexClassForIndex($classes, $isGroup, $index, $group);
115
        }
116 11
        $this->getLogger()->info(
117 11
            sprintf('It took me %d seconds to do all the indexing%s', (time() - $startTime), PHP_EOL)
118
        );
119
120 11
        return $groups;
121
    }
122
123
    /**
124
     * @param HTTPRequest $request
125
     * @return array
126
     */
127 11
    protected function taskSetup($request): array
128
    {
129 11
        $vars = $request->getVars();
130 11
        $this->debug = $this->debug || isset($vars['debug']);
131 11
        $group = $vars['group'] ?? 0;
132 11
        $start = $vars['start'] ?? 0;
133 11
        $group = ($start > $group) ? $start : $group;
134 11
        $isGroup = isset($vars['group']);
135
136 11
        return [$vars, $group, $isGroup];
137
    }
138
139
    /**
140
     * @param $vars
141
     * @param array $classes
142
     * @return bool|array
143
     */
144 11
    protected function getClasses($vars, array $classes): array
145
    {
146 11
        if (isset($vars['class'])) {
147 1
            return array_intersect($classes, [$vars['class']]);
148
        }
149
150 10
        return $classes;
151
    }
152
153
    /**
154
     * @param $vars
155
     * @param $indexName
156
     * @param BaseIndex $index
157
     * @return mixed
158
     * @throws Exception
159
     */
160 11
    protected function clearIndex($vars, $indexName, BaseIndex $index)
161
    {
162 11
        if (!empty($vars['clear'])) {
163 1
            $this->getLogger()->info(sprintf('Clearing index %s', $indexName));
164 1
            $this->service->doManipulate(ArrayList::create([]), SolrCoreService::DELETE_TYPE_ALL, $index);
165
        }
166
167 11
        return $vars;
168
    }
169
170
    /**
171
     * @param $classes
172
     * @param $isGroup
173
     * @param BaseIndex $index
174
     * @param $group
175
     * @return int
176
     * @throws Exception
177
     */
178 11
    protected function indexClassForIndex($classes, $isGroup, BaseIndex $index, $group): int
179
    {
180 11
        $groups = 0;
181 11
        foreach ($classes as $class) {
182 11
            $groups = $this->indexClass($isGroup, $class, $index, $group);
183
        }
184
185 11
        return $groups;
186
    }
187
188
    /**
189
     * @param bool $isGroup
190
     * @param string $class
191
     * @param BaseIndex $index
192
     * @param int $group
193
     * @return int
194
     * @throws Exception
195
     */
196 11
    private function indexClass($isGroup, $class, BaseIndex $index, int $group): int
197
    {
198 11
        $this->getLogger()->info(sprintf('Indexing %s for %s', $class, $index->getIndexName()), []);
199
200 11
        $batchLength = DocumentFactory::config()->get('batchLength');
201 11
        $groups = (int)ceil($class::get()->count() / $batchLength);
202 11
        $groups = $isGroup ? $group : $groups;
203 11
        while ($group <= $groups) { // Run from oldest to newest
204
            try {
205 11
                $this->doReindex($group, $class, $batchLength, $index);
206
            } catch (Exception $e) {
207
                $this->getLogger()->error($e->getMessage());
208
                $group++;
209
                continue;
210
            }
211 11
            $group++;
212 11
            $this->getLogger()->info(sprintf('Indexed group %s', $group));
213
        }
214
215 11
        return $groups;
216
    }
217
218
    /**
219
     * @param int $group
220
     * @param string $class
221
     * @param int $batchLength
222
     * @param BaseIndex $index
223
     * @throws Exception
224
     */
225 11
    private function doReindex($group, $class, $batchLength, BaseIndex $index): void
226
    {
227
        // Generate filtered list of local records
228 11
        $baseClass = DataObject::getSchema()->baseDataClass($class);
229 11
        $client = $index->getClient();
230
        /** @var DataList|DataObject[] $items */
231 11
        $items = DataObject::get($baseClass)
232 11
            ->sort('ID ASC')
233 11
            ->limit($batchLength, ($group * $batchLength));
234 11
        $update = $client->createUpdate();
235 11
        if ($items->count()) {
236 1
            $this->service->setInDebugMode($this->debug);
237 1
            $this->service->updateIndex($index, $items, $update);
238 1
            $update->addCommit();
239 1
            $client->update($update);
240
        }
241 11
    }
242
}
243