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