Passed
Push — master ( 6357e5...5e1177 )
by Timo
22:26
created

SuggestService::doASearch()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace ApacheSolrForTypo3\Solr\Domain\Search\Suggest;
4
5
/***************************************************************
6
 *  Copyright notice
7
 *
8
 *  (c) 2017 Franz Saris <[email protected]>
9
 *  All rights reserved
10
 *
11
 *  This script is part of the TYPO3 project. The TYPO3 project is
12
 *  free software; you can redistribute it and/or modify
13
 *  it under the terms of the GNU General Public License as published by
14
 *  the Free Software Foundation; either version 3 of the License, or
15
 *  (at your option) any later version.
16
 *
17
 *  The GNU General Public License can be found at
18
 *  http://www.gnu.org/copyleft/gpl.html.
19
 *
20
 *  This script is distributed in the hope that it will be useful,
21
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 *  GNU General Public License for more details.
24
 *
25
 *  This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27
28
use ApacheSolrForTypo3\Solr\ConnectionManager;
29
use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder;
30
use ApacheSolrForTypo3\Solr\Domain\Search\Query\SuggestQuery;
31
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Result\SearchResult;
32
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Result\SearchResultCollection;
33
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\SearchResultSet;
34
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\SearchResultSetService;
35
use ApacheSolrForTypo3\Solr\Domain\Search\SearchRequest;
36
use ApacheSolrForTypo3\Solr\Search;
37
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
38
use ApacheSolrForTypo3\Solr\System\Solr\ParsingUtil;
39
use TYPO3\CMS\Core\Utility\GeneralUtility;
40
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
41
42
/**
43
 * Class SuggestService
44
 *
45
 * @author Frans Saris <[email protected]>
46
 * @author Timo Hund <[email protected]>
47
 * @package ApacheSolrForTypo3\Solr\Domain\Search\Suggest
48
 */
