Passed
Push — master ( f63716...615686 )
by Timo
11:21 queued 06:56
created

InfoModuleController::collectConnectionInfos()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 37
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 37
ccs 0
cts 26
cp 0
rs 8.439
c 0
b 0
f 0
cc 5
eloc 23
nc 6
nop 0
crap 30
1
<?php
2
namespace ApacheSolrForTypo3\Solr\Controller\Backend\Search;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2010-2017 dkd Internet Service GmbH <[email protected]>
8
 *  All rights reserved
9
 *
10
 *  This script is part of the TYPO3 project. The TYPO3 project is
11
 *  free software; you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 2 of the License, or
14
 *  (at your option) any later version.
15
 *
16
 *  The GNU General Public License can be found at
17
 *  http://www.gnu.org/copyleft/gpl.html.
18
 *
19
 *  This script is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  This copyright notice MUST APPEAR in all copies of the script!
25
 ***************************************************************/
26
27
use ApacheSolrForTypo3\Solr\Api;
28
use ApacheSolrForTypo3\Solr\ConnectionManager;
29
use ApacheSolrForTypo3\Solr\Domain\Search\Statistics\StatisticsRepository;
30
use ApacheSolrForTypo3\Solr\System\Validator\Path;
31
use TYPO3\CMS\Backend\Template\ModuleTemplate;
32
use TYPO3\CMS\Core\Messaging\FlashMessage;
33
use TYPO3\CMS\Core\Registry;
34
use TYPO3\CMS\Core\Utility\GeneralUtility;
35
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
36
37
/**
38
 * Info Module
39
 */
