Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
44 / 44
100.00% covered (success)
100.00%
9 / 9
CRAP
100.00% covered (success)
100.00%
1 / 1
SubPageOfFeature
100.00% covered (success)
100.00%
44 / 44
100.00% covered (success)
100.00%
9 / 9
19
100.00% covered (success)
100.00%
1 / 1
 getKeywords
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCrossSearchStrategy
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 doApply
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 getFilterQuery
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 doGetFilterQuery
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 parseValue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 doParseValue
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 doGetHLFields
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
3
 buildHighlightFields
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace CirrusSearch\Query;
4
5use CirrusSearch\CrossSearchStrategy;
6use CirrusSearch\Parser\AST\KeywordFeatureNode;
7use CirrusSearch\Query\Builder\QueryBuildingContext;
8use CirrusSearch\Search\Fetch\HighlightedField;
9use CirrusSearch\Search\Fetch\HighlightFieldGenerator;
10use CirrusSearch\Search\SearchContext;
11use CirrusSearch\WarningCollector;
12use Elastica\Query\AbstractQuery;
13use Elastica\Query\MatchQuery;
14use Elastica\Query\MultiMatch;
15
16/**
17 * subpagesof, find subpages of a given page
18 * uses the prefix field, very similar to the prefix except
19 * that it enforces a trailing / and is not a greedy keyword
20 */
21class SubPageOfFeature extends SimpleKeywordFeature implements FilterQueryFeature, HighlightingFeature {
22    /**
23     * @return string[]
24     */
25    protected function getKeywords() {
26        return [ 'subpageof' ];
27    }
28
29    /**
30     * @param KeywordFeatureNode $node
31     * @return CrossSearchStrategy
32     */
33    public function getCrossSearchStrategy( KeywordFeatureNode $node ) {
34        return CrossSearchStrategy::allWikisStrategy();
35    }
36
37    /**
38     * @param SearchContext $context
39     * @param string $key The keyword
40     * @param string $value The value attached to the keyword with quotes stripped
41     * @param string $quotedValue The original value in the search string, including quotes if used
42     * @param bool $negated Is the search negated? Not used to generate the returned AbstractQuery,
43     *  that will be negated as necessary. Used for any other building/context necessary.
44     * @return array Two element array, first an AbstractQuery or null to apply to the
45     *  query. Second a boolean indicating if the quotedValue should be kept in the search
46     *  string.
47     */
48    protected function doApply( SearchContext $context, $key, $value, $quotedValue, $negated ) {
49        $parsedValue = $this->doParseValue( $value );
50        if ( $parsedValue === null ) {
51            return [ null, false ];
52        }
53        $q = $this->doGetFilterQuery( $parsedValue );
54        if ( !$negated ) {
55            foreach ( $this->doGetHLFields( $parsedValue, $context->getFetchPhaseBuilder() ) as $f ) {
56                $context->getFetchPhaseBuilder()->addHLField( $f );
57            }
58        }
59        return [ $q, false ];
60    }
61
62    /**
63     * @param KeywordFeatureNode $node
64     * @param QueryBuildingContext $context
65     * @return AbstractQuery|null
66     */
67    public function getFilterQuery( KeywordFeatureNode $node, QueryBuildingContext $context ) {
68        if ( $node->getParsedValue() === null ) {
69            return null;
70        }
71        return $this->doGetFilterQuery( $node->getParsedValue() );
72    }
73
74    private function doGetFilterQuery( array $parsedValue ): AbstractQuery {
75        $query = new MultiMatch();
76        $query->setFields( [ 'title.prefix', 'redirect.title.prefix' ] );
77        $query->setQuery( $parsedValue['prefix'] );
78        return $query;
79    }
80
81    /**
82     * @inheritDoc
83     */
84    public function parseValue( $key, $value, $quotedValue, $valueDelimiter, $suffix, WarningCollector $warningCollector ) {
85        return $this->doParseValue( $value );
86    }
87
88    /**
89     * @param string $value
90     * @return array|null
91     */
92    private function doParseValue( $value ) {
93        if ( $value !== '' ) {
94            $lastC = substr( $value, -1 );
95            if ( $lastC !== '/' && $lastC !== '*' ) {
96                $value .= '/';
97            } elseif ( $lastC === '*' ) {
98                $value = substr( $value, 0, -1 );
99            }
100            return [ 'prefix' => $value ];
101        }
102        return null;
103    }
104
105    /**
106     * @param array $parsedValue
107     * @param HighlightFieldGenerator $highlightFieldGenerator
108     * @return HighlightedField[]
109     */
110    private function doGetHLFields( array $parsedValue, HighlightFieldGenerator $highlightFieldGenerator ) {
111        $hlfields = [];
112        $definition = [
113            HighlightedField::TARGET_TITLE_SNIPPET => 'title.prefix',
114            HighlightedField::TARGET_REDIRECT_SNIPPET => 'redirect.title.prefix',
115        ];
116        $first = true;
117        foreach ( $definition as $target => $esfield ) {
118            $field = $highlightFieldGenerator->newHighlightField( $esfield, $target,
119                 HighlightedField::EXPERT_SYNTAX_PRIORITY );
120            $field->setHighlightQuery( new MatchQuery( $esfield, $parsedValue['prefix'] ) );
121            $field->setNumberOfFragments( 1 );
122            $field->setFragmentSize( 10000 );
123            if ( $first ) {
124                $first = false;
125            } else {
126                $field->skipIfLastMatched();
127            }
128            $hlfields[] = $field;
129        }
130        return $hlfields;
131    }
132
133    /**
134     * @inheritDoc
135     */
136    public function buildHighlightFields( KeywordFeatureNode $node, QueryBuildingContext $context ) {
137        return $this->doGetHLFields( $node->getParsedValue(), $context->getHighlightFieldGenerator() );
138    }
139}