aboutsummaryrefslogtreecommitdiff
path: root/app/Models/EntryDAO.php
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2022-06-02 08:41:08 +0200
committerGravatar GitHub <noreply@github.com> 2022-06-02 08:41:08 +0200
commitf85c510ed49be031145f6b35e815ce890cd4f9aa (patch)
treec7ac947ba5ddacf85dc5d97330f38f7d91b0964c /app/Models/EntryDAO.php
parentf988b996ab69104bc45b222fa88d34b5c78f98b3 (diff)
New search engine (#4378)
* New possibility to invoke user queries from a search expression From the search field: `S:"My query"`. Can be combined with other filters such as `S:"My query" date:P3d` as long as the user queries do not contain `OR`. A use-case is to have an RSS filter with a stable address or an external API call with the ability to update the user query. * Draft of parenthesis logic * More draft * Working parenthesis (a OR b) (c OR d) * Working (A) OR (B) * Support nested parentheses + unit tests + documentation * search:MySearch and S:3
Diffstat (limited to 'app/Models/EntryDAO.php')
-rw-r--r--app/Models/EntryDAO.php510
1 files changed, 267 insertions, 243 deletions
diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php
index 8f248e20f..426c294c4 100644
--- a/app/Models/EntryDAO.php
+++ b/app/Models/EntryDAO.php
@@ -2,23 +2,27 @@
class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
- public function isCompressed(): bool {
+ public static function isCompressed(): bool {
return true;
}
- public function hasNativeHex(): bool {
+ public static function hasNativeHex(): bool {
return true;
}
- public function sqlHexDecode(string $x): string {
+ protected static function sqlConcat($s1, $s2) {
+ return 'CONCAT(' . $s1 . ',' . $s2 . ')'; //MySQL
+ }
+
+ public static function sqlHexDecode(string $x): string {
return 'unhex(' . $x . ')';
}
- public function sqlHexEncode(string $x): string {
+ public static function sqlHexEncode(string $x): string {
return 'hex(' . $x . ')';
}
- public function sqlIgnoreConflict(string $sql): string {
+ public static function sqlIgnoreConflict(string $sql): string {
return str_replace('INSERT INTO ', 'INSERT IGNORE INTO ', $sql);
}
@@ -90,14 +94,14 @@ SQL;
public function addEntry(array $valuesTmp, bool $useTmpTable = true) {
if ($this->addEntryPrepared == null) {
- $sql = $this->sqlIgnoreConflict(
+ $sql = static::sqlIgnoreConflict(
'INSERT INTO `_' . ($useTmpTable ? 'entrytmp' : 'entry') . '` (id, guid, title, author, '
- . ($this->isCompressed() ? 'content_bin' : 'content')
+ . (static::isCompressed() ? 'content_bin' : 'content')
. ', link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) '
. 'VALUES(:id, :guid, :title, :author, '
- . ($this->isCompressed() ? 'COMPRESS(:content)' : ':content')
+ . (static::isCompressed() ? 'COMPRESS(:content)' : ':content')
. ', :link, :date, :last_seen, '
- . $this->sqlHexDecode(':hash')
+ . static::sqlHexDecode(':hash')
. ', :is_read, :is_favorite, :id_feed, :tags)');
$this->addEntryPrepared = $this->pdo->prepare($sql);
}
@@ -132,7 +136,7 @@ SQL;
$valuesTmp['tags'] = safe_utf8($valuesTmp['tags']);
$this->addEntryPrepared->bindParam(':tags', $valuesTmp['tags']);
- if ($this->hasNativeHex()) {
+ if (static::hasNativeHex()) {
$this->addEntryPrepared->bindParam(':hash', $valuesTmp['hash']);
} else {
$valuesTmp['hashBin'] = hex2bin($valuesTmp['hash']);
@@ -189,9 +193,9 @@ SQL;
if ($this->updateEntryPrepared === null) {
$sql = 'UPDATE `_entry` '
. 'SET title=:title, author=:author, '
- . ($this->isCompressed() ? 'content_bin=COMPRESS(:content)' : 'content=:content')
+ . (static::isCompressed() ? 'content_bin=COMPRESS(:content)' : 'content=:content')
. ', link=:link, date=:date, `lastSeen`=:last_seen'
- . ', hash=' . $this->sqlHexDecode(':hash')
+ . ', hash=' . static::sqlHexDecode(':hash')
. ', is_read=COALESCE(:is_read, is_read)'
. ', tags=:tags '
. 'WHERE id_feed=:id_feed AND guid=:guid';
@@ -226,7 +230,7 @@ SQL;
$valuesTmp['tags'] = safe_utf8($valuesTmp['tags']);
$this->updateEntryPrepared->bindParam(':tags', $valuesTmp['tags']);
- if ($this->hasNativeHex()) {
+ if (static::hasNativeHex()) {
$this->updateEntryPrepared->bindParam(':hash', $valuesTmp['hash']);
} else {
$valuesTmp['hashBin'] = hex2bin($valuesTmp['hash']);
@@ -649,8 +653,8 @@ SQL;
public function selectAll() {
$sql = 'SELECT id, guid, title, author, '
- . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
- . ', link, date, `lastSeen`, ' . $this->sqlHexEncode('hash') . ' AS hash, is_read, is_favorite, id_feed, tags '
+ . (static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
+ . ', link, date, `lastSeen`, ' . static::sqlHexEncode('hash') . ' AS hash, is_read, is_favorite, id_feed, tags '
. 'FROM `_entry`';
$stm = $this->pdo->query($sql);
while ($row = $stm->fetch(PDO::FETCH_ASSOC)) {
@@ -662,7 +666,7 @@ SQL;
public function searchByGuid($id_feed, $guid) {
// un guid est unique pour un flux donné
$sql = 'SELECT id, guid, title, author, '
- . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
+ . (static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
. ', link, date, is_read, is_favorite, id_feed, tags '
. 'FROM `_entry` WHERE id_feed=:id_feed AND guid=:guid';
$stm = $this->pdo->prepare($sql);
@@ -676,7 +680,7 @@ SQL;
/** @return FreshRSS_Entry|null */
public function searchById($id) {
$sql = 'SELECT id, guid, title, author, '
- . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
+ . (static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
. ', link, date, is_read, is_favorite, id_feed, tags '
. 'FROM `_entry` WHERE id=:id';
$stm = $this->pdo->prepare($sql);
@@ -696,281 +700,301 @@ SQL;
return isset($res[0]) ? $res[0] : null;
}
- protected function sqlConcat($s1, $s2) {
- return 'CONCAT(' . $s1 . ',' . $s2 . ')'; //MySQL
- }
+ /** @param FreshRSS_BooleanSearch $filters */
+ public static function sqlBooleanSearch(string $alias, $filters, int $level = 0) {
+ $search = '';
+ $values = [];
- /**
- * @param FreshRSS_BooleanSearch|null $filters
- */
- protected function sqlListEntriesWhere(string $alias = '', $filters = null, int $state = FreshRSS_Entry::STATE_ALL,
- string $order = 'DESC', string $firstId = '', int $date_min = 0) {
- $search = ' ';
- $values = array();
- if ($state & FreshRSS_Entry::STATE_NOT_READ) {
- if (!($state & FreshRSS_Entry::STATE_READ)) {
- $search .= 'AND ' . $alias . 'is_read=0 ';
+ $isOpen = false;
+ foreach ($filters->searches() as $filter) {
+ if ($filter == null) {
+ continue;
}
- } elseif ($state & FreshRSS_Entry::STATE_READ) {
- $search .= 'AND ' . $alias . 'is_read=1 ';
- }
- if ($state & FreshRSS_Entry::STATE_FAVORITE) {
- if (!($state & FreshRSS_Entry::STATE_NOT_FAVORITE)) {
- $search .= 'AND ' . $alias . 'is_favorite=1 ';
- }
- } elseif ($state & FreshRSS_Entry::STATE_NOT_FAVORITE) {
- $search .= 'AND ' . $alias . 'is_favorite=0 ';
- }
-
- switch ($order) {
- case 'DESC':
- case 'ASC':
- break;
- default:
- throw new FreshRSS_EntriesGetter_Exception('Bad order in Entry->listByType: [' . $order . ']!');
- }
- if ($firstId !== '') {
- $search .= 'AND ' . $alias . 'id ' . ($order === 'DESC' ? '<=' : '>=') . ' ? ';
- $values[] = $firstId;
- }
- if ($date_min > 0) {
- $search .= 'AND ' . $alias . 'id >= ? ';
- $values[] = $date_min . '000000';
- }
- if ($filters && count($filters->searches()) > 0) {
- $isOpen = false;
- foreach ($filters->searches() as $filter) {
- if ($filter == null) {
- continue;
+ if ($filter instanceof FreshRSS_BooleanSearch) {
+ // BooleanSearches are combined by AND (default) or OR (special case) operator and are recursive
+ list($filterValues, $filterSearch) = self::sqlBooleanSearch($alias, $filter, $level + 1);
+ $filterSearch = trim($filterSearch);
+
+ if ($filterSearch !== '') {
+ if ($search !== '') {
+ $search .= $filter->operator();
+ }
+ $search .= ' (' . $filterSearch . ') ';
+ $values = array_merge($values, $filterValues);
}
- $sub_search = '';
-
- if ($filter->getEntryIds()) {
- foreach ($filter->getEntryIds() as $entry_ids) {
- $sub_search .= 'AND ' . $alias . 'id IN (';
- foreach ($entry_ids as $entry_id) {
- $sub_search .= '?,';
- $values[] = $entry_id;
- }
- $sub_search = rtrim($sub_search, ',');
- $sub_search .= ') ';
+ continue;
+ }
+ // Searches are combined by OR and are not recursive
+ $sub_search = '';
+ if ($filter->getEntryIds()) {
+ foreach ($filter->getEntryIds() as $entry_ids) {
+ $sub_search .= 'AND ' . $alias . 'id IN (';
+ foreach ($entry_ids as $entry_id) {
+ $sub_search .= '?,';
+ $values[] = $entry_id;
}
+ $sub_search = rtrim($sub_search, ',');
+ $sub_search .= ') ';
}
- if ($filter->getNotEntryIds()) {
- foreach ($filter->getNotEntryIds() as $entry_ids) {
- $sub_search .= 'AND ' . $alias . 'id NOT IN (';
- foreach ($entry_ids as $entry_id) {
- $sub_search .= '?,';
- $values[] = $entry_id;
- }
- $sub_search = rtrim($sub_search, ',');
- $sub_search .= ') ';
+ }
+ if ($filter->getNotEntryIds()) {
+ foreach ($filter->getNotEntryIds() as $entry_ids) {
+ $sub_search .= 'AND ' . $alias . 'id NOT IN (';
+ foreach ($entry_ids as $entry_id) {
+ $sub_search .= '?,';
+ $values[] = $entry_id;
}
+ $sub_search = rtrim($sub_search, ',');
+ $sub_search .= ') ';
}
+ }
- if ($filter->getMinDate()) {
- $sub_search .= 'AND ' . $alias . 'id >= ? ';
- $values[] = "{$filter->getMinDate()}000000";
- }
- if ($filter->getMaxDate()) {
- $sub_search .= 'AND ' . $alias . 'id <= ? ';
- $values[] = "{$filter->getMaxDate()}000000";
- }
- if ($filter->getMinPubdate()) {
- $sub_search .= 'AND ' . $alias . 'date >= ? ';
- $values[] = $filter->getMinPubdate();
- }
- if ($filter->getMaxPubdate()) {
- $sub_search .= 'AND ' . $alias . 'date <= ? ';
- $values[] = $filter->getMaxPubdate();
- }
+ if ($filter->getMinDate()) {
+ $sub_search .= 'AND ' . $alias . 'id >= ? ';
+ $values[] = "{$filter->getMinDate()}000000";
+ }
+ if ($filter->getMaxDate()) {
+ $sub_search .= 'AND ' . $alias . 'id <= ? ';
+ $values[] = "{$filter->getMaxDate()}000000";
+ }
+ if ($filter->getMinPubdate()) {
+ $sub_search .= 'AND ' . $alias . 'date >= ? ';
+ $values[] = $filter->getMinPubdate();
+ }
+ if ($filter->getMaxPubdate()) {
+ $sub_search .= 'AND ' . $alias . 'date <= ? ';
+ $values[] = $filter->getMaxPubdate();
+ }
- //Negation of date intervals must be combined by OR
- if ($filter->getNotMinDate() || $filter->getNotMaxDate()) {
- $sub_search .= 'AND (';
- if ($filter->getNotMinDate()) {
- $sub_search .= $alias . 'id < ?';
- $values[] = "{$filter->getNotMinDate()}000000";
- if ($filter->getNotMaxDate()) {
- $sub_search .= ' OR ';
- }
- }
+ //Negation of date intervals must be combined by OR
+ if ($filter->getNotMinDate() || $filter->getNotMaxDate()) {
+ $sub_search .= 'AND (';
+ if ($filter->getNotMinDate()) {
+ $sub_search .= $alias . 'id < ?';
+ $values[] = "{$filter->getNotMinDate()}000000";
if ($filter->getNotMaxDate()) {
- $sub_search .= $alias . 'id > ?';
- $values[] = "{$filter->getNotMaxDate()}000000";
+ $sub_search .= ' OR ';
}
- $sub_search .= ') ';
}
- if ($filter->getNotMinPubdate() || $filter->getNotMaxPubdate()) {
- $sub_search .= 'AND (';
- if ($filter->getNotMinPubdate()) {
- $sub_search .= $alias . 'date < ?';
- $values[] = $filter->getNotMinPubdate();
- if ($filter->getNotMaxPubdate()) {
- $sub_search .= ' OR ';
- }
- }
- if ($filter->getNotMaxPubdate()) {
- $sub_search .= $alias . 'date > ?';
- $values[] = $filter->getNotMaxPubdate();
- }
- $sub_search .= ') ';
+ if ($filter->getNotMaxDate()) {
+ $sub_search .= $alias . 'id > ?';
+ $values[] = "{$filter->getNotMaxDate()}000000";
}
-
- if ($filter->getFeedIds()) {
- foreach ($filter->getFeedIds() as $feed_ids) {
- $sub_search .= 'AND ' . $alias . 'id_feed IN (';
- foreach ($feed_ids as $feed_id) {
- $sub_search .= '?,';
- $values[] = $feed_id;
- }
- $sub_search = rtrim($sub_search, ',');
- $sub_search .= ') ';
+ $sub_search .= ') ';
+ }
+ if ($filter->getNotMinPubdate() || $filter->getNotMaxPubdate()) {
+ $sub_search .= 'AND (';
+ if ($filter->getNotMinPubdate()) {
+ $sub_search .= $alias . 'date < ?';
+ $values[] = $filter->getNotMinPubdate();
+ if ($filter->getNotMaxPubdate()) {
+ $sub_search .= ' OR ';
}
}
- if ($filter->getNotFeedIds()) {
- foreach ($filter->getNotFeedIds() as $feed_ids) {
- $sub_search .= 'AND ' . $alias . 'id_feed NOT IN (';
- foreach ($feed_ids as $feed_id) {
- $sub_search .= '?,';
- $values[] = $feed_id;
- }
- $sub_search = rtrim($sub_search, ',');
- $sub_search .= ') ';
- }
+ if ($filter->getNotMaxPubdate()) {
+ $sub_search .= $alias . 'date > ?';
+ $values[] = $filter->getNotMaxPubdate();
}
+ $sub_search .= ') ';
+ }
- if ($filter->getLabelIds()) {
- foreach ($filter->getLabelIds() as $label_ids) {
- if ($label_ids === '*') {
- $sub_search .= 'AND EXISTS (SELECT et.id_tag FROM `_entrytag` et WHERE et.id_entry = ' . $alias . 'id) ';
- } else {
- $sub_search .= 'AND ' . $alias . 'id IN (SELECT et.id_entry FROM `_entrytag` et WHERE et.id_tag IN (';
- foreach ($label_ids as $label_id) {
- $sub_search .= '?,';
- $values[] = $label_id;
- }
- $sub_search = rtrim($sub_search, ',');
- $sub_search .= ')) ';
- }
+ if ($filter->getFeedIds()) {
+ foreach ($filter->getFeedIds() as $feed_ids) {
+ $sub_search .= 'AND ' . $alias . 'id_feed IN (';
+ foreach ($feed_ids as $feed_id) {
+ $sub_search .= '?,';
+ $values[] = $feed_id;
}
+ $sub_search = rtrim($sub_search, ',');
+ $sub_search .= ') ';
}
- if ($filter->getNotLabelIds()) {
- foreach ($filter->getNotLabelIds() as $label_ids) {
- if ($label_ids === '*') {
- $sub_search .= 'AND NOT EXISTS (SELECT et.id_tag FROM `_entrytag` et WHERE et.id_entry = ' . $alias . 'id) ';
- } else {
- $sub_search .= 'AND ' . $alias . 'id NOT IN (SELECT et.id_entry FROM `_entrytag` et WHERE et.id_tag IN (';
- foreach ($label_ids as $label_id) {
- $sub_search .= '?,';
- $values[] = $label_id;
- }
- $sub_search = rtrim($sub_search, ',');
- $sub_search .= ')) ';
- }
+ }
+ if ($filter->getNotFeedIds()) {
+ foreach ($filter->getNotFeedIds() as $feed_ids) {
+ $sub_search .= 'AND ' . $alias . 'id_feed NOT IN (';
+ foreach ($feed_ids as $feed_id) {
+ $sub_search .= '?,';
+ $values[] = $feed_id;
}
+ $sub_search = rtrim($sub_search, ',');
+ $sub_search .= ') ';
}
+ }
- if ($filter->getLabelNames()) {
- foreach ($filter->getLabelNames() as $label_names) {
- $sub_search .= 'AND ' . $alias . 'id IN (SELECT et.id_entry FROM `_entrytag` et, `_tag` t WHERE et.id_tag = t.id AND t.name IN (';
- foreach ($label_names as $label_name) {
+ if ($filter->getLabelIds()) {
+ foreach ($filter->getLabelIds() as $label_ids) {
+ if ($label_ids === '*') {
+ $sub_search .= 'AND EXISTS (SELECT et.id_tag FROM `_entrytag` et WHERE et.id_entry = ' . $alias . 'id) ';
+ } else {
+ $sub_search .= 'AND ' . $alias . 'id IN (SELECT et.id_entry FROM `_entrytag` et WHERE et.id_tag IN (';
+ foreach ($label_ids as $label_id) {
$sub_search .= '?,';
- $values[] = $label_name;
+ $values[] = $label_id;
}
$sub_search = rtrim($sub_search, ',');
$sub_search .= ')) ';
}
}
- if ($filter->getNotLabelNames()) {
- foreach ($filter->getNotLabelNames() as $label_names) {
- $sub_search .= 'AND ' . $alias . 'id NOT IN (SELECT et.id_entry FROM `_entrytag` et, `_tag` t WHERE et.id_tag = t.id AND t.name IN (';
- foreach ($label_names as $label_name) {
+ }
+ if ($filter->getNotLabelIds()) {
+ foreach ($filter->getNotLabelIds() as $label_ids) {
+ if ($label_ids === '*') {
+ $sub_search .= 'AND NOT EXISTS (SELECT et.id_tag FROM `_entrytag` et WHERE et.id_entry = ' . $alias . 'id) ';
+ } else {
+ $sub_search .= 'AND ' . $alias . 'id NOT IN (SELECT et.id_entry FROM `_entrytag` et WHERE et.id_tag IN (';
+ foreach ($label_ids as $label_id) {
$sub_search .= '?,';
- $values[] = $label_name;
+ $values[] = $label_id;
}
$sub_search = rtrim($sub_search, ',');
$sub_search .= ')) ';
}
}
+ }
- if ($filter->getAuthor()) {
- foreach ($filter->getAuthor() as $author) {
- $sub_search .= 'AND ' . $alias . 'author LIKE ? ';
- $values[] = "%{$author}%";
+ if ($filter->getLabelNames()) {
+ foreach ($filter->getLabelNames() as $label_names) {
+ $sub_search .= 'AND ' . $alias . 'id IN (SELECT et.id_entry FROM `_entrytag` et, `_tag` t WHERE et.id_tag = t.id AND t.name IN (';
+ foreach ($label_names as $label_name) {
+ $sub_search .= '?,';
+ $values[] = $label_name;
}
+ $sub_search = rtrim($sub_search, ',');
+ $sub_search .= ')) ';
}
- if ($filter->getIntitle()) {
- foreach ($filter->getIntitle() as $title) {
- $sub_search .= 'AND ' . $alias . 'title LIKE ? ';
- $values[] = "%{$title}%";
+ }
+ if ($filter->getNotLabelNames()) {
+ foreach ($filter->getNotLabelNames() as $label_names) {
+ $sub_search .= 'AND ' . $alias . 'id NOT IN (SELECT et.id_entry FROM `_entrytag` et, `_tag` t WHERE et.id_tag = t.id AND t.name IN (';
+ foreach ($label_names as $label_name) {
+ $sub_search .= '?,';
+ $values[] = $label_name;
}
+ $sub_search = rtrim($sub_search, ',');
+ $sub_search .= ')) ';
}
- if ($filter->getTags()) {
- foreach ($filter->getTags() as $tag) {
- $sub_search .= 'AND ' . $alias . 'tags LIKE ? ';
- $values[] = "%{$tag}%";
- }
+ }
+
+ if ($filter->getAuthor()) {
+ foreach ($filter->getAuthor() as $author) {
+ $sub_search .= 'AND ' . $alias . 'author LIKE ? ';
+ $values[] = "%{$author}%";
}
- if ($filter->getInurl()) {
- foreach ($filter->getInurl() as $url) {
- $sub_search .= 'AND ' . $this->sqlConcat($alias . 'link', $alias . 'guid') . ' LIKE ? ';
- $values[] = "%{$url}%";
- }
+ }
+ if ($filter->getIntitle()) {
+ foreach ($filter->getIntitle() as $title) {
+ $sub_search .= 'AND ' . $alias . 'title LIKE ? ';
+ $values[] = "%{$title}%";
}
+ }
+ if ($filter->getTags()) {
+ foreach ($filter->getTags() as $tag) {
+ $sub_search .= 'AND ' . $alias . 'tags LIKE ? ';
+ $values[] = "%{$tag}%";
+ }
+ }
+ if ($filter->getInurl()) {
+ foreach ($filter->getInurl() as $url) {
+ $sub_search .= 'AND ' . static::sqlConcat($alias . 'link', $alias . 'guid') . ' LIKE ? ';
+ $values[] = "%{$url}%";
+ }
+ }
- if ($filter->getNotAuthor()) {
- foreach ($filter->getNotAuthor() as $author) {
- $sub_search .= 'AND (NOT ' . $alias . 'author LIKE ?) ';
- $values[] = "%{$author}%";
- }
+ if ($filter->getNotAuthor()) {
+ foreach ($filter->getNotAuthor() as $author) {
+ $sub_search .= 'AND (NOT ' . $alias . 'author LIKE ?) ';
+ $values[] = "%{$author}%";
}
- if ($filter->getNotIntitle()) {
- foreach ($filter->getNotIntitle() as $title) {
- $sub_search .= 'AND (NOT ' . $alias . 'title LIKE ?) ';
- $values[] = "%{$title}%";
- }
+ }
+ if ($filter->getNotIntitle()) {
+ foreach ($filter->getNotIntitle() as $title) {
+ $sub_search .= 'AND (NOT ' . $alias . 'title LIKE ?) ';
+ $values[] = "%{$title}%";
}
- if ($filter->getNotTags()) {
- foreach ($filter->getNotTags() as $tag) {
- $sub_search .= 'AND (NOT ' . $alias . 'tags LIKE ?) ';
- $values[] = "%{$tag}%";
- }
+ }
+ if ($filter->getNotTags()) {
+ foreach ($filter->getNotTags() as $tag) {
+ $sub_search .= 'AND (NOT ' . $alias . 'tags LIKE ?) ';
+ $values[] = "%{$tag}%";
}
- if ($filter->getNotInurl()) {
- foreach ($filter->getNotInurl() as $url) {
- $sub_search .= 'AND (NOT ' . $this->sqlConcat($alias . 'link', $alias . 'guid') . ' LIKE ?) ';
- $values[] = "%{$url}%";
- }
+ }
+ if ($filter->getNotInurl()) {
+ foreach ($filter->getNotInurl() as $url) {
+ $sub_search .= 'AND (NOT ' . static::sqlConcat($alias . 'link', $alias . 'guid') . ' LIKE ?) ';
+ $values[] = "%{$url}%";
}
+ }
- if ($filter->getSearch()) {
- foreach ($filter->getSearch() as $search_value) {
- $sub_search .= 'AND ' . $this->sqlConcat($alias . 'title',
- $this->isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ? ';
- $values[] = "%{$search_value}%";
- }
+ if ($filter->getSearch()) {
+ foreach ($filter->getSearch() as $search_value) {
+ $sub_search .= 'AND ' . static::sqlConcat($alias . 'title',
+ static::isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ? ';
+ $values[] = "%{$search_value}%";
}
- if ($filter->getNotSearch()) {
- foreach ($filter->getNotSearch() as $search_value) {
- $sub_search .= 'AND (NOT ' . $this->sqlConcat($alias . 'title',
- $this->isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ?) ';
- $values[] = "%{$search_value}%";
- }
+ }
+ if ($filter->getNotSearch()) {
+ foreach ($filter->getNotSearch() as $search_value) {
+ $sub_search .= 'AND (NOT ' . static::sqlConcat($alias . 'title',
+ static::isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ?) ';
+ $values[] = "%{$search_value}%";
}
+ }
- if ($sub_search != '') {
- if ($isOpen) {
- $search .= 'OR ';
- } else {
- $search .= 'AND (';
- $isOpen = true;
- }
- $search .= '(' . substr($sub_search, 4) . ') ';
+ if ($sub_search != '') {
+ if ($isOpen) {
+ $search .= ' OR ';
+ } else {
+ $isOpen = true;
}
+ // Remove superfluous leading 'AND '
+ $search .= '(' . substr($sub_search, 4) . ')';
}
- if ($isOpen) {
- $search .= ') ';
+ }
+
+ return [ $values, $search ];
+ }
+
+ /** @param FreshRSS_BooleanSearch|null $filters */
+ protected function sqlListEntriesWhere(string $alias = '', $filters = null, int $state = FreshRSS_Entry::STATE_ALL,
+ string $order = 'DESC', string $firstId = '', int $date_min = 0) {
+ $search = ' ';
+ $values = array();
+ if ($state & FreshRSS_Entry::STATE_NOT_READ) {
+ if (!($state & FreshRSS_Entry::STATE_READ)) {
+ $search .= 'AND ' . $alias . 'is_read=0 ';
+ }
+ } elseif ($state & FreshRSS_Entry::STATE_READ) {
+ $search .= 'AND ' . $alias . 'is_read=1 ';
+ }
+ if ($state & FreshRSS_Entry::STATE_FAVORITE) {
+ if (!($state & FreshRSS_Entry::STATE_NOT_FAVORITE)) {
+ $search .= 'AND ' . $alias . 'is_favorite=1 ';
+ }
+ } elseif ($state & FreshRSS_Entry::STATE_NOT_FAVORITE) {
+ $search .= 'AND ' . $alias . 'is_favorite=0 ';
+ }
+
+ switch ($order) {
+ case 'DESC':
+ case 'ASC':
+ break;
+ default:
+ throw new FreshRSS_EntriesGetter_Exception('Bad order in Entry->listByType: [' . $order . ']!');
+ }
+ if ($firstId !== '') {
+ $search .= 'AND ' . $alias . 'id ' . ($order === 'DESC' ? '<=' : '>=') . ' ? ';
+ $values[] = $firstId;
+ }
+ if ($date_min > 0) {
+ $search .= 'AND ' . $alias . 'id >= ? ';
+ $values[] = $date_min . '000000';
+ }
+ if ($filters && count($filters->searches()) > 0) {
+ list($filterValues, $filterSearch) = self::sqlBooleanSearch($alias, $filters);
+ $filterSearch = trim($filterSearch);
+ if ($filterSearch !== '') {
+ $search .= 'AND (' . $filterSearch . ') ';
+ $values = array_merge($values, $filterValues);
}
}
return array($values, $search);
@@ -1040,7 +1064,7 @@ SQL;
list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filters, $date_min);
$sql = 'SELECT e0.id, e0.guid, e0.title, e0.author, '
- . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
+ . (static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
. ', e0.link, e0.date, e0.is_read, e0.is_favorite, e0.id_feed, e0.tags '
. 'FROM `_entry` e0 '
. 'INNER JOIN ('
@@ -1085,7 +1109,7 @@ SQL;
}
$sql = 'SELECT id, guid, title, author, '
- . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
+ . (static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
. ', link, date, is_read, is_favorite, id_feed, tags '
. 'FROM `_entry` '
. 'WHERE id IN (' . str_repeat('?,', count($ids) - 1). '?) '
@@ -1124,7 +1148,7 @@ SQL;
return $result;
}
$guids = array_unique($guids);
- $sql = 'SELECT guid, ' . $this->sqlHexEncode('hash') .
+ $sql = 'SELECT guid, ' . static::sqlHexEncode('hash') .
' AS hex_hash FROM `_entry` WHERE id_feed=? AND guid IN (' . str_repeat('?,', count($guids) - 1). '?)';
$stm = $this->pdo->prepare($sql);
$values = array($id_feed);