Compare commits

..

No commits in common. "main" and "feat/analyzer-refactor" have entirely different histories.

35 changed files with 123 additions and 200 deletions

View File

@ -18,32 +18,9 @@ fulltext search with one that is completely relying on the proven elasticsearch
## Installation ## Installation
```bash ```bash
composer require sbp/flarum-search composer require blomstra/search:*
``` ```
## Notes
This fork is now maintained as `sbp/flarum-search`.
Intentional compatibility leftovers:
- settings keys remain `blomstra-search.*`
- some translation keys remain `blomstra-search.*`
- CLI command names may still remain `blomstra:*` until a later migration
Search behavior:
- title autocomplete enabled
- fuzzy title search enabled
- exact + fuzzy content search on full search page
- no content autocomplete in dropdown (performance decision)
``
Repository can be found here: https://gitea.muehlberger.net/sbp-jm/sbp-flarum-search
Enable the extension in the admin area and configure the Elasticsearch endpoint and index name in the extension settings. Enable the extension in the admin area and configure the Elasticsearch endpoint and index name in the extension settings.
## Setting up the index ## Setting up the index

View File

@ -1,5 +1,5 @@
{ {
"name": "sbp/flarum-search", "name": "blomstra/search",
"description": "Replaces Flarum search with one powered by an elastic search server.", "description": "Replaces Flarum search with one powered by an elastic search server.",
"keywords": [ "keywords": [
"extension", "extension",
@ -11,20 +11,16 @@
"elasticsearch" "elasticsearch"
], ],
"support": { "support": {
"email": "mueh+git@sbp.de", "email": "helpdesk@blomstra.net",
"forum": "https://blomstra.community/t/ext-search" "forum": "https://blomstra.community/t/ext-search"
}, },
"type": "flarum-extension", "type": "flarum-extension",
"license": "MIT", "license": "MIT",
"authors": [ "authors": [
{
"name": "sbp",
"email": "info@sbp.de"
},
{ {
"name": "Team Blomstra", "name": "Team Blomstra",
"email": "development@blomstra.net", "email": "development@blomstra.net",
"role": "Original Developer" "role": "Developer"
} }
], ],
"require": { "require": {
@ -47,7 +43,7 @@
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Sbp\\Search\\": "src/" "Blomstra\\Search\\": "src/"
} }
} }
} }

View File

