From f855dbdca6e95ac367b7a9dae9d3a866e1f85d37 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 2 Sep 2013 22:06:51 +0200 Subject: [PATCH 1/9] SQL and model optimisation Big effect (on speed and memory), but few changes :-) Drastically reduced the number of SQL requests needed (from 233 down to 8 to load the home page with my own data set = 140 feeds in 15 categories). Drastically reduced the amount of data transferred from MySQL to PHP. --- app/models/Category.php | 99 +++++++++++++++++++++--- app/models/EntriesGetter.php | 24 ++++-- app/models/Entry.php | 97 ++++++++++++++--------- app/models/Feed.php | 17 +++- app/views/helpers/view/normal_view.phtml | 5 +- app/views/helpers/view/reader_view.phtml | 5 +- 6 files changed, 189 insertions(+), 58 deletions(-) diff --git a/app/models/Category.php b/app/models/Category.php index e733b15ba..1e9ec9e80 100755 --- a/app/models/Category.php +++ b/app/models/Category.php @@ -4,11 +4,22 @@ class Category extends Model { private $id = false; private $name; private $color; + private $nbFeed = -1; + private $nbNotRead = -1; private $feeds = null; - public function __construct ($name = '', $color = '#0062BE') { + public function __construct ($name = '', $color = '#0062BE', $feeds = null) { $this->_name ($name); $this->_color ($color); + if (!empty($feeds)) { + $this->_feeds ($feeds); + $this->nbFeed = 0; + $this->nbNotRead = 0; + foreach ($feeds as $feed) { + $this->nbFeed++; + $this->nbNotRead += $feed->nbNotRead (); + } + } } public function id () { @@ -25,17 +36,31 @@ class Category extends Model { return $this->color; } public function nbFeed () { + if ($this->nbFeed < 0) { $catDAO = new CategoryDAO (); - return $catDAO->countFeed ($this->id ()); + $this->nbFeed = $catDAO->countFeed ($this->id ()); + } + + return $this->nbFeed; } public function nbNotRead () { + if ($this->nbNotRead < 0) { $catDAO = new CategoryDAO (); - return $catDAO->countNotRead ($this->id ()); + $this->nbNotRead = $catDAO->countNotRead ($this->id ()); + } + + return $this->nbNotRead; } public function feeds () { if (is_null ($this->feeds)) { $feedDAO = new FeedDAO (); $this->feeds = $feedDAO->listByCategory ($this->id ()); + $this->nbFeed = 0; + $this->nbNotRead = 0; + foreach ($this->feeds as $feed) { + $this->nbFeed++; + $this->nbNotRead += $feed->nbNotRead (); + } } return $this->feeds; @@ -150,12 +175,23 @@ class CategoryDAO extends Model_pdo { } } - public function listCategories () { - $sql = 'SELECT * FROM ' . $this->prefix . 'category ORDER BY name'; - $stm = $this->bd->prepare ($sql); - $stm->execute (); - - return HelperCategory::daoToCategory ($stm->fetchAll (PDO::FETCH_ASSOC)); + public function listCategories ($prePopulateFeeds = true) { //TODO: Search code-base for places where $prePopulateFeeds should be false + if ($prePopulateFeeds) { + $sql = 'SELECT c.id as c_id, c.name as c_name, c.color as c_color, count(e.id) as nbNotRead, f.* ' + . 'FROM ' . $this->prefix . 'category c ' + . 'INNER JOIN ' . $this->prefix . 'feed f ON f.category = c.id ' + . 'LEFT OUTER JOIN ' . $this->prefix . 'entry e ON e.id_feed = f.id AND e.is_read = 0 ' + . 'GROUP BY f.id ' + . 'ORDER BY c.name, f.name'; + $stm = $this->bd->prepare ($sql); + $stm->execute (); + return HelperCategory::daoToCategoryPrepopulated ($stm->fetchAll (PDO::FETCH_ASSOC)); + } else { + $sql = 'SELECT * FROM ' . $this->prefix . 'category ORDER BY name'; + $stm = $this->bd->prepare ($sql); + $stm->execute (); + return HelperCategory::daoToCategory ($stm->fetchAll (PDO::FETCH_ASSOC)); + } } public function getDefault () { @@ -220,6 +256,51 @@ class CategoryDAO extends Model_pdo { } class HelperCategory { + public static function findFeed($categories, $feed_id) { + foreach ($categories as $category) { + foreach ($category->feeds () as $feed) { + if ($feed->id () === $feed_id) { + return $feed; + } + } + } + return null; + } + + public static function daoToCategoryPrepopulated ($listDAO) { + $list = array (); + + if (!is_array ($listDAO)) { + $listDAO = array ($listDAO); + } + + $previousLine = null; + $feedsDao = array(); + $nbLinesMinus1 = count($listDAO) - 1; + for ($i = 0; $i <= $nbLinesMinus1; $i++) { + $line = $listDAO[$i]; + $cat_id = $line['c_id']; + if (($i > 0) && (($cat_id !== $previousLine['c_id']) || ($i === $nbLinesMinus1))) { //End of current category + $cat = new Category ( + $previousLine['c_name'], + $previousLine['c_color'], + HelperFeed::daoToFeed ($feedsDao) + ); + $cat->_id ($previousLine['c_id']); + $list[] = $cat; + + $feedsDao = array(); //Prepare for next category + $previousLine = $line; + $feedsDao[] = $line; + } else { + $previousLine = $line; + $feedsDao[] = $line; + } + } + + return $list; + } + public static function daoToCategory ($listDAO) { $list = array (); diff --git a/app/models/EntriesGetter.php b/app/models/EntriesGetter.php index ca92804a7..dc7ecca7c 100644 --- a/app/models/EntriesGetter.php +++ b/app/models/EntriesGetter.php @@ -94,41 +94,51 @@ class EntriesGetter { public function execute () { $entryDAO = new EntryDAO (); - HelperEntry::$nb = $this->nb; - HelperEntry::$first = $this->first; + HelperEntry::$nb = $this->nb; //TODO: Update: Now done in SQL + HelperEntry::$first = $this->first; //TODO: Update: Now done in SQL HelperEntry::$filter = $this->filter; switch ($this->type['type']) { case 'all': list ($this->entries, $this->next) = $entryDAO->listEntries ( $this->state, - $this->order + $this->order, + $this->first, + $this->nb ); break; case 'favoris': list ($this->entries, $this->next) = $entryDAO->listFavorites ( $this->state, - $this->order + $this->order, + $this->first, + $this->nb ); break; case 'public': list ($this->entries, $this->next) = $entryDAO->listPublic ( $this->state, - $this->order + $this->order, + $this->first, + $this->nb ); break; case 'c': list ($this->entries, $this->next) = $entryDAO->listByCategory ( $this->type['id'], $this->state, - $this->order + $this->order, + $this->first, + $this->nb ); break; case 'f': list ($this->entries, $this->next) = $entryDAO->listByFeed ( $this->type['id'], $this->state, - $this->order + $this->order, + $this->first, + $this->nb ); break; default: diff --git a/app/models/Entry.php b/app/models/Entry.php index c247c6362..4043e8f01 100755 --- a/app/models/Entry.php +++ b/app/models/Entry.php @@ -375,12 +375,15 @@ class EntryDAO extends Model_pdo { } } - public function listWhere ($where, $state, $order, $values = array ()) { + private function listWhere ($where, $state, $order, $limitFromId = '', $limitCount = '', $values = array ()) { if ($state == 'not_read') { $where .= ' AND is_read = 0'; } elseif ($state == 'read') { $where .= ' AND is_read = 1'; } + if (!empty($limitFromId)) { + $where .= ' AND date ' . ($order === 'low_to_high' ? '<=' : '>=') . ' (SELECT date from freshrss_entry WHERE id = "' . $limitFromId . '")'; + } if ($order == 'low_to_high') { $order = ' DESC'; @@ -390,78 +393,96 @@ class EntryDAO extends Model_pdo { $sql = 'SELECT e.* FROM ' . $this->prefix . 'entry e' . ' INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id' . $where - . ' ORDER BY date' . $order; + . ' ORDER BY date' . $order . ', id' . $order; + + if (!empty($limitCount)) { + $sql .= ' LIMIT ' . $limitCount; //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ + } + $stm = $this->bd->prepare ($sql); $stm->execute ($values); return HelperEntry::daoToEntry ($stm->fetchAll (PDO::FETCH_ASSOC)); } - public function listEntries ($state, $order = 'high_to_low') { - return $this->listWhere (' WHERE priority > 0', $state, $order); + public function listEntries ($state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') { + return $this->listWhere (' WHERE priority > 0', $state, $order, $limitFromId, $limitCount + 1); } - public function listFavorites ($state, $order = 'high_to_low') { - return $this->listWhere (' WHERE is_favorite = 1', $state, $order); + public function listFavorites ($state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') { + return $this->listWhere (' WHERE is_favorite = 1', $state, $order, $limitFromId, $limitCount + 1); } - public function listPublic ($state, $order = 'high_to_low') { - return $this->listWhere (' WHERE is_public = 1', $state, $order); + public function listPublic ($state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') { + return $this->listWhere (' WHERE is_public = 1', $state, $order, $limitFromId, $limitCount + 1); } - public function listByCategory ($cat, $state, $order = 'high_to_low') { - return $this->listWhere (' WHERE category = ?', $state, $order, array ($cat)); + public function listByCategory ($cat, $state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') { + return $this->listWhere (' WHERE category = ?', $state, $order, $limitFromId, $limitCount + 1, array ($cat)); } - public function listByFeed ($feed, $state, $order = 'high_to_low') { - return $this->listWhere (' WHERE id_feed = ?', $state, $order, array ($feed)); + public function listByFeed ($feed, $state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') { + return $this->listWhere (' WHERE id_feed = ?', $state, $order, $limitFromId, $limitCount + 1, array ($feed)); } - public function count () { - $sql = 'SELECT COUNT(*) AS count FROM ' . $this->prefix . 'entry e INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id WHERE priority > 0'; + public function countUnreadRead () { + $sql = 'SELECT is_read, COUNT(*) AS count FROM ' . $this->prefix . 'entry e INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id WHERE priority > 0 GROUP BY is_read'; $stm = $this->bd->prepare ($sql); $stm->execute (); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - return $res[0]['count']; + $readUnread = array('unread' => 0, 'read' => 0); + foreach ($res as $line) { + switch (intval($line['is_read'])) { + case 0: $readUnread['unread'] = intval($line['count']); break; + case 1: $readUnread['read'] = intval($line['count']); break; + } + } + return $readUnread; } - public function countNotRead () { - $sql = 'SELECT COUNT(*) AS count FROM ' . $this->prefix . 'entry e INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id WHERE is_read=0 AND priority > 0'; - $stm = $this->bd->prepare ($sql); - $stm->execute (); - $res = $stm->fetchAll (PDO::FETCH_ASSOC); - - return $res[0]['count']; + public function count () { //Deprecated: use countUnreadRead() instead + $unreadRead = $this->countUnreadRead (); //This makes better use of caching + return $unreadRead['unread'] + $unreadRead['read']; + } + public function countNotRead () { //Deprecated: use countUnreadRead() instead + $unreadRead = $this->countUnreadRead (); //This makes better use of caching + return $unreadRead['unread']; } - public function countNotReadByFeed ($id) { + /*public function countNotReadByFeed ($id) { //Is this used? $sql = 'SELECT COUNT(*) AS count FROM ' . $this->prefix . 'entry WHERE is_read = 0 AND id_feed = ?'; $stm = $this->bd->prepare ($sql); $stm->execute (array ($id)); $res = $stm->fetchAll (PDO::FETCH_ASSOC); return $res[0]['count']; - } + }*/ - public function countNotReadByCat ($id) { + /*public function countNotReadByCat ($id) { //Is this used? $sql = 'SELECT COUNT(*) AS count FROM ' . $this->prefix . 'entry e INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id WHERE is_read=0 AND category = ?'; $stm = $this->bd->prepare ($sql); $stm->execute (array ($id)); $res = $stm->fetchAll (PDO::FETCH_ASSOC); return $res[0]['count']; - } + }*/ - public function countNotReadFavorites () { - $sql = 'SELECT COUNT(*) AS count FROM ' . $this->prefix . 'entry WHERE is_read=0 AND is_favorite=1'; + public function countUnreadReadFavorites () { + $sql = 'SELECT is_read, COUNT(*) AS count FROM ' . $this->prefix . 'entry WHERE is_favorite=1 GROUP BY is_read'; $stm = $this->bd->prepare ($sql); $stm->execute (); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - - return $res[0]['count']; + $readUnread = array('unread' => 0, 'read' => 0); + foreach ($res as $line) { + switch (intval($line['is_read'])) { + case 0: $readUnread['unread'] = intval($line['count']); break; + case 1: $readUnread['read'] = intval($line['count']); break; + } + } + return $readUnread; } - public function countFavorites () { - $sql = 'SELECT COUNT(*) AS count FROM ' . $this->prefix . 'entry WHERE is_favorite=1'; - $stm = $this->bd->prepare ($sql); - $stm->execute (); - $res = $stm->fetchAll (PDO::FETCH_ASSOC); - - return $res[0]['count']; + /*public function countNotReadFavorites () { //Is this used? //Deprecated: use countUnreadReadFavorites() instead + $unreadRead = $this->countUnreadReadFavorites (); //This makes better use of caching + return $unreadRead['unread']; + }*/ + public function countFavorites () { //Deprecated: use countUnreadReadFavorites() instead + $unreadRead = $this->countUnreadReadFavorites (); //This makes better use of caching + return $unreadRead['unread'] + $unreadRead['read']; } public function optimizeTable() { @@ -505,7 +526,7 @@ class HelperEntry { $list[$key] = self::createEntry ($dao); $count++; - $first_is_found = true; + $first_is_found = true; //TODO: Update: Now done in SQL } if ($count >= self::$nb) { $break_after = true; diff --git a/app/models/Feed.php b/app/models/Feed.php index 678809af6..e9e21f06a 100644 --- a/app/models/Feed.php +++ b/app/models/Feed.php @@ -4,6 +4,7 @@ class Feed extends Model { private $id = null; private $url; private $category = '000000'; + private $nbNotRead = -1; private $entries = null; private $name = ''; private $website = ''; @@ -82,8 +83,12 @@ class Feed extends Model { return $feedDAO->countEntries ($this->id ()); } public function nbNotRead () { + if ($this->nbNotRead < 0) { $feedDAO = new FeedDAO (); - return $feedDAO->countNotRead ($this->id ()); + $this->nbNotRead = $feedDAO->countNotRead ($this->id ()); + } + + return $this->nbNotRead; } public function favicon () { $file = '/data/favicons/' . $this->id () . '.ico'; @@ -162,6 +167,12 @@ class Feed extends Model { } $this->keep_history = $value; } + public function _nbNotRead ($value) { //Alex + if (!is_int (intval ($value))) { + $value = -1; + } + $this->nbNotRead = $value; + } public function load () { if (!is_null ($this->url)) { @@ -506,7 +517,9 @@ class HelperFeed { $list[$key]->_httpAuth (base64_decode ($dao['httpAuth'])); $list[$key]->_error ($dao['error']); $list[$key]->_keepHistory ($dao['keep_history']); - + if (isset ($dao['nbNotRead'])) { + $list[$key]->_nbNotRead ($dao['nbNotRead']); + } if (isset ($dao['id'])) { $list[$key]->_id ($dao['id']); } diff --git a/app/views/helpers/view/normal_view.phtml b/app/views/helpers/view/normal_view.phtml index fb4e60cf6..5b9f03cdd 100644 --- a/app/views/helpers/view/normal_view.phtml +++ b/app/views/helpers/view/normal_view.phtml @@ -42,7 +42,10 @@ if (isset ($this->entryPaginator) && !$this->entryPaginator->isEmpty ()) { - feed (true); ?> + cat_aside, $item->feed ()); //We most likely already have the feed object in cache + if (empty($feed)) $feed = $item->feed (true); + ?>
  • name (); ?>
  • title (); ?>
  • date (); ?>
  • diff --git a/app/views/helpers/view/reader_view.phtml b/app/views/helpers/view/reader_view.phtml index 46a65a4e3..7b1e2bca0 100644 --- a/app/views/helpers/view/reader_view.phtml +++ b/app/views/helpers/view/reader_view.phtml @@ -11,7 +11,10 @@ if (isset ($this->entryPaginator) && !$this->entryPaginator->isEmpty ()) {
    - feed (true); ?> + cat_aside, $item->feed ()); //We most likely already have the feed object in cache + if (empty($feed)) $feed = $item->feed (true); + ?> name (); ?> From f55ae730e6477f37566722961ca7e7ab9ad7f3c5 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 3 Sep 2013 23:35:33 +0200 Subject: [PATCH 2/9] Disable SQL LIMIT optimisation during search This patch is to make search work again after the new SQL optimisations, by removing some of the optimisations when searching is used. Optimisation of search is left for some future work. The whole base is indeed transfered from MySQL to PHP, which is not good. --- app/models/EntriesGetter.php | 12 +++++++----- app/models/Entry.php | 12 ++++++------ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/models/EntriesGetter.php b/app/models/EntriesGetter.php index dc7ecca7c..803aad732 100644 --- a/app/models/EntriesGetter.php +++ b/app/models/EntriesGetter.php @@ -98,13 +98,15 @@ class EntriesGetter { HelperEntry::$first = $this->first; //TODO: Update: Now done in SQL HelperEntry::$filter = $this->filter; + $sqlLimit = (empty ($this->filter['words']) && empty ($this->filter['tags'])) ? $this->nb : ''; //Disable SQL LIMIT optimisation during search //TODO: Do better! + switch ($this->type['type']) { case 'all': list ($this->entries, $this->next) = $entryDAO->listEntries ( $this->state, $this->order, $this->first, - $this->nb + $sqlLimit ); break; case 'favoris': @@ -112,7 +114,7 @@ class EntriesGetter { $this->state, $this->order, $this->first, - $this->nb + $sqlLimit ); break; case 'public': @@ -120,7 +122,7 @@ class EntriesGetter { $this->state, $this->order, $this->first, - $this->nb + $sqlLimit ); break; case 'c': @@ -129,7 +131,7 @@ class EntriesGetter { $this->state, $this->order, $this->first, - $this->nb + $sqlLimit ); break; case 'f': @@ -138,7 +140,7 @@ class EntriesGetter { $this->state, $this->order, $this->first, - $this->nb + $sqlLimit ); break; default: diff --git a/app/models/Entry.php b/app/models/Entry.php index 4043e8f01..53f09b9c9 100755 --- a/app/models/Entry.php +++ b/app/models/Entry.php @@ -396,7 +396,7 @@ class EntryDAO extends Model_pdo { . ' ORDER BY date' . $order . ', id' . $order; if (!empty($limitCount)) { - $sql .= ' LIMIT ' . $limitCount; //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ + $sql .= ' LIMIT ' . ($limitCount + 1); //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ } $stm = $this->bd->prepare ($sql); @@ -405,19 +405,19 @@ class EntryDAO extends Model_pdo { return HelperEntry::daoToEntry ($stm->fetchAll (PDO::FETCH_ASSOC)); } public function listEntries ($state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') { - return $this->listWhere (' WHERE priority > 0', $state, $order, $limitFromId, $limitCount + 1); + return $this->listWhere (' WHERE priority > 0', $state, $order, $limitFromId, $limitCount); } public function listFavorites ($state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') { - return $this->listWhere (' WHERE is_favorite = 1', $state, $order, $limitFromId, $limitCount + 1); + return $this->listWhere (' WHERE is_favorite = 1', $state, $order, $limitFromId, $limitCount); } public function listPublic ($state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') { - return $this->listWhere (' WHERE is_public = 1', $state, $order, $limitFromId, $limitCount + 1); + return $this->listWhere (' WHERE is_public = 1', $state, $order, $limitFromId, $limitCount); } public function listByCategory ($cat, $state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') { - return $this->listWhere (' WHERE category = ?', $state, $order, $limitFromId, $limitCount + 1, array ($cat)); + return $this->listWhere (' WHERE category = ?', $state, $order, $limitFromId, $limitCount, array ($cat)); } public function listByFeed ($feed, $state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') { - return $this->listWhere (' WHERE id_feed = ?', $state, $order, $limitFromId, $limitCount + 1, array ($feed)); + return $this->listWhere (' WHERE id_feed = ?', $state, $order, $limitFromId, $limitCount, array ($feed)); } public function countUnreadRead () { From 040e72fe4f4d6105239dc21d68e97dc8be724dc3 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 4 Sep 2013 02:20:02 +0200 Subject: [PATCH 3/9] Work around In the current SQL request with LIMIT, if many dates are identical, the pagination may not work properly. Added a little more tolerance, but will have to be solved better. --- app/models/Entry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/Entry.php b/app/models/Entry.php index 53f09b9c9..a763feca0 100755 --- a/app/models/Entry.php +++ b/app/models/Entry.php @@ -396,7 +396,7 @@ class EntryDAO extends Model_pdo { . ' ORDER BY date' . $order . ', id' . $order; if (!empty($limitCount)) { - $sql .= ' LIMIT ' . ($limitCount + 1); //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ + $sql .= ' LIMIT ' . ($limitCount + 2); //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ } $stm = $this->bd->prepare ($sql); From cf62bcd3d31a801a16a06608b6a953613f68fbde Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 4 Sep 2013 09:28:27 +0200 Subject: [PATCH 4/9] Handle paging for entries with identical date Paging now works even when many entries have the same date. SQL speed could probably be improved by testing first on date, and then on CONCAT. Also, having an index on date would probably help too. --- app/models/Entry.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/Entry.php b/app/models/Entry.php index a763feca0..a27cce148 100755 --- a/app/models/Entry.php +++ b/app/models/Entry.php @@ -381,8 +381,8 @@ class EntryDAO extends Model_pdo { } elseif ($state == 'read') { $where .= ' AND is_read = 1'; } - if (!empty($limitFromId)) { - $where .= ' AND date ' . ($order === 'low_to_high' ? '<=' : '>=') . ' (SELECT date from freshrss_entry WHERE id = "' . $limitFromId . '")'; + if (!empty($limitFromId)) { //TODO: Consider using LPAD(e.date, 11) //CONCAT is for cases when many entries have the same date + $where .= ' AND CONCAT(e.date, e.id) ' . ($order === 'low_to_high' ? '<=' : '>=') . ' (SELECT CONCAT(s.date, s.id) from freshrss_entry s WHERE s.id = "' . $limitFromId . '")'; } if ($order == 'low_to_high') { @@ -393,7 +393,7 @@ class EntryDAO extends Model_pdo { $sql = 'SELECT e.* FROM ' . $this->prefix . 'entry e' . ' INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id' . $where - . ' ORDER BY date' . $order . ', id' . $order; + . ' ORDER BY e.date' . $order . ', e.id' . $order; if (!empty($limitCount)) { $sql .= ' LIMIT ' . ($limitCount + 2); //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ From e15b7fd6ec5ae770ce232b2a0cea7b281254d96e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 4 Sep 2013 20:22:49 +0200 Subject: [PATCH 5/9] Correct bug: last category was not shown --- app/models/Category.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/models/Category.php b/app/models/Category.php index 1e9ec9e80..362bce621 100755 --- a/app/models/Category.php +++ b/app/models/Category.php @@ -281,6 +281,9 @@ class HelperCategory { $line = $listDAO[$i]; $cat_id = $line['c_id']; if (($i > 0) && (($cat_id !== $previousLine['c_id']) || ($i === $nbLinesMinus1))) { //End of current category + if ($i === $nbLinesMinus1) { //End of table + $feedsDao[] = $line; + } $cat = new Category ( $previousLine['c_name'], $previousLine['c_color'], From 7627970862e03a9061ddced3423689e9dccd6f45 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 12 Sep 2013 22:41:09 +0200 Subject: [PATCH 6/9] Corrected bug with the default "No Category" The SQL optimisation patch had introduced a bug with "No Category", now solved --- app/controllers/feedController.php | 1 + app/models/Category.php | 2 +- app/models/Feed.php | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/controllers/feedController.php b/app/controllers/feedController.php index 18a46f322..d685857bd 100755 --- a/app/controllers/feedController.php +++ b/app/controllers/feedController.php @@ -17,6 +17,7 @@ class feedController extends ActionController { if (Request::isPost ()) { $url = Request::param ('url_rss'); $cat = Request::param ('category'); + if (empty($cat)) $cat = '000000'; $user = Request::param ('username'); $pass = Request::param ('password'); $params = array (); diff --git a/app/models/Category.php b/app/models/Category.php index 362bce621..70cc0d16f 100755 --- a/app/models/Category.php +++ b/app/models/Category.php @@ -179,7 +179,7 @@ class CategoryDAO extends Model_pdo { if ($prePopulateFeeds) { $sql = 'SELECT c.id as c_id, c.name as c_name, c.color as c_color, count(e.id) as nbNotRead, f.* ' . 'FROM ' . $this->prefix . 'category c ' - . 'INNER JOIN ' . $this->prefix . 'feed f ON f.category = c.id ' + . 'LEFT OUTER JOIN ' . $this->prefix . 'feed f ON f.category = c.id ' . 'LEFT OUTER JOIN ' . $this->prefix . 'entry e ON e.id_feed = f.id AND e.is_read = 0 ' . 'GROUP BY f.id ' . 'ORDER BY c.name, f.name'; diff --git a/app/models/Feed.php b/app/models/Feed.php index e9e21f06a..2a93be757 100644 --- a/app/models/Feed.php +++ b/app/models/Feed.php @@ -502,6 +502,9 @@ class HelperFeed { } foreach ($listDAO as $key => $dao) { + if (empty ($dao['url'])) { + continue; + } if (isset ($dao['id'])) { $key = $dao['id']; } From d9975d86a2d159c0f2442b9ee02f0523b8577af3 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sat, 14 Sep 2013 21:59:54 +0200 Subject: [PATCH 7/9] =?UTF-8?q?Issue=20#155=20:=20suppression=20fonctions?= =?UTF-8?q?=20comment=C3=A9es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/Entry.php | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/app/models/Entry.php b/app/models/Entry.php index 63e573722..d73ce7245 100755 --- a/app/models/Entry.php +++ b/app/models/Entry.php @@ -461,24 +461,6 @@ class EntryDAO extends Model_pdo { return $unreadRead['unread']; } - /*public function countNotReadByFeed ($id) { //Is this used? - $sql = 'SELECT COUNT(*) AS count FROM ' . $this->prefix . 'entry WHERE is_read = 0 AND id_feed = ?'; - $stm = $this->bd->prepare ($sql); - $stm->execute (array ($id)); - $res = $stm->fetchAll (PDO::FETCH_ASSOC); - - return $res[0]['count']; - }*/ - - /*public function countNotReadByCat ($id) { //Is this used? - $sql = 'SELECT COUNT(*) AS count FROM ' . $this->prefix . 'entry e INNER JOIN ' . $this->prefix . 'feed f ON e.id_feed = f.id WHERE is_read=0 AND category = ?'; - $stm = $this->bd->prepare ($sql); - $stm->execute (array ($id)); - $res = $stm->fetchAll (PDO::FETCH_ASSOC); - - return $res[0]['count']; - }*/ - public function countUnreadReadFavorites () { $sql = 'SELECT is_read, COUNT(*) AS count FROM ' . $this->prefix . 'entry WHERE is_favorite=1 GROUP BY is_read'; $stm = $this->bd->prepare ($sql); @@ -493,10 +475,7 @@ class EntryDAO extends Model_pdo { } return $readUnread; } - /*public function countNotReadFavorites () { //Is this used? //Deprecated: use countUnreadReadFavorites() instead - $unreadRead = $this->countUnreadReadFavorites (); //This makes better use of caching - return $unreadRead['unread']; - }*/ + public function countFavorites () { //Deprecated: use countUnreadReadFavorites() instead $unreadRead = $this->countUnreadReadFavorites (); //This makes better use of caching return $unreadRead['unread'] + $unreadRead['read']; From e19695e14bdf3ea1baf04141f346060751eb1789 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sat, 14 Sep 2013 22:10:35 +0200 Subject: [PATCH 8/9] Issue #155 : correction fonction _nbNotRead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intval renvoyant toujours un integer, le test is_int() passait toujours, c'est corrigé maintenant --- app/models/Feed.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/models/Feed.php b/app/models/Feed.php index f72008c05..061a6a33a 100644 --- a/app/models/Feed.php +++ b/app/models/Feed.php @@ -167,11 +167,12 @@ class Feed extends Model { } $this->keep_history = $value; } - public function _nbNotRead ($value) { //Alex - if (!is_int (intval ($value))) { + public function _nbNotRead ($value) { + if (!is_int ($value)) { $value = -1; } - $this->nbNotRead = $value; + + $this->nbNotRead = intval ($value); } public function load () { From 35dcb5e39aee8029fee541d5649456bc385cd649 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 15 Sep 2013 11:29:31 +0200 Subject: [PATCH 9/9] Issue #155 : correction bug prefix table SQL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le préfixe freshrss_ était rentré en dur dans une des requêtes --- app/models/Entry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/Entry.php b/app/models/Entry.php index d73ce7245..ed350cad8 100755 --- a/app/models/Entry.php +++ b/app/models/Entry.php @@ -399,7 +399,7 @@ class EntryDAO extends Model_pdo { $where .= ' AND is_read = 1'; } if (!empty($limitFromId)) { //TODO: Consider using LPAD(e.date, 11) //CONCAT is for cases when many entries have the same date - $where .= ' AND CONCAT(e.date, e.id) ' . ($order === 'low_to_high' ? '<=' : '>=') . ' (SELECT CONCAT(s.date, s.id) from freshrss_entry s WHERE s.id = "' . $limitFromId . '")'; + $where .= ' AND CONCAT(e.date, e.id) ' . ($order === 'low_to_high' ? '<=' : '>=') . ' (SELECT CONCAT(s.date, s.id) FROM ' . $this->prefix . 'entry s WHERE s.id = "' . $limitFromId . '")'; } if ($order == 'low_to_high') {