Skip to content

Commit

Permalink
feat: indexer API & tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
SychO9 committed Oct 4, 2023
1 parent c2b73ea commit 343f1b2
Show file tree
Hide file tree
Showing 41 changed files with 325 additions and 62 deletions.
2 changes: 1 addition & 1 deletion extensions/likes/extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
->listen(PostWasUnliked::class, Listener\SendNotificationWhenPostIsUnliked::class)
->subscribe(Listener\SaveLikesToDatabase::class),

(new Extend\Search(DatabaseSearchDriver::class))
(new Extend\SearchDriver(DatabaseSearchDriver::class))
->addFilter(PostSearcher::class, LikedByFilter::class)
->addFilter(UserSearcher::class, LikedFilter::class),

Expand Down
2 changes: 1 addition & 1 deletion extensions/lock/extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@
(new Extend\Policy())
->modelPolicy(Discussion::class, Access\DiscussionPolicy::class),

(new Extend\Search(DatabaseSearchDriver::class))
(new Extend\SearchDriver(DatabaseSearchDriver::class))
->addFilter(DiscussionSearcher::class, LockedFilter::class),
];
2 changes: 1 addition & 1 deletion extensions/mentions/extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
->listen(Hidden::class, Listener\UpdateMentionsMetadataWhenInvisible::class)
->listen(Deleted::class, Listener\UpdateMentionsMetadataWhenInvisible::class),

(new Extend\Search(DatabaseSearchDriver::class))
(new Extend\SearchDriver(DatabaseSearchDriver::class))
->addFilter(PostSearcher::class, Filter\MentionedFilter::class)
->addFilter(PostSearcher::class, Filter\MentionedPostFilter::class),

Expand Down
2 changes: 1 addition & 1 deletion extensions/nicknames/extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
(new Extend\Validator(UserValidator::class))
->configure(AddNicknameValidation::class),

(new Extend\Search(DatabaseSearchDriver::class))
(new Extend\SearchDriver(DatabaseSearchDriver::class))
->setFulltext(UserSearcher::class, NicknameFullTextFilter::class),

(new Extend\Policy())
Expand Down
4 changes: 2 additions & 2 deletions extensions/nicknames/src/NicknameFullTextFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ private function getUserSearchSubQuery(string $searchValue): Builder
->orWhere('nickname', 'like', "{$searchValue}%");
}

public function search(SearchState $state, string $query): void
public function search(SearchState $state, string $value): void
{
$state->getQuery()
->whereIn(
'id',
$this->getUserSearchSubQuery($query)
$this->getUserSearchSubQuery($value)
);
}
}
2 changes: 1 addition & 1 deletion extensions/sticky/extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
->listen(DiscussionWasStickied::class, [Listener\CreatePostWhenDiscussionIsStickied::class, 'whenDiscussionWasStickied'])
->listen(DiscussionWasUnstickied::class, [Listener\CreatePostWhenDiscussionIsStickied::class, 'whenDiscussionWasUnstickied']),

(new Extend\Search(DatabaseSearchDriver::class))
(new Extend\SearchDriver(DatabaseSearchDriver::class))
->addFilter(DiscussionSearcher::class, StickyFilter::class)
->addMutator(DiscussionSearcher::class, PinStickiedDiscussionsToTop::class),
];
2 changes: 1 addition & 1 deletion extensions/subscriptions/extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
->listen(Deleted::class, Listener\DeleteNotificationWhenPostIsHiddenOrDeleted::class)
->listen(Posted::class, Listener\FollowAfterReply::class),

(new Extend\Search(DatabaseSearchDriver::class))
(new Extend\SearchDriver(DatabaseSearchDriver::class))
->addFilter(DiscussionSearcher::class, SubscriptionFilter::class)
->addMutator(DiscussionSearcher::class, HideIgnoredFromAllDiscussionsPage::class),

Expand Down
2 changes: 1 addition & 1 deletion extensions/suspend/extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
(new Extend\User())
->permissionGroups(RevokeAccessFromSuspendedUsers::class),

(new Extend\Search(DatabaseSearchDriver::class))
(new Extend\SearchDriver(DatabaseSearchDriver::class))
->addFilter(UserSearcher::class, SuspendedFilter::class),