@ -10,7 +10,7 @@
* *
*/ */
namespace Sbp\Search; namespace Blomstra\Search;
use Flarum\Extend as Flarum; use Flarum\Extend as Flarum;
@ -26,8 +26,8 @@ return [
new Flarum\Locales(__DIR__.'/resources/locale'), new Flarum\Locales(__DIR__.'/resources/locale'),
(new Flarum\Routes('api')) (new Flarum\Routes('api'))
->get('/sbp/search/{type}', 'sbp.search', Api\Controllers\SearchController::class) ->get('/blomstra/search/{type}', 'blomstra.search', Api\Controllers\SearchController::class)
->put('/sbp/search/index', 'sbp.search.index', Api\Controllers\IndexController::class), ->put('/blomstra/search/index', 'blomstra.search.index', Api\Controllers\IndexController::class),
(new Flarum\Console()) (new Flarum\Console())
->command(Commands\BuildCommand::class), ->command(Commands\BuildCommand::class),

2
js/dist/admin.js vendored
View File

@ -1,2 +1,2 @@
(()=>{var a={n:t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return a.d(e,{a:e}),e},d:(t,e)=>{for(var n in e)a.o(e,n)&&!a.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},o:(a,t)=>Object.prototype.hasOwnProperty.call(a,t)};(()=>{"use strict";function t(a,e){return t=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(a,t){return a.__proto__=t,a},t(a,e)}const e=flarum.core.compat["admin/app"];var n=a.n(e);const r=flarum.core.compat["common/extend"],s=flarum.core.compat["admin/components/DashboardPage"];var i=a.n(s);const l=flarum.core.compat["admin/components/DashboardWidget"];var o=a.n(l);const c=flarum.core.compat["common/components/Alert"];var h=a.n(c),d=function(a){var e,r;function s(){return a.apply(this,arguments)||this}r=a,(e=s).prototype=Object.create(r.prototype),e.prototype.constructor=e,t(e,r);var i=s.prototype;return i.className=function(){return"ReindexWarningWidget"},i.content=function(){return m(h(),{type:"warning",dismissible:!1,icon:"fas fa-exclamation-triangle",title:n().translator.trans("blomstra-search.admin.reindex-required.title")},n().translator.trans("blomstra-search.admin.reindex-required.detail"))},s}(o());n().initializers.add("sbp-search",function(){var a=n().data.settings["blomstra-search.active-index"],t=n().data.settings["blomstra-search.index-compatible"];a&&"v3"!==t&&(0,r.extend)(i().prototype,"availableWidgets",function(a){a.add("blomstra-search-reindex",m(d),110)}),n().extensionData.for("sbp-search").registerSetting(function(){var t=n().data.settings["blomstra-search.indexed-analyzer"];if(!a||!t)return null;var e=this.setting("blomstra-search.analyzer-language")()||"english",r=this.setting("blomstra-search.stem-exclusion")()||"",s=n().data.settings["blomstra-search.indexed-stem-exclusion"]||"";return e===t&&r===s?null:m(h(),{type:"warning",dismissible:!1,icon:"fas fa-exclamation-triangle"},n().translator.trans("blomstra-search.admin.index-settings-changed"))}).registerSetting({setting:"blomstra-search.elastic-endpoint",label:n().translator.trans("blomstra-search.admin.elastic-endpoint"),type:"input"}).registerSetting({setting:"blomstra-search.elastic-username",label:n().translator.trans("blomstra-search.admin.elastic-username"),type:"input"}).registerSetting({setting:"blomstra-search.elastic-password",label:n().translator.trans("blomstra-search.admin.elastic-password"),type:"password"}).registerSetting({setting:"blomstra-search.elastic-index",label:n().translator.trans("blomstra-search.admin.elastic-index"),default:"flarum",type:"input"}).registerSetting({setting:"blomstra-search.search-discussion-subjects",label:n().translator.trans("blomstra-search.admin.search-discussion-subjects"),type:"switch"}).registerSetting({setting:"blomstra-search.search-post-bodies",label:n().translator.trans("blomstra-search.admin.search-post-bodies"),type:"switch"}).registerSetting({setting:"blomstra-search.analyzer-language",label:n().translator.trans("blomstra-search.admin.analyzer.label"),help:n().translator.trans("blomstra-search.admin.analyzer.help"),type:"select",options:{arabic:"Arabic",armenian:"Armenian",basque:"Basque",bengali:"Bengali",brazilian:"Brazilian Portuguese",bulgarian:"Bulgarian",catalan:"Catalan",cjk:"CJK (Chinese, Japanese, Korean)",czech:"Czech",danish:"Danish",dutch:"Dutch",english:"English",estonian:"Estonian",finnish:"Finnish",french:"French",galician:"Galician",german:"German",greek:"Greek",hindi:"Hindi",hungarian:"Hungarian",indonesian:"Indonesian",irish:"Irish",italian:"Italian",latvian:"Latvian",lithuanian:"Lithuanian",norwegian:"Norwegian",persian:"Persian",portuguese:"Portuguese",romanian:"Romanian",russian:"Russian",sorani:"Sorani (Kurdish)",spanish:"Spanish",standard:"Standard (generic)",swedish:"Swedish",turkish:"Turkish",thai:"Thai"},default:"english"}).registerSetting(function(){var a="cjk"===(this.setting("blomstra-search.analyzer-language")()||"english");return this.buildSettingComponent({setting:"blomstra-search.stem-exclusion",type:"textarea",label:n().translator.trans("blomstra-search.admin.settings.stem-exclusion.label"),help:n().translator.trans("blomstra-search.admin.settings.stem-exclusion.help"),disabled:a})}).registerSetting(function(){var a="cjk"===(this.setting("blomstra-search.analyzer-language")()||"english");return this.buildSettingComponent({setting:"blomstra-search.min-search-length",label:n().translator.trans("blomstra-search.admin.min-search-length.label"),help:n().translator.trans("blomstra-search.admin.min-search-length.help"),type:"select",options:{1:"1",2:"2",3:"3",4:"4"},default:n().data.settings["blomstra-search.min-search-length"],disabled:a})})})})(),module.exports={}})(); (()=>{var a={n:t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return a.d(e,{a:e}),e},d:(t,e)=>{for(var n in e)a.o(e,n)&&!a.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},o:(a,t)=>Object.prototype.hasOwnProperty.call(a,t)};(()=>{"use strict";function t(a,e){return t=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(a,t){return a.__proto__=t,a},t(a,e)}const e=flarum.core.compat["admin/app"];var n=a.n(e);const r=flarum.core.compat["common/extend"],s=flarum.core.compat["admin/components/DashboardPage"];var i=a.n(s);const l=flarum.core.compat["admin/components/DashboardWidget"];var o=a.n(l);const c=flarum.core.compat["common/components/Alert"];var h=a.n(c),d=function(a){var e,r;function s(){return a.apply(this,arguments)||this}r=a,(e=s).prototype=Object.create(r.prototype),e.prototype.constructor=e,t(e,r);var i=s.prototype;return i.className=function(){return"ReindexWarningWidget"},i.content=function(){return m(h(),{type:"warning",dismissible:!1,icon:"fas fa-exclamation-triangle",title:n().translator.trans("blomstra-search.admin.reindex-required.title")},n().translator.trans("blomstra-search.admin.reindex-required.detail"))},s}(o());n().initializers.add("blomstra-search",function(){var a=n().data.settings["blomstra-search.active-index"],t=n().data.settings["blomstra-search.index-compatible"];a&&"v3"!==t&&(0,r.extend)(i().prototype,"availableWidgets",function(a){a.add("blomstra-search-reindex",m(d),110)}),n().extensionData.for("blomstra-search").registerSetting(function(){var t=n().data.settings["blomstra-search.indexed-analyzer"];if(!a||!t)return null;var e=this.setting("blomstra-search.analyzer-language")()||"english",r=this.setting("blomstra-search.stem-exclusion")()||"",s=n().data.settings["blomstra-search.indexed-stem-exclusion"]||"";return e===t&&r===s?null:m(h(),{type:"warning",dismissible:!1,icon:"fas fa-exclamation-triangle"},n().translator.trans("blomstra-search.admin.index-settings-changed"))}).registerSetting({setting:"blomstra-search.elastic-endpoint",label:n().translator.trans("blomstra-search.admin.elastic-endpoint"),type:"input"}).registerSetting({setting:"blomstra-search.elastic-username",label:n().translator.trans("blomstra-search.admin.elastic-username"),type:"input"}).registerSetting({setting:"blomstra-search.elastic-password",label:n().translator.trans("blomstra-search.admin.elastic-password"),type:"password"}).registerSetting({setting:"blomstra-search.elastic-index",label:n().translator.trans("blomstra-search.admin.elastic-index"),default:"flarum",type:"input"}).registerSetting({setting:"blomstra-search.search-discussion-subjects",label:n().translator.trans("blomstra-search.admin.search-discussion-subjects"),type:"switch"}).registerSetting({setting:"blomstra-search.search-post-bodies",label:n().translator.trans("blomstra-search.admin.search-post-bodies"),type:"switch"}).registerSetting({setting:"blomstra-search.analyzer-language",label:n().translator.trans("blomstra-search.admin.analyzer.label"),help:n().translator.trans("blomstra-search.admin.analyzer.help"),type:"select",options:{arabic:"Arabic",armenian:"Armenian",basque:"Basque",bengali:"Bengali",brazilian:"Brazilian Portuguese",bulgarian:"Bulgarian",catalan:"Catalan",cjk:"CJK (Chinese, Japanese, Korean)",czech:"Czech",danish:"Danish",dutch:"Dutch",english:"English",estonian:"Estonian",finnish:"Finnish",french:"French",galician:"Galician",german:"German",greek:"Greek",hindi:"Hindi",hungarian:"Hungarian",indonesian:"Indonesian",irish:"Irish",italian:"Italian",latvian:"Latvian",lithuanian:"Lithuanian",norwegian:"Norwegian",persian:"Persian",portuguese:"Portuguese",romanian:"Romanian",russian:"Russian",sorani:"Sorani (Kurdish)",spanish:"Spanish",swedish:"Swedish",turkish:"Turkish",thai:"Thai"},default:"english"}).registerSetting(function(){var a="cjk"===(this.setting("blomstra-search.analyzer-language")()||"english");return this.buildSettingComponent({setting:"blomstra-search.stem-exclusion",type:"textarea",label:n().translator.trans("blomstra-search.admin.settings.stem-exclusion.label"),help:n().translator.trans("blomstra-search.admin.settings.stem-exclusion.help"),disabled:a})}).registerSetting(function(){var a="cjk"===(this.setting("blomstra-search.analyzer-language")()||"english");return this.buildSettingComponent({setting:"blomstra-search.min-search-length",label:n().translator.trans("blomstra-search.admin.min-search-length.label"),help:n().translator.trans("blomstra-search.admin.min-search-length.help"),type:"select",options:{1:"1",2:"2",3:"3",4:"4"},default:n().data.settings["blomstra-search.min-search-length"],disabled:a})})})})(),module.exports={}})();
//# sourceMappingURL=admin.js.map //# sourceMappingURL=admin.js.map

