Passed
Pull Request — master (#34)
by Alaa
01:44
created

DatabaseTermIdsResolver   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 106
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 14
lcom 1
cbo 1
dl 0
loc 106
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A resolveTermIds() 0 25 4
A selectTerms() 0 13 1
A loadTypes() 0 7 2
A addResultTerms() 0 13 2
A connectDbr() 0 5 2
A connectDbw() 0 5 2
1
<?php
2
3
namespace Wikibase\TermStore\MediaWiki\PackagePrivate;
4
5
use InvalidArgumentException;
6
use stdClass;
7
use Wikimedia\Rdbms\IDatabase;
8
use Wikimedia\Rdbms\ILoadBalancer;
9
use Wikimedia\Rdbms\IResultWrapper;
10
11
/**
12
 * Term ID resolver using the normalized database schema.
13
 *
14
 * @license GPL-2.0-or-later
15
 */
16
class DatabaseTermIdsResolver implements TermIdsResolver {
17
18
	/** @var TypeIdsResolver */
19
	private $typeIdsResolver;
20
21
	/** @var ILoadBalancer */
22
	private $lb;
23
24
	/** @var IDatabase */
25
	private $dbr = null;
26
27
	/** @var IDatabase */
28
	private $dbw = null;
29
30
	public function __construct(
31
		TypeIdsResolver $typeIdsResolver,
32
		ILoadBalancer $lb
33
	) {
34
		$this->typeIdsResolver = $typeIdsResolver;
35
		$this->lb = $lb;
36
	}
37
38
	/*
39
	 * Term data is first read from the replica; if that returns less rows than we asked for,
40
	 * then there are some new rows in the master that were not yet replicated, and we fall back
41
	 * to the master. As the internal relations of the term store never change (for example,
42
	 * a term_in_lang row will never suddenly point to a different text_in_lang), a master fallback
43
	 * should never be necessary in any other case. However, callers need to consider where they
44
	 * got the list of term IDs they pass into this method from: if it’s from a replica, they may
45
	 * still see outdated data overall.
46
	 */
47
	public function resolveTermIds( array $termIds ): array {
48
		$terms = [];
49
		$this->connectDbr();
50
51
		$replicaResult = $this->selectTerms( $this->dbr, $termIds );
52
		$types = $this->loadTypes( $replicaResult );
53
		$replicaTermIds = [];
54
55
		foreach ( $replicaResult as $row ) {
56
			$replicaTermIds[] = $row->wbtl_id;
57
			$this->addResultTerms( $terms, $row, $types );
58
		}
59
60
		if ( count( $replicaTermIds ) !== count( $termIds ) ) {
61
			$masterTermIds = array_values( array_diff( $termIds, $replicaTermIds ) );
62
			$this->connectDbw();
63
			$masterResult = $this->selectTerms( $this->dbw, $masterTermIds );
64
			$types += $this->loadTypes( $masterResult );
65
			foreach ( $masterResult as $row ) {
66
				$this->addResultTerms( $terms, $row, $types );
67
			}
68
		}
69
70
		return $terms;
71
	}
72
73
	private function selectTerms( IDatabase $db, array $termIds ): IResultWrapper {
74
		return $db->select(
75
			[ 'wbt_term_in_lang', 'wbt_text_in_lang', 'wbt_text' ],
76
			[ 'wbtl_id', 'wbtl_type_id', 'wbxl_language', 'wbx_text' ],
77
			[
78
				'wbtl_id' => $termIds,
79
				// join conditions
80
				'wbtl_text_in_lang_id=wbxl_id',
81
				'wbxl_text_id=wbx_id',
82
			],
83
			__METHOD__
84
		);
85
	}
86
87
	private function loadTypes( IResultWrapper $result ) {
88
		$typeIds = [];
89
		foreach ( $result as $row ) {
90
			$typeIds[] = $row->wbtl_type_id;
91
		}
92
		return $this->typeIdsResolver->resolveTypeIds( $typeIds );
93
	}
94
95
	private function addResultTerms( array &$terms, stdClass $row, array $types ) {
96
		$typeId = $row->wbtl_type_id;
97
		if ( !isset( $types[$typeId] ) ) {
98
			throw new InvalidArgumentException(
99
				'Type ID ' . $typeId . ' was not found!' );
100
		}
101
102
		$type = $types[$typeId];
103
		$lang = $row->wbxl_language;
104
		$text = $row->wbx_text;
105
106
		$terms[$type][$lang][] = $text;
107
	}
108
109
	private function connectDbr() {
110
		if ( $this->dbr === null ) {
111
			$this->dbr = $this->lb->getConnection( ILoadBalancer::DB_REPLICA );
112
		}
113
	}
114
115
	private function connectDbw() {
116
		if ( $this->dbw === null ) {
117
			$this->dbw = $this->lb->getConnection( ILoadBalancer::DB_MASTER );
118
		}
119
	}
120
121
}
122