1
0
mirror of https://github.com/flarum/core.git synced 2025-08-05 07:57:46 +02:00

Improve fulltext search API and interface

This commit is contained in:
Toby Zerner
2015-07-07 20:35:18 +09:30
parent 662a4dc54f
commit 5e982a39c5
12 changed files with 62 additions and 81 deletions

View File

@@ -45,7 +45,7 @@ class DiscussionsServiceProvider extends ServiceProvider
public function register()
{
$this->app->bind(
'Flarum\Core\Discussions\Search\Fulltext\DriverInterface',
'Flarum\Core\Discussions\Search\Fulltext\Driver',
'Flarum\Core\Discussions\Search\Fulltext\MySqlFulltextDriver'
);

View File

@@ -30,26 +30,13 @@ class DiscussionSearch extends Search
}
/**
* Set the relevant post IDs for a result.
* Set the relevant post IDs for the results.
*
* @param int $discussionId
* @param int[] $postIds
* @param array $relevantPostIds
* @return void
*/
public function setRelevantPostIds($discussionId, array $postIds)
public function setRelevantPostIds(array $relevantPostIds)
{
$this->relevantPostIds[$discussionId] = $postIds;
}
/**
* Add a relevant post ID for a discussion result.
*
* @param int $discussionId
* @param int $postId
* @return void
*/
public function addRelevantPostId($discussionId, $postId)
{
$this->relevantPostIds[$discussionId][] = $postId;
$this->relevantPostIds = $relevantPostIds;
}
}

View File

@@ -0,0 +1,13 @@
<?php namespace Flarum\Core\Discussions\Search\Fulltext;
interface Driver
{
/**
* Return an array of arrays of post IDs, grouped by discussion ID, which
* match the given string.
*
* @param string $string
* @return array
*/
public function match($string);
}

View File

@@ -1,6 +0,0 @@
<?php namespace Flarum\Core\Discussions\Search\Fulltext;
interface DriverInterface
{
public function match($string);
}

View File

@@ -2,12 +2,24 @@
use Flarum\Core\Posts\Post;
class MySqlFulltextDriver implements DriverInterface
class MySqlFulltextDriver implements Driver
{
/**
* {@inheritdoc}
*/
public function match($string)
{
return Post::whereRaw('MATCH (`content`) AGAINST (? IN BOOLEAN MODE)', [$string])
$discussionIds = Post::where('type', 'comment')
->whereRaw('MATCH (`content`) AGAINST (? IN BOOLEAN MODE)', [$string])
->orderByRaw('MATCH (`content`) AGAINST (?) DESC', [$string])
->lists('id');
->lists('discussion_id', 'id');
$relevantPostIds = [];
foreach ($discussionIds as $postId => $discussionId) {
$relevantPostIds[$discussionId][] = $postId;
}
return $relevantPostIds;
}
}

View File

@@ -1,7 +1,7 @@
<?php namespace Flarum\Core\Discussions\Search\Gambits;
use Flarum\Core\Discussions\Search\DiscussionSearch;
use Flarum\Core\Posts\PostRepository;
use Flarum\Core\Discussions\Search\Fulltext\Driver;
use Flarum\Core\Search\Search;
use Flarum\Core\Search\Gambit;
use LogicException;
@@ -9,16 +9,16 @@ use LogicException;
class FulltextGambit implements Gambit
{
/**
* @var PostRepository
* @var Driver
*/
protected $posts;
protected $fulltext;
/**
* @param PostRepository $posts
* @param Driver $fulltext
*/
public function __construct(PostRepository $posts)
public function __construct(Driver $fulltext)
{
$this->posts = $posts;
$this->fulltext = $fulltext;
}
/**
@@ -30,18 +30,14 @@ class FulltextGambit implements Gambit
throw new LogicException('This gambit can only be applied on a DiscussionSearch');
}
$posts = $this->posts->findByContent($bit, $search->getActor());
$relevantPostIds = $this->fulltext->match($bit);
$discussions = [];
foreach ($posts as $post) {
$discussions[] = $id = $post->discussion_id;
$search->addRelevantPostId($id, $post->id);
}
$discussions = array_unique($discussions);
$discussionIds = array_keys($relevantPostIds);
// TODO: implement negate (match for - at start of string)
$search->getQuery()->whereIn('id', $discussions);
$search->setRelevantPostIds($relevantPostIds);
$search->setDefaultSort(['id' => $discussions]);
$search->getQuery()->whereIn('id', $discussionIds);
$search->setDefaultSort(['id' => $discussionIds]);
}
}

View File

@@ -4,7 +4,7 @@ use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Flarum\Core\Users\User;
use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Discussions\Search\Fulltext\DriverInterface;
use Flarum\Core\Discussions\Search\Fulltext\Driver;
// TODO: In some cases, the use of a post repository incurs extra query expense,
// because for every post retrieved we need to check if the discussion it's in
@@ -28,13 +28,6 @@ use Flarum\Core\Discussions\Search\Fulltext\DriverInterface;
class PostRepository
{
protected $fulltext;
public function __construct(DriverInterface $fulltext)
{
$this->fulltext = $fulltext;
}
/**
* Find a post by ID, optionally making sure it is visible to a certain
* user, or throw an exception.
@@ -99,31 +92,6 @@ class PostRepository
return $this->filterVisibleTo($posts, $actor);
}
/**
* Find posts by matching a string of words against their content,
* optionally making sure they are visible to a certain user.
*
* @param string $string
* @param \Flarum\Core\Users\User|null $actor
* @return \Illuminate\Database\Eloquent\Collection
*/
public function findByContent($string, User $actor = null)
{
$ids = $this->fulltext->match($string);
$ids = $this->filterDiscussionVisibleTo($ids, $actor);
$query = Post::select('id', 'discussion_id')->whereIn('id', $ids);
foreach ($ids as $id) {
$query->orderByRaw('id != ?', [$id]);
}
$posts = $query->get();
return $this->filterVisibleTo($posts, $actor);
}
/**
* Get the position within a discussion where a post with a certain number
* is. If the post with that number does not exist, the index of the

View File

@@ -30,7 +30,8 @@ class IndexAction extends ClientAction
$params = [
'sort' => $sort ? $this->sortMap[$sort] : '',
'q' => $q
'filter' => ['q' => $q],
'include' => $q ? 'startUser,lastUser,relevantPosts,relevantPosts.discussion,relevantPosts.user' : ''
];
// FIXME: make sure this is extensible. Support pagination.