Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 37 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 1 |
PhraseQueryNodeHandler | |
0.00% |
0 / 37 |
|
0.00% |
0 / 3 |
42 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
6 | |||
transform | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
12 | |||
getTermScoringFieldQueryBuilder | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace Wikibase\MediaInfo\Search\ASTQueryBuilder; |
4 | |
5 | use CirrusSearch\Parser\AST\PhraseQueryNode; |
6 | use Elastica\Query\AbstractQuery; |
7 | use Elastica\Query\BoolQuery; |
8 | use Elastica\Query\MatchPhrase; |
9 | use Elastica\Query\MatchQuery; |
10 | |
11 | class 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 | } |