Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
57 / 57 |
|
100.00% |
5 / 5 |
CRAP | |
100.00% |
1 / 1 |
ResultBuilder | |
100.00% |
57 / 57 |
|
100.00% |
5 / 5 |
18 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
createResultArray | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
2 | |||
buildEntry | |
100.00% |
20 / 20 |
|
100.00% |
1 / 1 |
7 | |||
checkAndSetAlias | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
mergeWithTraditionalSearchResults | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
5 |
1 | <?php |
2 | |
3 | namespace PropertySuggester; |
4 | |
5 | use MediaWiki\Api\ApiResult; |
6 | use PropertySuggester\Suggesters\Suggestion; |
7 | use Wikibase\DataAccess\PrefetchingTermLookup; |
8 | use Wikibase\Lib\LanguageFallbackChainFactory; |
9 | use Wikibase\Lib\Store\EntityTitleLookup; |
10 | use Wikibase\Lib\Store\LanguageFallbackLabelDescriptionLookup; |
11 | |
12 | /** |
13 | * ResultBuilder builds Json-compatible array structure from suggestions |
14 | * |
15 | * @author BP2013N2 |
16 | * @license GPL-2.0-or-later |
17 | */ |
18 | class ResultBuilder { |
19 | |
20 | /** |
21 | * @var EntityTitleLookup |
22 | */ |
23 | private $entityTitleLookup; |
24 | |
25 | /** |
26 | * @var ApiResult |
27 | */ |
28 | private $result; |
29 | |
30 | /** |
31 | * @var string |
32 | */ |
33 | private $searchPattern; |
34 | |
35 | /** |
36 | * @var PrefetchingTermLookup |
37 | */ |
38 | private $prefetchingTermLookup; |
39 | |
40 | /** |
41 | * @var LanguageFallbackChainFactory |
42 | */ |
43 | private $languageFallbackChainFactory; |
44 | |
45 | /** |
46 | * @param ApiResult $result |
47 | * @param PrefetchingTermLookup $prefetchingTermLookup |
48 | * @param LanguageFallbackChainFactory $languageFallbackChainFactory |
49 | * @param EntityTitleLookup $entityTitleLookup |
50 | * @param string $search |
51 | */ |
52 | public function __construct( |
53 | ApiResult $result, |
54 | PrefetchingTermLookup $prefetchingTermLookup, |
55 | LanguageFallbackChainFactory $languageFallbackChainFactory, |
56 | EntityTitleLookup $entityTitleLookup, |
57 | $search |
58 | ) { |
59 | $this->entityTitleLookup = $entityTitleLookup; |
60 | $this->prefetchingTermLookup = $prefetchingTermLookup; |
61 | $this->languageFallbackChainFactory = $languageFallbackChainFactory; |
62 | $this->result = $result; |
63 | $this->searchPattern = '/^' . preg_quote( $search, '/' ) . '/i'; |
64 | } |
65 | |
66 | public function createResultArray( array $suggestions, string $language ): array { |
67 | $ids = []; |
68 | foreach ( $suggestions as $suggestion ) { |
69 | $ids[] = $suggestion->getPropertyId(); |
70 | } |
71 | $fallbackChain = $this->languageFallbackChainFactory->newFromLanguageCode( $language ); |
72 | |
73 | $this->prefetchingTermLookup->prefetchTerms( |
74 | $ids, |
75 | [ 'label', 'description', 'alias' ], |
76 | $fallbackChain->getFetchLanguageCodes() |
77 | ); |
78 | |
79 | $fallbackTermLookup = new LanguageFallbackLabelDescriptionLookup( |
80 | $this->prefetchingTermLookup, |
81 | $fallbackChain |
82 | ); |
83 | return array_map( function ( Suggestion $suggestion ) use ( $language, $fallbackTermLookup ) { |
84 | return $this->buildEntry( $suggestion, $language, $fallbackTermLookup ); |
85 | }, $suggestions ); |
86 | } |
87 | |
88 | /** |
89 | * @param Suggestion $suggestion |
90 | * @param string $language |
91 | * @param LanguageFallbackLabelDescriptionLookup $fallbackTermLookup |
92 | * |
93 | * @return array |
94 | */ |
95 | private function buildEntry( |
96 | Suggestion $suggestion, |
97 | string $language, |
98 | LanguageFallbackLabelDescriptionLookup $fallbackTermLookup |
99 | ): array { |
100 | $id = $suggestion->getPropertyId(); |
101 | $entry = [ |
102 | 'id' => $id->getSerialization(), |
103 | 'url' => $this->entityTitleLookup->getTitleForId( $id )->getFullURL(), |
104 | 'rating' => $suggestion->getProbability(), |
105 | ]; |
106 | |
107 | $label = $fallbackTermLookup->getLabel( $id ); |
108 | if ( $label !== null ) { |
109 | $entry['label'] = $label->getText(); |
110 | } |
111 | |
112 | $description = $fallbackTermLookup->getDescription( $id ); |
113 | if ( $description !== null ) { |
114 | $entry['description'] = $description->getText(); |
115 | } |
116 | |
117 | $aliases = $this->prefetchingTermLookup->getPrefetchedAliases( $id, $language ); |
118 | if ( is_array( $aliases ) && $aliases ) { |
119 | $this->checkAndSetAlias( $entry, $aliases[0] ); |
120 | } |
121 | |
122 | if ( !isset( $entry['label'] ) ) { |
123 | $entry['label'] = $id->getSerialization(); |
124 | } elseif ( preg_match( $this->searchPattern, $entry['label'] ) ) { |
125 | // No aliases needed in the output when the label already is a successful match. |
126 | unset( $entry['aliases'] ); |
127 | } |
128 | |
129 | return $entry; |
130 | } |
131 | |
132 | /** |
133 | * @param array &$entry |
134 | * @param string $alias |
135 | */ |
136 | private function checkAndSetAlias( array &$entry, $alias ) { |
137 | if ( preg_match( $this->searchPattern, $alias ) ) { |
138 | if ( !isset( $entry['aliases'] ) ) { |
139 | $entry['aliases'] = []; |
140 | ApiResult::setIndexedTagName( $entry['aliases'], 'alias' ); |
141 | } |
142 | $entry['aliases'][] = $alias; |
143 | } |
144 | } |
145 | |
146 | /** |
147 | * @param array[] $entries |
148 | * @param array[] $searchResults |
149 | * @param int $resultSize |
150 | * @return array[] representing Json |
151 | */ |
152 | public function mergeWithTraditionalSearchResults( |
153 | array $entries, |
154 | array $searchResults, |
155 | $resultSize |
156 | ) { |
157 | // Avoid duplicates |
158 | $existingKeys = []; |
159 | foreach ( $entries as $entry ) { |
160 | $existingKeys[$entry['id']] = true; |
161 | } |
162 | |
163 | $distinctCount = count( $entries ); |
164 | foreach ( $searchResults as $result ) { |
165 | if ( !array_key_exists( $result['id'], $existingKeys ) ) { |
166 | $entries[] = $result; |
167 | $distinctCount++; |
168 | if ( $distinctCount >= $resultSize ) { |
169 | break; |
170 | } |
171 | } |
172 | } |
173 | return $entries; |
174 | } |
175 | |
176 | } |