aboutsummaryrefslogtreecommitdiff
path: root/app/Models
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2025-10-16 21:15:44 +0200
committerGravatar GitHub <noreply@github.com> 2025-10-16 21:15:44 +0200
commitf08f7dcff988de90e899ce310246f0b552a4fe3d (patch)
tree8a96a66aa59e3b1bf675757c5fc555e9187b8625 /app/Models
parentca091fbef4823159060275a293eaa4e65ecaf1c0 (diff)
Sort by article length (#8119)
* Sort by article length fix https://github.com/FreshRSS/Extensions/issues/378 Very basic using simply SQL `LENGTH()` function. <img width="492" height="217" alt="image" src="https://github.com/user-attachments/assets/7cf37303-76c8-4411-b8b1-075e81535b60" /> * Improve content length retrieval
Diffstat (limited to 'app/Models')
-rw-r--r--app/Models/Context.php4
-rw-r--r--app/Models/Entry.php15
-rw-r--r--app/Models/EntryDAO.php31
-rw-r--r--app/Models/UserConfiguration.php2
4 files changed, 36 insertions, 16 deletions
diff --git a/app/Models/Context.php b/app/Models/Context.php
index c0e25dffd..8cd78c779 100644
--- a/app/Models/Context.php
+++ b/app/Models/Context.php
@@ -42,7 +42,7 @@ final class FreshRSS_Context {
public static int $state = 0;
/** @var 'ASC'|'DESC' */
public static string $order = 'DESC';
- /** @var 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand'|'lastUserModified' */
+ /** @var 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand'|'lastUserModified'|'length' */
public static string $sort = 'id';
public static int $number = 0;
public static int $offset = 0;
@@ -252,7 +252,7 @@ final class FreshRSS_Context {
$order = Minz_Request::paramString('order', true) ?: FreshRSS_Context::userConf()->sort_order;
self::$order = in_array($order, ['ASC', 'DESC'], true) ? $order : 'DESC';
$sort = Minz_Request::paramString('sort', true) ?: FreshRSS_Context::userConf()->sort;
- self::$sort = in_array($sort, ['id', 'c.name', 'date', 'f.name', 'link', 'title', 'rand', 'lastUserModified'], true) ? $sort : 'id';
+ self::$sort = in_array($sort, ['id', 'c.name', 'date', 'f.name', 'link', 'title', 'rand', 'lastUserModified', 'length'], true) ? $sort : 'id';
self::$number = Minz_Request::paramInt('nb') ?: FreshRSS_Context::userConf()->posts_per_page;
if (self::$number > FreshRSS_Context::userConf()->max_posts_per_rss) {
self::$number = max(
diff --git a/app/Models/Entry.php b/app/Models/Entry.php
index 296c3860f..ea124794a 100644
--- a/app/Models/Entry.php
+++ b/app/Models/Entry.php
@@ -28,6 +28,7 @@ class FreshRSS_Entry extends Minz_Model {
/** In microseconds */
private string $date_added = '0';
private string $hash = '';
+ private ?int $sqlContentLength = null;
private ?bool $is_read;
private ?bool $is_favorite;
private bool $is_updated = false;
@@ -56,7 +57,8 @@ class FreshRSS_Entry extends Minz_Model {
/** @param array{id?:string,id_feed?:int,guid?:string,title?:string,author?:string,content?:string,link?:string,
* date?:int|string,lastSeen?:int,lastUserModified?:int,
- * hash?:string,is_read?:bool|int,is_favorite?:bool|int,tags?:string|array<string>,attributes?:?string,thumbnail?:string,timestamp?:string} $dao */
+ * hash?:string,is_read?:bool|int,is_favorite?:bool|int,tags?:string|array<string>,attributes?:?string,thumbnail?:string,timestamp?:string,
+ * content_length?:int} $dao */
public static function fromArray(array $dao): FreshRSS_Entry {
if (empty($dao['content']) || !is_string($dao['content'])) {
$dao['content'] = '';
@@ -108,6 +110,9 @@ class FreshRSS_Entry extends Minz_Model {
if (!empty($dao['hash'])) {
$entry->_hash($dao['hash']);
}
+ if (isset($dao['content_length']) && is_numeric($dao['content_length'])) {
+ $entry->_sqlContentLength((int)$dao['content_length']);
+ }
return $entry;
}
@@ -500,6 +505,10 @@ HTML;
return $this->hash;
}
+ public function sqlContentLength(): ?int {
+ return $this->sqlContentLength;
+ }
+
public function _hash(string $value): string {
$value = trim($value);
if (ctype_xdigit($value)) {
@@ -508,6 +517,10 @@ HTML;
return $this->hash;
}
+ public function _sqlContentLength(int $value): void {
+ $this->sqlContentLength = $value > 0 ? $value : null;
+ }
+
/** @param int|numeric-string $value String is for compatibility with 32-bit platforms */
public function _id(int|string $value): void {
if (is_int($value)) {
diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php
index c9fae7923..0d34df3ac 100644
--- a/app/Models/EntryDAO.php
+++ b/app/Models/EntryDAO.php
@@ -812,27 +812,31 @@ SQL;
public function searchByGuid(int $id_feed, string $guid): ?FreshRSS_Entry {
$content = static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content';
+ $contentLength = 'LENGTH(' . (static::isCompressed() ? 'content_bin' : 'content') . ') AS content_length';
$hash = static::sqlHexEncode('hash');
$sql = <<<SQL
-SELECT id, guid, title, author, {$content}, link, date, `lastSeen`, `lastUserModified`, {$hash} AS hash, is_read, is_favorite, id_feed, tags, attributes
+SELECT id, guid, title, author, {$content}, link, date, `lastSeen`, `lastUserModified`, {$hash} AS hash, is_read, is_favorite, id_feed, tags, attributes,
+ {$contentLength}
FROM `_entry` WHERE id_feed=:id_feed AND guid=:guid
SQL;
$res = $this->fetchAssoc($sql, [':id_feed' => $id_feed, ':guid' => $guid]);
/** @var list<array{id:string,id_feed:int,guid:string,title:string,author:string,content:string,link:string,date:int,
- * is_read:int,is_favorite:int,tags:string,attributes:?string}> $res */
+ * is_read:int,is_favorite:int,tags:string,attributes:?string,content_length:int}> $res */
return isset($res[0]) ? FreshRSS_Entry::fromArray($res[0]) : null;
}
public function searchById(string $id): ?FreshRSS_Entry {
$content = static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content';
+ $contentLength = 'LENGTH(' . (static::isCompressed() ? 'content_bin' : 'content') . ') AS content_length';
$hash = static::sqlHexEncode('hash');
$sql = <<<SQL
-SELECT id, guid, title, author, {$content}, link, date, `lastSeen`, `lastUserModified`, {$hash} AS hash, is_read, is_favorite, id_feed, tags, attributes
+SELECT id, guid, title, author, {$content}, link, date, `lastSeen`, `lastUserModified`, {$hash} AS hash, is_read, is_favorite, id_feed, tags, attributes,
+ {$contentLength}
FROM `_entry` WHERE id=:id
SQL;
$res = $this->fetchAssoc($sql, [':id' => $id]);
- /** @var list<array{'id':string,'id_feed':int,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int,
- * 'is_read':int,'is_favorite':int,'tags':string,'attributes':?string}> $res */
+ /** @var list<array{id:string,id_feed:int,guid:string,title:string,author:string,content:string,link:string,date:int,
+ * is_read:int,is_favorite:int,tags:string,attributes:?string,content_length:int}> $res */
return isset($res[0]) ? FreshRSS_Entry::fromArray($res[0]) : null;
}
@@ -1256,7 +1260,7 @@ SQL;
/**
* @param numeric-string $id_min
* @param numeric-string $id_max
- * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand'|'lastUserModified' $sort
+ * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand'|'lastUserModified'|'length' $sort
* @param 'ASC'|'DESC' $order
* @param numeric-string $continuation_id
* @param list<string|int> $continuation_values
@@ -1324,12 +1328,13 @@ SQL;
$values[] = $id_min;
}
- if ($continuation_id !== '0' && in_array($sort, ['c.name', 'date', 'f.name', 'link', 'title', 'lastUserModified'], true)) {
+ if ($continuation_id !== '0' && in_array($sort, ['c.name', 'date', 'f.name', 'link', 'title', 'lastUserModified', 'length'], true)) {
$sign = $order === 'ASC' ? '>' : '<';
$orderBy = match ($sort) {
'c.name' => 'c.name',
'f.name' => 'f.name',
'lastUserModified' => $alias . '`lastUserModified`',
+ 'length' => 'LENGTH(' . $alias . (static::isCompressed() ? 'content_bin' : 'content') . ')',
default => $alias . $sort,
};
// Keyset pagination (Compatibility syntax due to poor performance of tuple syntax in MySQL https://bugs.mysql.com/bug.php?id=104128)
@@ -1367,7 +1372,7 @@ SQL;
* @param int $id category/feed/tag ID
* @param numeric-string $id_min
* @param numeric-string $id_max
- * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand'|'lastUserModified' $sort
+ * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand'|'lastUserModified'|'length' $sort
* @param 'ASC'|'DESC' $order
* @param numeric-string $continuation_id
* @param list<string|int> $continuation_values
@@ -1426,11 +1431,12 @@ SQL;
}
$order = in_array($order, ['ASC', 'DESC'], true) ? $order : 'DESC';
- $sort = in_array($sort, ['id', 'c.name', 'date', 'f.name', 'link', 'title', 'rand', 'lastUserModified'], true) ? $sort : 'id';
+ $sort = in_array($sort, ['id', 'c.name', 'date', 'f.name', 'link', 'title', 'rand', 'lastUserModified', 'length'], true) ? $sort : 'id';
$orderBy = match ($sort) {
'c.name' => 'c.name',
'f.name' => 'f.name',
'lastUserModified' => 'e.`lastUserModified`',
+ 'length' => 'LENGTH(e.' . (static::isCompressed() ? 'content_bin' : 'content') . ')',
'rand' => static::sqlRandom(),
default => 'e.' . $sort,
};
@@ -1460,7 +1466,7 @@ SQL;
* @param int $id category/feed/tag ID
* @param numeric-string $id_min
* @param numeric-string $id_max
- * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand'|'lastUserModified' $sort
+ * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand'|'lastUserModified'|'length' $sort
* @param 'ASC'|'DESC' $order
* @param numeric-string $continuation_id
* @param list<string|int> $continuation_values
@@ -1470,7 +1476,7 @@ SQL;
string $id_min = '0', string $id_max = '0', string $sort = 'id', string $order = 'DESC',
string $continuation_id = '0', array $continuation_values = [], int $limit = 1, int $offset = 0): PDOStatement|false {
$order = in_array($order, ['ASC', 'DESC'], true) ? $order : 'DESC';
- $sort = in_array($sort, ['id', 'c.name', 'date', 'f.name', 'link', 'title', 'rand', 'lastUserModified'], true) ? $sort : 'id';
+ $sort = in_array($sort, ['id', 'c.name', 'date', 'f.name', 'link', 'title', 'rand', 'lastUserModified', 'length'], true) ? $sort : 'id';
[$values, $sql] = $this->sqlListWhere($type, $id, $state, $filters, id_min: $id_min, id_max: $id_max, sort: $sort, order: $order,
continuation_id: $continuation_id, continuation_values: $continuation_values, limit: $limit, offset: $offset);
@@ -1479,6 +1485,7 @@ SQL;
'c.name' => 'c0.name',
'f.name' => 'f0.name',
'lastUserModified' => 'e0.`lastUserModified`',
+ 'length' => 'LENGTH(e0.' . (static::isCompressed() ? 'content_bin' : 'content') . ')',
'rand' => static::sqlRandom(),
default => 'e0.' . $sort,
};
@@ -1523,7 +1530,7 @@ SQL;
* @param int $id category/feed/tag ID
* @param numeric-string $id_min
* @param numeric-string $id_max
- * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand'|'lastUserModified' $sort
+ * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand'|'lastUserModified'|'length' $sort
* @param 'ASC'|'DESC' $order
* @param numeric-string $continuation_id
* @param list<string|int> $continuation_values
diff --git a/app/Models/UserConfiguration.php b/app/Models/UserConfiguration.php
index 88263e1ad..eaa08d92d 100644
--- a/app/Models/UserConfiguration.php
+++ b/app/Models/UserConfiguration.php
@@ -55,7 +55,7 @@ declare(strict_types=1);
* @property bool $show_nav_buttons
* @property 'big'|'small'|'none' $mark_read_button
* @property 'ASC'|'DESC' $sort_order
- * @property 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand' $sort
+ * @property 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand'|'length' $sort
* @property array<int,array<string,string>> $sharing
* @property array<string,string> $shortcuts
* @property bool $sides_close_article