File diff suppressed because one or more lines are too long

2
js/dist/forum.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */

File diff suppressed because one or more lines are too long

View File

@ -25,7 +25,7 @@ class ReindexWarningWidget extends DashboardWidget {
} }
} }
app.initializers.add('sbp-search', () => { app.initializers.add('blomstra-search', () => {
const activeIndex = app.data.settings['blomstra-search.active-index']; const activeIndex = app.data.settings['blomstra-search.active-index'];
const compatVersion = app.data.settings['blomstra-search.index-compatible']; const compatVersion = app.data.settings['blomstra-search.index-compatible'];
@ -75,7 +75,7 @@ app.initializers.add('sbp-search', () => {
}; };
app.extensionData app.extensionData
.for('sbp-search') .for('blomstra-search')
.registerSetting(function (this: any) { .registerSetting(function (this: any) {
// No index yet, or first build predates this tracking — stay silent. // No index yet, or first build predates this tracking — stay silent.
const indexedAnalyzer = app.data.settings['blomstra-search.indexed-analyzer']; const indexedAnalyzer = app.data.settings['blomstra-search.indexed-analyzer'];

View File

@ -6,9 +6,10 @@ import DiscussionListState from 'flarum/forum/states/DiscussionListState';
export default function extendDiscussionState() { export default function extendDiscussionState() {
override(DiscussionListState.prototype, 'loadPage', async function (this: DiscussionListState, original, page: number = 1) { override(DiscussionListState.prototype, 'loadPage', async function (this: DiscussionListState, original, page: number = 1) {
const preloaded = app.data.apiDocument || null;
// If existing payload is given or no search is made, fallback on native page. // If existing payload is given or no search is made, fallback on native page.
if (!this.requestParams()?.filter?.q) return original.call(this, page); if (preloaded || !this.requestParams()?.filter?.q) return original.call(this, page);
const params = this.requestParams(); const params = this.requestParams();
params.page = { params.page = {
@ -16,13 +17,18 @@ export default function extendDiscussionState() {
...params.page, ...params.page,
}; };
// Default to latest sort when searching without an explicit sort parameter.
// Matches the backend default and keeps the dropdown label in sync.
if (!params.sort) {
params.sort = '-lastPostedAt';
}
if (Array.isArray(params.include)) { if (Array.isArray(params.include)) {
params.include = params.include.join(','); params.include = params.include.join(',');
} }
// Construct API search URI // Construct API search URI
const url = `${app.forum.attribute('apiUrl')}/sbp/search/${this.type}`; const url = `${app.forum.attribute('apiUrl')}/blomstra/search/${this.type}`;
// Make API GET request // Make API GET request
const results = await app.request({ params, url, method: 'GET' }); const results = await app.request({ params, url, method: 'GET' });

View File

@ -23,6 +23,9 @@ export default class DiscussionsSearchSource implements SearchSource {
async search(query: string): Promise<void> { async search(query: string): Promise<void> {
// Suppress ES instant results when "search within comments" is ON; // Suppress ES instant results when "search within comments" is ON;
// only usernames are shown in that mode. // only usernames are shown in that mode.
if (app.forum.attribute('blomstraSearchPostBodies')) {
return;
}
query = query.toLowerCase(); query = query.toLowerCase();
@ -35,7 +38,7 @@ export default class DiscussionsSearchSource implements SearchSource {
}; };
// Construct API search URI // Construct API search URI
const url = `${app.forum.attribute('apiUrl')}/sbp/search/${this.type}`; const url = `${app.forum.attribute('apiUrl')}/blomstra/search/${this.type}`;
// Make API GET request // Make API GET request
const results = await app.request({ params, url, method: 'GET' }); const results = await app.request({ params, url, method: 'GET' });
@ -69,7 +72,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, sort: 'latest' })}>
{app.translator.trans('core.forum.search.all_discussions_button', { query })} {app.translator.trans('core.forum.search.all_discussions_button', { query })}
</LinkButton> </LinkButton>
</li>, </li>,

View File

@ -10,7 +10,7 @@
* *
*/ */
namespace Sbp\Search\Api; namespace Blomstra\Search\Api;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;

View File

@ -10,9 +10,9 @@
* *
*/ */
namespace Sbp\Search\Api\Controllers; namespace Blomstra\Search\Api\Controllers;
use Sbp\Search\Commands\BuildCommand; use Blomstra\Search\Commands\BuildCommand;
use Laminas\Diactoros\Response\EmptyResponse; use Laminas\Diactoros\Response\EmptyResponse;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;

View File

@ -10,15 +10,15 @@
* *
*/ */
namespace Sbp\Search\Api\Controllers; namespace Blomstra\Search\Api\Controllers;
use Sbp\Search\Elasticsearch\HasChildQuery; use Blomstra\Search\Elasticsearch\HasChildQuery;
use Sbp\Search\Elasticsearch\MatchPhraseQuery; use Blomstra\Search\Elasticsearch\MatchPhraseQuery;
use Sbp\Search\Elasticsearch\MatchQuery; use Blomstra\Search\Elasticsearch\MatchQuery;
use Sbp\Search\Elasticsearch\TermsQuery; use Blomstra\Search\Elasticsearch\TermsQuery;
use Sbp\Search\Searchers\CommentPostSearcher; use Blomstra\Search\Searchers\CommentPostSearcher;
use Sbp\Search\Searchers\DiscussionSearcher; use Blomstra\Search\Searchers\DiscussionSearcher;
use Sbp\Search\Searchers\Searcher; use Blomstra\Search\Searchers\Searcher;
use Elasticsearch\Client; use Elasticsearch\Client;
use Flarum\Api\Controller\ListDiscussionsController; use Flarum\Api\Controller\ListDiscussionsController;
use Flarum\Api\Serializer\DiscussionSerializer; use Flarum\Api\Serializer\DiscussionSerializer;
@ -37,7 +37,7 @@ use Illuminate\Support\Str;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Spatie\ElasticsearchQueryBuilder\Builder; use Spatie\ElasticsearchQueryBuilder\Builder;
use Sbp\Search\Elasticsearch\BoolQuery; use Blomstra\Search\Elasticsearch\BoolQuery;
use Spatie\ElasticsearchQueryBuilder\Queries\TermQuery; use Spatie\ElasticsearchQueryBuilder\Queries\TermQuery;
use Spatie\ElasticsearchQueryBuilder\Sorts\Sort; use Spatie\ElasticsearchQueryBuilder\Sorts\Sort;
use Tobscure\JsonApi\Document; use Tobscure\JsonApi\Document;
@ -176,7 +176,7 @@ class SearchController extends ListDiscussionsController
}); });
$document->addPaginationLinks( $document->addPaginationLinks(
$this->uri->to('api')->route('sbp.search', ['type' => 'discussions']), $this->uri->to('api')->route('blomstra.search', ['type' => 'discussions']),
$request->getQueryParams(), $request->getQueryParams(),
$offset, $offset,
$limit, $limit,
@ -265,7 +265,7 @@ class SearchController extends ListDiscussionsController
]); ]);
$document->addPaginationLinks( $document->addPaginationLinks(
$this->uri->to('api')->route('sbp.search', ['type' => 'discussions']), $this->uri->to('api')->route('blomstra.search', ['type' => 'discussions']),
$request->getQueryParams(), $request->getQueryParams(),
$offset, $offset,
$limit, $limit,
@ -294,30 +294,11 @@ class SearchController extends ListDiscussionsController
return $discussions; return $discussions;
} }
protected function buildAutocompleteQuery(string $search): MatchQuery
protected function buildAutocompleteQuery(string $search): BoolQuery
{ {
$query = BoolQuery::create(); return (new MatchQuery('title.autocomplete', $search))
// Title prefix match (stronger)
$query->add(
(new MatchQuery('title.autocomplete', $search))
->operator('and') ->operator('and')
->boost(1.0), ->boost(1.0);
'should'
);
// Content prefix match (weaker)
$query->add(
(new MatchQuery('content.autocomplete', $search))
->operator('and')
->boost(0.4),
'should'
);
$query->minimumShouldMatch(1);
return $query;
} }
/** /**
@ -336,31 +317,6 @@ class SearchController extends ListDiscussionsController
protected function buildTextQuery(string $search, User $actor, bool $needsScoring = false): BoolQuery protected function buildTextQuery(string $search, User $actor, bool $needsScoring = false): BoolQuery
{ {
$textQuery = BoolQuery::create(); $textQuery = BoolQuery::create();
// Strong direct title match for the full search page.
$textQuery->add(
(new MatchQuery('title', $search))
->operator('and')
->boost(4.0),
'should'
);
// Low-weight prefix match on title.autocomplete for partial title hits.
$textQuery->add(
(new MatchQuery('title.autocomplete', $search))
->operator('and')
->boost(0.35),
'should'
);
// Low-weight prefix match on title.autocomplete for the full search page.
// Exact / normal title matches should still rank higher than prefix-only hits.
$textQuery->add(
(new MatchQuery('title.autocomplete', $search))
->operator('and')
->boost(0.35),
'should'
);
if ($this->discussionSearcher?->enabled()) { if ($this->discussionSearcher?->enabled()) {
$textQuery->add($this->buildShouldClauses($search, $this->discussionSearcher->boost()), 'should'); $textQuery->add($this->buildShouldClauses($search, $this->discussionSearcher->boost()), 'should');
@ -396,7 +352,6 @@ class SearchController extends ListDiscussionsController
} }
if ($this->matchWords) { if ($this->matchWords) {
$should->add((new MatchQuery('content', $search))->operator('and')->boost(1.8 * $boost), 'should'); $should->add((new MatchQuery('content', $search))->operator('and')->boost(1.8 * $boost), 'should');
$should->add((new MatchQuery('content.autocomplete', $search))->operator('and')->boost(0.30 * $boost), 'should');
} }
return $should; return $should;

View File

@ -10,11 +10,11 @@
* *
*/ */
namespace Sbp\Search\Commands; namespace Blomstra\Search\Commands;
use Sbp\Search\Jobs\Job; use Blomstra\Search\Jobs\Job;
use Sbp\Search\Jobs\UpdateSearchJob; use Blomstra\Search\Jobs\UpdateSearchJob;
use Sbp\Search\Seeders\Seeder; use Blomstra\Search\Seeders\Seeder;
use Elasticsearch\Client; use Elasticsearch\Client;
use Elasticsearch\Common\Exceptions\ElasticsearchException; use Elasticsearch\Common\Exceptions\ElasticsearchException;
use Flarum\Settings\SettingsRepositoryInterface; use Flarum\Settings\SettingsRepositoryInterface;
@ -581,42 +581,29 @@ protected function mappingProperties(): array
]; ];
} }
$contentMapping = [
'type' => 'text',
'analyzer' => 'flarum_analyzer',
];
// Only add the autocomplete subfield for non-CJK languages.
if ($language !== 'cjk') {
$contentMapping['fields'] = [
'autocomplete' => [
'type' => 'text',
'analyzer' => 'flarum_title_autocomplete',
'search_analyzer' => 'flarum_title_search',
],
];
}
return [ return [
'_meta' => ['index_compat_version' => self::INDEX_COMPAT_VERSION], '_meta' => ['index_compat_version' => self::INDEX_COMPAT_VERSION],
'properties' => [ 'properties' => [
'join_field' => ['type' => 'join', 'relations' => ['discussion' => 'post']], 'join_field' => ['type' => 'join', 'relations' => ['discussion' => 'post']],
'discussion_id' => ['type' => 'integer'], 'discussion_id' => ['type' => 'integer'],
'content' => $contentMapping, 'content' => ['type' => 'text', 'analyzer' => 'flarum_analyzer'],
'title' => $titleMapping, 'title' => $titleMapping,
'rawId' => ['type' => 'integer'], 'rawId' => ['type' => 'integer'],
'created_at' => ['type' => 'date'], 'created_at' => ['type' => 'date'],
'updated_at' => ['type' => 'date'], 'updated_at' => ['type' => 'date'],
'is_private' => ['type' => 'boolean'], 'is_private' => ['type' => 'boolean'],
'user_id' => ['type' => 'integer'],
'groups' => ['type' => 'keyword'],
'comment_count' => ['type' => 'integer'],
'tags' => ['type' => 'keyword'],
'is_sticky' => ['type' => 'boolean'], 'is_sticky' => ['type' => 'boolean'],
'groups' => ['type' => 'integer'],
'tags' => ['type' => 'integer'],
'recipient_groups' => ['type' => 'integer'],
'recipient_users' => ['type' => 'integer'],
'comment_count' => ['type' => 'integer'],
'view_count' => ['type' => 'integer'],
'is_hidden' => ['type' => 'boolean'], 'is_hidden' => ['type' => 'boolean'],
], ],
]; ];
} }
/** /**
* Query ES for rawIds already indexed in $targetIndex for the given $joinRelation and ID range. * Query ES for rawIds already indexed in $targetIndex for the given $joinRelation and ID range.
* Retries on transient ES failures (NoNodesAvailableException / other ElasticsearchException) * Retries on transient ES failures (NoNodesAvailableException / other ElasticsearchException)

View File

@ -10,7 +10,7 @@
* *
*/ */
namespace Sbp\Search\Elasticsearch; namespace Blomstra\Search\Elasticsearch;
class BoolQuery extends \Spatie\ElasticsearchQueryBuilder\Queries\BoolQuery class BoolQuery extends \Spatie\ElasticsearchQueryBuilder\Queries\BoolQuery
{ {

View File

@ -10,7 +10,7 @@
* *
*/ */
namespace Sbp\Search\Elasticsearch; namespace Blomstra\Search\Elasticsearch;
use Spatie\ElasticsearchQueryBuilder\Queries\Query; use Spatie\ElasticsearchQueryBuilder\Queries\Query;

View File

@ -10,7 +10,7 @@
* *
*/ */
namespace Sbp\Search\Elasticsearch; namespace Blomstra\Search\Elasticsearch;
use Spatie\ElasticsearchQueryBuilder\Queries\MatchQuery; use Spatie\ElasticsearchQueryBuilder\Queries\MatchQuery;

View File

@ -10,7 +10,7 @@
* *
*/ */
namespace Sbp\Search\Elasticsearch; namespace Blomstra\Search\Elasticsearch;
class MatchQuery extends \Spatie\ElasticsearchQueryBuilder\Queries\MatchQuery class MatchQuery extends \Spatie\ElasticsearchQueryBuilder\Queries\MatchQuery
{ {

View File

@ -10,7 +10,7 @@
* *
*/ */
namespace Sbp\Search\Elasticsearch; namespace Blomstra\Search\Elasticsearch;
use Spatie\ElasticsearchQueryBuilder\Queries\Query; use Spatie\ElasticsearchQueryBuilder\Queries\Query;

View File

@ -10,7 +10,7 @@
* *
*/ */
namespace Sbp\Search\Elasticsearch; namespace Blomstra\Search\Elasticsearch;
use Spatie\ElasticsearchQueryBuilder\Queries\Query; use Spatie\ElasticsearchQueryBuilder\Queries\Query;

View File

@ -10,7 +10,7 @@
* *
*/ */
namespace Sbp\Search\Elasticsearch; namespace Blomstra\Search\Elasticsearch;
class WildcardQuery extends \Spatie\ElasticsearchQueryBuilder\Queries\WildcardQuery class WildcardQuery extends \Spatie\ElasticsearchQueryBuilder\Queries\WildcardQuery
{ {

View File

@ -10,7 +10,7 @@
* *
*/ */
namespace Sbp\Search\Exceptions; namespace Blomstra\Search\Exceptions;
use Throwable; use Throwable;

View File

@ -10,7 +10,7 @@
* *
*/ */
namespace Sbp\Search\Jobs; namespace Blomstra\Search\Jobs;
use Elasticsearch\Client; use Elasticsearch\Client;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;

View File

@ -10,9 +10,9 @@
* *
*/ */
namespace Sbp\Search\Jobs; namespace Blomstra\Search\Jobs;
use Sbp\Search\Seeders\Seeder; use Blomstra\Search\Seeders\Seeder;
use Flarum\Queue\AbstractJob; use Flarum\Queue\AbstractJob;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;

View File

@ -10,9 +10,9 @@
* *
*/ */
namespace Sbp\Search\Jobs; namespace Blomstra\Search\Jobs;
use Sbp\Search\Exceptions\SeedingException; use Blomstra\Search\Exceptions\SeedingException;
use Elasticsearch\Client; use Elasticsearch\Client;
use Illuminate\Database\Eloquent\Collection as EloquentCollection; use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;

View File

@ -10,7 +10,7 @@
* *
*/ */
namespace Sbp\Search\Jobs; namespace Blomstra\Search\Jobs;
use Elasticsearch\Client; use Elasticsearch\Client;
use Flarum\Api\Serializer\DiscussionSerializer; use Flarum\Api\Serializer\DiscussionSerializer;

View File

@ -10,12 +10,12 @@
* *
*/ */
namespace Sbp\Search; namespace Blomstra\Search;
use Sbp\Search\Jobs\DeletingJob; use Blomstra\Search\Jobs\DeletingJob;
use Sbp\Search\Jobs\Job; use Blomstra\Search\Jobs\Job;
use Sbp\Search\Jobs\UpdateSearchJob; use Blomstra\Search\Jobs\UpdateSearchJob;
use Sbp\Search\Jobs\ViewsSearchJob; use Blomstra\Search\Jobs\ViewsSearchJob;
use Elasticsearch\Client as Elastic; use Elasticsearch\Client as Elastic;
use Elasticsearch\ClientBuilder; use Elasticsearch\ClientBuilder;
use Flarum\Api\Client; use Flarum\Api\Client;

View File

@ -10,7 +10,7 @@
* *
*/ */
namespace Sbp\Search\Save; namespace Blomstra\Search\Save;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;

View File

@ -10,9 +10,9 @@
* *
*/ */
namespace Sbp\Search\Searchers; namespace Blomstra\Search\Searchers;
use Sbp\Search\Seeders\CommentSeeder; use Blomstra\Search\Seeders\CommentSeeder;
class CommentPostSearcher extends Searcher class CommentPostSearcher extends Searcher
{ {

View File

@ -10,9 +10,9 @@
* *
*/ */
namespace Sbp\Search\Searchers; namespace Blomstra\Search\Searchers;
use Sbp\Search\Seeders\DiscussionSeeder; use Blomstra\Search\Seeders\DiscussionSeeder;
class DiscussionSearcher extends Searcher class DiscussionSearcher extends Searcher
{ {

View File

@ -10,9 +10,9 @@
* *
*/ */
namespace Sbp\Search\Searchers; namespace Blomstra\Search\Searchers;
use Sbp\Search\Seeders\Seeder; use Blomstra\Search\Seeders\Seeder;
use Flarum\Settings\SettingsRepositoryInterface; use Flarum\Settings\SettingsRepositoryInterface;
abstract class Searcher abstract class Searcher

View File

@ -10,9 +10,9 @@
* *
*/ */
namespace Sbp\Search\Seeders; namespace Blomstra\Search\Seeders;
use Sbp\Search\Save\Document; use Blomstra\Search\Save\Document;
use Flarum\Api\Serializer\DiscussionSerializer; use Flarum\Api\Serializer\DiscussionSerializer;
use Flarum\Api\Serializer\PostSerializer; use Flarum\Api\Serializer\PostSerializer;
use Flarum\Discussion\Discussion; use Flarum\Discussion\Discussion;

View File

@ -10,9 +10,9 @@
* *
*/ */
namespace Sbp\Search\Seeders; namespace Blomstra\Search\Seeders;
use Sbp\Search\Save\Document; use Blomstra\Search\Save\Document;
use Flarum\Api\Serializer\DiscussionSerializer; use Flarum\Api\Serializer\DiscussionSerializer;
use Flarum\Discussion\Discussion; use Flarum\Discussion\Discussion;
use Flarum\Discussion\Event as Core; use Flarum\Discussion\Event as Core;

View File

@ -10,9 +10,9 @@
* *
*/ */
namespace Sbp\Search\Seeders; namespace Blomstra\Search\Seeders;
use Sbp\Search\Save\Document; use Blomstra\Search\Save\Document;
use Flarum\Extension\ExtensionManager; use Flarum\Extension\ExtensionManager;
use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;