Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
86.59% covered (warning)
86.59%
71 / 82
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
SuggestionGenerator
86.59% covered (warning)
86.59%
71 / 82
50.00% covered (danger)
50.00%
2 / 4
20.97
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 generateSuggestionsByItem
81.48% covered (warning)
81.48%
22 / 27
0.00% covered (danger)
0.00%
0 / 1
6.23
 generateSuggestionsByPropertyList
80.00% covered (warning)
80.00%
24 / 30
0.00% covered (danger)
0.00%
0 / 1
7.39
 filterSuggestions
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2
3namespace PropertySuggester;
4
5use InvalidArgumentException;
6use MediaWiki\Status\Status;
7use Message;
8use PropertySuggester\Suggesters\SuggesterEngine;
9use PropertySuggester\Suggesters\Suggestion;
10use Wikibase\DataModel\Entity\Item;
11use Wikibase\DataModel\Entity\ItemId;
12use Wikibase\DataModel\Entity\NumericPropertyId;
13use Wikibase\DataModel\Services\Lookup\EntityLookup;
14use Wikibase\DataModel\Services\Lookup\UnresolvedEntityRedirectException;
15use Wikibase\Repo\Api\EntitySearchException;
16use Wikibase\Repo\Api\EntitySearchHelper;
17
18/**
19 * API module helper to generate property suggestions.
20 *
21 * @author BP2013N2
22 * @license GPL-2.0-or-later
23 */
24class SuggestionGenerator {
25
26    /**
27     * @var EntityLookup
28     */
29    private $entityLookup;
30
31    /**
32     * @var EntitySearchHelper
33     */
34    private $entityTermSearchHelper;
35
36    /**
37     * @var SuggesterEngine
38     */
39    private $suggester;
40
41    /**
42     * @var SuggesterEngine|null
43     */
44    private $fallbackSuggester;
45
46    public function __construct(
47        EntityLookup $entityLookup,
48        EntitySearchHelper $entityTermSearchHelper,
49        SuggesterEngine $suggester,
50        ?SuggesterEngine $fallbackSuggester = null
51    ) {
52        $this->entityLookup = $entityLookup;
53        $this->entityTermSearchHelper = $entityTermSearchHelper;
54        $this->suggester = $suggester;
55        $this->fallbackSuggester = $fallbackSuggester;
56    }
57
58    /**
59     * @param string $itemIdString
60     * @param int $limit
61     * @param float $minProbability
62     * @param string $context
63     * @param string $include One of the SuggesterEngine::SUGGEST_* constants
64     * @return Status containing Suggestion[]
65     */
66    public function generateSuggestionsByItem(
67        $itemIdString,
68        $limit,
69        $minProbability,
70        $context,
71        $include
72    ): Status {
73        try {
74            $itemId = new ItemId( $itemIdString );
75        } catch ( InvalidArgumentException $e ) {
76            return Status::newFatal( 'wikibase-api-invalid-entity-id' );
77        }
78        /** @var Item $item */
79        try {
80            $item = $this->entityLookup->getEntity( $itemId );
81        } catch ( UnresolvedEntityRedirectException $e ) {
82            return Status::newFatal( 'wikibase-api-unresolved-redirect' );
83        }
84
85        if ( $item === null ) {
86            return Status::newFatal( 'wikibase-api-no-such-entity',
87                Message::plaintextParam( $itemIdString ) );
88        }
89        '@phan-var Item $item';
90
91        $suggestions = $this->suggester->suggestByItem(
92            $item,
93            $limit,
94            $minProbability,
95            $context,
96            $include
97        );
98
99        if ( $suggestions === null ) {
100            if ( $this->fallbackSuggester === null ) {
101                return Status::newGood( [] );
102            }
103            $suggestions = $this->fallbackSuggester->suggestByItem(
104                $item,
105                $limit,
106                $minProbability,
107                $context,
108                $include
109            );
110        }
111        return Status::newGood( $suggestions );
112    }
113
114    /**
115     * @param string[] $propertyIdList A list of property-id-strings
116     * @param string[] $typesIdList A list of types-id-strings
117     * @param int $limit
118     * @param float $minProbability
119     * @param string $context
120     * @param string $include One of the SuggesterEngine::SUGGEST_* constants
121     * @return Status containing Suggestion[]
122     */
123    public function generateSuggestionsByPropertyList(
124        array $propertyIdList,
125        array $typesIdList,
126        $limit,
127        $minProbability,
128        $context,
129        $include
130    ): Status {
131        $propertyIds = [];
132        foreach ( $propertyIdList as $stringId ) {
133            try {
134                $propertyIds[] = new NumericPropertyId( $stringId );
135            } catch ( InvalidArgumentException $e ) {
136                return Status::newFatal( 'wikibase-api-invalid-property-id' );
137            }
138        }
139
140        $typesIds = [];
141        foreach ( $typesIdList as $stringId ) {
142            try {
143                $typesIds[] = new ItemId( $stringId );
144            } catch ( InvalidArgumentException $e ) {
145                return Status::newFatal( 'wikibase-api-invalid-entity-id' );
146            }
147        }
148
149        $suggestions = $this->suggester->suggestByPropertyIds(
150            $propertyIds,
151            $typesIds,
152            $limit,
153            $minProbability,
154            $context,
155            $include
156        );
157
158        if ( $suggestions === null ) {
159            if ( $this->fallbackSuggester === null ) {
160                return Status::newGood( [] );
161            }
162            $suggestions = $this->fallbackSuggester->suggestByPropertyIds(
163                $propertyIds,
164                $typesIds,
165                $limit,
166                $minProbability,
167                $context,
168                $include
169            );
170        }
171
172        return Status::newGood( $suggestions );
173    }
174
175    /**
176     * @param Suggestion[] $suggestions
177     * @param string $search
178     * @param string $language
179     * @param int $resultSize
180     * @return Suggestion[]
181     * @throws EntitySearchException
182     */
183    public function filterSuggestions( array $suggestions, $search, $language, $resultSize ) {
184        if ( !$search ) {
185            return array_slice( $suggestions, 0, $resultSize );
186        }
187
188        $searchResults = $this->entityTermSearchHelper->getRankedSearchResults(
189            $search,
190            $language,
191            'property',
192            $resultSize,
193            true,
194            null
195        );
196
197        $id_set = [];
198        foreach ( $searchResults as $searchResult ) {
199            // @phan-suppress-next-next-line PhanUndeclaredMethod getEntityId() returns PropertyId
200            // as requested above and that implements getNumericId()
201            $id_set[$searchResult->getEntityId()->getNumericId()] = true;
202        }
203
204        $matching_suggestions = [];
205        $count = 0;
206        foreach ( $suggestions as $suggestion ) {
207            if ( array_key_exists( $suggestion->getPropertyId()->getNumericId(), $id_set ) ) {
208                $matching_suggestions[] = $suggestion;
209                if ( ++$count === $resultSize ) {
210                    break;
211                }
212            }
213        }
214        return $matching_suggestions;
215    }
216
217}