Complex classes like SolrService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use SolrService, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 41 | class SolrService extends \Apache_Solr_Service |
||
| 42 | { |
||
| 43 | const LUKE_SERVLET = 'admin/luke'; |
||
| 44 | const SYSTEM_SERVLET = 'admin/system'; |
||
| 45 | const PLUGINS_SERVLET = 'admin/plugins'; |
||
| 46 | const CORES_SERVLET = 'admin/cores'; |
||
| 47 | const SCHEMA_SERVLET = 'schema'; |
||
| 48 | const SYNONYMS_SERVLET = 'schema/analysis/synonyms/'; |
||
| 49 | const STOPWORDS_SERVLET = 'schema/analysis/stopwords/'; |
||
| 50 | |||
| 51 | const SCHEME_HTTP = 'http'; |
||
| 52 | const SCHEME_HTTPS = 'https'; |
||
| 53 | |||
| 54 | /** |
||
| 55 | * Server connection scheme. http or https. |
||
| 56 | * |
||
| 57 | * @var string |
||
| 58 | */ |
||
| 59 | protected $_scheme = self::SCHEME_HTTP; |
||
| 60 | |||
| 61 | /** |
||
| 62 | * Constructed servlet URL for Luke |
||
| 63 | * |
||
| 64 | * @var string |
||
| 65 | */ |
||
| 66 | protected $_lukeUrl; |
||
| 67 | |||
| 68 | /** |
||
| 69 | * Constructed servlet URL for plugin information |
||
| 70 | * |
||
| 71 | * @var string |
||
| 72 | */ |
||
| 73 | protected $_pluginsUrl; |
||
| 74 | |||
| 75 | /** |
||
| 76 | * @var string |
||
| 77 | */ |
||
| 78 | protected $_coresUrl; |
||
| 79 | |||
| 80 | /** |
||
| 81 | * @var string |
||
| 82 | */ |
||
| 83 | protected $_extractUrl; |
||
| 84 | |||
| 85 | /** |
||
| 86 | * @var string |
||
| 87 | */ |
||
| 88 | protected $_synonymsUrl; |
||
| 89 | |||
| 90 | /** |
||
| 91 | * @var string |
||
| 92 | */ |
||
| 93 | protected $_stopWordsUrl; |
||
| 94 | |||
| 95 | /** |
||
| 96 | * @var string |
||
| 97 | */ |
||
| 98 | protected $_schemaUrl; |
||
| 99 | |||
| 100 | /** |
||
| 101 | * @var bool |
||
| 102 | */ |
||
| 103 | protected $debug = false; |
||
| 104 | |||
| 105 | /** |
||
| 106 | * @var \Apache_Solr_Response |
||
| 107 | */ |
||
| 108 | protected $responseCache = null; |
||
| 109 | |||
| 110 | /** |
||
| 111 | * @var bool |
||
| 112 | */ |
||
| 113 | protected $hasSearched = false; |
||
| 114 | |||
| 115 | /** |
||
| 116 | * @var array |
||
| 117 | */ |
||
| 118 | protected $lukeData = []; |
||
| 119 | |||
| 120 | protected $systemData = null; |
||
| 121 | protected $pluginsData = null; |
||
| 122 | |||
| 123 | protected $solrconfigName = null; |
||
| 124 | |||
| 125 | /** |
||
| 126 | * @var TypoScriptConfiguration |
||
| 127 | */ |
||
| 128 | protected $configuration; |
||
| 129 | |||
| 130 | /** |
||
| 131 | * @var array |
||
| 132 | */ |
||
| 133 | protected static $pingCache = []; |
||
| 134 | |||
| 135 | /** |
||
| 136 | * @var SynonymParser |
||
| 137 | */ |
||
| 138 | protected $synonymParser = null; |
||
| 139 | |||
| 140 | /** |
||
| 141 | * @var StopWordParser |
||
| 142 | */ |
||
| 143 | protected $stopWordParser = null; |
||
| 144 | |||
| 145 | /** |
||
| 146 | * @var SchemaParser |
||
| 147 | */ |
||
| 148 | protected $schemaParser = null; |
||
| 149 | |||
| 150 | /** |
||
| 151 | * @var Schema |
||
| 152 | */ |
||
| 153 | protected $schema; |
||
| 154 | |||
| 155 | /** |
||
| 156 | * @var \ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager |
||
| 157 | */ |
||
| 158 | protected $logger = null; |
||
| 159 | |||
| 160 | /** |
||
| 161 | * Constructor |
||
| 162 | * |
||
| 163 | * @param string $host Solr host |
||
| 164 | * @param string $port Solr port |
||
| 165 | * @param string $path Solr path |
||
| 166 | * @param string $scheme Scheme, defaults to http, can be https |
||
| 167 | * @param TypoScriptConfiguration $typoScriptConfiguration |
||
| 168 | * @param SynonymParser $synonymParser |
||
| 169 | * @param StopWordParser $stopWordParser |
||
| 170 | * @param SchemaParser $schemaParser |
||
| 171 | */ |
||
| 172 | 85 | public function __construct( |
|
| 173 | $host = '', |
||
| 174 | $port = '8983', |
||
| 175 | $path = '/solr/', |
||
| 176 | $scheme = 'http', |
||
| 177 | TypoScriptConfiguration $typoScriptConfiguration = null, |
||
| 178 | SynonymParser $synonymParser = null, |
||
| 179 | StopWordParser $stopWordParser = null, |
||
| 180 | SchemaParser $schemaParser = null |
||
| 181 | ) { |
||
| 182 | 85 | $this->logger = GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__); |
|
| 183 | |||
| 184 | 85 | $this->setScheme($scheme); |
|
| 185 | 84 | $this->configuration = is_null($typoScriptConfiguration) ? Util::getSolrConfiguration() : $typoScriptConfiguration; |
|
| 186 | 84 | $this->synonymParser = is_null($synonymParser) ? GeneralUtility::makeInstance(SynonymParser::class) : $synonymParser; |
|
| 187 | 84 | $this->stopWordParser = is_null($stopWordParser) ? GeneralUtility::makeInstance(StopWordParser::class) : $stopWordParser; |
|
| 188 | 84 | $this->schemaParser = is_null($schemaParser) ? GeneralUtility::makeInstance(SchemaParser::class) : $schemaParser; |
|
| 189 | |||
| 190 | 84 | $this->initializeTimeoutFromConfiguration(); |
|
| 191 | |||
| 192 | 84 | parent::__construct($host, $port, $path); |
|
| 193 | 84 | } |
|
| 194 | |||
| 195 | /** |
||
| 196 | * Initializes the timeout from TypoScript when configuration is present. |
||
| 197 | * |
||
| 198 | * @return void |
||
| 199 | */ |
||
| 200 | 84 | protected function initializeTimeoutFromConfiguration() |
|
| 201 | { |
||
| 202 | 84 | $timeout = $this->configuration->getSolrTimeout(); |
|
| 203 | 84 | if ($timeout > 0) { |
|
| 204 | 1 | $this->getHttpTransport()->setDefaultTimeout($timeout); |
|
| 205 | } |
||
| 206 | 84 | } |
|
| 207 | |||
| 208 | /** |
||
| 209 | * Creates a string representation of the Solr connection. Specifically |
||
| 210 | * will return the Solr URL. |
||
| 211 | * |
||
| 212 | * @return string The Solr URL. |
||
| 213 | */ |
||
| 214 | 53 | public function __toString() |
|
| 215 | { |
||
| 216 | 53 | return $this->_scheme . '://' . $this->_host . ':' . $this->_port . $this->_path; |
|
| 217 | } |
||
| 218 | |||
| 219 | /** |
||
| 220 | * Returns the current time in milliseconds. |
||
| 221 | * |
||
| 222 | * @return double |
||
| 223 | */ |
||
| 224 | 1 | protected function getMilliseconds() |
|
| 225 | { |
||
| 226 | 1 | return GeneralUtility::milliseconds(); |
|
| 227 | } |
||
| 228 | |||
| 229 | /** |
||
| 230 | * Performs a search. |
||
| 231 | * |
||
| 232 | * @param string $query query string / search term |
||
| 233 | * @param int $offset result offset for pagination |
||
| 234 | * @param int $limit number of results to retrieve |
||
| 235 | * @param array $params additional HTTP GET parameters |
||
| 236 | * @param string $method The HTTP method (Apache_Solr_Service::METHOD_GET or Apache_Solr_Service::METHOD::POST) |
||
| 237 | * @return \Apache_Solr_Response Solr response |
||
| 238 | * @throws \RuntimeException if Solr returns a HTTP status code other than 200 |
||
| 239 | */ |
||
| 240 | 27 | public function search($query, $offset = 0, $limit = 10, $params = [], $method = self::METHOD_GET) |
|
| 241 | { |
||
| 242 | 27 | $response = parent::search($query, $offset, $limit, $params, $method); |
|
| 243 | 27 | $this->hasSearched = true; |
|
| 244 | |||
| 245 | 27 | $this->responseCache = $response; |
|
| 246 | |||
| 247 | 27 | if ($response->getHttpStatus() != 200) { |
|
| 248 | 1 | throw new \RuntimeException( |
|
| 249 | 'Invalid query. Solr returned an error: ' |
||
| 250 | 1 | . $response->getHttpStatus() . ' ' |
|
| 251 | 1 | . $response->getHttpStatusMessage(), |
|
| 252 | 1 | 1293109870 |
|
| 253 | ); |
||
| 254 | } |
||
| 255 | |||
| 256 | 26 | return $response; |
|
| 257 | } |
||
| 258 | |||
| 259 | /** |
||
| 260 | * Call the /admin/ping servlet, can be used to quickly tell if a connection to the |
||
| 261 | * server is available. |
||
| 262 | * |
||
| 263 | * Simply overrides the SolrPhpClient implementation, changing ping from a |
||
| 264 | * HEAD to a GET request, see http://forge.typo3.org/issues/44167 |
||
| 265 | * |
||
| 266 | * Also does not report the time, see https://forge.typo3.org/issues/64551 |
||
| 267 | * |
||
| 268 | * @param int $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2) |
||
| 269 | * @param boolean $useCache indicates if the ping result should be cached in the instance or not |
||
| 270 | * @return bool TRUE if Solr can be reached, FALSE if not |
||
| 271 | */ |
||
| 272 | 50 | public function ping($timeout = 2, $useCache = true) |
|
| 273 | { |
||
| 274 | 50 | $httpResponse = $this->performPingRequest($timeout, $useCache); |
|
| 275 | 50 | return ($httpResponse->getStatusCode() === 200); |
|
| 276 | } |
||
| 277 | |||
| 278 | /** |
||
| 279 | * Call the /admin/ping servlet, can be used to get the runtime of a ping request. |
||
| 280 | * |
||
| 281 | * @param int $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2) |
||
| 282 | * @param boolean $useCache indicates if the ping result should be cached in the instance or not |
||
| 283 | * @return double runtime in milliseconds |
||
| 284 | * @throws \ApacheSolrForTypo3\Solr\PingFailedException |
||
| 285 | */ |
||
| 286 | 1 | public function getPingRoundTripRuntime($timeout = 2, $useCache = true) |
|
| 287 | { |
||
| 288 | 1 | $start = $this->getMilliseconds(); |
|
| 289 | 1 | $httpResponse = $this->performPingRequest($timeout, $useCache); |
|
| 290 | 1 | $end = $this->getMilliseconds(); |
|
| 291 | |||
| 292 | 1 | if ($httpResponse->getStatusCode() !== 200) { |
|
| 293 | $message = 'Solr ping failed with unexpected response code: ' . $httpResponse->getStatusCode(); |
||
| 294 | /** @var $exception \ApacheSolrForTypo3\Solr\PingFailedException */ |
||
| 295 | $exception = GeneralUtility::makeInstance(PingFailedException::class, $message); |
||
| 296 | $exception->setHttpResponse($httpResponse); |
||
| 297 | throw $exception; |
||
| 298 | } |
||
| 299 | |||
| 300 | 1 | return $end - $start; |
|
| 301 | } |
||
| 302 | |||
| 303 | /** |
||
| 304 | * Performs a ping request and returns the result. |
||
| 305 | * |
||
| 306 | * @param int $timeout |
||
| 307 | * @param boolean $useCache indicates if the ping result should be cached in the instance or not |
||
| 308 | * @return \Apache_Solr_HttpTransport_Response |
||
| 309 | */ |
||
| 310 | 51 | protected function performPingRequest($timeout = 2, $useCache = true) |
|
| 311 | { |
||
| 312 | 51 | $cacheKey = (string)($this); |
|
| 313 | 51 | if ($useCache && isset(static::$pingCache[$cacheKey])) { |
|
| 314 | 44 | return static::$pingCache[$cacheKey]; |
|
| 315 | } |
||
| 316 | |||
| 317 | 51 | $pingResult = $this->getHttpTransport()->performGetRequest($this->_pingUrl, $timeout); |
|
| 318 | |||
| 319 | 51 | if ($useCache) { |
|
| 320 | 50 | static::$pingCache[$cacheKey] = $pingResult; |
|
| 321 | } |
||
| 322 | |||
| 323 | 51 | return $pingResult; |
|
| 324 | } |
||
| 325 | |||
| 326 | /** |
||
| 327 | * Performs a content and meta data extraction request. |
||
| 328 | * |
||
| 329 | * @param ExtractingQuery $query An extraction query |
||
| 330 | * @return array An array containing the extracted content [0] and meta data [1] |
||
| 331 | */ |
||
| 332 | 1 | public function extractByQuery(ExtractingQuery $query) |
|
| 333 | { |
||
| 334 | $headers = [ |
||
| 335 | 1 | 'Content-Type' => 'multipart/form-data; boundary=' . $query->getMultiPartPostDataBoundary() |
|
| 336 | ]; |
||
| 337 | |||
| 338 | try { |
||
| 339 | 1 | $response = $this->requestServlet( |
|
| 340 | 1 | self::EXTRACT_SERVLET, |
|
| 341 | 1 | $query->getQueryParameters(), |
|
| 342 | 1 | 'POST', |
|
| 343 | $headers, |
||
| 344 | 1 | $query->getRawPostFileData() |
|
| 345 | ); |
||
| 346 | } catch (\Exception $e) { |
||
| 347 | $this->logger->log( |
||
| 348 | SolrLogManager::ERROR, |
||
| 349 | 'Extracting text and meta data through Solr Cell over HTTP POST', |
||
| 350 | [ |
||
| 351 | 'query' => (array)$query, |
||
| 352 | 'parameters' => $query->getQueryParameters(), |
||
| 353 | 'file' => $query->getFile(), |
||
| 354 | 'headers' => $headers, |
||
| 355 | 'query url' => self::EXTRACT_SERVLET, |
||
| 356 | 'exception' => $e->getMessage() |
||
| 357 | ] |
||
| 358 | ); |
||
| 359 | } |
||
| 360 | |||
| 361 | return [ |
||
| 362 | 1 | $response->extracted, |
|
| 363 | 1 | (array)$response->extracted_metadata |
|
| 364 | ]; |
||
| 365 | } |
||
| 366 | |||
| 367 | /** |
||
| 368 | * Make a request to a servlet (a path) that's not a standard path. |
||
| 369 | * |
||
| 370 | * @param string $servlet Path to be added to the base Solr path. |
||
| 371 | * @param array $parameters Optional, additional request parameters when constructing the URL. |
||
| 372 | * @param string $method HTTP method to use, defaults to GET. |
||
| 373 | * @param array $requestHeaders Key value pairs of header names and values. Should include 'Content-Type' for POST and PUT. |
||
| 374 | * @param string $rawPost Must be an empty string unless method is POST or PUT. |
||
| 375 | * @param float|bool $timeout Read timeout in seconds, defaults to FALSE. |
||
| 376 | * @return \Apache_Solr_Response Response object |
||
| 377 | * @throws \Apache_Solr_HttpTransportException if returned HTTP status is other than 200 |
||
| 378 | */ |
||
| 379 | 1 | public function requestServlet($servlet, $parameters = [], $method = 'GET', $requestHeaders = [], $rawPost = '', $timeout = false) |
|
| 380 | { |
||
| 381 | // Add default parameters |
||
| 382 | 1 | $parameters['wt'] = self::SOLR_WRITER; |
|
| 383 | 1 | $parameters['json.nl'] = $this->_namedListTreatment; |
|
| 384 | 1 | $url = $this->_constructUrl($servlet, $parameters); |
|
| 385 | |||
| 386 | 1 | $httpResponse = $this->getResponseFromTransport($url, $method, $requestHeaders, $rawPost, $timeout); |
|
| 387 | 1 | $solrResponse = new \Apache_Solr_Response($httpResponse, $this->_createDocuments, $this->_collapseSingleValueArrays); |
|
| 388 | 1 | if ($solrResponse->getHttpStatus() != 200) { |
|
| 389 | throw new \Apache_Solr_HttpTransportException($solrResponse); |
||
| 390 | } |
||
| 391 | |||
| 392 | 1 | return $solrResponse; |
|
| 393 | } |
||
| 394 | |||
| 395 | /** |
||
| 396 | * Decides which transport method to used, depending on the request method and retrieves the response. |
||
| 397 | * |
||
| 398 | * @param string $url |
||
| 399 | * @param string $method |
||
| 400 | * @param array $requestHeaders |
||
| 401 | * @param string $rawPost |
||
| 402 | * @param float|bool $timeout |
||
| 403 | * @return \Apache_Solr_HttpTransport_Response |
||
| 404 | */ |
||
| 405 | 1 | protected function getResponseFromTransport($url, $method, $requestHeaders, $rawPost, $timeout) |
|
| 406 | { |
||
| 407 | 1 | $httpTransport = $this->getHttpTransport(); |
|
| 408 | |||
| 409 | 1 | if ($method == self::METHOD_GET) { |
|
| 410 | return $httpTransport->performGetRequest($url, $timeout); |
||
| 411 | } |
||
| 412 | 1 | if ($method == self::METHOD_POST) { |
|
| 413 | // FIXME should respect all headers, not only Content-Type |
||
| 414 | 1 | return $httpTransport->performPostRequest($url, $rawPost, $requestHeaders['Content-Type'], $timeout); |
|
| 415 | } |
||
| 416 | |||
| 417 | throw new \InvalidArgumentException('$method should be GET or POST'); |
||
| 418 | } |
||
| 419 | |||
| 420 | /** |
||
| 421 | * Return a valid http URL given this server's scheme, host, port, and path |
||
| 422 | * and a provided servlet name. |
||
| 423 | * |
||
| 424 | * @param string $servlet Servlet name |
||
| 425 | * @param array $params Additional URL parameters to attach to the end of the URL |
||
| 426 | * @return string Servlet URL |
||
| 427 | */ |
||
| 428 | 84 | protected function _constructUrl($servlet, $params = []) |
|
| 429 | { |
||
| 430 | 84 | $url = parent::_constructUrl($servlet, $params); |
|
| 431 | |||
| 432 | 84 | if (!GeneralUtility::isFirstPartOfStr($url, $this->_scheme)) { |
|
| 433 | 2 | $parsedUrl = parse_url($url); |
|
| 434 | |||
| 435 | // unfortunately can't use str_replace as it replace all |
||
| 436 | // occurrences of $needle and can't be limited to replace only once |
||
| 437 | 2 | $url = $this->_scheme . substr($url, strlen($parsedUrl['scheme'])); |
|
| 438 | } |
||
| 439 | |||
| 440 | 84 | return $url; |
|
| 441 | } |
||
| 442 | |||
| 443 | /** |
||
| 444 | * Returns the set scheme |
||
| 445 | * |
||
| 446 | * @return string |
||
| 447 | */ |
||
| 448 | 1 | public function getScheme() |
|
| 452 | |||
| 453 | /** |
||
| 454 | * Set the scheme used. If empty will fallback to constants |
||
| 455 | * |
||
| 456 | * @param string $scheme Either http or https |
||
| 457 | * @throws \UnexpectedValueException |
||
| 458 | */ |
||
| 459 | 85 | public function setScheme($scheme) |
|
| 460 | { |
||
| 461 | // Use the provided scheme or use the default |
||
| 462 | 85 | if (empty($scheme)) { |
|
| 463 | 2 | throw new \UnexpectedValueException('Scheme parameter is empty', 1380756390); |
|
| 464 | } |
||
| 465 | |||
| 466 | 84 | $isHttpOrHttps = in_array($scheme, [self::SCHEME_HTTP, self::SCHEME_HTTPS]); |
|
| 467 | 84 | if (!$isHttpOrHttps) { |
|
| 468 | 1 | throw new \UnexpectedValueException('Unsupported scheme parameter, scheme must be http or https', 1380756442); |
|
| 469 | } |
||
| 470 | |||
| 471 | // we have a valid scheme |
||
| 472 | 84 | $this->_scheme = $scheme; |
|
| 473 | |||
| 474 | 84 | if ($this->_urlsInited) { |
|
| 475 | 1 | $this->_initUrls(); |
|
| 476 | } |
||
| 477 | 84 | } |
|
| 478 | |||
| 479 | /** |
||
| 480 | * get field meta data for the index |
||
| 481 | * |
||
| 482 | * @param int $numberOfTerms Number of top terms to fetch for each field |
||
| 483 | * @return array |
||
| 484 | */ |
||
| 485 | public function getFieldsMetaData($numberOfTerms = 0) |
||
| 486 | { |
||
| 487 | return $this->getLukeMetaData($numberOfTerms)->fields; |
||
| 488 | } |
||
| 489 | |||
| 490 | /** |
||
| 491 | * Retrieves meta data about the index from the luke request handler |
||
| 492 | * |
||
| 493 | * @param int $numberOfTerms Number of top terms to fetch for each field |
||
| 494 | * @return \Apache_Solr_Response Index meta data |
||
| 495 | */ |
||
| 496 | public function getLukeMetaData($numberOfTerms = 0) |
||
| 497 | { |
||
| 498 | if (!isset($this->lukeData[$numberOfTerms])) { |
||
| 499 | $lukeUrl = $this->_constructUrl( |
||
| 500 | self::LUKE_SERVLET, |
||
| 501 | [ |
||
| 502 | 'numTerms' => $numberOfTerms, |
||
| 503 | 'wt' => self::SOLR_WRITER, |
||
| 504 | 'fl' => '*' |
||
| 505 | ] |
||
| 506 | ); |
||
| 507 | |||
| 508 | $this->lukeData[$numberOfTerms] = $this->_sendRawGet($lukeUrl); |
||
| 509 | } |
||
| 510 | |||
| 511 | return $this->lukeData[$numberOfTerms]; |
||
| 512 | } |
||
| 513 | |||
| 514 | /** |
||
| 515 | * Central method for making a get operation against this Solr Server |
||
| 516 | * |
||
| 517 | * @param string $url |
||
| 518 | * @param float|bool $timeout Read timeout in seconds |
||
| 519 | * @return \Apache_Solr_Response |
||
| 520 | */ |
||
| 521 | 37 | protected function _sendRawGet($url, $timeout = false) |
|
| 522 | { |
||
| 523 | 37 | $logSeverity = SolrLogManager::INFO; |
|
| 524 | |||
| 525 | try { |
||
| 526 | 37 | $response = parent::_sendRawGet($url, $timeout); |
|
| 527 | 2 | } catch (Apache_Solr_HttpTransportException $e) { |
|
|
|
|||
| 528 | 2 | $logSeverity = SolrLogManager::ERROR; |
|
| 529 | 2 | $response = $e->getResponse(); |
|
| 530 | } |
||
| 531 | |||
| 532 | 37 | if ($this->configuration->getLoggingQueryRawGet() || $response->getHttpStatus() != 200) { |
|
| 533 | $logData = [ |
||
| 534 | 2 | 'query url' => $url, |
|
| 535 | 2 | 'response' => (array)$response |
|
| 536 | ]; |
||
| 537 | |||
| 538 | 2 | if (!empty($e)) { |
|
| 539 | 2 | $logData['exception'] = $e->__toString(); |
|
| 540 | } else { |
||
| 541 | // trigger data parsing |
||
| 542 | $response->response; |
||
| 543 | $logData['response data'] = print_r($response, true); |
||
| 544 | } |
||
| 545 | |||
| 546 | 2 | $this->logger->log( |
|
| 547 | $logSeverity, |
||
| 548 | 2 | 'Querying Solr using GET', |
|
| 549 | $logData |
||
| 550 | ); |
||
| 551 | } |
||
| 552 | |||
| 553 | 37 | return $response; |
|
| 554 | } |
||
| 555 | |||
| 556 | /** |
||
| 557 | * Returns whether a search has been executed or not. |
||
| 558 | * |
||
| 559 | * @return bool TRUE if a search has been executed, FALSE otherwise |
||
| 560 | */ |
||
| 561 | public function hasSearched() |
||
| 562 | { |
||
| 563 | return $this->hasSearched; |
||
| 564 | } |
||
| 565 | |||
| 566 | /** |
||
| 567 | * Gets the most recent response (if any) |
||
| 568 | * |
||
| 569 | * @return \Apache_Solr_Response Most recent response, or NULL if a search has not been executed yet. |
||
| 570 | */ |
||
| 571 | 1 | public function getResponse() |
|
| 572 | { |
||
| 573 | 1 | return $this->responseCache; |
|
| 574 | } |
||
| 575 | |||
| 576 | /** |
||
| 577 | * Enable/Disable debug mode |
||
| 578 | * |
||
| 579 | * @param bool $debug TRUE to enable debug mode, FALSE to turn off, off by default |
||
| 580 | */ |
||
| 581 | public function setDebug($debug) |
||
| 582 | { |
||
| 583 | $this->debug = (boolean)$debug; |
||
| 584 | } |
||
| 585 | |||
| 586 | /** |
||
| 587 | * Gets information about the plugins installed in Solr |
||
| 588 | * |
||
| 589 | * @return array A nested array of plugin data. |
||
| 590 | */ |
||
| 591 | 1 | public function getPluginsInformation() |
|
| 592 | { |
||
| 593 | 1 | if (empty($this->pluginsData)) { |
|
| 594 | 1 | $pluginsInformation = $this->_sendRawGet($this->_pluginsUrl); |
|
| 595 | |||
| 596 | // access a random property to trigger response parsing |
||
| 597 | 1 | $pluginsInformation->responseHeader; |
|
| 598 | 1 | $this->pluginsData = $pluginsInformation; |
|
| 599 | } |
||
| 600 | |||
| 601 | 1 | return $this->pluginsData; |
|
| 602 | } |
||
| 603 | |||
| 604 | /** |
||
| 605 | * Gets the name of the schema.xml file installed and in use on the Solr |
||
| 606 | * server. |
||
| 607 | * |
||
| 608 | * @deprecated use getSchema()->getName() instead will be removed in 7.0 |
||
| 609 | * @return string Name of the active schema.xml |
||
| 610 | */ |
||
| 611 | public function getSchemaName() |
||
| 612 | { |
||
| 613 | GeneralUtility::logDeprecatedFunction(); |
||
| 614 | return $this->getSchema()->getName(); |
||
| 615 | } |
||
| 616 | |||
| 617 | /** |
||
| 618 | * Gets information about the Solr server |
||
| 619 | * |
||
| 620 | * @return array A nested array of system data. |
||
| 621 | */ |
||
| 622 | 3 | public function getSystemInformation() |
|
| 634 | |||
| 635 | /** |
||
| 636 | * Gets the name of the solrconfig.xml file installed and in use on the Solr |
||
| 637 | * server. |
||
| 638 | * |
||
| 639 | * @return string Name of the active solrconfig.xml |
||
| 640 | */ |
||
| 641 | 1 | public function getSolrconfigName() |
|
| 642 | { |
||
| 643 | 1 | if (is_null($this->solrconfigName)) { |
|
| 644 | 1 | $solrconfigXmlUrl = $this->_scheme . '://' |
|
| 645 | 1 | . $this->_host . ':' . $this->_port |
|
| 646 | 1 | . $this->_path . 'admin/file/?file=solrconfig.xml'; |
|
| 647 | 1 | $response = $this->_sendRawGet($solrconfigXmlUrl); |
|
| 648 | |||
| 649 | 1 | $solrconfigXml = simplexml_load_string($response->getRawResponse()); |
|
| 650 | 1 | if ($solrconfigXml === false) { |
|
| 651 | throw new \InvalidArgumentException('No valid xml response from schema file: ' . $solrconfigXmlUrl); |
||
| 652 | } |
||
| 653 | 1 | $this->solrconfigName = (string)$solrconfigXml->attributes()->name; |
|
| 654 | } |
||
| 655 | |||
| 656 | 1 | return $this->solrconfigName; |
|
| 657 | } |
||
| 658 | |||
| 659 | /** |
||
| 660 | * Gets the Solr server's version number. |
||
| 661 | * |
||
| 662 | * @return string Solr version number |
||
| 663 | */ |
||
| 664 | 2 | public function getSolrServerVersion() |
|
| 665 | { |
||
| 666 | 2 | $systemInformation = $this->getSystemInformation(); |
|
| 667 | |||
| 668 | // don't know why $systemInformation->lucene->solr-spec-version won't work |
||
| 669 | 2 | $luceneInformation = (array)$systemInformation->lucene; |
|
| 670 | 2 | return $luceneInformation['solr-spec-version']; |
|
| 671 | } |
||
| 672 | |||
| 673 | /** |
||
| 674 | * Deletes all index documents of a certain type and does a commit |
||
| 675 | * afterwards. |
||
| 676 | * |
||
| 677 | * @param string $type The type of documents to delete, usually a table name. |
||
| 678 | * @param bool $commit Will commit immediately after deleting the documents if set, defaults to TRUE |
||
| 679 | */ |
||
| 680 | public function deleteByType($type, $commit = true) |
||
| 681 | { |
||
| 682 | $this->deleteByQuery('type:' . trim($type)); |
||
| 683 | |||
| 684 | if ($commit) { |
||
| 685 | $this->commit(false, false, false); |
||
| 686 | } |
||
| 687 | } |
||
| 688 | |||
| 689 | /** |
||
| 690 | * Raw Delete Method. Takes a raw post body and sends it to the update service. Body should be |
||
| 691 | * a complete and well formed "delete" xml document |
||
| 692 | * |
||
| 693 | * @param string $rawPost Expected to be utf-8 encoded xml document |
||
| 694 | * @param float|int $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception) |
||
| 695 | * @return \Apache_Solr_Response |
||
| 696 | */ |
||
| 697 | 11 | public function delete($rawPost, $timeout = 3600) |
|
| 698 | { |
||
| 699 | 11 | $response = $this->_sendRawPost($this->_updateUrl, $rawPost, $timeout); |
|
| 700 | |||
| 701 | 11 | $this->logger->log( |
|
| 702 | 11 | SolrLogManager::NOTICE, |
|
| 703 | 11 | 'Delete Query sent.', |
|
| 704 | [ |
||
| 705 | 11 | 'query' => $rawPost, |
|
| 706 | 11 | 'query url' => $this->_updateUrl, |
|
| 707 | 11 | 'response' => (array)$response |
|
| 708 | ] |
||
| 709 | ); |
||
| 710 | |||
| 711 | 11 | return $response; |
|
| 712 | } |
||
| 713 | |||
| 714 | /** |
||
| 715 | * Central method for making a post operation against this Solr Server |
||
| 716 | * |
||
| 717 | * @param string $url |
||
| 718 | * @param string $rawPost |
||
| 719 | * @param float|bool $timeout Read timeout in seconds |
||
| 720 | * @param string $contentType |
||
| 721 | * @return \Apache_Solr_Response |
||
| 722 | */ |
||
| 723 | 61 | protected function _sendRawPost( |
|
| 724 | $url, |
||
| 725 | $rawPost, |
||
| 726 | $timeout = false, |
||
| 727 | $contentType = 'text/xml; charset=UTF-8' |
||
| 728 | ) { |
||
| 729 | 61 | $logSeverity = SolrLogManager::INFO; |
|
| 730 | |||
| 731 | try { |
||
| 732 | 61 | $response = parent::_sendRawPost($url, $rawPost, $timeout, |
|
| 733 | $contentType); |
||
| 734 | } catch (Apache_Solr_HttpTransportException $e) { |
||
| 735 | $logSeverity = SolrLogManager::ERROR; |
||
| 736 | $response = $e->getResponse(); |
||
| 737 | } |
||
| 738 | |||
| 739 | 61 | if ($this->configuration->getLoggingQueryRawPost() || $response->getHttpStatus() != 200) { |
|
| 740 | $logData = [ |
||
| 741 | 'query url' => $url, |
||
| 742 | 'content' => $rawPost, |
||
| 743 | 'response' => (array)$response |
||
| 744 | ]; |
||
| 745 | |||
| 746 | if (!empty($e)) { |
||
| 747 | $logData['exception'] = $e->__toString(); |
||
| 748 | } |
||
| 749 | |||
| 750 | $this->logger->log( |
||
| 751 | $logSeverity, |
||
| 752 | 'Querying Solr using POST', |
||
| 753 | $logData |
||
| 754 | ); |
||
| 755 | } |
||
| 756 | |||
| 757 | 61 | return $response; |
|
| 758 | } |
||
| 759 | |||
| 760 | /** |
||
| 761 | * Get currently configured synonyms |
||
| 762 | * |
||
| 763 | * @param string $baseWord If given a base word, retrieves the synonyms for that word only |
||
| 764 | * @return array |
||
| 765 | */ |
||
| 766 | 1 | public function getSynonyms($baseWord = '') |
|
| 767 | { |
||
| 768 | 1 | $this->initializeSynonymsUrl(); |
|
| 769 | 1 | $synonymsUrl = $this->_synonymsUrl; |
|
| 770 | 1 | if (!empty($baseWord)) { |
|
| 771 | 1 | $synonymsUrl .= '/' . $baseWord; |
|
| 772 | } |
||
| 773 | |||
| 774 | 1 | $response = $this->_sendRawGet($synonymsUrl); |
|
| 775 | 1 | return $this->synonymParser->parseJson($baseWord, $response->getRawResponse()); |
|
| 776 | } |
||
| 777 | |||
| 778 | /** |
||
| 779 | * Add list of synonyms for base word to managed synonyms map |
||
| 780 | * |
||
| 781 | * @param string $baseWord |
||
| 782 | * @param array $synonyms |
||
| 783 | * |
||
| 784 | * @return \Apache_Solr_Response |
||
| 785 | * |
||
| 786 | * @throws \Apache_Solr_InvalidArgumentException If $baseWord or $synonyms are empty |
||
| 787 | */ |
||
| 788 | 1 | public function addSynonym($baseWord, array $synonyms) |
|
| 789 | { |
||
| 790 | 1 | $this->initializeSynonymsUrl(); |
|
| 791 | 1 | $json = $this->synonymParser->toJson($baseWord, $synonyms); |
|
| 792 | 1 | return $this->_sendRawPost($this->_synonymsUrl, $json, |
|
| 793 | 1 | $this->getHttpTransport()->getDefaultTimeout(), 'application/json'); |
|
| 794 | } |
||
| 795 | |||
| 796 | /** |
||
| 797 | * Remove a synonym from the synonyms map |
||
| 798 | * |
||
| 799 | * @param string $baseWord |
||
| 800 | * @return \Apache_Solr_Response |
||
| 801 | * @throws \Apache_Solr_InvalidArgumentException |
||
| 802 | */ |
||
| 803 | 1 | public function deleteSynonym($baseWord) |
|
| 804 | { |
||
| 805 | 1 | $this->initializeSynonymsUrl(); |
|
| 806 | 1 | if (empty($baseWord)) { |
|
| 807 | throw new \Apache_Solr_InvalidArgumentException('Must provide base word.'); |
||
| 808 | } |
||
| 809 | |||
| 810 | 1 | return $this->_sendRawDelete($this->_synonymsUrl . '/' . $baseWord); |
|
| 811 | } |
||
| 812 | |||
| 813 | /** |
||
| 814 | * Central method for making a HTTP DELETE operation against the Solr server |
||
| 815 | * |
||
| 816 | * @param string $url |
||
| 817 | * @param bool|float $timeout Read timeout in seconds |
||
| 818 | * @return \Apache_Solr_Response |
||
| 819 | */ |
||
| 820 | 2 | protected function _sendRawDelete($url, $timeout = false) |
|
| 821 | { |
||
| 822 | 2 | $logSeverity = SolrLogManager::INFO; |
|
| 823 | |||
| 824 | try { |
||
| 825 | 2 | $httpTransport = $this->getHttpTransport(); |
|
| 826 | |||
| 827 | 2 | $httpResponse = $httpTransport->performDeleteRequest($url, |
|
| 828 | $timeout); |
||
| 829 | 2 | $solrResponse = new \Apache_Solr_Response($httpResponse, |
|
| 830 | 2 | $this->_createDocuments, $this->_collapseSingleValueArrays); |
|
| 831 | |||
| 832 | 2 | if ($solrResponse->getHttpStatus() != 200) { |
|
| 833 | 2 | throw new \Apache_Solr_HttpTransportException($solrResponse); |
|
| 834 | } |
||
| 835 | 2 | } catch (Apache_Solr_HttpTransportException $e) { |
|
| 836 | 2 | $logSeverity = SolrLogManager::ERROR; |
|
| 837 | 2 | $solrResponse = $e->getResponse(); |
|
| 838 | } |
||
| 839 | |||
| 840 | 2 | if ($this->configuration->getLoggingQueryRawDelete() || $solrResponse->getHttpStatus() != 200) { |
|
| 841 | $logData = [ |
||
| 842 | 2 | 'query url' => $url, |
|
| 843 | 2 | 'response' => (array)$solrResponse |
|
| 844 | ]; |
||
| 845 | |||
| 846 | 2 | if (!empty($e)) { |
|
| 847 | 2 | $logData['exception'] = $e->__toString(); |
|
| 848 | } else { |
||
| 849 | // trigger data parsing |
||
| 850 | $solrResponse->response; |
||
| 851 | $logData['response data'] = print_r($solrResponse, true); |
||
| 852 | } |
||
| 853 | |||
| 854 | 2 | $this->logger->log( |
|
| 855 | $logSeverity, |
||
| 856 | 2 | 'Querying Solr using DELETE', |
|
| 857 | $logData |
||
| 858 | ); |
||
| 859 | } |
||
| 860 | |||
| 861 | 2 | return $solrResponse; |
|
| 862 | } |
||
| 863 | |||
| 864 | /** |
||
| 865 | * Get currently configured stop words |
||
| 866 | * |
||
| 867 | * @return array |
||
| 868 | */ |
||
| 869 | 2 | public function getStopWords() |
|
| 870 | { |
||
| 871 | 2 | $this->initializeStopWordsUrl(); |
|
| 872 | 2 | $response = $this->_sendRawGet($this->_stopWordsUrl); |
|
| 873 | 2 | return $this->stopWordParser->parseJson($response->getRawResponse()); |
|
| 874 | } |
||
| 875 | |||
| 876 | /** |
||
| 877 | * Adds stop words to the managed stop word list |
||
| 878 | * |
||
| 879 | * @param array|string $stopWords string for a single word, array for multiple words |
||
| 880 | * @return \Apache_Solr_Response |
||
| 881 | * @throws \Apache_Solr_InvalidArgumentException If $stopWords is empty |
||
| 882 | */ |
||
| 883 | 1 | public function addStopWords($stopWords) |
|
| 884 | { |
||
| 885 | 1 | $this->initializeStopWordsUrl(); |
|
| 886 | 1 | $json = $this->stopWordParser->toJson($stopWords); |
|
| 887 | 1 | return $this->_sendRawPost($this->_stopWordsUrl, $json, |
|
| 888 | 1 | $this->getHttpTransport()->getDefaultTimeout(), 'application/json'); |
|
| 889 | } |
||
| 890 | |||
| 891 | /** |
||
| 892 | * Deletes a words from the managed stop word list |
||
| 893 | * |
||
| 894 | * @param string $stopWord stop word to delete |
||
| 895 | * @return \Apache_Solr_Response |
||
| 896 | * @throws \Apache_Solr_InvalidArgumentException If $stopWords is empty |
||
| 897 | */ |
||
| 898 | 1 | public function deleteStopWord($stopWord) |
|
| 899 | { |
||
| 900 | 1 | $this->initializeStopWordsUrl(); |
|
| 901 | 1 | if (empty($stopWord)) { |
|
| 902 | throw new \Apache_Solr_InvalidArgumentException('Must provide stop word.'); |
||
| 903 | } |
||
| 904 | |||
| 905 | 1 | return $this->_sendRawDelete($this->_stopWordsUrl . '/' . $stopWord); |
|
| 906 | } |
||
| 907 | |||
| 908 | /** |
||
| 909 | * Returns the core name from the configured path. |
||
| 910 | * |
||
| 911 | * @return string |
||
| 912 | */ |
||
| 913 | 1 | public function getCoreName() |
|
| 914 | { |
||
| 915 | 1 | $paths = explode('/', trim($this->_path, '/')); |
|
| 916 | |||
| 917 | 1 | return (string)array_pop($paths); |
|
| 918 | } |
||
| 919 | |||
| 920 | /** |
||
| 921 | * Reloads the current core |
||
| 922 | * |
||
| 923 | * @return \Apache_Solr_Response |
||
| 924 | */ |
||
| 925 | 1 | public function reloadCore() |
|
| 926 | { |
||
| 927 | 1 | return $this->reloadCoreByName($this->getCoreName()); |
|
| 928 | } |
||
| 929 | |||
| 930 | /** |
||
| 931 | * Reloads a core of the connection by a given corename. |
||
| 932 | * |
||
| 933 | * @param string $coreName |
||
| 934 | * @return \Apache_Solr_Response |
||
| 935 | */ |
||
| 936 | 1 | public function reloadCoreByName($coreName) |
|
| 941 | |||
| 942 | /** |
||
| 943 | * initializes various URLs, including the Luke URL |
||
| 944 | * |
||
| 945 | * @return void |
||
| 946 | */ |
||
| 947 | 84 | protected function _initUrls() |
|
| 948 | { |
||
| 949 | 84 | parent::_initUrls(); |
|
| 950 | |||
| 951 | 84 | $this->_lukeUrl = $this->_constructUrl( |
|
| 952 | 84 | self::LUKE_SERVLET, |
|
| 953 | [ |
||
| 954 | 84 | 'numTerms' => '0', |
|
| 955 | 84 | 'wt' => self::SOLR_WRITER |
|
| 956 | ] |
||
| 957 | ); |
||
| 958 | |||
| 959 | 84 | $this->_pluginsUrl = $this->_constructUrl( |
|
| 960 | 84 | self::PLUGINS_SERVLET, |
|
| 961 | 84 | ['wt' => self::SOLR_WRITER] |
|
| 962 | ); |
||
| 963 | |||
| 964 | 84 | $pathElements = explode('/', trim($this->_path, '/')); |
|
| 965 | 84 | $this->_coresUrl = |
|
| 966 | 84 | $this->_scheme . '://' . |
|
| 967 | 84 | $this->_host . ':' . |
|
| 968 | 84 | $this->_port . '/' . |
|
| 969 | 84 | $pathElements[0] . '/' . |
|
| 970 | 84 | self::CORES_SERVLET; |
|
| 971 | |||
| 972 | 84 | $this->_schemaUrl = $this->_constructUrl(self::SCHEMA_SERVLET); |
|
| 973 | 84 | } |
|
| 974 | |||
| 975 | /** |
||
| 976 | * @return void |
||
| 977 | */ |
||
| 978 | 1 | protected function initializeSynonymsUrl() |
|
| 985 | |||
| 986 | /** |
||
| 987 | * @return void |
||
| 988 | */ |
||
| 989 | 2 | protected function initializeStopWordsUrl() |
|
| 997 | |||
| 998 | /** |
||
| 999 | * Get the configured schema for the current core. |
||
| 1000 | * |
||
| 1001 | * @return Schema |
||
| 1002 | */ |
||
| 1003 | 4 | public function getSchema() |
|
| 1012 | } |
||
| 1013 |