(new Extend\View())
Expand Down
2 changes: 1 addition & 1 deletion extensions/tags/extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@
->listen(DiscussionWasTagged::class, Listener\CreatePostWhenTagsAreChanged::class)
->subscribe(Listener\UpdateTagMetadata::class),

(new Extend\Search(DatabaseSearchDriver::class))
(new Extend\SearchDriver(DatabaseSearchDriver::class))
->addFilter(PostSearcher::class, PostTagFilter::class)
->addFilter(DiscussionSearcher::class, TagFilter::class)
->addMutator(DiscussionSearcher::class, HideHiddenTagsFromAllDiscussionsPage::class)
Expand Down
9 changes: 7 additions & 2 deletions extensions/tags/src/Api/Controller/ListTagsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
use Flarum\Http\RequestUtil;
use Flarum\Http\UrlGenerator;
use Flarum\Search\SearchCriteria;
use Flarum\Search\SearchManager;
use Flarum\Tags\Api\Serializer\TagSerializer;
use Flarum\Tags\Search\TagSearcher;
use Flarum\Tags\Tag;
use Flarum\Tags\TagRepository;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
Expand All @@ -35,7 +37,7 @@ class ListTagsController extends AbstractListController

public function __construct(
protected TagRepository $tags,
protected TagSearcher $searcher,
protected SearchManager $search,
protected UrlGenerator $url
) {
}
Expand All @@ -53,7 +55,10 @@ protected function data(ServerRequestInterface $request, Document $document): it
}

if (array_key_exists('q', $filters)) {
$results = $this->searcher->search(new SearchCriteria($actor, $filters), $limit, $offset);
$results = $this->search
->for(Tag::class)
->search(new SearchCriteria($actor, $filters, $limit, $offset));

$tags = $results->getResults();

$document->addPaginationLinks(
Expand Down
4 changes: 2 additions & 2 deletions extensions/tags/src/Search/FulltextFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ private function getTagSearchSubQuery(string $searchValue): Builder
->orWhere('slug', 'like', "$searchValue%");
}

public function search(SearchState $state, string $query): void
public function search(SearchState $state, string $value): void
{
$state->getQuery()
->whereIn(
'id',
$this->getTagSearchSubQuery($query)
$this->getTagSearchSubQuery($value)
);
}
}
2 changes: 1 addition & 1 deletion extensions/tags/src/Search/TagSearcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

