Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.55% covered (success)
96.55%
28 / 29
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
DescriptionsField
96.55% covered (success)
96.55%
28 / 29
75.00% covered (warning)
75.00%
3 / 4
11
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getMapping
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
4
 getFieldData
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 getEngineHints
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
1<?php
2
3namespace Wikibase\Search\Elastic\Fields;
4
5use CirrusSearch\CirrusSearch;
6use SearchEngine;
7use Wikibase\DataModel\Entity\EntityDocument;
8use Wikibase\DataModel\Term\DescriptionsProvider;
9
10/**
11 * Field which contains per-language specific descriptions.
12 *
13 * @license GPL-2.0-or-later
14 * @author Stas Malyshev
15 */
16class DescriptionsField extends TermIndexField {
17
18    /**
19     * Field name
20     */
21    public const NAME = 'descriptions';
22
23    /**
24     * List of available languages
25     * @var string[]
26     */
27    private $languages;
28    /**
29     * @var array
30     */
31    private $stemmingSettings;
32
33    /**
34     * @param string[] $languages Available languages list.
35     * @param array $stemmingSettings Stemming config
36     */
37    public function __construct( array $languages, array $stemmingSettings ) {
38        $this->languages = $languages;
39        parent::__construct( static::NAME, \SearchIndexField::INDEX_TYPE_NESTED );
40        $this->stemmingSettings = $stemmingSettings;
41    }
42
43    /**
44     * @param SearchEngine $engine
45     * @return null|array
46     */
47    public function getMapping( SearchEngine $engine ) {
48        // Since we need a specially tuned field, we can not use
49        // standard search engine types.
50        if ( !( $engine instanceof CirrusSearch ) ) {
51            // For now only Cirrus/Elastic is supported
52            return [];
53        }
54
55        $config = [
56            'type' => 'object',
57            'properties' => []
58        ];
59        foreach ( $this->languages as $language ) {
60            // TODO: here we probably will need better language-specific analyzers
61            if ( empty( $this->stemmingSettings[$language]['index'] ) ) {
62                $langConfig = $this->getUnindexedField();
63            } else {
64                $langConfig = $this->getTokenizedSubfield( $engine->getConfig(),
65                    $language . '_text',
66                    $language . '_text_search'
67                );
68            }
69            $langConfig['fields']['plain'] = $this->getTokenizedSubfield( $engine->getConfig(), $language . '_plain',
70                    $language . '_plain_search' );
71            $config['properties'][$language] = $langConfig;
72        }
73
74        return $config;
75    }
76
77    /**
78     * @param EntityDocument $entity
79     *
80     * @return array|null Array of descriptions in available languages.
81     */
82    public function getFieldData( EntityDocument $entity ) {
83        if ( !( $entity instanceof DescriptionsProvider ) ) {
84            return null;
85        }
86        $data = [];
87        foreach ( $entity->getDescriptions() as $language => $desc ) {
88            // While wikibase can only have a single description,
89            // WikibaseMediaInfo reports an array of descriptions. To keep the
90            // constructed search docs consistent report an array here as well.
91            $data[$language] = [ $desc->getText() ];
92        }
93        // Shouldn't return empty arrays, that will be encoded to json as an
94        // empty list instead of an empty map. Elastic doesn't mind, but this
95        // allows more consistency working with the resulting cirrus docs
96        return $data ?: null;
97    }
98
99    /**
100     * Set engine hints.
101     * Specifically, sets noop hint so that descriptions would be compared
102     * as arrays and removal of description would be processed correctly.
103     * @param SearchEngine $engine
104     * @return array
105     */
106    public function getEngineHints( SearchEngine $engine ) {
107        if ( !( $engine instanceof CirrusSearch ) ) {
108            // For now only Cirrus/Elastic is supported
109            return [];
110        }
111        return [ \CirrusSearch\Search\CirrusIndexField::NOOP_HINT => "equals" ];
112    }
113
114}