<?php namespace QuasselRestSearch; require_once 'Backend.php'; class PostgresSmartBackend implements Backend { private $db; private $options; private $enable_ranking; function __construct(\PDO $db, array $options, bool $enable_ranking) { $this->db = $db; $timeout = $options["timeout"]; $this->db->exec("SET statement_timeout = $timeout;"); $this->options = $options; $this->enable_ranking = $enable_ranking; } public function findUser(): \PDOStatement { return $this->db->prepare(" SELECT * FROM quasseluser WHERE quasseluser.username = :username "); } private function tsQueryFunction(): string { return array_key_exists('tsqueryfunction', $this->options) ? $this->options['tsqueryfunction'] : "plainto_tsquery('english', :query)"; } function rankingParameters(): array { if ($this->enable_ranking) { return [ ":config_normalization", ":weight_content", ":weight_type", ":weight_time" ]; } else { return [ ":weight_type", ":weight_time" ]; } } private function rankingFunction(): string { if ($this->enable_ranking) { return "( (ts_rank_cd(tsv, query, :config_normalization) ^ (2 ^ :weight_content)) * ((CASE WHEN TYPE IN (1, 4) THEN 1.0 WHEN TYPE IN (2, 1024, 2048, 4096, 16384) THEN 0.8 WHEN TYPE IN (32, 64, 128, 256, 512, 32768, 65536) THEN 0.6 WHEN TYPE IN (8, 16, 8192, 131072) THEN 0.4 ELSE 0.2 END) ^ (2 ^ :weight_type)) * ((1 / (EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) - EXTRACT(EPOCH FROM time))) ^ (2 ^ :weight_time)) )"; } else { return "( ((CASE WHEN TYPE IN (1, 4) THEN 1.0 WHEN TYPE IN (2, 1024, 2048, 4096, 16384) THEN 0.8 WHEN TYPE IN (32, 64, 128, 256, 512, 32768, 65536) THEN 0.6 WHEN TYPE IN (8, 16, 8192, 131072) THEN 0.4 ELSE 0.2 END) ^ (2 ^ :weight_type)) * ((1 / (EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) - EXTRACT(EPOCH FROM time))) ^ (2 ^ :weight_time)) )"; } } public function findInBuffers(): \PDOStatement { $tsQueryFunction = $this->tsQueryFunction(); $rankingFunction = $this->rankingFunction(); return $this->db->prepare(" WITH matching_backlog AS ( SELECT * FROM backlog WHERE tsv @@ $tsQueryFunction ) SELECT ranked_messages.bufferid, ranked_messages.buffername, ranked_messages.networkname, ranked_messages.messageid, ranked_messages.type, ranked_messages.time, ranked_messages.sender, ts_headline(replace(replace(ranked_messages.message, '<', '<'), '>', '>'), query, 'HighlightAll=TRUE') AS message FROM (SELECT matching_messages.*, network.networkname, sender.sender, rank() OVER ( PARTITION BY matching_messages.bufferid ORDER BY matching_messages.rank_value DESC ) AS rank, first_value(rank_value) OVER ( PARTITION BY matching_messages.bufferid ORDER BY matching_messages.rank_value DESC ) AS max_rank_value FROM (SELECT backlog.messageid, backlog.bufferid, buffer.buffername, buffer.networkid, backlog.senderid, backlog.type, backlog.time, backlog.message, query, $rankingFunction AS rank_value FROM matching_backlog AS backlog JOIN buffer ON backlog.bufferid = buffer.bufferid , $tsQueryFunction query WHERE buffer.userid = :userid AND (:ignore_since::BOOLEAN OR backlog.time > :since::TIMESTAMP) AND (:ignore_before::BOOLEAN OR backlog.time < :before::TIMESTAMP) AND (:ignore_buffer::BOOLEAN OR buffer.buffername ~* :buffer) AND backlog.type & 23559 > 0 ) matching_messages JOIN sender ON matching_messages.senderid = sender.senderid JOIN network ON matching_messages.networkid = network.networkid WHERE (:ignore_network::BOOLEAN OR network.networkname ~* :network) AND (:ignore_sender::BOOLEAN OR sender.sender ~* :sender) ) ranked_messages WHERE ranked_messages.rank <= :limit ORDER BY ranked_messages.max_rank_value DESC, ranked_messages.rank_value DESC "); } public function findInBuffersCount(): \PDOStatement { $tsQueryFunction = $this->tsQueryFunction(); return $this->db->prepare(" SELECT backlog.bufferid, COUNT(*) > (:limit::INT + :offset::INT) AS hasmore FROM backlog JOIN buffer ON backlog.bufferid = buffer.bufferid JOIN sender ON backlog.senderid = sender.senderid JOIN network ON buffer.networkid = network.networkid , $tsQueryFunction query WHERE buffer.userid = :userid AND (:ignore_since::BOOLEAN OR backlog.time > :since::TIMESTAMP) AND (:ignore_before::BOOLEAN OR backlog.time < :before::TIMESTAMP) AND (:ignore_buffer::BOOLEAN OR buffer.buffername ~* :buffer) AND (:ignore_network::BOOLEAN OR network.networkname ~* :network) AND (:ignore_sender::BOOLEAN OR sender.sender ~* :sender) AND backlog.tsv @@ query AND backlog.type & 23559 > 0 GROUP BY backlog.bufferid; "); } public function findInBuffer(): \PDOStatement { $tsQueryFunction = $this->tsQueryFunction(); $rankingFunction = $this->rankingFunction(); return $this->db->prepare(" WITH matching_backlog AS ( SELECT * FROM backlog WHERE tsv @@ $tsQueryFunction ) SELECT matching_messages.messageid, matching_messages.time, sender.sender, ts_headline(replace(replace(matching_messages.message, '<', '<'), '>', '>'), query, 'HighlightAll=TRUE') AS message FROM (SELECT backlog.messageid, backlog.bufferid, buffer.buffername, buffer.networkid, backlog.senderid, backlog.type, backlog.time, backlog.message, query, $rankingFunction AS rank_value FROM matching_backlog AS backlog JOIN buffer ON backlog.bufferid = buffer.bufferid , $tsQueryFunction query WHERE buffer.userid = :userid AND buffer.bufferid = :bufferid AND (:ignore_since::BOOLEAN OR backlog.time > :since::TIMESTAMP) AND (:ignore_before::BOOLEAN OR backlog.time < :before::TIMESTAMP) AND backlog.type & 23559 > 0 ) matching_messages JOIN sender ON matching_messages.senderid = sender.senderid JOIN network ON matching_messages.networkid = network.networkid WHERE (:ignore_sender::BOOLEAN OR sender.sender ~* :sender) ORDER BY matching_messages.rank_value DESC LIMIT :limit OFFSET :offset "); } public function findInBufferCount(): \PDOStatement { $tsQueryFunction = $this->tsQueryFunction(); return $this->db->prepare(" SELECT COUNT(*) > (:limit::INT + :offset::INT) AS hasmore FROM backlog JOIN buffer ON backlog.bufferid = buffer.bufferid JOIN sender ON backlog.senderid = sender.senderid , $tsQueryFunction query WHERE buffer.userid = :userid AND backlog.bufferid = :bufferid AND (:ignore_since::BOOLEAN OR backlog.time > :since::TIMESTAMP) AND (:ignore_before::BOOLEAN OR backlog.time < :before::TIMESTAMP) AND (:ignore_sender::BOOLEAN OR sender.sender ~* :sender) AND backlog.tsv @@ query AND backlog.type & 23559 > 0 GROUP BY backlog.bufferid; "); } public function loadAfter(): \PDOStatement { return $this->db->prepare(" SELECT backlog.messageid, backlog.bufferid, buffer.buffername, sender.sender, backlog.type, backlog.time, network.networkname, backlog.message FROM backlog JOIN sender ON backlog.senderid = sender.senderid JOIN buffer ON backlog.bufferid = buffer.bufferid JOIN network ON buffer.networkid = network.networkid WHERE buffer.userid = :userid AND backlog.bufferid = :bufferid AND backlog.messageid > :anchor ORDER BY backlog.messageid ASC LIMIT :limit; "); } public function loadBefore(): \PDOStatement { return $this->db->prepare(" SELECT * FROM (SELECT backlog.messageid, backlog.bufferid, buffer.buffername, sender.sender, backlog.type, backlog.time, network.networkname, backlog.message FROM backlog JOIN sender ON backlog.senderid = sender.senderid JOIN buffer ON backlog.bufferid = buffer.bufferid JOIN network ON buffer.networkid = network.networkid WHERE buffer.userid = :userid AND backlog.bufferid = :bufferid AND backlog.messageid < :anchor ORDER BY backlog.messageid DESC LIMIT :limit) t ORDER BY messageid ASC; "); } }