Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
Searcher
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 4
56
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 performSearch
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
20
 newLog
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 getRelevanceRescoreConfigurations
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace GeoData;
4
5use CirrusSearch\ElasticsearchIntermediary;
6use CirrusSearch\Search\SearchContext;
7use CirrusSearch\SearchConfig;
8use CirrusSearch\SearchRequestLog;
9use Elastica\Exception\ExceptionInterface;
10use Elastica\Exception\ResponseException;
11use Elastica\Search;
12use MediaWiki\Config\ConfigException;
13use MediaWiki\MediaWikiServices;
14use MediaWiki\User\UserIdentity;
15use MediaWiki\WikiMap\WikiMap;
16use StatusValue;
17
18/**
19 * Performs ES searches via CirrusSearch infrastructure
20 */
21class Searcher extends ElasticsearchIntermediary {
22
23    /** @var SearchConfig */
24    private $config;
25
26    /**
27     * @param UserIdentity|null $user
28     * @throws ConfigException
29     */
30    public function __construct( UserIdentity $user = null ) {
31        /** @var SearchConfig $config */
32        $config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'CirrusSearch' );
33        '@phan-var SearchConfig $config';
34        $this->config = $config;
35        $connection = new \CirrusSearch\Connection( $config );
36
37        parent::__construct( $connection, $user, 0 );
38    }
39
40    /**
41     * Perform search
42     *
43     * @param \Elastica\Query $query
44     * @param int[] $namespaces Namespaces used
45     * @param string $queryType Query description for logging
46     * @return \StatusValue Holds a \Elastica\ResultSet
47     * @throws ExceptionInterface
48     */
49    public function performSearch( \Elastica\Query $query, array $namespaces, string $queryType ): StatusValue {
50        $indexSuffix = $this->connection->pickIndexSuffixForNamespaces( $namespaces );
51        $index = $this->connection->getIndex( WikiMap::getCurrentWikiId(), $indexSuffix );
52        $search = $index->createSearch( $query );
53
54        $this->connection->setTimeout( $this->getClientTimeout( $queryType ) );
55        $search->setOption( Search::OPTION_TIMEOUT, $this->getTimeout( $queryType ) );
56
57        try {
58            $log = $this->newLog( 'performing {queryType}', $queryType, [], $namespaces );
59            $this->start( $log );
60            $result = $search->search();
61            if ( !$result->getResponse()->isOk() ) {
62                $req = $this->connection->getClient()->getLastRequest();
63                // Not really the right exception, this is probably a status code problem.
64                // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
65                throw new ResponseException( $req, $result->getResponse() );
66            }
67            $this->success();
68        } catch ( ExceptionInterface $ex ) {
69            return $this->failure( $ex );
70        }
71
72        $status = StatusValue::newGood( $result );
73        if ( $result->getResponse()->getData()['timed_out'] ?? false ) {
74            // only partial results returned
75            $status->warning( 'geodata-search-timeout' );
76        }
77        return $status;
78    }
79
80    /**
81     * @param string $description
82     * @param string $queryType
83     * @param string[] $extra
84     * @param array|null $namespaces
85     * @return SearchRequestLog
86     */
87    protected function newLog(
88        $description,
89        $queryType,
90        array $extra = [],
91        array $namespaces = null
92    ): SearchRequestLog {
93        return new SearchRequestLog(
94            $this->connection->getClient(),
95            $description,
96            $queryType,
97            $extra,
98            $namespaces
99        );
100    }
101
102    /**
103     * @param int[] $namespaces
104     * @return array[] Rescore configurations as used by elasticsearch.
105     */
106    public function getRelevanceRescoreConfigurations( array $namespaces ): array {
107        $searchContext = new SearchContext( $this->config, $namespaces );
108        return $searchContext->getRescore();
109    }
110}