Completed
Pull Request — master (#25)
by Matt
02:07
created

postgres::get_query()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 17
nc 1
nop 4
1
<?php
2
/**
3
 *
4
 * Precise Similar Topics
5
 *
6
 * @copyright (c) 2018 Matt Friedman
7
 * @license GNU General Public License, version 2 (GPL-2.0)
8
 *
9
 */
10
11
namespace vse\similartopics\driver;
12
13
class postgres implements driver_interface
14
{
15
	/** @var \phpbb\db\driver\driver_interface */
16
	protected $db;
17
18
	/** @var string */
19
	protected $ts_name;
20
21
	/**
22
	 * mysql constructor.
23
	 *
24
	 * @param \phpbb\db\driver\driver_interface $db
25
	 * @param \phpbb\config\config              $config
26
	 */
27
	public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\config\config $config)
28
	{
29
		$this->db = $db;
30
		$this->set_ts_name($config['pst_postgres_ts_name']);
31
	}
32
33
	/**
34
	 * {@inheritdoc}
35
	 */
36
	public function get_name()
37
	{
38
		return 'postgres';
39
	}
40
41
	/**
42
	 * {@inheritdoc}
43
	 */
44
	public function get_type()
45
	{
46
		return 'postgres';
47
	}
48
49
	/**
50
	 * {@inheritdoc}
51
	 */
52
	public function get_query($topic_id, $topic_title, $length, $sensitivity)
53
	{
54
		$ts_query_text	= $this->db->sql_escape(str_replace(' ', '|',  $topic_title));
55
56
		return array(
57
			'SELECT'	=> "f.forum_id, f.forum_name, t.*,
58
				ts_rank_cd(to_tsvector('{$this->ts_name}', t.topic_title), '$ts_query_text', 32) AS score",
59
60
			'FROM'		=> array(
61
				TOPICS_TABLE	=> 't',
62
			),
63
			'LEFT_JOIN'	=> array(
64
				array(
65
					'FROM'	=>	array(FORUMS_TABLE	=> 'f'),
66
					'ON'	=> 'f.forum_id = t.forum_id',
67
				),
68
			),
69
			'WHERE'		=> "ts_rank_cd(to_tsvector('{$this->ts_name}', t.topic_title), '$ts_query_text', 32) >= " . (float) $sensitivity/10 . '
70
				AND t.topic_status <> ' . ITEM_MOVED . '
71
				AND t.topic_visibility = ' . ITEM_APPROVED . '
72
				AND t.topic_time > (extract(epoch from current_timestamp)::integer - ' . (int) $length . ')
73
				AND t.topic_id <> ' . (int) $topic_id,
74
			'ORDER_BY'	=> 'score DESC, t.topic_time DESC',
75
		);
76
	}
77
78
	/**
79
	 * Check if the database is using PostgreSQL
80
	 *
81
	 * @access public
82
	 * @return bool True if is postgresql, false otherwise
83
	 */
84
	public function is_postgres()
85
	{
86
		return ($this->db->get_sql_layer() === 'postgres');
87
	}
88
89
	/**
90
	 * {@inheritdoc}
91
	 */
92
	public function is_supported()
93
	{
94
		return $this->is_postgres();
95
	}
96
97
	/**
98
	 * {@inheritdoc}
99
	 */
100
	public function is_index($field = 'topic_title')
101
	{
102
		$is_index = false;
103
104
		foreach ($this->get_pg_indexes($field) as $index)
105
		{
106
			if ($index === TOPICS_TABLE . '_' . $this->ts_name . '_' . $field)
107
			{
108
				$is_index = true;
109
				break;
110
			}
111
		}
112
113
		return $is_index;
114
	}
115
116
	/**
117
	 * {@inheritdoc}
118
	 */
119
	public function create_fulltext_index($field = 'topic_title')
120
	{
121
		$new_index = TOPICS_TABLE . '_' . $this->ts_name . '_' . $field;
122
123
		$indexed = false;
124
125
		foreach ($this->get_pg_indexes() as $index)
126
		{
127
			if ($index === $new_index)
128
			{
129
				$indexed = true;
130
			}
131
			else
132
			{
133
				$sql = 'DROP INDEX ' . $index;
134
				$this->db->sql_query($sql);
135
			}
136
		}
137
138
		if (!$indexed)
139
		{
140
			$sql = 'CREATE INDEX ' . $this->db->sql_escape($new_index) . ' 
141
				ON '  . TOPICS_TABLE . " 
142
				USING gin (to_tsvector ('" . $this->db->sql_escape($this->ts_name) . "', " . $this->db->sql_escape($field) . '))';
143
			$this->db->sql_query($sql);
144
		}
145
	}
146
147
	/**
148
	 * Set the PostgreSQL Text Search name (dictionary)
149
	 *
150
	 * @param string $ts_name Dictionary name
151
	 * @return \vse\similartopics\driver\postgres
152
	 */
153
	public function set_ts_name($ts_name)
154
	{
155
		$this->ts_name = $ts_name ?: 'simple';
156
157
		return $this;
158
	}
159
160
	/**
161
	 * get all PostgreSQL FULLTEXT indexes on field in topics table
162
	 *
163
	 * @access public
164
	 * @param string $field name of a field
165
	 * @return array contains index names
166
	 */
167
	public function get_pg_indexes($field = 'topic_title')
168
	{
169
		$indexes = array();
170
171
		if (!$this->is_postgres())
172
		{
173
			return $indexes;
174
		}
175
176
		$sql = "SELECT c2.relname
177
		FROM pg_catalog.pg_class c1, pg_catalog.pg_index i, pg_catalog.pg_class c2, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) indexdef
178
		WHERE c1.relname = '" . TOPICS_TABLE . "'
179
				AND position('to_tsvector' in indexdef) > 0
180
				AND pg_catalog.pg_table_is_visible(c1.oid)
181
				AND c1.oid = i.indrelid
182
				AND i.indexrelid = c2.oid;";
183
		$result = $this->db->sql_query($sql);
184
185
		while ($row = $this->db->sql_fetchrow($result))
186
		{
187
			if (strpos($row['relname'], $field) !== false)
188
			{
189
				$indexes[] = $row['relname'];
190
			}
191
		}
192
		$this->db->sql_freeresult($result);
193
194
		return $indexes;
195
	}
196
197
	/**
198
	 * Get list of PostgreSQL text search names
199
	 *
200
	 * @return array array of text search names
201
	 */
202
	public function get_cfgname_list()
203
	{
204
		$sql = 'SELECT cfgname AS ts_name FROM pg_ts_config';
205
		$result = $this->db->sql_query($sql);
206
		$ts_options = $this->db->sql_fetchrowset($result);
207
		$this->db->sql_freeresult($result);
208
209
		return $ts_options;
210
	}
211
}
212