40
class InfoModuleController extends AbstractModuleController
41
{
42
    /**
43
     * @var ConnectionManager
44
     */
45
    protected $solrConnectionManager;
46
47
    /**
48
     * Initializes the controller before invoking an action method.
49
     */
50
    protected function initializeAction()
51
    {
52
        parent::initializeAction();
53
        $this->solrConnectionManager = GeneralUtility::makeInstance(ConnectionManager::class);
54
    }
55
    /**
56
     * Set up the doc header properly here
57
     *
58
     * @param ViewInterface $view
59
     * @return void
60
     */
61
    protected function initializeView(ViewInterface $view)
62
    {
63
        parent::initializeView($view);
64
65
        /* @var ModuleTemplate $module */ // holds the state of chosen tab
66
        $module = GeneralUtility::makeInstance(ModuleTemplate::class);
67
        $coreOptimizationTabs = $module->getDynamicTabMenu([], 'coreOptimization');
68
        $this->view->assign('tabs', $coreOptimizationTabs);
69
    }
70
71
    /**
72
     * Index action, shows an overview of the state of the Solr index
73
     *
74
     * @return void
75
     */
76
    public function indexAction()
77
    {
78
        if ($this->selectedSite === null) {
79
            $this->view->assign('can_not_proceed', true);
80
            return;
81
        }
82
83
        $this->collectConnectionInfos();
84
        $this->collectStatistics();
85
        $this->collectIndexFieldsInfo();
86
    }
87
88
    /**
89
     * Checks whether the configured Solr server can be reached and provides a
90
     * flash message according to the result of the check.
91
     *
92
     * @return void
93
     */
94
    protected function collectConnectionInfos()
95
    {
96
        $connectedHosts = [];
97
        $missingHosts = [];
98
        $invalidPaths = [];
99
100
        /* @var Path $path */
101
        $path = GeneralUtility::makeInstance(Path::class);
102
        $connections = $this->solrConnectionManager->getConnectionsBySite($this->selectedSite);
103
104
        if (empty($connections)) {
105
            $this->view->assign('can_not_proceed', true);
106
            return;
107
        }
108
109
        foreach ($connections as $connection) {
110
            $coreUrl = $connection->getScheme() . '://' . $connection->getHost() . ':' . $connection->getPort() . $connection->getPath();
111
112
            if ($connection->ping()) {
113
                $connectedHosts[] = $coreUrl;
114
            } else {
115
                $missingHosts[] = $coreUrl;
116
            }
117
118
            if (!$path->isValidSolrPath($connection->getPath())) {
119
                $invalidPaths[] = $connection->getPath();
120
            }
121
        }
122
123
        $this->view->assignMultiple([
124
            'site' => $this->selectedSite,
125
            'apiKey' => Api::getApiKey(),
126
            'connectedHosts' => $connectedHosts,
127
            'missingHosts' => $missingHosts,
128
            'invalidPaths' => $invalidPaths
129
        ]);
130
    }
131
132
    /**
133
     * Index action, shows an overview of the state of the Solr index
134
     *
135
     * @return void
136
     */
137
    protected function collectStatistics()
138
    {
139
        // TODO make time frame user adjustable, for now it's last 30 days
140
141
        $siteRootPageId = $this->selectedSite->getRootPageId();
142
        /* @var StatisticsRepository $statisticsRepository */
143
        $statisticsRepository = GeneralUtility::makeInstance(StatisticsRepository::class);
144
145
        // @TODO: Do we want Typoscript constants to restrict the results?
146
        $this->view->assign(
147
            'top_search_phrases',
148
            $statisticsRepository->getTopKeyWordsWithHits($siteRootPageId, 30, 5)
149
        );
150
        $this->view->assign(
151
            'top_search_phrases_without_hits',
152
            $statisticsRepository->getTopKeyWordsWithoutHits($siteRootPageId, 30, 5)
153
        );
154
        $this->view->assign(
155
            'search_phrases_statistics',
156
            $statisticsRepository->getSearchStatistics($siteRootPageId, 30, 100)
157
        );
158
159
        $labels = [];
160
        $data = [];
161
        $chartData = $statisticsRepository->getQueriesOverTime($siteRootPageId, 30, 86400);
162
        foreach ($chartData as $bucket) {
163
            $labels[] = strftime('%x', $bucket['timestamp']);
164
            $data[] = (int)$bucket['numQueries'];
165
        }
166
167
        $this->view->assign('queriesChartLabels', json_encode($labels));
168
        $this->view->assign('queriesChartData', json_encode($data));
169
    }
170
171
    /**
172
     * Gets Luke meta data for the currently selected core and provides a list
173
     * of that data.
174
     *
175
     * @return void
176
     */
177
    protected function collectIndexFieldsInfo()
178
    {
179
        $indexFieldsInfoByCorePaths = [];
180
181
        $solrCoreConnections = $this->solrConnectionManager->getConnectionsBySite($this->selectedSite);
182
        foreach ($solrCoreConnections as $solrCoreConnection) {
183
            $indexFieldsInfo = [
184
                'corePath' => $solrCoreConnection->getPath()
185
            ];
186
            if ($solrCoreConnection->ping()) {
187
                $lukeData = $solrCoreConnection->getLukeMetaData();
188
189
                /* @var Registry $registry */
190
                $registry = GeneralUtility::makeInstance(Registry::class);
191
                $limit = $registry->get('tx_solr', 'luke.limit', 20000);
192
                $limitNote = '';
193
194
                if (isset($lukeData->index->numDocs) && $lukeData->index->numDocs > $limit) {
195
                    $limitNote = '<em>Too many terms</em>';
196
                } elseif (isset($lukeData->index->numDocs)) {
197
                    $limitNote = 'Nothing indexed';
198
                    // below limit, so we can get more data
199
                    // Note: we use 2 since 1 fails on Ubuntu Hardy.
200
                    $lukeData = $solrCoreConnection->getLukeMetaData(2);
201
                }
202
203
                $fields = $this->getFields($lukeData, $limitNote);
204
                $coreMetrics = $this->getCoreMetrics($lukeData, $fields);
205
206
                $indexFieldsInfo['noError'] = 'OK';
207
                $indexFieldsInfo['fields'] = $fields;
208
                $indexFieldsInfo['coreMetrics'] = $coreMetrics;
209
            } else {
210
                $indexFieldsInfo['noError'] = null;
211
212
                $this->addFlashMessage(
213
                    '',
214
                    'Unable to contact Apache Solr server: ' . $this->selectedSite->getLabel() . ' ' . $solrCoreConnection->getPath(),
215
                    FlashMessage::ERROR
216
                );
217
            }
218
            $indexFieldsInfoByCorePaths[$solrCoreConnection->getPath()] = $indexFieldsInfo;
219
        }
220
        $this->view->assign('indexFieldsInfoByCorePaths', $indexFieldsInfoByCorePaths);
221
    }
222
223
    /**
224
     * Gets field metrics.
225
     *
226
     * @param \Apache_Solr_Response $lukeData Luke index data
227
     * @param string $limitNote Note to display if there are too many documents in the index to show number of terms for a field
228
     *
229
     * @return array An array of field metrics
230
     */
231
    protected function getFields(\Apache_Solr_Response $lukeData, $limitNote)
232
    {
233
        $rows = [];
234
235
        $fields = (array)$lukeData->fields;
236
        foreach ($fields as $name => $field) {
237
            $rows[$name] = [
238
                'name' => $name,
239
                'type' => $field->type,
240
                'docs' => isset($field->docs) ? $field->docs : 0,
241
                'terms' => isset($field->distinct) ? $field->distinct : $limitNote
242
            ];
243
        }
244
        ksort($rows);
245
246
        return $rows;
247
    }
248
249
    /**
250
     * Gets general core metrics.
251
     *
252
     * @param \Apache_Solr_Response $lukeData Luke index data
253
     * @param array $fields Fields metrics
254
     *
255
     * @return array An array of core metrics
256
     */
257
    protected function getCoreMetrics(\Apache_Solr_Response $lukeData, array $fields)
258
    {
259
        $coreMetrics = [
260
            'numberOfDocuments' => $lukeData->index->numDocs,
261
            'numberOfDeletedDocuments' => $lukeData->index->deletedDocs,
262
            'numberOfTerms' => $lukeData->index->numTerms,
263
            'numberOfFields' => count($fields)
264
        ];
265
266
        return $coreMetrics;
267
    }
268
}
269