Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
PhraseQueryNodeHandler
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 3
42
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
6
 transform
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
12
 getTermScoringFieldQueryBuilder
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace Wikibase\MediaInfo\Search\ASTQueryBuilder;
4
5use CirrusSearch\Parser\AST\PhraseQueryNode;
6use Elastica\Query\AbstractQuery;
7use Elastica\Query\BoolQuery;
8use Elastica\Query\MatchPhrase;
9use Elastica\Query\MatchQuery;
10
11class PhraseQueryNodeHandler implements ParsedNodeHandlerInterface {
12    /** @var PhraseQueryNode */
13    private $node;
14
15    /** @var WikibaseEntitiesHandler */
16    private $entitiesHandler;
17
18    /** @var FieldIterator */
19    private $termScoringFieldIterator;
20
21    public function __construct(
22        PhraseQueryNode $node,
23        WikibaseEntitiesHandler $entitiesHandler,
24        array $languages,
25        array $stemmingSettings,
26        array $boosts,
27        array $decays
28    ) {
29        $this->node = $node;
30        $this->entitiesHandler = $entitiesHandler;
31
32        $fields = array_keys( $boosts );
33        $fields = $node->isStem() ?
34            array_intersect( $fields, FieldIterator::STEMMED_FIELDS ) :
35            array_intersect( $fields, FieldIterator::PLAIN_FIELDS );
36        // "field:[suggest] was indexed without position data; cannot run PhraseQuery"
37        $fields = array_diff( $fields, [ 'suggest' ] );
38
39        $this->termScoringFieldIterator = new FieldIterator(
40            $this->getTermScoringFieldQueryBuilder( $node->getPhrase(), $node->getSlop() ),
41            $fields,
42            $languages,
43            $stemmingSettings,
44            $boosts,
45            $decays
46        );
47    }
48
49    public function transform(): AbstractQuery {
50        $query = new BoolQuery();
51        $query->setMinimumShouldMatch( 1 );
52
53        // phrase query
54        $phraseQuery = new BoolQuery();
55        // filter does all the work, should's are for scoring.
56        $phraseQuery->setMinimumShouldMatch( 0 );
57        $field = $this->node->isStem() ? 'all' : 'all.plain';
58        $phraseQuery->addFilter(
59            // quick overall filter; this is faster at narrowing down
60            // the resultset then matching all fields individually
61            ( new MatchPhrase() )
62                ->setFieldQuery( $field, $this->node->getPhrase() )
63                ->setFieldParam( $field, 'slop', max( 0, $this->node->getSlop() ) )
64        );
65        foreach ( $this->termScoringFieldIterator as $fieldQuery ) {
66            // also add scoring clauses per field, for weighted scoring
67            $phraseQuery->addShould( $fieldQuery );
68        }
69        $query->addShould( $phraseQuery );
70
71        // wikibase entities (that match phrase query)
72        $query->addShould( $this->entitiesHandler->transform() );
73
74        return $query;
75    }
76
77    /**
78     * @param string $phrase
79     * @param int $slop
80     * @return FieldQueryBuilderInterface
81     */
82    private function getTermScoringFieldQueryBuilder( $phrase, $slop ): FieldQueryBuilderInterface {
83        return new class( $phrase, $slop ) implements FieldQueryBuilderInterface {
84            /** @var string */
85            private $phrase;
86
87            /** @var int */
88            private $slop;
89
90            public function __construct( $phrase, $slop ) {
91                $this->phrase = $phrase;
92                $this->slop = $slop;
93            }
94
95            public function getQuery( $field, $boost ): AbstractQuery {
96                return ( new MatchQuery() )
97                    ->setFieldQuery( $field, $this->phrase )
98                    ->setFieldBoost( $field, $boost );
99            }
100        };
101    }
102}