Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
MetaNamespaceStore
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 7
132
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 docId
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 buildIndexProperties
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 reindex
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 find
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 queryFilter
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 buildDocuments
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3namespace CirrusSearch\MetaStore;
4
5use Elastica\Document;
6use Elastica\Index;
7use Elastica\Query\BoolQuery;
8use Elastica\Query\Ids;
9use Elastica\Query\MatchQuery;
10use MediaWiki\Language\Language;
11use MediaWiki\WikiMap\WikiMap;
12
13class MetaNamespaceStore implements MetaStore {
14    /** @const Value of metastore 'type' field for our documents */
15    public const METASTORE_TYPE = 'namespace';
16
17    /** @var Index */
18    private $index;
19
20    /** @var string */
21    private $wikiId;
22
23    public function __construct( Index $index, ?string $wikiId = null ) {
24        $this->index = $index;
25        $this->wikiId = $wikiId ?? WikiMap::getCurrentWikiId();
26    }
27
28    /**
29     * @param string $wikiId Wiki namespace belongs to
30     * @param int $nsId Id of the namespace
31     * @return string Metastore document id
32     */
33    public static function docId( $wikiId, $nsId ) {
34        return implode( '-', [
35            self::METASTORE_TYPE,
36            $wikiId, $nsId
37        ] );
38    }
39
40    /**
41     * @return array Custom field mappings for metastore index
42     */
43    public function buildIndexProperties() {
44        return [
45            'namespace_name' => [
46                'type' => 'text',
47                'analyzer' => 'near_match_asciifolding',
48                'norms' => false,
49                'index_options' => 'docs',
50            ],
51        ];
52    }
53
54    /**
55     * Delete and re-index all namespaces for current wiki
56     *
57     * @param Language $lang Content language of the wiki
58     */
59    public function reindex( Language $lang ) {
60        $documents = $this->buildDocuments( $lang );
61        $docIds = [];
62        foreach ( $documents as $doc ) {
63            $docIds[] = $doc->getId();
64        }
65        $this->index->deleteByQuery( \Elastica\Query::create(
66            $this->queryFilter()->addMustNot( new Ids( $docIds ) ) ) );
67        $this->index->addDocuments( $documents );
68    }
69
70    /**
71     * Find namespaces on the current wiki very similar to $name.
72     * Invoking from a user request must be gated on a PoolCounter.
73     *
74     * @param string $name Namespace to search for
75     * @param array $queryOptions Query parameters to send to elasticsearch
76     * @return \Elastica\ResultSet
77     */
78    public function find( $name, array $queryOptions = [] ) {
79        $bool = $this->queryFilter();
80        $bool->addMust( ( new MatchQuery() )
81            ->setField( 'namespace_name', $name ) );
82        $query = ( new \Elastica\Query( $bool ) )
83            ->setParam( '_source', [ 'namespace_id' ] )
84            ->setParam( 'stats', [ 'namespace' ] );
85
86        return $this->index->search( $query, $queryOptions );
87    }
88
89    private function queryFilter(): BoolQuery {
90        return ( new BoolQuery() )
91            ->addFilter( new MatchQuery( 'type', self::METASTORE_TYPE ) )
92            ->addFilter( new MatchQuery( 'wiki', $this->wikiId ) );
93    }
94
95    private function buildDocuments( Language $lang ): array {
96        $namesByNsId = [];
97        foreach ( $lang->getNamespaceIds() as $name => $nsId ) {
98            if ( $name ) {
99                $namesByNsId[$nsId][] = $name;
100            }
101        }
102        $documents = [];
103        foreach ( $namesByNsId as $nsId => $names ) {
104            $documents[] = new Document( self::docId( $this->wikiId, $nsId ), [
105                'type' => self::METASTORE_TYPE,
106                'wiki' => $this->wikiId,
107                'namespace_id' => $nsId,
108                'namespace_name' => $names,
109            ], '_doc' );
110        }
111        return $documents;
112    }
113}