moved to elastic

This commit is contained in:
Daniel Klabbers 2021-10-14 12:31:45 +02:00
parent e2e56831e5
commit f69541d38a
9 changed files with 96 additions and 51 deletions

View File

@ -21,10 +21,8 @@
], ],
"require": { "require": {
"php": ">= 8.0", "php": ">= 8.0",
"flarum/core": "^1.0.0" "flarum/core": "^1.0.0",
}, "elasticsearch/elasticsearch": "7.*"
"suggest": {
"meilisearch/meilisearch-php": "Required to use the MeiliSearch engine (^0.19)."
}, },
"extra": { "extra": {
"flarum-extension": { "flarum-extension": {

2
js/dist/forum.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -64,7 +64,7 @@ export default class DiscussionsSearchSource implements SearchSource {
return ( return (
<> <>
<li className="Dropdown-header">{app.translator.trans('core.forum.search.discussions_heading')}</li>, <li className="Dropdown-header">{app.translator.trans('core.forum.search.discussions_heading')}</li>
<li> <li>
<LinkButton icon="fas fa-search" href={app.route('index', { q: query })}> <LinkButton icon="fas fa-search" href={app.route('index', { q: query })}>
{app.translator.trans('core.forum.search.all_discussions_button', { query })} {app.translator.trans('core.forum.search.all_discussions_button', { query })}

View File

@ -3,6 +3,7 @@
namespace Blomstra\Search\Api\Controllers; namespace Blomstra\Search\Api\Controllers;
use Blomstra\Search\Schemas\Schema; use Blomstra\Search\Schemas\Schema;
use Elasticsearch\Client;
use Flarum\Api\Controller\AbstractListController; use Flarum\Api\Controller\AbstractListController;
use Flarum\Extension\ExtensionManager; use Flarum\Extension\ExtensionManager;
use Flarum\Group\Group; use Flarum\Group\Group;
@ -11,17 +12,16 @@ use Flarum\User\User;
use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use MeiliSearch\Client;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document; use Tobscure\JsonApi\Document;
class SearchController extends AbstractListController class SearchController extends AbstractListController
{ {
public function __construct(protected Client $meili)
{}
protected function data(ServerRequestInterface $request, Document $document) protected function data(ServerRequestInterface $request, Document $document)
{ {
/** @var Client $client */
$client = resolve('blomstra.search.elastic');
$index = Arr::get($request->getQueryParams(), 'index'); $index = Arr::get($request->getQueryParams(), 'index');
$actor = RequestUtil::getActor($request); $actor = RequestUtil::getActor($request);
@ -30,20 +30,26 @@ class SearchController extends AbstractListController
$schema = $this->getSchema($index); $schema = $this->getSchema($index);
$result = $this->meili->index($index)->search($filters['q'], [ $result = $client->search([
'offset' => $this->extractOffset($request), 'index' => $index,
'limit' => $this->extractLimit($request), 'from' => $this->extractOffset($request),
'size' => $this->extractLimit($request),
'sort' => $this->extractSort($request), 'sort' => $this->extractSort($request),
// Filters based on permissions etc.. 'body' => [
'filter' => $this->getFilters($actor) 'query' => [
'multi_match' => [
'query' => $filters['q'],
'fields' => array_keys($schema->fulltext(new ($schema::model())))
]
]
]
]); ]);
$this->serializer = $schema::serializer(); $this->serializer = $schema::serializer();
return Collection::make($result->getHits()) $ids = Collection::make(Arr::get($result, 'hits.hits'))->pluck('_id')->toArray();
->map(function ($hit) use ($schema) {
return $schema::model()::query()->find($hit['id']); return $schema::model()::query()->findMany($ids);
});
} }
protected function getSchema(string $index): ?Schema protected function getSchema(string $index): ?Schema

View File

@ -4,12 +4,12 @@ namespace Blomstra\Search\Commands;
use Blomstra\Search\Observe\SavingJob; use Blomstra\Search\Observe\SavingJob;
use Blomstra\Search\Schemas\Schema; use Blomstra\Search\Schemas\Schema;
use Elasticsearch\Client;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Queue\Queue; use Illuminate\Contracts\Queue\Queue;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use MeiliSearch\Client;
class RebuildDocumentsCommand extends Command class RebuildDocumentsCommand extends Command
{ {
@ -24,8 +24,8 @@ class RebuildDocumentsCommand extends Command
/** @var Queue $queue */ /** @var Queue $queue */
$queue = $container->make(Queue::class); $queue = $container->make(Queue::class);
/** @var Client $meili */ /** @var Client $client */
$meili = $container->make(Client::class); $client = $container->make('blomstra.search.elastic');
/** @var Schema $schema */ /** @var Schema $schema */
foreach ($schemas as $schema) { foreach ($schemas as $schema) {
@ -33,13 +33,21 @@ class RebuildDocumentsCommand extends Command
$model = $schema::model(); $model = $schema::model();
// Flush the index. // Flush the index.
if ($this->option('flush')) $meili->index($schema::index())->delete(); if ($this->option('flush')) $client->indices()->delete([
'index' => $schema::index()
]);
$model::query()->chunk(50, function (Collection $collection) use ($model, $queue) { $total = 0;
$model::query()->chunk(50, function (Collection $collection) use ($model, $queue, &$total) {
$queue->push(new SavingJob($model, $collection)); $queue->push(new SavingJob($model, $collection));
$this->info("Pushed {$collection->count()} into the index"); $this->info("Pushed {$collection->count()} into the index.");
$total += $collection->count();
}); });
$this->info("Pushed a total of $total into the index.");
} }
} }
} }

View File

@ -2,41 +2,70 @@
namespace Blomstra\Search\Observe; namespace Blomstra\Search\Observe;
use Elasticsearch\Client;
use Illuminate\Contracts\Container\Container;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use MeiliSearch\Client;
class SavingJob extends Job class SavingJob extends Job
{ {
public function __construct(protected string $class, protected Collection $models) public function __construct(protected string $class, protected Collection $models)
{} {}
public function handle(Client $meili) public function handle(Container $container)
{ {
/** @var Client $client */
$client = $container->make('blomstra.search.elastic');
$schema = $this->getSchema(); $schema = $this->getSchema();
if (! $schema) return; if (! $schema) return;
if($first = $this->models->first()) { if($first = $this->models->first()) {
$properties = [];
foreach (array_keys($schema->fulltext($first)) as $key) {
$properties[$key] = ['type' => 'text'];
}
// Set up the index // Set up the index
$meili->index($schema::index())->updateSettings([ // if (! $client->indices()->exists([
'filterableAttributes' => array_keys($schema->filters($first)), // 'index' => $schema::index(),
'searchableAttributes' => array_keys($schema->fulltext($first)), // 'expand_wildcards' => 'none'
]); // ])) {
// $client->indices()->create([
// 'index' => $schema::index(),
// 'body' => [
// 'mappings' => [
// 'properties' => $properties
// ],
// 'settings' => [
// 'index' => [
// 'query' => [
// 'default_field' => array_keys($schema->fulltext($first))
// ]
// ]
// ]
// ]
// ]);
// }
} }
// Preparing body for storing. // Preparing body for storing.
$body = $this->models->map(function (Model $model) use ($schema) { $body = $this->models->map(function (Model $model) use ($schema) {
return array_merge( return [
$schema->fulltext($model), ['index' => ['_id' => $model->getKey(), '_index' => $schema::index()]],
$schema->filters($model), [ array_merge(
$model->getKeyName() => $model->getKey() $schema->fulltext($model),
]); $schema->filters($model))
});
$meili->index($schema::index())->addDocuments( ];
$body->toArray(), })->flatten(1);
(new $this->class)->getKeyName()
); $response = $client->bulk([
'index' => $schema::index(),
'body' => $body->toArray(),
'refresh' => true
]);
} }
} }

View File

@ -6,13 +6,14 @@ use Blomstra\Search\Observe\DeletingJob;
use Blomstra\Search\Observe\SavingJob; use Blomstra\Search\Observe\SavingJob;
use Blomstra\Search\Schemas\DiscussionSchema; use Blomstra\Search\Schemas\DiscussionSchema;
use Blomstra\Search\Schemas\Schema; use Blomstra\Search\Schemas\Schema;
use Elasticsearch\ClientBuilder;
use Flarum\Foundation\AbstractServiceProvider; use Flarum\Foundation\AbstractServiceProvider;
use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Queue\Queue; use Illuminate\Contracts\Queue\Queue;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use MeiliSearch\Client; use Psr\Log\LoggerInterface;
class Provider extends AbstractServiceProvider class Provider extends AbstractServiceProvider
{ {
@ -20,16 +21,16 @@ class Provider extends AbstractServiceProvider
{ {
$this->container->tag([DiscussionSchema::class], 'blomstra.search.schemas'); $this->container->tag([DiscussionSchema::class], 'blomstra.search.schemas');
$this->container->singleton(Client::class, function (Container $container) { $this->container->singleton('blomstra.search.elastic', function (Container $container) {
$config = $container->make('flarum.config') ?? []; $config = $container->make('flarum.config') ?? [];
$meili = Arr::get($config, 'meili'); $elastic = Arr::get($config, 'elastic');
if (! $meili) { return ClientBuilder::create()
return null; ->setHosts([$elastic['endpoint']])
} ->setBasicAuthentication($elastic['username'], $elastic['password'])
->setLogger($container->make(LoggerInterface::class))
return new Client($meili); ->build();
}); });
} }

View File

@ -44,10 +44,13 @@ class DiscussionSchema extends Schema
})->flatten(); })->flatten();
} }
if (! $permissions || $permissions->isEmpty()) { if (! $discussion->is_private && (! $permissions || $permissions->isEmpty())) {
$permissions = Permission::query() $permissions = Permission::query()
->where('permission', 'viewForum') ->where('permission', 'viewForum')
->pluck('group_id'); ->pluck('group_id');
} else {
$permissions = collect();
} }
$filters['groups'] = $permissions->toArray(); $filters['groups'] = $permissions->toArray();