Browse Source

Simplify editionLatest Database method

Also adjust label querying to take hidden marks into account
rpm
J. King 3 years ago
parent
commit
f0bfe1fdff
  1. 76
      lib/Database.php
  2. 7
      tests/cases/Database/SeriesArticle.php
  3. 27
      tests/cases/Database/SeriesLabel.php

76
lib/Database.php

@ -79,7 +79,11 @@ class Database {
/** Returns the bare name of the calling context's calling method, when __FUNCTION__ is not appropriate */
protected function caller(): string {
return debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3)[2]['function'];
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 4);
if ($trace[2]['function'] === "articleQuery") {
return $trace[3]['function'];
}
return $trace[2]['function'];
}
/** Returns the current (actual) schema version of the database; compared against self::SCHEMA_VERSION to know when an upgrade is required */
@ -748,7 +752,7 @@ class Database {
i.url as favicon,
t.top as top_folder,
coalesce(s.title, f.title) as title,
coalesce((articles - hidden - marked + hidden_marked), articles) as unread
coalesce((articles - hidden - marked), articles) as unread
FROM arsse_subscriptions as s
left join topmost as t on t.f_id = s.folder
join arsse_feeds as f on f.id = s.feed
@ -757,9 +761,8 @@ class Database {
left join (
select
subscription,
sum(cast((\"read\" = 1 and hidden = 0) as integer)) as marked,
sum(cast((\"read\" = 0 and hidden = 1) as integer)) as hidden,
sum(cast((\"read\" = 1 and hidden = 1) as integer)) as hidden_marked
sum(hidden) as hidden,
sum(cast((\"read\" = 1 and hidden = 0) as integer)) as marked
from arsse_marks group by subscription
) as mark_stats on mark_stats.subscription = s.id"
);
@ -1206,12 +1209,11 @@ class Database {
* Each record includes the following keys:
*
* - "owner": The user for whom to apply the filters
* - "sub": The subscription ID which ties the user to the feed
* - "keep": The "keep" rule; any articles which fail to match this rule are hidden
* - "block": The block rule; any article which matches this rule are hidden
*/
public function feedRulesGet(int $feedID): Db\Result {
return $this->db->prepare("SELECT owner, id as sub, keep_rule as keep, block_rule as block from arsse_subscriptions where feed = ? and (coalesce(keep_rule, '') || coalesce(block_rule, '')) <> ''", "int")->run($feedID);
return $this->db->prepare("SELECT owner, keep_rule as keep, block_rule as block from arsse_subscriptions where feed = ? and (coalesce(keep_rule, '') || coalesce(block_rule, '')) <> ''", "int")->run($feedID);
}
/** Retrieves various identifiers for the latest $count articles in the given newsfeed. The identifiers are:
@ -1310,6 +1312,7 @@ class Database {
return [
'id' => "arsse_articles.id",
'edition' => "latest_editions.edition",
'latest_edition' => "max(latest_editions.edition)",
'url' => "arsse_articles.url",
'title' => "arsse_articles.title",
'author' => "arsse_articles.author",
@ -1385,6 +1388,7 @@ class Database {
}
$outColumns = implode(",", $outColumns);
}
assert(strlen($outColumns) > 0, new \Exception("No input columns matched whitelist"));
// define the basic query, to which we add lots of stuff where necessary
$q = new Query(
"SELECT
@ -1895,14 +1899,8 @@ class Database {
/** Returns the numeric identifier of the most recent edition of an article matching the given context */
public function editionLatest(string $user, Context $context = null): int {
$context = $context ?? new Context;
$q = new Query("SELECT max(arsse_editions.id) from arsse_editions left join arsse_articles on article = arsse_articles.id join arsse_subscriptions on arsse_articles.feed = arsse_subscriptions.feed and arsse_subscriptions.owner = ?", "str", $user);
if ($context->subscription()) {
// if a subscription is specified, make sure it exists
$this->subscriptionValidateId($user, $context->subscription);
// a simple WHERE clause is required here
$q->setWhere("arsse_subscriptions.id = ?", "int", $context->subscription);
}
return (int) $this->db->prepare($q->getQuery(), $q->getTypes())->run($q->getValues())->getValue();
$q = $this->articleQuery($user, $context, ["latest_edition"]);
return (int) $this->db->prepare((string) $q, $q->getTypes())->run($q->getValues())->getValue();
}
/** Returns a map between all the given edition identifiers and their associated article identifiers */
@ -1945,14 +1943,19 @@ class Database {
return $this->db->prepare(
"SELECT * FROM (
SELECT
id,name,coalesce(articles,0) as articles,coalesce(marked,0) as \"read\"
id,
name,
coalesce(articles - coalesce(hidden, 0), 0) as articles,
coalesce(marked, 0) as \"read\"
from arsse_labels
left join (
SELECT label, sum(assigned) as articles from arsse_label_members group by label
) as label_stats on label_stats.label = arsse_labels.id
left join (
SELECT
label, sum(\"read\") as marked
SELECT
label,
sum(hidden) as hidden,
sum(cast((\"read\" = 1 and hidden = 0) as integer)) as marked
from arsse_marks
join arsse_subscriptions on arsse_subscriptions.id = arsse_marks.subscription
join arsse_label_members on arsse_label_members.article = arsse_marks.article
@ -2007,14 +2010,19 @@ class Database {
$type = $byName ? "str" : "int";
$out = $this->db->prepare(
"SELECT
id,name,coalesce(articles,0) as articles,coalesce(marked,0) as \"read\"
id,
name,
coalesce(articles - coalesce(hidden, 0), 0) as articles,
coalesce(marked, 0) as \"read\"
FROM arsse_labels
left join (
SELECT label, sum(assigned) as articles from arsse_label_members group by label
) as label_stats on label_stats.label = arsse_labels.id
left join (
SELECT
label, sum(\"read\") as marked
SELECT
label,
sum(hidden) as hidden,
sum(cast((\"read\" = 1 and hidden = 0) as integer)) as marked
from arsse_marks
join arsse_subscriptions on arsse_subscriptions.id = arsse_marks.subscription
join arsse_label_members on arsse_label_members.article = arsse_marks.article
@ -2069,19 +2077,25 @@ class Database {
* @param boolean $byName Whether to interpret the $id parameter as the label's name (true) or identifier (false)
*/
public function labelArticlesGet(string $user, $id, bool $byName = false): array {
// just do a syntactic check on the label ID
$this->labelValidateId($user, $id, $byName, false);
$field = !$byName ? "id" : "name";
$type = !$byName ? "int" : "str";
$out = $this->db->prepare("SELECT article from arsse_label_members join arsse_labels on label = id where assigned = 1 and $field = ? and owner = ? order by article", $type, "str")->run($id, $user)->getAll();
$c = (new Context)->hidden(false);
if ($byName) {
$c->labelName($id);
} else {
$c->label($id);
}
try {
$q = $this->articleQuery($user, $c);
$out = $this->db->prepare((string) $q, $q->getTypes())->run($q->getValues())->getAll();
} catch (Db\ExceptionInput $e) {
if ($e->getCode() === 10235) {
throw new Db\ExceptionInput("subjectMissing", ["action" => __FUNCTION__, "field" => "label", 'id' => $id]);
}
throw $e;
}
if (!$out) {
// if no results were returned, do a full validation on the label ID
$this->labelValidateId($user, $id, $byName, true, true);
// if the validation passes, return the empty result
return $out;
} else {
// flatten the result to return just the article IDs in a simple array
return array_column($out, "article");
return array_column($out, "id");
}
}

7
tests/cases/Database/SeriesArticle.php

@ -251,7 +251,7 @@ trait SeriesArticle {
[12, 3,0,1,'2017-01-01 00:00:00','ack',0],
[12, 4,1,1,'2017-01-01 00:00:00','ach',0],
[1, 2,0,0,'2010-01-01 00:00:00','Some Note',0],
[3, 5,0,0,'2000-01-01 00:00:00','',1],
[3, 6,0,0,'2000-01-01 00:00:00','',1],
[6, 1,0,1,'2010-01-01 00:00:00','',1],
[6, 2,1,0,'2010-01-01 00:00:00','',1],
],
@ -447,8 +447,8 @@ trait SeriesArticle {
'Starred and Read in subscription' => [(new Context)->starred(true)->unread(false)->subscription(5), []],
'Annotated' => [(new Context)->annotated(true), [2]],
'Not annotated' => [(new Context)->annotated(false), [1,3,4,5,6,7,8,19,20]],
'Hidden' => [(new Context)->hidden(true), [5]],
'Not hidden' => [(new Context)->hidden(false), [1,2,3,4,6,7,8,19,20]],
'Hidden' => [(new Context)->hidden(true), [6]],
'Not hidden' => [(new Context)->hidden(false), [1,2,3,4,5,7,8,19,20]],
'Labelled' => [(new Context)->labelled(true), [1,5,8,19,20]],
'Not labelled' => [(new Context)->labelled(false), [2,3,4,6,7]],
'Not after edition 999' => [(new Context)->subscription(5)->latestEdition(999), [19]],
@ -985,6 +985,7 @@ trait SeriesArticle {
public function testFetchLatestEdition(): void {
$this->assertSame(1001, Arsse::$db->editionLatest($this->user));
$this->assertSame(4, Arsse::$db->editionLatest($this->user, (new Context)->subscription(12)));
$this->assertSame(5, Arsse::$db->editionLatest("john.doe@example.com", (new Context)->subscription(3)->hidden(false)));
}
public function testFetchLatestEditionOfMissingSubscription(): void {

27
tests/cases/Database/SeriesLabel.php

@ -194,20 +194,22 @@ trait SeriesLabel {
'read' => "bool",
'starred' => "bool",
'modified' => "datetime",
'hidden' => "bool",
],
'rows' => [
[1, 1,1,1,'2000-01-01 00:00:00'],
[5, 19,1,0,'2000-01-01 00:00:00'],
[5, 20,0,1,'2010-01-01 00:00:00'],
[7, 20,1,0,'2010-01-01 00:00:00'],
[8, 102,1,0,'2000-01-02 02:00:00'],
[9, 103,0,1,'2000-01-03 03:00:00'],
[9, 104,1,1,'2000-01-04 04:00:00'],
[10,105,0,0,'2000-01-05 05:00:00'],
[11, 19,0,0,'2017-01-01 00:00:00'],
[11, 20,1,0,'2017-01-01 00:00:00'],
[12, 3,0,1,'2017-01-01 00:00:00'],
[12, 4,1,1,'2017-01-01 00:00:00'],
[1, 1,1,1,'2000-01-01 00:00:00',0],
[5, 19,1,0,'2000-01-01 00:00:00',0],
[5, 20,0,1,'2010-01-01 00:00:00',0],
[7, 20,1,0,'2010-01-01 00:00:00',0],
[8, 102,1,0,'2000-01-02 02:00:00',0],
[9, 103,0,1,'2000-01-03 03:00:00',0],
[9, 104,1,1,'2000-01-04 04:00:00',0],
[10,105,0,0,'2000-01-05 05:00:00',0],
[11, 19,0,0,'2017-01-01 00:00:00',0],
[11, 20,1,0,'2017-01-01 00:00:00',0],
[12, 3,0,1,'2017-01-01 00:00:00',0],
[12, 4,1,1,'2017-01-01 00:00:00',0],
[4, 8,0,0,'2000-01-02 02:00:00',1]
],
],
'arsse_labels' => [
@ -237,6 +239,7 @@ trait SeriesLabel {
[2,20,5,1],
[1, 5,3,0],
[2, 5,3,1],
[2, 8,4,1],
],
],
];

Loading…
Cancel
Save