Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
7.14% covered (danger)
7.14%
2 / 28
22.22% covered (danger)
22.22%
2 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
CirrusSearchResult
7.14% covered (danger)
7.14%
2 / 28
22.22% covered (danger)
22.22%
2 / 9
220.97
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 initFromTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isBrokenTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isMissingRevision
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
42
 increment
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getTitle
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFile
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 initText
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 containsHighlight
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDocId
n/a
0 / 0
n/a
0 / 0
0
 getScore
n/a
0 / 0
n/a
0 / 0
0
 getExplanation
n/a
0 / 0
n/a
0 / 0
0
 clearRedirectTitle
n/a
0 / 0
n/a
0 / 0
0
1<?php
2
3namespace CirrusSearch\Search;
4
5use CirrusSearch\Searcher;
6use CirrusSearch\Util;
7use File;
8use LogicException;
9use MediaWiki\MediaWikiServices;
10use MediaWiki\Title\Title;
11use SearchResult;
12use SearchResultTrait;
13
14/**
15 * Base class for SearchResult
16 */
17abstract class CirrusSearchResult extends SearchResult {
18    use SearchResultTrait;
19
20    /**
21     * @var string Counter title for identified missing revisions
22     */
23    private const MISSING_REVISION_TOTAL = 'missing_revision_total';
24
25    /**
26     * @var Title
27     */
28    private $title;
29
30    /**
31     * @var ?File
32     */
33    private $file;
34
35    /**
36     * @var bool
37     */
38    private $checkedForFile = false;
39
40    /**
41     * @param Title $title
42     */
43    public function __construct( Title $title ) {
44        $this->title = $title;
45    }
46
47    /**
48     * Initialize from a Title and if possible initializes a corresponding
49     * File.
50     *
51     * @param Title $title
52     */
53    final protected function initFromTitle( $title ) {
54        // Everything is done in the constructor.
55        // XXX: we do not call the SearchResultInitFromTitle hook
56        // this hook is designed to fetch a particular revision
57        // but the way cirrus works does not allow to vary the revision
58        // text being displayed at query time.
59    }
60
61    /**
62     * Check if this is result points to an invalid title
63     *
64     * @return bool
65     */
66    final public function isBrokenTitle() {
67        // Title is mandatory in the constructor it would have failed earlier if the Title was broken
68        return false;
69    }
70
71    /**
72     * Check if target page is missing, happens when index is out of date
73     *
74     * @return bool
75     */
76    final public function isMissingRevision() {
77        global $wgCirrusSearchDevelOptions;
78        if ( isset( $wgCirrusSearchDevelOptions['ignore_missing_rev'] ) ) {
79            return false;
80        }
81        if ( !$this->getTitle()->isKnown() ) {
82            $this->increment( self::MISSING_REVISION_TOTAL, 'title' );
83            return true;
84        }
85        // Similarly if we matched due to a redirect
86        if ( $this->getRedirectTitle() && !$this->getRedirectTitle()->isKnown() ) {
87            // There may be other reasons this result matched, for now keep it in the results
88            // but clear the redirect.
89            $redirectIsOnlyMatch = $this->clearRedirectTitle();
90            $this->increment(
91                self::MISSING_REVISION_TOTAL,
92                $redirectIsOnlyMatch ? 'only_redirect' : 'redirect'
93            );
94        }
95
96        return false;
97    }
98
99    private function increment( string $counter, string $problem ) {
100        Util::getStatsFactory()
101            ->getCounter( $counter )
102            ->setLabel( 'problem', $problem )
103            ->increment();
104    }
105
106    /**
107     * @return Title
108     */
109    final public function getTitle() {
110        return $this->title;
111    }
112
113    /**
114     * Get the file for this page, if one exists
115     * @return File|null
116     */
117    final public function getFile() {
118        if ( !$this->checkedForFile && $this->getTitle()->getNamespace() === NS_FILE ) {
119            $this->checkedForFile = true;
120            $this->file = MediaWikiServices::getInstance()->getRepoGroup()
121                ->findFile( $this->title );
122        }
123        return $this->file;
124    }
125
126    /**
127     * Lazy initialization of article text from DB
128     * @return never
129     */
130    final protected function initText() {
131        throw new LogicException( "initText() should not be called on CirrusSearchResult, " .
132            "content must be fetched directly from the backend at query time." );
133    }
134
135    /**
136     * @param string $text A snippet from the search highlighter
137     * @return bool True when the string contains highlight markers
138     */
139    protected function containsHighlight( string $text ): bool {
140        return strpos( $text, Searcher::HIGHLIGHT_PRE ) !== false;
141    }
142
143    /**
144     * @return string
145     */
146    abstract public function getDocId();
147
148    /**
149     * @return float
150     */
151    abstract public function getScore();
152
153    /**
154     * @return array|null
155     */
156    abstract public function getExplanation();
157
158    /**
159     * Clear any redirect match so it won't be part of the result.
160     *
161     * @return bool True if the redirect was the only snippet available
162     *  for this result.
163     */
164    abstract protected function clearRedirectTitle(): bool;
165}