[CONTROLLER][Feeds] Implement query for home feed: note-from:subscribed

This commit is contained in:
Hugo Sales 2021-12-24 09:27:24 +00:00
parent e3efd25b43
commit dabf5576d3
Signed by: someonewithpc
GPG Key ID: 7D0C7EAFC9D835A0
6 changed files with 48 additions and 29 deletions

View File

@ -25,6 +25,7 @@ use App\Core\Cache;
use App\Core\Event;
use App\Core\Modules\Component;
use App\Core\Router\RouteLoader;
use App\Entity\Actor;
use App\Entity\Note;
use App\Util\Formatting;
use Component\Attachment\Controller as C;
@ -74,7 +75,7 @@ class Attachment extends Component
/**
* Populate $note_expr with the criteria for looking for notes with attachments
*/
public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, &$note_expr, &$actor_expr): bool
public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, ?Actor $actor, &$note_expr, &$actor_expr)
{
$include_term = str_contains($term, ':') ? explode(':', $term)[1] : $term;
if (Formatting::startsWith($term, ['note-types:', 'notes-incude:', 'note-filter:'])) {

View File

@ -27,18 +27,21 @@ use App\Core\DB\DB;
use App\Core\Event;
use App\Core\Modules\Component;
use App\Entity\Actor;
use App\Entity\Subscription;
use App\Util\Formatting;
use Component\Search\Util\Parser;
use Doctrine\Common\Collections\ExpressionBuilder;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
class Feed extends Component
{
public static function query(string $query, int $page, ?string $language = null): array
public static function query(string $query, int $page, ?string $language = null, ?Actor $actor = null): array
{
$note_criteria = null;
$actor_criteria = null;
if (!empty($query = trim($query))) {
[$note_criteria, $actor_criteria] = Parser::parse($query, $language);
[$note_criteria, $actor_criteria] = Parser::parse($query, $language, $actor);
}
$note_qb = DB::createQueryBuilder();
$actor_qb = DB::createQueryBuilder();
@ -62,34 +65,45 @@ class Feed extends Component
return ['notes' => $notes ?? null, 'actors' => $actors ?? null];
}
public function onSearchQueryAddJoins(QueryBuilder &$note_qb, QueryBuilder &$actor_qb)
{
$note_qb->leftJoin(Subscription::class, 'subscription', Expr\Join::WITH, 'note.actor_id = subscription.subscribed');
return Event::next;
}
/**
* Convert $term to $note_expr and $actor_expr, search criteria. Handles searching for text
* notes, for different types of actors and for the content of text notes
*/
public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, &$note_expr, &$actor_expr): bool
public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, ?Actor $actor, &$note_expr, &$actor_expr)
{
if (str_contains($term, ':')) {
$term = explode(':', $term);
if (Formatting::startsWith($term[0], 'note-')) {
switch ($term[0]) {
case 'note-local':
$note_expr = $eb->eq('note.is_local', filter_var($term[1], \FILTER_VALIDATE_BOOLEAN));
break;
case 'note-types':
case 'notes-include':
case 'note-filter':
if (\is_null($note_expr)) {
$note_expr = [];
}
if (array_intersect(explode(',', $term[1]), ['text', 'words']) !== []) {
$note_expr[] = $eb->neq('note.content', null);
} else {
$note_expr[] = $eb->eq('note.content', null);
}
break;
case 'note-conversation':
$note_expr = $eb->eq('note.conversation_id', (int)trim($term[1]));
break;
case 'note-local':
$note_expr = $eb->eq('note.is_local', filter_var($term[1], \FILTER_VALIDATE_BOOLEAN));
break;
case 'note-types':
case 'notes-include':
case 'note-filter':
if (\is_null($note_expr)) {
$note_expr = [];
}
if (array_intersect(explode(',', $term[1]), ['text', 'words']) !== []) {
$note_expr[] = $eb->neq('note.content', null);
} else {
$note_expr[] = $eb->eq('note.content', null);
}
break;
case 'note-conversation':
$note_expr = $eb->eq('note.conversation_id', (int) trim($term[1]));
break;
case 'note-from':
if ($term[1] === 'subscribed') {
$note_expr = $eb->eq('subscription.subscriber', $actor->getId());
}
break;
}
} elseif (Formatting::startsWith($term, 'actor-')) {
switch ($term[0]) {

View File

@ -31,10 +31,10 @@ use App\Util\Formatting;
use App\Util\Functional as GSF;
use Component\Language\Controller as C;
use Doctrine\Common\Collections\ExpressionBuilder;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Functional as F;
use Symfony\Component\HttpFoundation\Request;
class Language extends Component
{
@ -46,7 +46,9 @@ class Language extends Component
public function onFilterNoteList(?Actor $actor, array &$notes, Request $request)
{
if (\is_null($actor)) return Event::next;
if (\is_null($actor)) {
return Event::next;
}
$notes = F\select(
$notes,
fn (Note $n) => \in_array($n->getLanguageId(), ActorLanguage::getActorRelatedLanguagesIds($actor)),
@ -58,7 +60,7 @@ class Language extends Component
/**
* Populate $note_expr or $actor_expr with an expression to match a language
*/
public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, &$note_expr, &$actor_expr): bool
public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, ?Actor $actor, &$note_expr, &$actor_expr)
{
$search_term = str_contains($term, ':') ? explode(':', $term)[1] : $term;

View File

@ -24,6 +24,7 @@ declare(strict_types = 1);
namespace Component\Search\Util;
use App\Core\Event;
use App\Entity\Actor;
use App\Util\Exception\ServerException;
use Doctrine\Common\Collections\Criteria;
@ -52,7 +53,7 @@ abstract class Parser
*
* @return Criteria[]
*/
public static function parse(string $input, ?string $language = null, int $level = 0): array
public static function parse(string $input, ?string $language = null, ?Actor $actor = null, int $level = 0): array
{
if ($level === 0) {
$input = trim(preg_replace(['/\s+/', '/\s+AND\s+/', '/\s+OR\s+/'], [' ', '&', '|'], $input), ' |&');
@ -77,7 +78,7 @@ abstract class Parser
$term = mb_substr($input, $left, $end ? null : $right - $left);
$note_res = null;
$actor_res = null;
Event::handle('SearchCreateExpression', [$eb, $term, $language, &$note_res, &$actor_res]);
Event::handle('SearchCreateExpression', [$eb, $term, $language, $actor, &$note_res, &$actor_res]);
if (\is_null($note_res) && \is_null($actor_res)) {
throw new ServerException("No one claimed responsibility for a match term: {$term}");
}

View File

@ -155,7 +155,7 @@ class Tag extends Component
*
* $term /^(note|tag|people|actor)/ means we want to match only either a note or an actor
*/
public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, &$note_expr, &$actor_expr): bool
public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, ?Actor $actor, &$note_expr, &$actor_expr)
{
$search_term = str_contains($term, ':#') ? explode(':', $term)[1] : $term;
$canon_search_term = self::canonicalTag($search_term, $language);

View File

@ -74,9 +74,10 @@ class Feeds extends FeedController
public function home(Request $request): array
{
$data = Feed::query(
query: 'from:subscribed-actors OR from:subscribed-groups',
query: 'note-from:subscribed',
page: $this->int('p'),
language: Common::actor()?->getTopLanguage()?->getLocale(),
actor: Common::actor(),
);
return [
'_template' => 'feed/feed.html.twig',