Passed
Push — master ( 0720cb...a95893 )
by Timo
02:36
created

InfoModuleController::getFields()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

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