Merge branch 'dk/analyzer' into main

This commit is contained in:
Daniël Klabbers 2021-10-31 09:17:35 +01:00
commit ef4b02b562
10 changed files with 113 additions and 30 deletions

View File

@ -9,6 +9,10 @@ return [
(new Flarum\Frontend('forum')) (new Flarum\Frontend('forum'))
->js(__DIR__ . '/js/dist/forum.js'), ->js(__DIR__ . '/js/dist/forum.js'),
(new Flarum\Frontend('admin'))
->js(__DIR__ . '/js/dist/admin.js'),
(new Flarum\Locales(__DIR__ . '/resources/locale')),
(new Flarum\Routes('api')) (new Flarum\Routes('api'))
->get('/blomstra/search/{type}', 'blomstra.search', Api\Controllers\SearchController::class), ->get('/blomstra/search/{type}', 'blomstra.search', Api\Controllers\SearchController::class),

1
js/admin.ts Normal file
View File

@ -0,0 +1 @@
export * from './src/admin';

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

2
js/package-lock.json generated
View File

@ -14,7 +14,7 @@
"flarum-webpack-config": "^1.0.0", "flarum-webpack-config": "^1.0.0",
"pusher-js": "^7.0.3", "pusher-js": "^7.0.3",
"webpack": "^4.46.0", "webpack": "^4.46.0",
"webpack-cli": "^4.9.0" "webpack-cli": "^4.9.1"
}, },
"devDependencies": { "devDependencies": {
"prettier": "^2.4.1" "prettier": "^2.4.1"

44
js/src/admin/index.ts Normal file
View File

@ -0,0 +1,44 @@
import app from 'flarum/admin/app';
app.initializers.add('blomstra-search', () => {
const languages = new Map;
['arabic', 'armenian', 'basque', 'bengali', 'brazilian', 'bulgarian', 'catalan', 'cjk', 'czech',
'danish', 'dutch', 'english', 'estonian', 'finnish', 'french', 'galician', 'german', 'greek',
'hindi', 'hungarian', 'indonesian', 'irish', 'italian', 'latvian', 'lithuanian', 'norwegian',
'persian', 'portuguese', 'romanian', 'russian', 'sorani', 'spanish', 'swedish', 'turkish', 'thai']
.forEach(language => {
languages.set(language, language);
});
app.extensionData
.for('blomstra-search')
.registerSetting({
setting: 'blomstra-search.elastic-endpoint',
label: app.translator.trans('blomstra-search.admin.elastic-endpoint'),
type: 'input'
})
.registerSetting({
setting: 'blomstra-search.elastic-username',
label: app.translator.trans('blomstra-search.admin.elastic-username'),
type: 'input'
})
.registerSetting({
setting: 'blomstra-search.elastic-password',
label: app.translator.trans('blomstra-search.admin.elastic-password'),
type: 'password'
})
.registerSetting({
setting: 'blomstra-search.elastic-index',
label: app.translator.trans('blomstra-search.admin.elastic-index'),
default: 'flarum',
type: 'input'
})
.registerSetting({
setting: 'blomstra-search.analyzer-language',
label: app.translator.trans('blomstra-search.admin.analyzer.label'),
help: app.translator.trans('blomstra-search.admin.analyzer.help'),
type: 'select',
options: Object.fromEntries(languages.entries()),
default: 'english',
})
});

11
resources/locale/en.yml Normal file
View File

@ -0,0 +1,11 @@
blomstra-search:
admin:
elastic-endpoint: Elastic Endpoint
elastic-username: => core.ref.username
elastic-password: => core.ref.password
elastic-index: Index
analyzer:
label: Analyzer language
help: |
The analyzer makes search understand stop words and undertakes language
specific improvements for indexing.

View File

@ -90,35 +90,45 @@ class SearchController extends ListDiscussionsController
} }
} }
// we need to retrieve all discussion ids and when the results are posts,
// their ids as most relevant post id
$results = Collection::make(Arr::get($result, 'hits.hits')) $results = Collection::make(Arr::get($result, 'hits.hits'))
->map(function ($hit) { ->map(function ($hit) {
$id = Str::after($hit['_source']['id'], ':'); $id = Str::after($hit['_source']['id'], ':');
$type = $hit['_source']['type']; $type = $hit['_source']['type'];
if ($type === 'posts') { if ($type === 'posts') {
/** @var Discussion $discussion */ return [
$discussion = Discussion::whereHas('posts', function ($query) use ($id) { 'most_relevant_post_id' => $id,
$query->where('id', $id); ];
})->first(); } else {
return [
$discussion->most_relevant_post_id = $id; 'discussion_id' => $id
];
} }
if ($type === 'discussions') { });
/** @var Discussion $discussion */
$discussion = Discussion::find($id);
$discussions = Discussion::query()
->whereIn('id', $results->pluck('discussion_id'))
->orWhereHas('posts', function ($query) use ($results) {
$query->whereIn('id', $results->pluck('most_relevant_post_id'));
})
->get()
->map(function (Discussion $discussion) use ($results) {
if (in_array($discussion->id, $results->pluck('discussion_id'))) {
$discussion->most_relevant_post_id = $discussion->first_post_id; $discussion->most_relevant_post_id = $discussion->first_post_id;
} else {
$post = $discussion->posts()->whereIn('id', $results->pluck('most_relevant_post_id'))->first();
$discussion->most_relevant_post_id = $post?->id ?? $discussion->first_post_id;
} }
return $discussion;
}) })
->keyBy('id') ->keyBy('id')
->unique(); ->unique();
$this->loadRelations($results, $include); $this->loadRelations($discussions, $include);
if ($relations = array_intersect($include, ['firstPost', 'lastPost', 'mostRelevantPost'])) { if ($relations = array_intersect($include, ['firstPost', 'lastPost', 'mostRelevantPost'])) {
foreach ($results as $discussion) { foreach ($discussions as $discussion) {
foreach ($relations as $relation) { foreach ($relations as $relation) {
if ($discussion->$relation) { if ($discussion->$relation) {
$discussion->$relation->discussion = $discussion; $discussion->$relation->discussion = $discussion;
@ -127,7 +137,7 @@ class SearchController extends ListDiscussionsController
} }
} }
return $results; return $discussions;
} }
protected function getDocument(string $type): ?ElasticDocument protected function getDocument(string $type): ?ElasticDocument

View File

@ -5,6 +5,7 @@ namespace Blomstra\Search\Commands;
use Blomstra\Search\Observe\SavingJob; use Blomstra\Search\Observe\SavingJob;
use Blomstra\Search\Seeders\Seeder; use Blomstra\Search\Seeders\Seeder;
use Elasticsearch\Client; use Elasticsearch\Client;
use Flarum\Settings\SettingsRepositoryInterface;
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;
@ -29,6 +30,11 @@ class RebuildDocumentsCommand extends Command
/** @var Client $client */ /** @var Client $client */
$client = $container->make('blomstra.search.elastic'); $client = $container->make('blomstra.search.elastic');
/** @var SettingsRepositoryInterface $settings */
$settings = $container->make(SettingsRepositoryInterface::class);
$analyzer = $settings->get('blomstra-search.analyzer-language', 'english');
// Flush the index. // Flush the index.
if ($this->option('flush')) { if ($this->option('flush')) {
$client->indices()->delete([ $client->indices()->delete([
@ -36,7 +42,15 @@ class RebuildDocumentsCommand extends Command
'ignore_unavailable' => true 'ignore_unavailable' => true
]); ]);
$client->indices()->create([ $client->indices()->create([
'index' => $container->make('blomstra.search.elastic_index') 'index' => $container->make('blomstra.search.elastic_index'),
// 'settings' => [
// 'analysis' => [
// 'analyzer' => [
// 'default' => $analyzer,
// 'default_search' => $analyzer
// ]
// ]
// ]
]); ]);
} }

View File

@ -7,10 +7,10 @@ use Blomstra\Search\Observe\SavingJob;
use Blomstra\Search\Seeders; use Blomstra\Search\Seeders;
use Elasticsearch\ClientBuilder; use Elasticsearch\ClientBuilder;
use Flarum\Foundation\AbstractServiceProvider; use Flarum\Foundation\AbstractServiceProvider;
use Flarum\Settings\SettingsRepositoryInterface;
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\Collection; use Illuminate\Support\Collection;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -23,29 +23,28 @@ class Provider extends AbstractServiceProvider
Seeders\CommentSeeder::class, Seeders\CommentSeeder::class,
], 'blomstra.search.seeders'); ], 'blomstra.search.seeders');
$config = $this->container->make('flarum.config') ?? []; /** @var SettingsRepositoryInterface $settings */
$elastic = Arr::get($config, 'elastic', []); $settings = $this->container->make(SettingsRepositoryInterface::class);
$this->container->singleton('blomstra.search.elastic', function (Container $container) use ($elastic) { $this->container->singleton('blomstra.search.elastic', function (Container $container) use ($settings) {
$builder = ClientBuilder::create() $builder = ClientBuilder::create()
->setHosts([$elastic['endpoint']]) ->setHosts([$settings->get('blomstra-search.elastic-endpoint')])
->setLogger($container->make(LoggerInterface::class)); ->setLogger($container->make(LoggerInterface::class));
if ($elastic['api-key'] ?? false) { if ($settings->get('blomstra-search.elastic-username')) {
$builder->setApiKey($elastic['api-id'], $elastic['api-key']); $builder->setBasicAuthentication(
} $settings->get('blomstra-search.elastic-username'),
if ($elastic['username'] ?? false) { $settings->get('blomstra-search.elastic-password')
$builder->setBasicAuthentication($elastic['username'], $elastic['password']); );
} }
return $builder->build(); return $builder->build();
}); });
$index = env('UUID') ?? Arr::get($elastic, 'index', 'flarum');
$this->container->instance( $this->container->instance(
'blomstra.search.elastic_index', 'blomstra.search.elastic_index',
$index $settings->get('blomstra-search.elastic-index', 'flarum')
); );
} }