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