Passed
Push — master ( fefd78...3cca91 )
by Robbie
04:31 queued 02:37
created

Search/Processors/SearchUpdateBatchedProcessor.php (2 issues)

Severity
1
<?php
2
3
namespace SilverStripe\FullTextSearch\Search\Processors;
4
5
use SilverStripe\Core\Config\Configurable;
6
7
/**
8
 * Provides batching of search updates
9
 */
10
abstract class SearchUpdateBatchedProcessor extends SearchUpdateProcessor
11
{
12
    use Configurable;
13
14
    /**
15
     * List of batches to be processed
16
     *
17
     * @var array
18
     */
19
    protected $batches;
20
21
    /**
22
     * Pointer to index of $batches assigned to $current.
23
     * Set to 0 (first index) if not started, or count + 1 if completed.
24
     *
25
     * @var int
26
     */
27
    protected $currentBatch;
28
29
    /**
30
     * List of indexes successfully comitted in the current batch
31
     *
32
     * @var array
33
     */
34
    protected $completedIndexes;
35
36
    /**
37
     * Maximum number of record-states to process in one batch.
38
     * Set to zero to process all records in a single batch
39
     *
40
     * @config
41
     * @var int
42
     */
43
    private static $batch_size = 100;
0 ignored issues
show
The private property $batch_size is not used, and could be removed.
Loading history...
44
45
    /**
46
     * Up to this number of additional ids can be added to any batch in order to reduce the number
47
     * of batches
48
     *
49
     * @config
50
     * @var int
51
     */
52
    private static $batch_soft_cap = 10;
0 ignored issues
show
The private property $batch_soft_cap is not used, and could be removed.
Loading history...
53
54
    public function __construct()
55
    {
56
        parent::__construct();
57
58
        $this->batches = array();
59
        $this->setBatch(0);
60
    }
61
62
    /**
63
     * Set the current batch index
64
     *
65
     * @param int $batch Index of the batch
66
     */
67
    protected function setBatch($batch)
68
    {
69
        $this->currentBatch = $batch;
70
    }
71
72
    protected function getSource()
73
    {
74
        if (isset($this->batches[$this->currentBatch])) {
75
            return $this->batches[$this->currentBatch];
76
        }
77
    }
78
79
    /**
80
     * Process the current queue
81
     *
82
     * @return boolean
83
     */
84
    public function process()
85
    {
86
        // Skip blank queues
87
        if (empty($this->batches)) {
88
            return true;
89
        }
90
91
        // Don't re-process completed queue
92
        if ($this->currentBatch >= count($this->batches)) {
93
            return true;
94
        }
95
96
        // Send current patch to indexes
97
        $this->prepareIndexes();
98
99
        // Advance to next batch if successful
100
        $this->setBatch($this->currentBatch + 1);
101
        return true;
102
    }
103
104
    /**
105
     * Segments batches acording to the specified rules
106
     *
107
     * @param array $source Source input
108
     * @return array Batches
109
     */
110
    protected function segmentBatches($source)
111
    {
112
        // Measure batch_size
113
        $batchSize = static::config()->get('batch_size');
114
        if ($batchSize === 0) {
115
            return array($source);
116
        }
117
        $softCap = static::config()->get('batch_soft_cap');
118
119
        // Clear batches
120
        $batches = array();
121
        $current = array();
122
        $currentSize = 0;
123
124
        // Build batches from data
125
        foreach ($source as $base => $statefulids) {
126
            if (!$statefulids) {
127
                continue;
128
            }
129
130
            foreach ($statefulids as $stateKey => $statefulid) {
131
                $state = $statefulid['state'];
132
                $ids = $statefulid['ids'];
133
                if (!$ids) {
134
                    continue;
135
                }
136
137
                // Extract items from $ids until empty
138
                while ($ids) {
139
                    // Estimate maximum number of items to take for this iteration, allowing for the soft cap
140
                    $take = $batchSize - $currentSize;
141
                    if (count($ids) <= $take + $softCap) {
142
                        $take += $softCap;
143
                    }
144
                    $items = array_slice($ids, 0, $take, true);
145
                    $ids = array_slice($ids, count($items), null, true);
146
147
                    // Update batch
148
                    $currentSize += count($items);
149
                    $merge = array(
150
                        $base => array(
151
                            $stateKey => array(
152
                                'state' => $state,
153
                                'ids' => $items
154
                            )
155
                        )
156
                    );
157
                    $current = $current ? array_merge_recursive($current, $merge) : $merge;
158
                    if ($currentSize >= $batchSize) {
159
                        $batches[] = $current;
160
                        $current = array();
161
                        $currentSize = 0;
162
                    }
163
                }
164
            }
165
        }
166
        // Add incomplete batch
167
        if ($currentSize) {
168
            $batches[] = $current;
169
        }
170
171
        return $batches;
172
    }
173
174
    public function batchData()
175
    {
176
        $this->batches = $this->segmentBatches($this->dirty);
177
        $this->setBatch(0);
178
    }
179
180
    public function triggerProcessing()
181
    {
182
        $this->batchData();
183
    }
184
}
185