Shared queries: combine user search structurally (#9544)

Follow-up to #9541.

In `p/api/query.php`, the optional `search` parameter of a shared user query was appended as a string to the user query's own search, and the result was parsed again.

This keeps both searches as parsed objects and combines them with a structural AND via `FreshRSS_BooleanSearch::add()`, avoiding the serialise/re-parse round trip. Adds a test for combining Boolean searches with `add()`.
This commit is contained in:
Mehmet authored and GitHub committed 2026-11-08 22:47:48 +01:00
1 parent 669997ecbe
commit 931a6b3bbd
2 files changed
+22 -9

No files matched your search

+13 -9
View File
@@ -101,7 +101,6 @@ foreach (FreshRSS_Context::userConf()->queries as $raw_query) {
}
Minz_Request::_param('state', (string)$query->getState());
$search = $query->getSearch()->toString();
// Note: we disallow references to user queries in public user search to avoid sniffing internal user queries
try {
$userSearch = new FreshRSS_BooleanSearch(Minz_Request::paramString('search', plaintext: true), 0, 'AND', allowUserQueries: false);
@@ -110,14 +109,8 @@ foreach (FreshRSS_Context::userConf()->queries as $raw_query) {
header('Content-Type: text/plain; charset=UTF-8');
die($e->getMessage());
}
if ($userSearch->toString() !== '') {
if ($search === '') {
$search = $userSearch->toString();
} else {
$search .= ' (' . $userSearch->toString() . ')';
}
}
Minz_Request::_param('search', $search);
// Only the search of the user query is passed as a string; the user search is combined structurally below
Minz_Request::_param('search', $query->getSearch()->toString());
break;
}
}
@@ -132,6 +125,17 @@ $view = new FreshRSS_View();
try {
FreshRSS_Context::updateUsingRequest(false);
if ($userSearch->toString() !== '') {
if (FreshRSS_Context::$search->toString() === '') {
FreshRSS_Context::$search = $userSearch;
} else {
// Combine with a structural AND, so that the user search cannot alter the search of the user query
$combinedSearch = new FreshRSS_BooleanSearch('');
$combinedSearch->add(FreshRSS_Context::$search);
$combinedSearch->add($userSearch);
FreshRSS_Context::$search = $combinedSearch;
}
}
$view->entries = FreshRSS_index_Controller::listEntriesByContext();
if (!$view->entries->valid()) { // Init the generator to consume the aggregated search and catch potential exceptions
$view->entries = new EmptyIterator();
+9
View File
@@ -757,6 +757,15 @@ final class SearchTest extends \PHPUnit\Framework\TestCase {
self::assertSame('(e.id >= ?) OR (e.`lastModified` >= ?)', $filterSearch);
self::assertSame([$startTime . '000000', $startTime], $filterValues);
}
public function test__add_boolean_searches_combines_with_and(): void {
$combined = new FreshRSS_BooleanSearch('');
$combined->add(new FreshRSS_BooleanSearch('ab'));
$combined->add(new FreshRSS_BooleanSearch('cd OR ef', 0, 'AND', allowUserQueries: false));
self::assertSame('ab (cd OR ef)', $combined->toString());
[$filterValues] = FreshRSS_EntryDAOPGSQL::sqlBooleanSearch('e.', $combined);
self::assertSame(['%ab%', '%ab%', '%cd%', '%cd%', '%ef%', '%ef%'], $filterValues);
}
/**
* @param array<string> $values