Firesphere/silverstripe-solr-search

View on GitHub
src/Jobs/SolrIndexJob.php

Summary

Maintainability
A
0 mins
Test Coverage
A
98%
<?php
/**
 * class SolrIndexJob|Firesphere\SolrSearch\Jobs\SolrIndexJob Index items from the CMS through a QueuedJob
 *
 * @package Firesphere\Solr\Search
 * @author Simon `Firesphere` Erkelens; Marco `Sheepy` Hermo
 * @copyright Copyright (c) 2018 - now() Firesphere & Sheepy
 */

namespace Firesphere\SolrSearch\Jobs;

use Exception;
use Firesphere\SolrSearch\Services\SolrCoreService;
use Firesphere\SolrSearch\Tasks\SolrIndexTask;
use ReflectionException;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Core\Injector\Injector;
use stdClass;
use Symbiote\QueuedJobs\Services\AbstractQueuedJob;
use Symbiote\QueuedJobs\Services\QueuedJobService;
use Solarium\Exception\HttpException;

/**
 * Class SolrIndexJob is a queued job to index all existing indexes and their classes.
 *
 * It always runs on all indexes, to make sure all indexes are up to date.
 *
 * @package Firesphere\Solr\Search
 */
class SolrIndexJob extends AbstractQueuedJob
{

    /**
     * The class that should be indexed.
     * If set, the task should run the given class with the given group
     * The class should be popped off the array at the end of each subset
     * so the next class becomes the class to index.
     *
     * Rinse and repeat for each class in the index, until this array is empty
     *
     * @var array
     */
    protected $classToIndex = [];
    /**
     * The indexes that need to run.
     *
     * @var array
     */
    protected $indexes;

    /**
     * My name
     *
     * @return string
     */
    public function getTitle()
    {
        return 'Index groups to Solr search';
    }

    /**
     * Process this job
     *
     * @return self
     * @throws Exception
     * @throws HTTPException
     */
    public function process()
    {
        $data = $this->jobData;

        $this->configureRun($data);

        $this->currentStep = $this->currentStep ?: 0;
        $index = Injector::inst()->get($this->indexes[0]);
        $this->classToIndex = count($this->classToIndex) ? $this->classToIndex : $index->getClasses();
        $indexArgs = [
            'group' => $this->currentStep,
            'index' => $this->indexes[0],
            'class' => $this->classToIndex[0],
        ];
        /** @var SolrIndexTask $task */
        $task = Injector::inst()->get(SolrIndexTask::class);
        $request = new HTTPRequest(
            'GET',
            '/dev/tasks/SolrIndexTask',
            $indexArgs
        );

        $result = $task->run($request);
        $this->totalSteps = $result;
        // If the result is false, the job should fail too
        // Thus, only set to true if the result isn't false :)
        $this->isComplete = true;

        return $this;
    }

    /**
     * Configure the run for the valid indexes
     *
     * @param stdClass|null $data
     */
    protected function configureRun($data)
    {
        // If null gets passed in, it goes a bit wonky with the check for indexes
        if (!$data) {
            $data = new stdClass();
            $data->indexes = null;
        }
        if (!isset($data->indexes) || !count($data->indexes)) { // If indexes are set, don't load them.
            $this->indexes = (new SolrCoreService())->getValidIndexes();
        } else {
            $this->setIndexes($data->indexes);
            $this->setClassToIndex($data->classToIndex);
        }
    }

    /**
     * Set up the next job if needed
     */
    public function afterComplete()
    {
        [$currentStep, $totalSteps] = $this->getNextSteps();
        // If there are no indexes left to run, let's call it a day
        if (count($this->indexes)) {
            $nextJob = new self();
            $jobData = new stdClass();

            $jobData->classToIndex = $this->getClassToIndex();
            $jobData->indexes = $this->getIndexes();
            $nextJob->setJobData($totalSteps, $currentStep, false, $jobData, []);

            // Add a wee break to let the system recover from this heavy operation
            Injector::inst()->get(QueuedJobService::class)
                ->queueJob($nextJob, date('Y-m-d H:i:00', strtotime('+1 minutes')));
        }
        parent::afterComplete();
    }

    /**
     * Get the next step to execute
     *
     * @return array
     */
    protected function getNextSteps(): array
    {
        $cores = SolrCoreService::config()->get('cpucores') ?: 1;
        // Force a single count for when the job is not run from CLI
        if (!Director::is_cli()) {
            $cores = 1;
        }
        $currentStep = $this->currentStep + $cores; // Add the amount of cores
        $totalSteps = $this->totalSteps;
        // No more steps to execute on this class, let's go to the next class
        if ($currentStep >= $totalSteps) {
            array_shift($this->classToIndex);
            // Reset the current step, a complete new set of data is coming
            $currentStep = 0;
            $totalSteps = 1;
        }
        // If there are no classes left in this index, go to the next index
        if (!count($this->classToIndex)) {
            array_shift($this->indexes);
            // Reset the current step, a complete new set of data is coming
            $currentStep = 0;
            $totalSteps = 1;
        }

        return [$currentStep, $totalSteps];
    }

    /**
     * Which Indexes should I index
     *
     * @return array
     */
    public function getClassToIndex(): array
    {
        return $this->classToIndex;
    }

    /**
     * Which classes should I index
     *
     * @param array $classToIndex
     * @return SolrIndexJob
     */
    public function setClassToIndex($classToIndex)
    {
        $this->classToIndex = $classToIndex;

        return $this;
    }

    /**
     * Get the indexes
     *
     * @return array
     */
    public function getIndexes(): array
    {
        return $this->indexes;
    }

    /**
     * Set the indexes if needed
     *
     * @param array $indexes
     * @return SolrIndexJob
     */
    public function setIndexes($indexes)
    {
        $this->indexes = $indexes;

        return $this;
    }
}