Passed
Push — master ( 2ec749...6a9421 )
by Simon
06:29
created

SolrIndexJob::getNextSteps()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5.0073

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 14
c 1
b 0
f 0
nc 8
nop 0
dl 0
loc 25
ccs 14
cts 15
cp 0.9333
crap 5.0073
rs 9.4888
1
<?php
2
/**
3
 * class SolrIndexJob|Firesphere\SolrSearch\Jobs\SolrIndexJob Index items from the CMS through a QueuedJob
4
 *
5
 * @package Firesphere\SolrSearch\Jobs
6
 * @author Simon `Firesphere` Erkelens; Marco `Sheepy` Hermo
7
 * @copyright Copyright (c) 2018 - now() Firesphere & Sheepy
8
 */
9
10
namespace Firesphere\SolrSearch\Jobs;
11
12
use Exception;
13
use Firesphere\SolrSearch\Services\SolrCoreService;
14
use Firesphere\SolrSearch\Tasks\SolrIndexTask;
15
use GuzzleHttp\Exception\GuzzleException;
16
use ReflectionException;
17
use SilverStripe\Control\Director;
18
use SilverStripe\Control\HTTPRequest;
19
use SilverStripe\Core\Injector\Injector;
20
use stdClass;
21
use Symbiote\QueuedJobs\Services\AbstractQueuedJob;
22
use Symbiote\QueuedJobs\Services\QueuedJobService;
23
24
/**
25
 * Class SolrIndexJob is a queued job to index all existing indexes and their classes.
26
 *
27
 * It always runs on all indexes, to make sure all indexes are up to date.
28
 *
29
 * @package Firesphere\SolrSearch\Jobs
30
 */
31
class SolrIndexJob extends AbstractQueuedJob
32
{
33
34
    /**
35
     * The class that should be indexed.
36
     * If set, the task should run the given class with the given group
37
     * The class should be popped off the array at the end of each subset
38
     * so the next class becomes the class to index.
39
     *
40
     * Rinse and repeat for each class in the index, until this array is empty
41
     *
42
     * @var array
43
     */
44
    protected $classToIndex = [];
45
46
    /**
47
     * The indexes that need to run.
48
     *
49
     * @var array
50
     */
51
    protected $indexes;
52
53
    /**
54
     * My name
55
     *
56
     * @return string
57
     */
58 2
    public function getTitle()
59
    {
60 2
        return 'Index groups to Solr search';
61
    }
62
63
    /**
64
     * Process this job
65
     *
66
     * @return self
67
     * @throws Exception
68
     * @throws GuzzleException
69
     */
70 1
    public function process()
71
    {
72 1
        $data = $this->jobData;
73
74 1
        $this->configureRun($data);
75
76 1
        $this->currentStep = $this->currentStep ?: 0;
77 1
        $index = Injector::inst()->get($this->indexes[0]);
78 1
        $this->classToIndex = count($this->classToIndex) ? $this->classToIndex : $index->getClasses();
79
        $indexArgs = [
80 1
            'group' => $this->currentStep,
81 1
            'index' => $this->indexes[0],
82 1
            'class' => $this->classToIndex[0],
83
        ];
84
        /** @var SolrIndexTask $task */
85 1
        $task = Injector::inst()->get(SolrIndexTask::class);
86 1
        $request = new HTTPRequest(
87 1
            'GET',
88 1
            '/dev/tasks/SolrIndexTask',
89 1
            $indexArgs
90
        );
91
92 1
        $result = $task->run($request);
93 1
        $this->totalSteps = $result;
94
        // If the result is false, the job should fail too
95
        // Thus, only set to true if the result isn't false :)
96 1
        $this->isComplete = true;
97
98
        /** @var self $this */
99 1
        return $this;
100
    }
101
102
    /**
103
     * Configure the run for the valid indexes
104
     *
105
     * @param stdClass|null $data
106
     * @throws ReflectionException
107
     */
108 1
    protected function configureRun($data)
109
    {
110
        // If null gets passed in, it goes a bit wonky with the check for indexes
111 1
        if (!$data) {
112 1
            $data = new stdClass();
113 1
            $data->indexes = null;
114
        }
115 1
        if (!isset($data->indexes) || !count($data->indexes)) { // If indexes are set, don't load them.
116 1
            $this->indexes = (new SolrCoreService())->getValidIndexes();
117
        } else {
118 1
            $this->setIndexes($data->indexes);
119 1
            $this->setClassToIndex($data->classToIndex);
120
        }
121 1
    }
122
123
    /**
124
     * Set up the next job if needed
125
     */
126 1
    public function afterComplete()
127
    {
128 1
        list($currentStep, $totalSteps) = $this->getNextSteps();
129
        // If there are no indexes left to run, let's call it a day
130 1
        if (count($this->indexes)) {
131 1
            $nextJob = new self();
132 1
            $jobData = new stdClass();
133
134 1
            $jobData->classToIndex = $this->getClassToIndex();
135 1
            $jobData->indexes = $this->getIndexes();
136 1
            $nextJob->setJobData($totalSteps, $currentStep, false, $jobData, []);
137
138
            // Add a wee break to let the system recover from this heavy operation
139 1
            Injector::inst()->get(QueuedJobService::class)
140 1
                ->queueJob($nextJob, date('Y-m-d H:i:00', strtotime('+1 minutes')));
141
        }
142 1
        parent::afterComplete();
143 1
    }
144
145
    /**
146
     * Get the next step to execute
147
     *
148
     * @return array
149
     */
150 1
    protected function getNextSteps(): array
151
    {
152 1
        $cores = SolrCoreService::config()->get('cpucores') ?: 1;
153
        // Force a single count for when the job is not run from CLI
154 1
        if (!Director::is_cli()) {
155
            $cores = 1;
156
        }
157 1
        $currentStep = $this->currentStep + $cores; // Add the amount of cores
158 1
        $totalSteps = $this->totalSteps;
159
        // No more steps to execute on this class, let's go to the next class
160 1
        if ($currentStep >= $totalSteps) {
161 1
            array_shift($this->classToIndex);
162
            // Reset the current step, a complete new set of data is coming
163 1
            $currentStep = 0;
164 1
            $totalSteps = 1;
165
        }
166
        // If there are no classes left in this index, go to the next index
167 1
        if (!count($this->classToIndex)) {
168 1
            array_shift($this->indexes);
169
            // Reset the current step, a complete new set of data is coming
170 1
            $currentStep = 0;
171 1
            $totalSteps = 1;
172
        }
173
174 1
        return [$currentStep, $totalSteps];
175
    }
176
177
    /**
178
     * Which Indexes should I index
179
     *
180
     * @return array
181
     */
182 3
    public function getClassToIndex(): array
183
    {
184 3
        return $this->classToIndex;
185
    }
186
187
    /**
188
     * Which classes should I index
189
     *
190
     * @param array $classToIndex
191
     * @return SolrIndexJob
192
     */
193 3
    public function setClassToIndex($classToIndex)
194
    {
195 3
        $this->classToIndex = $classToIndex;
196
197 3
        return $this;
198
    }
199
200
    /**
201
     * Get the indexes
202
     *
203
     * @return array
204
     */
205 3
    public function getIndexes(): array
206
    {
207 3
        return $this->indexes;
208
    }
209
210
    /**
211
     * Set the indexes if needed
212
     *
213
     * @param array $indexes
214
     * @return SolrIndexJob
215
     */
216 3
    public function setIndexes($indexes)
217
    {
218 3
        $this->indexes = $indexes;
219
220 3
        return $this;
221
    }
222
}
223