Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
67.44% covered (warning)
67.44%
29 / 43
42.86% covered (danger)
42.86%
3 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
FullTextResultsType
67.44% covered (warning)
67.44%
29 / 43
42.86% covered (danger)
42.86%
3 / 7
13.45
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 getSourceFiltering
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 getFields
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getHighlightingConfiguration
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 transformElasticsearchResult
64.29% covered (warning)
64.29%
18 / 28
0.00% covered (danger)
0.00%
0 / 1
4.73
 withFetchPhaseBuilder
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 createEmptyResult
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace CirrusSearch\Search;
4
5use CirrusSearch\Search\Fetch\FetchPhaseConfigBuilder;
6use CirrusSearch\Util;
7use Elastica\ResultSet as ElasticaResultSet;
8use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface;
9
10/**
11 * Result type for a full text search.
12 */
13final class FullTextResultsType extends BaseResultsType {
14    /**
15     * @var bool
16     */
17    private $searchContainedSyntax;
18
19    /**
20     * @var FetchPhaseConfigBuilder
21     */
22    private $fetchPhaseBuilder;
23    /**
24     * @var TitleHelper
25     */
26    private $titleHelper;
27
28    /**
29     * @var string[] list of extra fields to extract
30     */
31    private $extraFieldsToExtract;
32
33    /**
34     * @var bool if true, deduplicate results from file namespace
35     */
36    private bool $deduplicate;
37
38    /**
39     * @param FetchPhaseConfigBuilder $fetchPhaseBuilder
40     * @param bool $searchContainedSyntax
41     * @param TitleHelper $titleHelper
42     * @param string[] $extraFieldsToExtract
43     * @param bool $deduplicate
44     */
45    public function __construct(
46        FetchPhaseConfigBuilder $fetchPhaseBuilder,
47        $searchContainedSyntax,
48        TitleHelper $titleHelper,
49        array $extraFieldsToExtract = [],
50        bool $deduplicate = false
51    ) {
52        $this->fetchPhaseBuilder = $fetchPhaseBuilder;
53        $this->searchContainedSyntax = $searchContainedSyntax;
54        $this->titleHelper = $titleHelper;
55        $this->extraFieldsToExtract = $extraFieldsToExtract;
56        $this->deduplicate = $deduplicate;
57    }
58
59    /**
60     * @return false|string|array corresponding to Elasticsearch source filtering syntax
61     */
62    public function getSourceFiltering() {
63        return array_merge(
64            parent::getSourceFiltering(),
65            [ 'redirect.*', 'timestamp', 'text_bytes' ],
66            $this->extraFieldsToExtract
67        );
68    }
69
70    /**
71     * @return array
72     */
73    public function getFields() {
74        return [ "text.word_count" ]; // word_count is only a stored field and isn't part of the source.
75    }
76
77    /**
78     * Setup highlighting.
79     * Don't fragment title because it is small.
80     * Get just one fragment from the text because that is all we will display.
81     * Get one fragment from redirect title and heading each or else they
82     * won't be sorted by score.
83     *
84     * @param array $extraHighlightFields (deprecated and ignored)
85     * @return array|null of highlighting configuration
86     */
87    public function getHighlightingConfiguration( array $extraHighlightFields = [] ) {
88        $this->fetchPhaseBuilder->configureDefaultFullTextFields();
89        return $this->fetchPhaseBuilder->buildHLConfig();
90    }
91
92    /**
93     * @param ElasticaResultSet $result
94     * @return CirrusSearchResultSet
95     */
96    public function transformElasticsearchResult( ElasticaResultSet $result ) {
97        // Should we make this a concrete class?
98        return new class(
99            $this->titleHelper,
100            $this->fetchPhaseBuilder,
101            $result,
102            $this->searchContainedSyntax,
103            $this->extraFieldsToExtract,
104            $this->deduplicate
105        )
106                extends BaseCirrusSearchResultSet {
107            /** @var TitleHelper */
108            private $titleHelper;
109            /** @var FullTextCirrusSearchResultBuilder */
110            private $resultBuilder;
111            /** @var ElasticaResultSet */
112            private $results;
113            /** @var bool */
114            private $searchContainedSyntax;
115            /** @var bool if true, deduplicate results from file namespace */
116            private bool $deduplicate;
117            /** @var string[] array of titles for counting duplicates */
118            private array $fileTitles = [];
119            /** @var StatsdDataFactoryInterface metrics facade */
120            private StatsdDataFactoryInterface $statsd;
121
122            public function __construct(
123                TitleHelper $titleHelper,
124                FetchPhaseConfigBuilder $builder,
125                ElasticaResultSet $results,
126                $searchContainedSyntax,
127                array $extraFieldsToExtract,
128                bool $deduplicate
129            ) {
130                $this->titleHelper = $titleHelper;
131                $this->resultBuilder = new FullTextCirrusSearchResultBuilder( $this->titleHelper,
132                    $builder->getHLFieldsPerTargetAndPriority(), $extraFieldsToExtract );
133                $this->results = $results;
134                $this->searchContainedSyntax = $searchContainedSyntax;
135                $this->statsd = Util::getStatsDataFactory();
136                $this->deduplicate = $deduplicate;
137            }
138
139            /**
140             * @inheritDoc
141             */
142            protected function transformOneResult( \Elastica\Result $result ) {
143                $source = $result->getSource();
144                if ( $source['namespace'] === NS_FILE ) {
145                    if ( in_array( $source['title'], $this->fileTitles ) ) {
146                        $this->statsd->increment( 'CirrusSearch.results.file_duplicates' );
147                        if ( $this->deduplicate ) {
148                            return null;
149                        }
150                    } else {
151                        $this->fileTitles[] = $source['title'];
152                    }
153                }
154                return $this->resultBuilder->build( $result );
155            }
156
157            /**
158             * @return \Elastica\ResultSet|null
159             */
160            public function getElasticaResultSet() {
161                return $this->results;
162            }
163
164            /**
165             * @inheritDoc
166             */
167            public function searchContainedSyntax() {
168                return $this->searchContainedSyntax;
169            }
170
171            protected function getTitleHelper(): TitleHelper {
172                return $this->titleHelper;
173            }
174        };
175    }
176
177    /**
178     * @param FetchPhaseConfigBuilder $builder
179     * @return FullTextResultsType
180     */
181    public function withFetchPhaseBuilder( FetchPhaseConfigBuilder $builder ): FullTextResultsType {
182        return new self( $builder, $this->searchContainedSyntax, $this->titleHelper, $this->extraFieldsToExtract, $this->deduplicate );
183    }
184
185    /**
186     * @return CirrusSearchResultSet
187     */
188    public function createEmptyResult() {
189        return BaseCirrusSearchResultSet::emptyResultSet();
190    }
191}