Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.74% covered (success)
94.74%
18 / 19
85.71% covered (warning)
85.71%
6 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
InSourceFeature
94.74% covered (success)
94.74%
18 / 19
85.71% covered (warning)
85.71%
6 / 7
9.01
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
 getKeywords
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRegexHLFlavor
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 doApply
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getNonRegexFilterQuery
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 buildNonRegexHLFields
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 doGetNonRegexHLFields
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace CirrusSearch\Query;
4
5use CirrusSearch\CirrusConfigNames;
6use CirrusSearch\Parser\AST\KeywordFeatureNode;
7use CirrusSearch\Query\Builder\QueryBuildingContext;
8use CirrusSearch\Search\Escaper;
9use CirrusSearch\Search\Fetch\HighlightedField;
10use CirrusSearch\Search\Fetch\HighlightFieldGenerator;
11use CirrusSearch\Search\Filters;
12use CirrusSearch\Search\SearchContext;
13use CirrusSearch\SearchConfig;
14use Elastica\Query\AbstractQuery;
15use MediaWiki\MainConfigNames;
16
17/**
18 * Handles non-regexp version of insource: keyword.  The value
19 * (including possible quotes) is used as part of a QueryString
20 * query while allows some bit of advanced syntax. Because quotes
21 * are included, if present, multi-word queries containing AND or
22 * OR do not work.
23 *
24 * Examples:
25 *   insource:Foo
26 *   insource:Foo*
27 *   insource:"gold rush"
28 *
29 * Regex support:
30 *   insource:/abc?/
31 *
32 * Things that don't work:
33 *   insource:"foo*"
34 *   insource:"foo OR bar"
35 */
36class InSourceFeature extends BaseRegexFeature {
37
38    /**
39     * Source field
40     */
41    private const FIELD = 'source_text';
42
43    /**
44     * @var Escaper an escaper used to sanitize queries when not used as regular expression
45     *
46     * TODO: do not rely on escaper here, this should be consistent with what the Parser does.
47     * @see Filters::intitle()
48     */
49    private $escaper;
50
51    public function __construct( SearchConfig $config ) {
52        parent::__construct( $config, [ self::FIELD => HighlightedField::TARGET_MAIN_SNIPPET ] );
53        $this->escaper = new Escaper(
54            $config->get( MainConfigNames::LanguageCode ),
55            $config->get( CirrusConfigNames::AllowLeadingWildcard )
56        );
57    }
58
59    /**
60     * @return string[]
61     */
62    protected function getKeywords() {
63        return [ 'insource' ];
64    }
65
66    protected function getRegexHLFlavor(): string {
67        return "lucene_extended";
68    }
69
70    /**
71     * @param SearchContext $context
72     * @param string $key
73     * @param string $value
74     * @param string $quotedValue
75     * @param bool $negated
76     * @return array
77     */
78    protected function doApply( SearchContext $context, $key, $value, $quotedValue, $negated ) {
79        $filter = Filters::insource( $context->escaper(), $quotedValue );
80        if ( !$negated ) {
81            foreach ( $this->doGetNonRegexHLFields( $context->getFetchPhaseBuilder(), $filter ) as $field ) {
82                $context->getFetchPhaseBuilder()->addHLField( $field );
83            }
84        }
85        return [ $filter, false ];
86    }
87
88    /**
89     * @param KeywordFeatureNode $node
90     * @param QueryBuildingContext $context
91     * @return AbstractQuery|null
92     */
93    protected function getNonRegexFilterQuery( KeywordFeatureNode $node, QueryBuildingContext $context ) {
94        return Filters::insource( $this->escaper, $node->getQuotedValue() );
95    }
96
97    /**
98     * @inheritDoc
99     */
100    public function buildNonRegexHLFields( KeywordFeatureNode $node, QueryBuildingContext $buildingContext ) {
101        $query = Filters::insource( $this->escaper, $node->getQuotedValue() );
102        return $this->doGetNonRegexHLFields( $buildingContext->getHighlightFieldGenerator(), $query );
103    }
104
105    /**
106     * @param HighlightFieldGenerator $generator
107     * @param AbstractQuery $query
108     * @return HighlightedField[]
109     */
110    private function doGetNonRegexHLFields( HighlightFieldGenerator $generator, AbstractQuery $query ): array {
111        $field = $generator->newHighlightField( self::FIELD . '.plain',
112            HighlightedField::TARGET_MAIN_SNIPPET, HighlightedField::EXPERT_SYNTAX_PRIORITY );
113        $field->setHighlightQuery( $query );
114        return [ $field ];
115    }
116}