Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
57 / 57
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
ResultBuilder
100.00% covered (success)
100.00%
57 / 57
100.00% covered (success)
100.00%
5 / 5
18
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 createResultArray
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
2
 buildEntry
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
7
 checkAndSetAlias
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 mergeWithTraditionalSearchResults
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2
3namespace PropertySuggester;
4
5use ApiResult;
6use PropertySuggester\Suggesters\Suggestion;
7use Wikibase\DataAccess\PrefetchingTermLookup;
8use Wikibase\Lib\LanguageFallbackChainFactory;
9use Wikibase\Lib\Store\EntityTitleLookup;
10use 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 */
18class 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}