class TagSearcher extends AbstractSearcher
{
protected function getQuery(User $actor): Builder
public function getQuery(User $actor): Builder
{
return Tag::whereVisibleTo($actor)->select('tags.*');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ protected function data(ServerRequestInterface $request, Document $document): it

$tokens = $this->search
->for(AccessToken::class)
->search(new SearchCriteria($actor, $filter), $limit, $offset);
->search(new SearchCriteria($actor, $filter, $limit, $offset));

$document->addPaginationLinks(
$this->url->to('api')->route('access-tokens.index'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ protected function data(ServerRequestInterface $request, Document $document): it

$results = $this->search
->for(Discussion::class)
->search(new SearchCriteria($actor, $filters, $sort, $sortIsDefault), $limit, $offset);
->search(new SearchCriteria($actor, $filters, $limit, $offset, $sort, $sortIsDefault));

$document->addPaginationLinks(
$this->url->to('api')->route('discussions.index'),
Expand Down
2 changes: 1 addition & 1 deletion framework/core/src/Api/Controller/ListGroupsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ protected function data(ServerRequestInterface $request, Document $document): it

$queryResults = $this->search
->for(Group::class)
->search(new SearchCriteria($actor, $filters, $sort, $sortIsDefault), $limit, $offset);
->search(new SearchCriteria($actor, $filters, $limit, $offset, $sort, $sortIsDefault));

$document->addPaginationLinks(
$this->url->to('api')->route('groups.index'),
Expand Down
2 changes: 1 addition & 1 deletion framework/core/src/Api/Controller/ListPostsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ protected function data(ServerRequestInterface $request, Document $document): it

$results = $this->search
->for(Post::class)
->search(new SearchCriteria($actor, $filters, $sort, $sortIsDefault), $limit, $offset);
->search(new SearchCriteria($actor, $filters, $limit, $offset, $sort, $sortIsDefault));

$document->addPaginationLinks(
$this->url->to('api')->route('posts.index'),
Expand Down
2 changes: 1 addition & 1 deletion framework/core/src/Api/Controller/ListUsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ protected function data(ServerRequestInterface $request, Document $document): it

$results = $this->search
->for(User::class)
->search(new SearchCriteria($actor, $filters, $sort, $sortIsDefault), $limit, $offset);
->search(new SearchCriteria($actor, $filters, $limit, $offset, $sort, $sortIsDefault));

$document->addPaginationLinks(
$this->url->to('api')->route('users.index'),
Expand Down
4 changes: 4 additions & 0 deletions framework/core/src/Discussion/Discussion.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ public function hide(?User $actor = null): static
$this->hidden_user_id = $actor?->id;

$this->raise(new Hidden($this));

$this->fireCustomModelEvent('hidden', false);
}

return $this;
Expand All @@ -155,6 +157,8 @@ public function restore(): static
$this->hidden_user_id = null;

$this->raise(new Restored($this));

$this->fireCustomModelEvent('restored', false);
}

return $this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

class DiscussionSearcher extends AbstractSearcher
{
protected function getQuery(User $actor): Builder
public function getQuery(User $actor): Builder
{
return Discussion::whereVisibleTo($actor)->select('discussions.*');
}
Expand Down
16 changes: 8 additions & 8 deletions framework/core/src/Discussion/Search/FulltextFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,31 @@
*/
class FulltextFilter extends AbstractFulltextFilter
{
public function search(SearchState $state, string $query): void
public function search(SearchState $state, string $value): void
{
// Replace all non-word characters with spaces.
// We do this to prevent MySQL fulltext search boolean mode from taking
// effect: https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html
$bit = preg_replace('/[^\p{L}\p{N}\p{M}_]+/u', ' ', $query);
$value = preg_replace('/[^\p{L}\p{N}\p{M}_]+/u', ' ', $value);

$query = $state->getQuery();
$grammar = $query->getGrammar();

$discussionSubquery = Discussion::select('id')
->selectRaw('NULL as score')
->selectRaw('first_post_id as most_relevant_post_id')
->whereRaw('MATCH('.$grammar->wrap('discussions.title').') AGAINST (? IN BOOLEAN MODE)', [$bit]);
->whereRaw('MATCH('.$grammar->wrap('discussions.title').') AGAINST (? IN BOOLEAN MODE)', [$value]);

// Construct a subquery to fetch discussions which contain relevant
// posts. Retrieve the collective relevance of each discussion's posts,
// which we will use later in the order by clause, and also retrieve
// the ID of the most relevant post.
$subquery = Post::whereVisibleTo($state->getActor())
->select('posts.discussion_id')
->selectRaw('SUM(MATCH('.$grammar->wrap('posts.content').') AGAINST (?)) as score', [$bit])
->selectRaw('SUBSTRING_INDEX(GROUP_CONCAT('.$grammar->wrap('posts.id').' ORDER BY MATCH('.$grammar->wrap('posts.content').') AGAINST (?) DESC, '.$grammar->wrap('posts.number').'), \',\', 1) as most_relevant_post_id', [$bit])
->selectRaw('SUM(MATCH('.$grammar->wrap('posts.content').') AGAINST (?)) as score', [$value])
->selectRaw('SUBSTRING_INDEX(GROUP_CONCAT('.$grammar->wrap('posts.id').' ORDER BY MATCH('.$grammar->wrap('posts.content').') AGAINST (?) DESC, '.$grammar->wrap('posts.number').'), \',\', 1) as most_relevant_post_id', [$value])
->where('posts.type', 'comment')
->whereRaw('MATCH('.$grammar->wrap('posts.content').') AGAINST (? IN BOOLEAN MODE)', [$bit])
->whereRaw('MATCH('.$grammar->wrap('posts.content').') AGAINST (? IN BOOLEAN MODE)', [$value])
->groupBy('posts.discussion_id')
->union($discussionSubquery);

Expand All @@ -62,8 +62,8 @@ public function search(SearchState $state, string $query): void
->groupBy('discussions.id')
->addBinding($subquery->getBindings(), 'join');

$state->setDefaultSort(function ($query) use ($grammar, $bit) {
$query->orderByRaw('MATCH('.$grammar->wrap('discussions.title').') AGAINST (?) desc', [$bit]);
$state->setDefaultSort(function ($query) use ($grammar, $value) {
$query->orderByRaw('MATCH('.$grammar->wrap('discussions.title').') AGAINST (?) desc', [$value]);
$query->orderBy('posts_ft.score', 'desc');
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
use Flarum\Search\AbstractDriver;
use Flarum\Search\AbstractFulltextFilter;
use Flarum\Search\Database\AbstractSearcher;
use Flarum\Search\Database\DatabaseSearchState;
use Flarum\Search\SearchState;
use Flarum\Search\Filter\FilterInterface;
use Flarum\Search\SearchCriteria;
use Illuminate\Contracts\Container\Container;

class Search implements ExtenderInterface
class SearchDriver implements ExtenderInterface
{
private array $searchers = [];
private array $fulltext = [];
Expand Down Expand Up @@ -91,7 +91,7 @@ public function setFulltext(string $searcherClass, string $fulltextClass): self
* @param class-string<AbstractSearcher> $searcherClass : The class of the Searcher for this model
* This searcher must implement \Flarum\Search\SearcherInterface.
* Or extend \Flarum\Search\Database\AbstractSearcher if using the default driver.
* @param (callable(DatabaseSearchState $search, SearchCriteria $criteria): void)|class-string $callback
* @param (callable(SearchState $search, SearchCriteria $criteria): void)|class-string $callback
*
* The callback can be a closure or an invokable class, and should accept:
* - \Flarum\Search\SearchState $search
Expand Down
44 changes: 44 additions & 0 deletions framework/core/src/Extend/SearchIndex.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace Flarum\Extend;

use Flarum\Extend\ExtenderInterface;
use Flarum\Extension\Extension;
use Illuminate\Contracts\Container\Container;

class SearchIndex implements ExtenderInterface
{
private array $indexers = [];

/**
* Register an indexer for a resource.
*
* @param string $resourceClass: The class of the model you are indexing.
* @param string $indexerClass: The class of the indexer you are adding.
* This indexer must implement \Flarum\Search\IndexerInterface.
*/
public function indexer(string $resourceClass, string $indexerClass): self
{
$this->indexers[$resourceClass][] = $indexerClass;

return $this;
}

public function extend(Container $container, Extension $extension = null): void
{
if (empty($this->indexers)) {
return;
}

$container->extend('flarum.search.indexers', function (array $indexers) {
foreach ($this->indexers as $resourceClass => $indexerClasses) {
$indexers[$resourceClass] = array_merge(
$indexers[$resourceClass] ?? [],
$indexerClasses
);
}

return $indexers;
});
}
}
2 changes: 1 addition & 1 deletion framework/core/src/Group/Filter/GroupSearcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

class GroupSearcher extends AbstractSearcher
{
protected function getQuery(User $actor): Builder
public function getQuery(User $actor): Builder
{
return Group::whereVisibleTo($actor)->select('groups.*');
}
Expand Down
2 changes: 1 addition & 1 deletion framework/core/src/Http/Filter/AccessTokenSearcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

class AccessTokenSearcher extends AbstractSearcher
{
protected function getQuery(User $actor): Builder
public function getQuery(User $actor): Builder
{
return AccessToken::query()->whereVisibleTo($actor);
}
Expand Down
2 changes: 1 addition & 1 deletion framework/core/src/Post/Filter/PostSearcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

class PostSearcher extends AbstractSearcher
{
protected function getQuery(User $actor): Builder
public function getQuery(User $actor): Builder
{
return Post::whereVisibleTo($actor)->select('posts.*');
}
Expand Down
8 changes: 8 additions & 0 deletions framework/core/src/Search/AbstractDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
abstract class AbstractDriver
{
public function __construct(
/**
* @var array<class-string<AbstractModel>, class-string<SearcherInterface>>
*/
protected array $searchers
) {
}
Expand All @@ -17,4 +20,9 @@ public function searchers(): array
{
return $this->searchers;
}

public function supports(string $modelClass): bool
{
return isset($this->searchers[$modelClass]);
}
}
2 changes: 1 addition & 1 deletion framework/core/src/Search/AbstractFulltextFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ public function filter(SearchState $state, array|string $value, bool $negate): v
/**
* @param TState $state
*/
abstract public function search(SearchState $state, string $query): void;
abstract public function search(SearchState $state, string $value): void;
}
Loading

0 comments on commit 343f1b2

Please sign in to comment.