From 07308521792a182e9cd4de10d067b96c190d21a5 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 19 Aug 2014 08:20:47 -0400 Subject: [PATCH] Cache relation members for better performance (#3452) --- plugins/kolab_notes/kolab_notes.php | 89 +++---------------- .../lib/kolab_format_configuration.php | 25 ++++++ .../lib/kolab_storage_cache_configuration.php | 6 ++ plugins/libkolab/lib/kolab_storage_config.php | 79 ++++++++++++++++ 4 files changed, 123 insertions(+), 76 deletions(-) diff --git a/plugins/kolab_notes/kolab_notes.php b/plugins/kolab_notes/kolab_notes.php index 7553fd1b..a5723a1f 100644 --- a/plugins/kolab_notes/kolab_notes.php +++ b/plugins/kolab_notes/kolab_notes.php @@ -391,7 +391,7 @@ class kolab_notes extends rcube_plugin $this->rc->output->set_env('kolab_notes_template', array( '_from_mail' => true, 'title' => $message->get('subject'), - 'links' => array($this->get_message_reference($this->get_message_uri($message, $folder))), + 'links' => array($this->get_message_reference(kolab_storage_config::get_message_uri($message, $folder))), )); } } @@ -996,7 +996,6 @@ class kolab_notes extends rcube_plugin foreach ($relations as $relation) { if (empty($links)) { $config->delete($relation['uid']); - $this->relations = null; // clear in-memory cache } else { // make relation members up-to-date @@ -1010,7 +1009,6 @@ class kolab_notes extends rcube_plugin if (count($diff1) || count($diff2)) { $relation['members'] = $members; $config->save($relation, 'relation'); - $this->relations = null; // clear in-memory cache } $links = null; @@ -1025,7 +1023,6 @@ class kolab_notes extends rcube_plugin ); $config->save($relation, 'relation'); - $this->relations = null; // clear in-memory cache } } @@ -1070,33 +1067,11 @@ class kolab_notes extends rcube_plugin */ private function get_message_notes($message, $folder) { - $result = array(); - $uids = array(); + $config = kolab_storage_config::get_instance(); + $result = $config->get_message_relations($message, $folder, 'note'); - // TODO: only query for notes if message was flagged with $KolabNotes ? - - // get UIDs of assigned notes - foreach ($this->get_relations() as $relation) { - // get Folder/UIDs of relation members - $messages = kolab_storage_config::resolve_members($relation); - - if (!empty($messages[$folder]) && in_array($message->uid, $messages[$folder])) { - // find note UID(s) - foreach ($relation['members'] as $member) { - if (strpos($member, 'urn:uuid:') === 0) { - $uids[] = substr($member, 9); - } - } - } - } - - // get Note objects - if (!empty($uids)) { - $query = array(array('uid', '=', $uids)); - foreach (kolab_storage::select($query, 'note') as $record) { - $record['list'] = kolab_storage::folder_id($record['_mailbox']); - $result[] = $record; - } + foreach ($result as $idx => $note) { + $result[$idx]['list'] = kolab_storage::folder_id($note['_mailbox']); } return $result; @@ -1105,55 +1080,17 @@ class kolab_notes extends rcube_plugin /** * Find relation objects referring to specified note */ - private function get_relations($uid = null) + private function get_relations($uid) { - if (!isset($this->relations)) { - $config = kolab_storage_config::get_instance(); - $default = true; - $filter = array( - array('type', '=', 'relation'), - array('category', '=', 'generic') - ); - - $this->relations = $config->get_objects($filter, $default); - } - - if ($uid === null) { - return $this->relations; - } - - $result = array(); - $search = kolab_storage_config::build_member_url($uid); - - foreach ($this->relations as $relation) { - if (in_array($search, (array) $relation['members'])) { - $result[] = $relation; - } - } - - return $result; - } - - /** - * Build a URI representing the given message reference - */ - private function get_message_uri($headers, $folder) - { - $params = array( - 'folder' => $headers->folder ?: $folder, - 'uid' => $headers->uid, + $config = kolab_storage_config::get_instance(); + $default = true; + $filter = array( + array('type', '=', 'relation'), + array('category', '=', 'generic'), + array('member', '=', $uid), ); - if (($messageid = $headers->get('message-id', false)) && ($date = $headers->get('date', false))) { - $params['message-id'] = $messageid; - $params['date'] = $date; - - if ($subject = $headers->get('subject')) { - $params['subject'] = $subject; - } - } - - return kolab_storage_config::build_member_url($params); + return $config->get_objects($filter, $default, 100); } /** diff --git a/plugins/libkolab/lib/kolab_format_configuration.php b/plugins/libkolab/lib/kolab_format_configuration.php index 6b71f3ec..17b46a70 100644 --- a/plugins/libkolab/lib/kolab_format_configuration.php +++ b/plugins/libkolab/lib/kolab_format_configuration.php @@ -220,4 +220,29 @@ class kolab_format_configuration extends kolab_format return $tags; } + + /** + * Callback for kolab_storage_cache to get words to index for fulltext search + * + * @return array List of words to save in cache + */ + public function get_words() + { + $words = array(); + + foreach ((array)$this->data['members'] as $url) { + $member = kolab_storage_config::parse_member_url($url); + + if (empty($member)) { + if (strpos($url, 'urn:uuid:') === 0) { + $words[] = substr($url, 9); + } + } + else if (!empty($member['params']['message-id'])) { + $words[] = $member['params']['message-id']; + } + } + + return $words; + } } diff --git a/plugins/libkolab/lib/kolab_storage_cache_configuration.php b/plugins/libkolab/lib/kolab_storage_cache_configuration.php index 97315da2..ec015dd7 100644 --- a/plugins/libkolab/lib/kolab_storage_cache_configuration.php +++ b/plugins/libkolab/lib/kolab_storage_cache_configuration.php @@ -52,6 +52,12 @@ class kolab_storage_cache_configuration extends kolab_storage_cache $query[$idx][0] = 'tags'; $query[$idx][2] = count($param[2]) > 1 ? $param[2] : $param[2][0]; } + // convert member filter (we support only = operator with single value) + else if ($param[0] == 'member') { + $query[$idx][0] = 'words'; + $query[$idx][1] = '~'; + $query[$idx][2] = '^' . $param[2] . '$'; + } } } diff --git a/plugins/libkolab/lib/kolab_storage_config.php b/plugins/libkolab/lib/kolab_storage_config.php index beaa9288..9bc5d509 100644 --- a/plugins/libkolab/lib/kolab_storage_config.php +++ b/plugins/libkolab/lib/kolab_storage_config.php @@ -623,6 +623,12 @@ class kolab_storage_config array('category', '=', 'tag') ); + // use faster method + if ($uid && $uid != '*') { + $filter[] = array('member', '=', $uid); + return $this->get_objects($filter, $default); + } + $this->tags = $this->get_objects($filter, $default); } @@ -641,4 +647,77 @@ class kolab_storage_config return $result; } + + /** + * Find kolab objects assigned to specified e-mail message + * + * @param rcube_message $message E-mail message + * @param string $folder Folder name + * @param string $type Result objects type + * + * @return array List of kolab objects + */ + public function get_message_relations($message, $folder, $type) + { + $result = array(); + $uids = array(); + $default = true; + $uri = self::get_message_uri($message, $folder); + $filter = array( + array('type', '=', 'relation'), + array('category', '=', 'generic'), + // @TODO: what if Message-Id (and Date) does not exist? + array('member', '=', $message->get('message-id', false)), + ); + + // get UIDs of assigned notes + foreach ($this->get_objects($filter, $default) as $relation) { + // we don't need to update members if the URI is found + if (in_array($uri, $relation['members'])) { + // update members... + $messages = kolab_storage_config::resolve_members($relation); + // ...and check again + if (empty($messages[$folder]) || !in_array($message->uid, $messages[$folder])) { + continue; + } + } + + // find note UID(s) + foreach ($relation['members'] as $member) { + if (strpos($member, 'urn:uuid:') === 0) { + $uids[] = substr($member, 9); + } + } + } + + // get kolab objects of specified type + if (!empty($uids)) { + $query = array(array('uid', '=', array_unique($uids))); + $result = kolab_storage::select($query, $type); + } + + return $result; + } + + /** + * Build a URI representing the given message reference + */ + public static function get_message_uri($headers, $folder) + { + $params = array( + 'folder' => $headers->folder ?: $folder, + 'uid' => $headers->uid, + ); + + if (($messageid = $headers->get('message-id', false)) && ($date = $headers->get('date', false))) { + $params['message-id'] = $messageid; + $params['date'] = $date; + + if ($subject = $headers->get('subject')) { + $params['subject'] = $subject; + } + } + + return self::build_member_url($params); + } }