mirror of
https://github.com/flarum/framework.git
synced 2025-01-19 18:12:59 +08:00
Improve fulltext search API and interface
This commit is contained in:
parent
38c2ff0306
commit
42f1fa1272
|
@ -23,7 +23,10 @@ export default class DiscussionList extends Component {
|
|||
}
|
||||
params.sort = this.sortMap()[params.sort];
|
||||
if (params.q) {
|
||||
params.filter = params.filter || {};
|
||||
params.filter.q = params.q;
|
||||
params.include.push('relevantPosts', 'relevantPosts.discussion', 'relevantPosts.user');
|
||||
delete params.q;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ export default class DiscussionsSearchResults {
|
|||
|
||||
search(string) {
|
||||
this.results[string] = [];
|
||||
return app.store.find('discussions', {filter: {q: string}, page: {limit: 3}, include: 'relevantPosts,relevantPosts.discussion'}).then(results => {
|
||||
return app.store.find('discussions', {filter: {q: string}, page: {limit: 3}, include: 'relevantPosts,relevantPosts.discussion,relevantPosts.user'}).then(results => {
|
||||
this.results[string] = results;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ export default function(string, phrase, length) {
|
|||
return string;
|
||||
}
|
||||
|
||||
const regexp = regexp instanceof RegExp ? phrase : new RegExp(phrase, 'gi');
|
||||
const regexp = phrase instanceof RegExp ? phrase : new RegExp(phrase, 'gi');
|
||||
|
||||
let highlightedString = string;
|
||||
let start = 0;
|
||||
|
|
|
@ -113,7 +113,8 @@
|
|||
font-size: 14px;
|
||||
}
|
||||
& .relevant-posts {
|
||||
display: none;
|
||||
margin-left: -52px;
|
||||
margin-right: -65px;
|
||||
}
|
||||
& .count {
|
||||
margin-top: 11px;
|
||||
|
@ -242,7 +243,7 @@
|
|||
}
|
||||
}
|
||||
& .relevant-posts {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
|
||||
@media @phone {
|
||||
margin-left: -45px;
|
||||
|
@ -254,6 +255,12 @@
|
|||
display: block;
|
||||
padding: 10px 15px;
|
||||
border-bottom: 2px dotted @fl-body-bg;
|
||||
color: @fl-body-muted-color;
|
||||
transition: border-color 0.2s;
|
||||
|
||||
.discussion-list-item:hover & {
|
||||
border-color: lighten(@fl-body-secondary-color, 3%);
|
||||
}
|
||||
|
||||
& .avatar, & time {
|
||||
display: none;
|
||||
|
|
|
@ -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'
|
||||
);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
13
src/Core/Discussions/Search/Fulltext/Driver.php
Normal file
13
src/Core/Discussions/Search/Fulltext/Driver.php
Normal 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);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
<?php namespace Flarum\Core\Discussions\Search\Fulltext;
|
||||
|
||||
interface DriverInterface
|
||||
{
|
||||
public function match($string);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue
Block a user