49
class SuggestService {
50
51
    /**
52
     * @var TypoScriptFrontendController
53
     */
54
    protected $tsfe;
55
56
    /**
57
     * @var SearchResultSetService
58
     */
59
    protected $searchService;
60
61
    /**
62
     * @var TypoScriptConfiguration
63
     */
64
    protected $typoScriptConfiguration;
65
66
    /**
67
     * @var QueryBuilder
68
     */
69
    protected $queryBuilder;
70
71
    /**
72
     * SuggestService constructor.
73
     * @param TypoScriptFrontendController $tsfe
74
     * @param SearchResultSetService $searchResultSetService
75
     * @param QueryBuilder|null $queryBuilder
76
     */
77 4
    public function __construct(TypoScriptFrontendController $tsfe, SearchResultSetService $searchResultSetService, TypoScriptConfiguration $typoScriptConfiguration, QueryBuilder $queryBuilder = null)
78
    {
79 4
        $this->tsfe = $tsfe;
80 4
        $this->searchService = $searchResultSetService;
81 4
        $this->typoScriptConfiguration = $typoScriptConfiguration;
82 4
        $this->queryBuilder = $queryBuilder ?? GeneralUtility::makeInstance(QueryBuilder::class, /** @scrutinizer ignore-type */ $typoScriptConfiguration);
83 4
    }
84
85
    /**
86
     * Build an array structure of the suggestions.
87
     *
88
     * @param SearchRequest $searchRequest
89
     * @param array $additionalFilters
90
     * @return array
91
     */
92 4
    public function getSuggestions(SearchRequest $searchRequest, array $additionalFilters = []) : array
93
    {
94 4
        $requestId = (int)$this->tsfe->getRequestedId();
95 4
        $groupList = (string)$this->tsfe->gr_list;
0 ignored issues
show
Deprecated Code introduced by
The property TYPO3\CMS\Frontend\Contr...endController::$gr_list has been deprecated: since TYPO3 v9.4, will be removed in TYPO3 v10.0. User the information within the context "frontend.user" aspect. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

95
        $groupList = (string)/** @scrutinizer ignore-deprecated */ $this->tsfe->gr_list;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
96
97 4
        $suggestQuery = $this->queryBuilder->buildSuggestQuery($searchRequest->getRawUserQuery(), $additionalFilters, $requestId, $groupList);
98 4
        $solrSuggestions = $this->getSolrSuggestions($suggestQuery);
99
100 4
        if ($solrSuggestions === []) {
101 1
            return ['status' => false];
102
        }
103
104 3
        $maxSuggestions = $this->typoScriptConfiguration->getSuggestNumberOfSuggestions();
105 3
        $showTopResults = $this->typoScriptConfiguration->getSuggestShowTopResults();
106 3
        $suggestions    = $this->getSuggestionArray($suggestQuery, $solrSuggestions, $maxSuggestions);
107
108 3
        if (!$showTopResults) {
109 1
            return $this->getResultArray($searchRequest, $suggestions, [], false);
110
        }
111
112 2
        return $this->addTopResultsToSuggestions($searchRequest, $suggestions, $additionalFilters);
113
    }
114
115
    /**
116
     * Determines the top results and adds them to the suggestions.
117
     *
118
     * @param SearchRequest $searchRequest
119
     * @param array $suggestions
120
     * @param array $additionalFilters
121
     * @return array
122
     */
123 2
    protected function addTopResultsToSuggestions(SearchRequest $searchRequest, $suggestions, array $additionalFilters) : array
124
    {
125 2
        $maxDocuments = $this->typoScriptConfiguration->getSuggestNumberOfTopResults();
126
127
        // perform the current search.
128 2
        $searchRequest->setResultsPerPage($maxDocuments);
129 2
        $searchRequest->setAdditionalFilters($additionalFilters);
130
131 2
        $didASecondSearch = false;
132 2
        $documents = [];
133
134 2
        $searchResultSet = $this->doASearch($searchRequest);
135 2
        $results = $searchResultSet->getSearchResults();
136 2
        if (count($results) > 0) {
137 1
            $documents = $this->addDocumentsWhenLimitNotReached($documents, $results, $maxDocuments);
138
        }
139
140 2
        $suggestionKeys = array_keys($suggestions);
141 2
        $bestSuggestion = reset($suggestionKeys);
142 2
        $bestSuggestionRequest = $searchRequest->getCopyForSubRequest();
143 2
        $bestSuggestionRequest->setRawQueryString($bestSuggestion);
144 2
        $bestSuggestionRequest->setResultsPerPage($maxDocuments);
145 2
        $bestSuggestionRequest->setAdditionalFilters($additionalFilters);
146
147
        // No results found, use first proposed suggestion to perform the search
148 2
        if (count($documents) === 0 && !empty($suggestions) && ($searchResultSet = $this->doASearch($bestSuggestionRequest)) && count($searchResultSet->getSearchResults()) > 0) {
149 1
            $didASecondSearch = true;
150 1
            $documentsToAdd = $searchResultSet->getSearchResults();
151 1
            $documents = $this->addDocumentsWhenLimitNotReached($documents, $documentsToAdd, $maxDocuments);
152
        }
153
154 2
        return $this->getResultArray($searchRequest, $suggestions, $documents, $didASecondSearch);
155
    }
156
157
    /**
158
     * Retrieves the suggestions from the solr server.
159
     *
160
     * @param SuggestQuery $suggestQuery
161
     * @return array
162
     */
163 1
    protected function getSolrSuggestions(SuggestQuery $suggestQuery) : array
164
    {
165 1
        $pageId = $this->tsfe->getRequestedId();
166 1
        $languageId = $this->tsfe->sys_language_uid;
0 ignored issues
show
Deprecated Code introduced by
The property TYPO3\CMS\Frontend\Contr...ller::$sys_language_uid has been deprecated: since TYPO3 v9.4, will be removed in TYPO3 v10.0 - use LanguageAspect->getId() instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

166
        $languageId = /** @scrutinizer ignore-deprecated */ $this->tsfe->sys_language_uid;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
167 1
        $solr = GeneralUtility::makeInstance(ConnectionManager::class)->getConnectionByPageId($pageId, $languageId);
168 1
        $search = GeneralUtility::makeInstance(Search::class, /** @scrutinizer ignore-type */ $solr);
169 1
        $response = $search->search($suggestQuery, 0, 0);
170
171 1
        $rawResponse = $response->getRawResponse();
172 1
        $results = json_decode($rawResponse);
173 1
        $suggestConfig = $this->typoScriptConfiguration->getObjectByPath('plugin.tx_solr.suggest.');
174 1
        $facetSuggestions = $results->facet_counts->facet_fields->{$suggestConfig['suggestField']};
175 1
        $facetSuggestions = ParsingUtil::getMapArrayFromFlatArray($facetSuggestions);
176
177 1
        return $facetSuggestions ?? [];
178
    }
179
180
    /**
181
     * Extracts the suggestions from solr as array.
182
     *
183
     * @param SuggestQuery $suggestQuery
184
     * @param array $solrSuggestions
185
     * @param integer $maxSuggestions
186
     * @return array
187
     */
188 3
    protected function getSuggestionArray(SuggestQuery $suggestQuery, $solrSuggestions, $maxSuggestions) : array
189
    {
190 3
        $queryString = $suggestQuery->getQuery();
191 3
        $suggestionCount = 0;
192 3
        $suggestions = [];
193 3
        foreach ($solrSuggestions as $string => $count) {
194 3
            $suggestion = trim($queryString . ' ' . $string);
195 3
            $suggestions[$suggestion] = $count;
196 3
            $suggestionCount++;
197 3
            if ($suggestionCount === $maxSuggestions) {
198 3
                return $suggestions;
199
            }
200
        }
201
202 3
        return $suggestions;
203
    }
204
205
    /**
206
     * Adds documents from a collection to the result collection as soon as the limit is not reached.
207
     *
208
     * @param array $documents
209
     * @param SearchResultCollection $documentsToAdd
210
     * @param integer $maxDocuments
211
     * @return array
212
     */
213 2
    protected function addDocumentsWhenLimitNotReached(array $documents, SearchResultCollection $documentsToAdd, int $maxDocuments)  : array
214
    {
215 2
        $additionalTopResultsFields = $this->typoScriptConfiguration->getSuggestAdditionalTopResultsFields();
216
        /** @var SearchResult $document */
217 2
        foreach ($documentsToAdd as $document) {
218 2
            $documents[] = $this->getDocumentAsArray($document, $additionalTopResultsFields);
219 2
            if (count($documents) >= $maxDocuments) {
220 2
                return $documents;
221
            }
222
        }
223
224 1
        return $documents;
225
    }
226
227
    /**
228
     * Creates an array representation of the result and returns it.
229
     *
230
     * @param SearchResult $document
231
     * @param array $additionalTopResultsFields
232
     * @return array
233
     */
234 2
    protected function getDocumentAsArray(SearchResult $document, $additionalTopResultsFields = []) : array
235
    {
236
        $fields = [
237 2
            'link' => $document->getUrl(),
238 2
            'type' => $document['type_stringS'] ? $document['type_stringS'] : $document->getType(),
239 2
            'title' => $document->getTitle(),
240 2
            'content' => $document->getContent(),
241 2
            'group' => $document->getHasGroupItem() ? $document->getGroupItem()->getGroupValue() : '',
242 2
            'previewImage' => $document['previewImage_stringS'] ? $document['previewImage_stringS'] : '',
243
        ];
244 2
        foreach ($additionalTopResultsFields as $additionalTopResultsField) {
245
            $fields[$additionalTopResultsField] = $document[$additionalTopResultsField] ? $document[$additionalTopResultsField] : '';
246
        }
247 2
        return $fields;
248
    }
249
250
    /**
251
     * Runs a search and returns the results.
252
     *
253
     * @param SearchRequest $searchRequest
254
     * @return \ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\SearchResultSet
255
     */
256 2
    protected function doASearch($searchRequest) : SearchResultSet
257
    {
258 2
        return $this->searchService->search($searchRequest);
259
    }
260
261
    /**
262
     * Creates an result array with the required fields.
263
     *
264
     * @param SearchRequest $searchRequest
265
     * @param array $suggestions
266
     * @param array $documents
267
     * @param boolean $didASecondSearch
268
     * @return array
269
     */
270 3
    protected function getResultArray(SearchRequest $searchRequest, $suggestions, $documents, $didASecondSearch) : array
271
    {
272 3
        return ['suggestions' => $suggestions, 'suggestion' => $searchRequest->getRawUserQuery(), 'documents' => $documents, 'didSecondSearch' => $didASecondSearch];
273
    }
274
275
276
}
277