From 29ba86484999bb336dad8b3b74f1ff09e88f9e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Klabbers?= Date: Wed, 9 Nov 2022 18:06:48 +0100 Subject: [PATCH 1/3] settings configurations --- js/src/admin/index.ts | 25 +++++++ src/Api/Controllers/SearchController.php | 93 ++++++++++-------------- src/Provider.php | 6 ++ src/Searchers/CommentPostSearcher.php | 17 +++++ src/Searchers/DiscussionSearcher.php | 22 ++++++ src/Searchers/Searcher.php | 36 +++++++++ 6 files changed, 145 insertions(+), 54 deletions(-) create mode 100644 src/Searchers/CommentPostSearcher.php create mode 100644 src/Searchers/DiscussionSearcher.php create mode 100644 src/Searchers/Searcher.php diff --git a/js/src/admin/index.ts b/js/src/admin/index.ts index ce06dab..2cb3bd7 100644 --- a/js/src/admin/index.ts +++ b/js/src/admin/index.ts @@ -78,5 +78,30 @@ app.initializers.add('blomstra-search', () => { label: app.translator.trans('blomstra-search.admin.elastic-index'), default: 'flarum', type: 'input', + }) + .registerSetting({ + setting: 'blomstra-search.search-discussion-subjects', + label: app.translator.trans('blomstra-search.admin.search-discussion-subjects'), + type: 'switch', + }) + .registerSetting({ + setting: 'blomstra-search.search-post-bodies', + label: app.translator.trans('blomstra-search.admin.search-post-bodies'), + type: 'switch', + }) + .registerSetting({ + setting: 'blomstra-search.match-sentences', + label: app.translator.trans('blomstra-search.admin.match-sentences'), + type: 'switch', + }) + .registerSetting({ + setting: 'blomstra-search.match-words', + label: app.translator.trans('blomstra-search.admin.match-words'), + type: 'switch', + }) + .registerSetting({ + setting: 'blomstra-search.match-fragments', + label: app.translator.trans('blomstra-search.admin.match-fragments'), + type: 'switch', }); }); diff --git a/src/Api/Controllers/SearchController.php b/src/Api/Controllers/SearchController.php index 644e66d..2ab4e1b 100644 --- a/src/Api/Controllers/SearchController.php +++ b/src/Api/Controllers/SearchController.php @@ -6,6 +6,7 @@ use Blomstra\Search\Elasticsearch\MatchPhraseQuery; use Blomstra\Search\Elasticsearch\MatchQuery; use Blomstra\Search\Save\Document as ElasticDocument; use Blomstra\Search\Elasticsearch\TermsQuery; +use Blomstra\Search\Searchers\Searcher; use Elasticsearch\Client; use Flarum\Api\Controller\ListDiscussionsController; use Flarum\Api\Serializer\DiscussionSerializer; @@ -14,6 +15,7 @@ use Flarum\Extension\ExtensionManager; use Flarum\Group\Group; use Flarum\Http\RequestUtil; use Flarum\Http\UrlGenerator; +use Flarum\Settings\SettingsRepositoryInterface; use Flarum\User\User; use Illuminate\Contracts\Container\Container; use Illuminate\Support\Arr; @@ -37,8 +39,19 @@ class SearchController extends ListDiscussionsController 'commentCount' => 'comment_count' ]; - public function __construct(protected Client $elastic, protected UrlGenerator $uri) - {} + protected iterable $searchers; + protected bool $matchSentences; + protected bool $matchWords; + protected bool $matchFragments; + + public function __construct(protected Client $elastic, protected UrlGenerator $uri, Container $container, SettingsRepositoryInterface $settings) + { + $this->searchers = $container->tagged('blomstra.search.searchers'); + + $this->matchSentences = (bool) $settings->get('blomstra-search.match-sentences', true); + $this->matchWords = (bool) $settings->get('blomstra-search.match-words', true); + $this->matchFragments = (bool) $settings->get('blomstra-search.match-fragments', true); + } protected function data(ServerRequestInterface $request, Document $document) { @@ -59,13 +72,10 @@ class SearchController extends ListDiscussionsController $filterQuery = BoolQuery::create(); if (! empty($search)) { - $filterQuery - // @todo commented out to use only partial matching for now - ->add($this->sentenceMatch($search)) - ->add($this->wordMatch($search, 'and')) - ->add($this->wordMatch($search, 'or')) -// ->add($this->partialMatch($search)) - ; + if ($this->matchSentences) $filterQuery->add($this->sentenceMatch($search)); + if ($this->matchWords) $filterQuery->add($this->wordMatch($search, 'and')); + if ($this->matchWords) $filterQuery->add($this->wordMatch($search, 'or')); + if ($this->matchFragments) $filterQuery->add($this->partialMatch($search)); } $builder = (new Builder($this->elastic)) @@ -221,25 +231,28 @@ class SearchController extends ListDiscussionsController return $query; } + protected function boolQuery(Query $parent, float $boost = 1) + { + /** @var Searcher $searcher */ + foreach ($this->searchers as $searcher) { + $searcher = new $searcher; + + $parent->add( + BoolQuery::create() + ->add(TermQuery::create('type', $searcher->type()), 'filter') + ->add($parent->boost($boost * $searcher->boost())), + 'should' + ); + } + + return $parent; + } + protected function sentenceMatch(string $q): Query { $query = (new MatchPhraseQuery('content', $q)); - return BoolQuery::create() - // Discussion titles - ->add( - BoolQuery::create() - ->add(TermQuery::create('type', 'discussions'), 'filter') - ->add($query->boost(2)), - 'should' - ) - // Post bodies - ->add( - BoolQuery::create() - ->add(TermQuery::create('type', 'posts'), 'filter') - ->add($query->boost(1.9)), - 'should' - ); + return $this->boolQuery($query, 2); } protected function wordMatch(string $q, string $operator = 'or') @@ -249,42 +262,14 @@ class SearchController extends ListDiscussionsController $boost = $operator === 'and' ? 1 : .8; - return BoolQuery::create() - // Discussion titles - ->add( - BoolQuery::create() - ->add(TermQuery::create('type', 'discussions'), 'filter') - ->add($query->boost($boost * 1.8)), - 'should' - ) - // Post bodies - ->add( - BoolQuery::create() - ->add(TermQuery::create('type', 'posts'), 'filter') - ->add($query->boost($boost * 1.8)), - 'should' - ); + return $this->boolQuery($query, $boost); } protected function partialMatch(string $q) { $query = (new MatchQuery('content', $q)); - return BoolQuery::create() - // Discussion titles - ->add( - BoolQuery::create() - ->add(TermQuery::create('type', 'discussions'), 'filter') - ->add(clone $query->boost(1.6)), - 'should' - ) - // Post bodies - ->add( - BoolQuery::create() - ->add(TermQuery::create('type', 'posts'), 'filter') - ->add(clone $query->boost(1.3)), - 'should' - ); + return $this->boolQuery($query, .6); } diff --git a/src/Provider.php b/src/Provider.php index 4bd7994..479eb5e 100644 --- a/src/Provider.php +++ b/src/Provider.php @@ -5,6 +5,7 @@ namespace Blomstra\Search; use Blomstra\Search\Jobs\DeletingJob; use Blomstra\Search\Jobs\Job; use Blomstra\Search\Jobs\SavingJob; +use Blomstra\Search\Searchers; use Blomstra\Search\Seeders; use Elasticsearch\Client as Elastic; use Elasticsearch\ClientBuilder; @@ -79,6 +80,11 @@ class Provider extends AbstractServiceProvider return new Api\Client($pipe); } ); + + $this->container->tag([ + Searchers\DiscussionSearcher::class, + Searchers\CommentPostSearcher::class, + ], 'blomstra.search.searchers'); } public function boot() diff --git a/src/Searchers/CommentPostSearcher.php b/src/Searchers/CommentPostSearcher.php new file mode 100644 index 0000000..cd52a5c --- /dev/null +++ b/src/Searchers/CommentPostSearcher.php @@ -0,0 +1,17 @@ +setting('blomstra-search.admin.search-post-bodies', true); + + return boolval($enabled); + } +} diff --git a/src/Searchers/DiscussionSearcher.php b/src/Searchers/DiscussionSearcher.php new file mode 100644 index 0000000..3f218d9 --- /dev/null +++ b/src/Searchers/DiscussionSearcher.php @@ -0,0 +1,22 @@ +setting('blomstra-search.search-discussion-subjects', true); + + return boolval($enabled); + } + + public function boost(): float + { + return 1.5; + } +} diff --git a/src/Searchers/Searcher.php b/src/Searchers/Searcher.php new file mode 100644 index 0000000..400d37f --- /dev/null +++ b/src/Searchers/Searcher.php @@ -0,0 +1,36 @@ +seeder; + + if (empty($seeder)) throw new \InvalidArgumentException("Implement type or add \$seeder"); + + return (new $seeder)->type(); + } + + public function enabled(): bool + { + return true; + } + + public function boost(): float + { + return 1; + } + + protected function setting(string $key, $default = null) + { + return resolve(SettingsRepositoryInterface::class)->get($key, $default); + } +} From 4bc89949ec132a6db29dcebbc992a2bfdb09c835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Klabbers?= Date: Thu, 10 Nov 2022 12:54:26 +0100 Subject: [PATCH 2/3] complete testing --- extend.php | 8 +++++- js/dist/admin.js | 2 +- js/dist/admin.js.map | 2 +- js/src/admin/index.ts | 5 ---- resources/locale/en.yml | 4 +++ src/Api/Controllers/SearchController.php | 34 ++++++++++++------------ src/Searchers/CommentPostSearcher.php | 2 +- 7 files changed, 31 insertions(+), 26 deletions(-) diff --git a/extend.php b/extend.php index 012f9e6..8d054d3 100644 --- a/extend.php +++ b/extend.php @@ -19,5 +19,11 @@ return [ ->put('/blomstra/search/index', 'blomstra.search.index', Api\Controllers\IndexController::class), (new Flarum\Console) - ->command(Commands\BuildCommand::class) + ->command(Commands\BuildCommand::class), + + (new Flarum\Settings) + ->default('blomstra-search.search-discussion-subjects', true) + ->default('blomstra-search.search-post-bodies', true) + ->default('blomstra-search.match-sentences', true) + ->default('blomstra-search.match-words', true) ]; diff --git a/js/dist/admin.js b/js/dist/admin.js index f129413..85c91d9 100644 --- a/js/dist/admin.js +++ b/js/dist/admin.js @@ -1,2 +1,2 @@ -(()=>{var a={n:e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return a.d(t,{a:t}),t},d:(e,t)=>{for(var r in t)a.o(t,r)&&!a.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},o:(a,e)=>Object.prototype.hasOwnProperty.call(a,e),r:a=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(a,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(a,"__esModule",{value:!0})}},e={};(()=>{"use strict";a.r(e);const t=flarum.core.compat["admin/app"];var r=a.n(t);r().initializers.add("blomstra-search",(function(){var a=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((function(e){a.set(e,e)})),r().extensionData.for("blomstra-search").registerSetting({setting:"blomstra-search.elastic-endpoint",label:r().translator.trans("blomstra-search.admin.elastic-endpoint"),type:"input"}).registerSetting({setting:"blomstra-search.elastic-username",label:r().translator.trans("blomstra-search.admin.elastic-username"),type:"input"}).registerSetting({setting:"blomstra-search.elastic-password",label:r().translator.trans("blomstra-search.admin.elastic-password"),type:"password"}).registerSetting({setting:"blomstra-search.elastic-index",label:r().translator.trans("blomstra-search.admin.elastic-index"),default:"flarum",type:"input"}).registerSetting({setting:"blomstra-search.analyzer-language",label:r().translator.trans("blomstra-search.admin.analyzer.label"),help:r().translator.trans("blomstra-search.admin.analyzer.help"),type:"select",options:Object.fromEntries(a.entries()),default:"english"}).registerSetting({setting:"blomstra-search.elastic-index",label:r().translator.trans("blomstra-search.admin.elastic-index"),default:"flarum",type:"input"})}))})(),module.exports=e})(); +(()=>{var a={n:t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return a.d(e,{a:e}),e},d:(t,e)=>{for(var s in e)a.o(e,s)&&!a.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:e[s]})},o:(a,t)=>Object.prototype.hasOwnProperty.call(a,t),r:a=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(a,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(a,"__esModule",{value:!0})}},t={};(()=>{"use strict";a.r(t);const e=flarum.core.compat["admin/app"];var s=a.n(e);s().initializers.add("blomstra-search",(function(){var a=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((function(t){a.set(t,t)})),s().extensionData.for("blomstra-search").registerSetting({setting:"blomstra-search.elastic-endpoint",label:s().translator.trans("blomstra-search.admin.elastic-endpoint"),type:"input"}).registerSetting({setting:"blomstra-search.elastic-username",label:s().translator.trans("blomstra-search.admin.elastic-username"),type:"input"}).registerSetting({setting:"blomstra-search.elastic-password",label:s().translator.trans("blomstra-search.admin.elastic-password"),type:"password"}).registerSetting({setting:"blomstra-search.elastic-index",label:s().translator.trans("blomstra-search.admin.elastic-index"),default:"flarum",type:"input"}).registerSetting({setting:"blomstra-search.analyzer-language",label:s().translator.trans("blomstra-search.admin.analyzer.label"),help:s().translator.trans("blomstra-search.admin.analyzer.help"),type:"select",options:Object.fromEntries(a.entries()),default:"english"}).registerSetting({setting:"blomstra-search.elastic-index",label:s().translator.trans("blomstra-search.admin.elastic-index"),default:"flarum",type:"input"}).registerSetting({setting:"blomstra-search.search-discussion-subjects",label:s().translator.trans("blomstra-search.admin.search-discussion-subjects"),type:"switch"}).registerSetting({setting:"blomstra-search.search-post-bodies",label:s().translator.trans("blomstra-search.admin.search-post-bodies"),type:"switch"}).registerSetting({setting:"blomstra-search.match-sentences",label:s().translator.trans("blomstra-search.admin.match-sentences"),type:"switch"}).registerSetting({setting:"blomstra-search.match-words",label:s().translator.trans("blomstra-search.admin.match-words"),type:"switch"})}))})(),module.exports=t})(); //# sourceMappingURL=admin.js.map \ No newline at end of file diff --git a/js/dist/admin.js.map b/js/dist/admin.js.map index e9878c3..83b6f6c 100644 --- a/js/dist/admin.js.map +++ b/js/dist/admin.js.map @@ -1 +1 @@ -{"version":3,"file":"admin.js","mappings":"MACA,IAAIA,EAAsB,CCA1BA,EAAyBC,IACxB,IAAIC,EAASD,GAAUA,EAAOE,WAC7B,IAAOF,EAAiB,QACxB,IAAM,EAEP,OADAD,EAAoBI,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,GCLRF,EAAwB,CAACM,EAASC,KACjC,IAAI,IAAIC,KAAOD,EACXP,EAAoBS,EAAEF,EAAYC,KAASR,EAAoBS,EAAEH,EAASE,IAC5EE,OAAOC,eAAeL,EAASE,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,MCJ3ER,EAAwB,CAACc,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,GCClFf,EAAyBM,IACH,oBAAXa,QAA0BA,OAAOC,aAC1CV,OAAOC,eAAeL,EAASa,OAAOC,YAAa,CAAEC,MAAO,WAE7DX,OAAOC,eAAeL,EAAS,aAAc,CAAEe,OAAO,M,+BCLvD,MAAM,EAA+BC,OAAOC,KAAKC,OAAO,a,aCExDC,IAAAA,aAAAA,IAAqB,mBAAmB,WACtC,IAAMC,EAAY,IAAIC,IACtB,CACE,SACA,WACA,SACA,UACA,YACA,YACA,UACA,MACA,QACA,SACA,QACA,UACA,WACA,UACA,SACA,WACA,SACA,QACA,QACA,YACA,aACA,QACA,UACA,UACA,aACA,YACA,UACA,aACA,WACA,UACA,SACA,UACA,UACA,UACA,QACAC,SAAQ,SAACC,GACTH,EAAUI,IAAID,EAAUA,MAG1BJ,IAAAA,cAAAA,IACO,mBACJM,gBAAgB,CACfC,QAAS,mCACTC,MAAOR,IAAAA,WAAAA,MAAqB,0CAC5BS,KAAM,UAEPH,gBAAgB,CACfC,QAAS,mCACTC,MAAOR,IAAAA,WAAAA,MAAqB,0CAC5BS,KAAM,UAEPH,gBAAgB,CACfC,QAAS,mCACTC,MAAOR,IAAAA,WAAAA,MAAqB,0CAC5BS,KAAM,aAEPH,gBAAgB,CACfC,QAAS,gCACTC,MAAOR,IAAAA,WAAAA,MAAqB,uCAC5BU,QAAS,SACTD,KAAM,UAEPH,gBAAgB,CACfC,QAAS,oCACTC,MAAOR,IAAAA,WAAAA,MAAqB,wCAC5BW,KAAMX,IAAAA,WAAAA,MAAqB,uCAC3BS,KAAM,SACNG,QAAS3B,OAAO4B,YAAYZ,EAAUa,WACtCJ,QAAS,YAEVJ,gBAAgB,CACfC,QAAS,gCACTC,MAAOR,IAAAA,WAAAA,MAAqB,uCAC5BU,QAAS,SACTD,KAAM,c","sources":["webpack://@blomstra/realtime/webpack/bootstrap","webpack://@blomstra/realtime/webpack/runtime/compat get default export","webpack://@blomstra/realtime/webpack/runtime/define property getters","webpack://@blomstra/realtime/webpack/runtime/hasOwnProperty shorthand","webpack://@blomstra/realtime/webpack/runtime/make namespace object","webpack://@blomstra/realtime/external root \"flarum.core.compat['admin/app']\"","webpack://@blomstra/realtime/./src/admin/index.ts"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['admin/app'];","import app from 'flarum/admin/app';\n\napp.initializers.add('blomstra-search', () => {\n const languages = new Map();\n [\n 'arabic',\n 'armenian',\n 'basque',\n 'bengali',\n 'brazilian',\n 'bulgarian',\n 'catalan',\n 'cjk',\n 'czech',\n 'danish',\n 'dutch',\n 'english',\n 'estonian',\n 'finnish',\n 'french',\n 'galician',\n 'german',\n 'greek',\n 'hindi',\n 'hungarian',\n 'indonesian',\n 'irish',\n 'italian',\n 'latvian',\n 'lithuanian',\n 'norwegian',\n 'persian',\n 'portuguese',\n 'romanian',\n 'russian',\n 'sorani',\n 'spanish',\n 'swedish',\n 'turkish',\n 'thai',\n ].forEach((language) => {\n languages.set(language, language);\n });\n\n app.extensionData\n .for('blomstra-search')\n .registerSetting({\n setting: 'blomstra-search.elastic-endpoint',\n label: app.translator.trans('blomstra-search.admin.elastic-endpoint'),\n type: 'input',\n })\n .registerSetting({\n setting: 'blomstra-search.elastic-username',\n label: app.translator.trans('blomstra-search.admin.elastic-username'),\n type: 'input',\n })\n .registerSetting({\n setting: 'blomstra-search.elastic-password',\n label: app.translator.trans('blomstra-search.admin.elastic-password'),\n type: 'password',\n })\n .registerSetting({\n setting: 'blomstra-search.elastic-index',\n label: app.translator.trans('blomstra-search.admin.elastic-index'),\n default: 'flarum',\n type: 'input',\n })\n .registerSetting({\n setting: 'blomstra-search.analyzer-language',\n label: app.translator.trans('blomstra-search.admin.analyzer.label'),\n help: app.translator.trans('blomstra-search.admin.analyzer.help'),\n type: 'select',\n options: Object.fromEntries(languages.entries()),\n default: 'english',\n })\n .registerSetting({\n setting: 'blomstra-search.elastic-index',\n label: app.translator.trans('blomstra-search.admin.elastic-index'),\n default: 'flarum',\n type: 'input',\n });\n});\n"],"names":["__webpack_require__","module","getter","__esModule","d","a","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","flarum","core","compat","app","languages","Map","forEach","language","set","registerSetting","setting","label","type","default","help","options","fromEntries","entries"],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"admin.js","mappings":"MACA,IAAIA,EAAsB,CCA1BA,EAAyBC,IACxB,IAAIC,EAASD,GAAUA,EAAOE,WAC7B,IAAOF,EAAiB,QACxB,IAAM,EAEP,OADAD,EAAoBI,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,GCLRF,EAAwB,CAACM,EAASC,KACjC,IAAI,IAAIC,KAAOD,EACXP,EAAoBS,EAAEF,EAAYC,KAASR,EAAoBS,EAAEH,EAASE,IAC5EE,OAAOC,eAAeL,EAASE,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,MCJ3ER,EAAwB,CAACc,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,GCClFf,EAAyBM,IACH,oBAAXa,QAA0BA,OAAOC,aAC1CV,OAAOC,eAAeL,EAASa,OAAOC,YAAa,CAAEC,MAAO,WAE7DX,OAAOC,eAAeL,EAAS,aAAc,CAAEe,OAAO,M,+BCLvD,MAAM,EAA+BC,OAAOC,KAAKC,OAAO,a,aCExDC,IAAAA,aAAAA,IAAqB,mBAAmB,WACtC,IAAMC,EAAY,IAAIC,IACtB,CACE,SACA,WACA,SACA,UACA,YACA,YACA,UACA,MACA,QACA,SACA,QACA,UACA,WACA,UACA,SACA,WACA,SACA,QACA,QACA,YACA,aACA,QACA,UACA,UACA,aACA,YACA,UACA,aACA,WACA,UACA,SACA,UACA,UACA,UACA,QACAC,SAAQ,SAACC,GACTH,EAAUI,IAAID,EAAUA,MAG1BJ,IAAAA,cAAAA,IACO,mBACJM,gBAAgB,CACfC,QAAS,mCACTC,MAAOR,IAAAA,WAAAA,MAAqB,0CAC5BS,KAAM,UAEPH,gBAAgB,CACfC,QAAS,mCACTC,MAAOR,IAAAA,WAAAA,MAAqB,0CAC5BS,KAAM,UAEPH,gBAAgB,CACfC,QAAS,mCACTC,MAAOR,IAAAA,WAAAA,MAAqB,0CAC5BS,KAAM,aAEPH,gBAAgB,CACfC,QAAS,gCACTC,MAAOR,IAAAA,WAAAA,MAAqB,uCAC5BU,QAAS,SACTD,KAAM,UAEPH,gBAAgB,CACfC,QAAS,oCACTC,MAAOR,IAAAA,WAAAA,MAAqB,wCAC5BW,KAAMX,IAAAA,WAAAA,MAAqB,uCAC3BS,KAAM,SACNG,QAAS3B,OAAO4B,YAAYZ,EAAUa,WACtCJ,QAAS,YAEVJ,gBAAgB,CACfC,QAAS,gCACTC,MAAOR,IAAAA,WAAAA,MAAqB,uCAC5BU,QAAS,SACTD,KAAM,UAEPH,gBAAgB,CACfC,QAAS,6CACTC,MAAOR,IAAAA,WAAAA,MAAqB,oDAC5BS,KAAM,WAEPH,gBAAgB,CACfC,QAAS,qCACTC,MAAOR,IAAAA,WAAAA,MAAqB,4CAC5BS,KAAM,WAEPH,gBAAgB,CACfC,QAAS,kCACTC,MAAOR,IAAAA,WAAAA,MAAqB,yCAC5BS,KAAM,WAEPH,gBAAgB,CACfC,QAAS,8BACTC,MAAOR,IAAAA,WAAAA,MAAqB,qCAC5BS,KAAM,e","sources":["webpack://@blomstra/realtime/webpack/bootstrap","webpack://@blomstra/realtime/webpack/runtime/compat get default export","webpack://@blomstra/realtime/webpack/runtime/define property getters","webpack://@blomstra/realtime/webpack/runtime/hasOwnProperty shorthand","webpack://@blomstra/realtime/webpack/runtime/make namespace object","webpack://@blomstra/realtime/external root \"flarum.core.compat['admin/app']\"","webpack://@blomstra/realtime/./src/admin/index.ts"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['admin/app'];","import app from 'flarum/admin/app';\n\napp.initializers.add('blomstra-search', () => {\n const languages = new Map();\n [\n 'arabic',\n 'armenian',\n 'basque',\n 'bengali',\n 'brazilian',\n 'bulgarian',\n 'catalan',\n 'cjk',\n 'czech',\n 'danish',\n 'dutch',\n 'english',\n 'estonian',\n 'finnish',\n 'french',\n 'galician',\n 'german',\n 'greek',\n 'hindi',\n 'hungarian',\n 'indonesian',\n 'irish',\n 'italian',\n 'latvian',\n 'lithuanian',\n 'norwegian',\n 'persian',\n 'portuguese',\n 'romanian',\n 'russian',\n 'sorani',\n 'spanish',\n 'swedish',\n 'turkish',\n 'thai',\n ].forEach((language) => {\n languages.set(language, language);\n });\n\n app.extensionData\n .for('blomstra-search')\n .registerSetting({\n setting: 'blomstra-search.elastic-endpoint',\n label: app.translator.trans('blomstra-search.admin.elastic-endpoint'),\n type: 'input',\n })\n .registerSetting({\n setting: 'blomstra-search.elastic-username',\n label: app.translator.trans('blomstra-search.admin.elastic-username'),\n type: 'input',\n })\n .registerSetting({\n setting: 'blomstra-search.elastic-password',\n label: app.translator.trans('blomstra-search.admin.elastic-password'),\n type: 'password',\n })\n .registerSetting({\n setting: 'blomstra-search.elastic-index',\n label: app.translator.trans('blomstra-search.admin.elastic-index'),\n default: 'flarum',\n type: 'input',\n })\n .registerSetting({\n setting: 'blomstra-search.analyzer-language',\n label: app.translator.trans('blomstra-search.admin.analyzer.label'),\n help: app.translator.trans('blomstra-search.admin.analyzer.help'),\n type: 'select',\n options: Object.fromEntries(languages.entries()),\n default: 'english',\n })\n .registerSetting({\n setting: 'blomstra-search.elastic-index',\n label: app.translator.trans('blomstra-search.admin.elastic-index'),\n default: 'flarum',\n type: 'input',\n })\n .registerSetting({\n setting: 'blomstra-search.search-discussion-subjects',\n label: app.translator.trans('blomstra-search.admin.search-discussion-subjects'),\n type: 'switch',\n })\n .registerSetting({\n setting: 'blomstra-search.search-post-bodies',\n label: app.translator.trans('blomstra-search.admin.search-post-bodies'),\n type: 'switch',\n })\n .registerSetting({\n setting: 'blomstra-search.match-sentences',\n label: app.translator.trans('blomstra-search.admin.match-sentences'),\n type: 'switch',\n })\n .registerSetting({\n setting: 'blomstra-search.match-words',\n label: app.translator.trans('blomstra-search.admin.match-words'),\n type: 'switch',\n });\n});\n"],"names":["__webpack_require__","module","getter","__esModule","d","a","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","flarum","core","compat","app","languages","Map","forEach","language","set","registerSetting","setting","label","type","default","help","options","fromEntries","entries"],"sourceRoot":""} \ No newline at end of file diff --git a/js/src/admin/index.ts b/js/src/admin/index.ts index 2cb3bd7..e5dfff2 100644 --- a/js/src/admin/index.ts +++ b/js/src/admin/index.ts @@ -98,10 +98,5 @@ app.initializers.add('blomstra-search', () => { setting: 'blomstra-search.match-words', label: app.translator.trans('blomstra-search.admin.match-words'), type: 'switch', - }) - .registerSetting({ - setting: 'blomstra-search.match-fragments', - label: app.translator.trans('blomstra-search.admin.match-fragments'), - type: 'switch', }); }); diff --git a/resources/locale/en.yml b/resources/locale/en.yml index f9d4759..5d72824 100644 --- a/resources/locale/en.yml +++ b/resources/locale/en.yml @@ -9,3 +9,7 @@ blomstra-search: help: | The analyzer makes search understand stop words and undertakes language specific improvements for indexing. + search-discussion-subjects: Search inside discussion titles + search-post-bodies: Search inside comments + match-sentences: Match search term against full sentence + match-words: Match search term against full words diff --git a/src/Api/Controllers/SearchController.php b/src/Api/Controllers/SearchController.php index 2ab4e1b..0239378 100644 --- a/src/Api/Controllers/SearchController.php +++ b/src/Api/Controllers/SearchController.php @@ -39,18 +39,24 @@ class SearchController extends ListDiscussionsController 'commentCount' => 'comment_count' ]; - protected iterable $searchers; + protected Collection $searchers; protected bool $matchSentences; protected bool $matchWords; - protected bool $matchFragments; public function __construct(protected Client $elastic, protected UrlGenerator $uri, Container $container, SettingsRepositoryInterface $settings) { - $this->searchers = $container->tagged('blomstra.search.searchers'); + $this->searchers = $this->gatherSearchers($container->tagged('blomstra.search.searchers'), $container); $this->matchSentences = (bool) $settings->get('blomstra-search.match-sentences', true); $this->matchWords = (bool) $settings->get('blomstra-search.match-words', true); - $this->matchFragments = (bool) $settings->get('blomstra-search.match-fragments', true); + } + + protected function gatherSearchers(iterable $searchers, Container $container) + { + return collect($searchers) + ->map(fn ($searcher) => new $searcher) + ->filter(fn (Searcher $searcher) => $searcher->enabled()); + } protected function data(ServerRequestInterface $request, Document $document) @@ -75,7 +81,6 @@ class SearchController extends ListDiscussionsController if ($this->matchSentences) $filterQuery->add($this->sentenceMatch($search)); if ($this->matchWords) $filterQuery->add($this->wordMatch($search, 'and')); if ($this->matchWords) $filterQuery->add($this->wordMatch($search, 'or')); - if ($this->matchFragments) $filterQuery->add($this->partialMatch($search)); } $builder = (new Builder($this->elastic)) @@ -231,21 +236,23 @@ class SearchController extends ListDiscussionsController return $query; } - protected function boolQuery(Query $parent, float $boost = 1) + protected function boolQuery(Query $parent, float $boost = 1): Query { + $bool = new BoolQuery; + /** @var Searcher $searcher */ foreach ($this->searchers as $searcher) { $searcher = new $searcher; - $parent->add( + $bool->add( BoolQuery::create() ->add(TermQuery::create('type', $searcher->type()), 'filter') - ->add($parent->boost($boost * $searcher->boost())), + ->add(clone $parent->boost($boost * $searcher->boost())), 'should' ); } - return $parent; + return $bool; } protected function sentenceMatch(string $q): Query @@ -255,7 +262,7 @@ class SearchController extends ListDiscussionsController return $this->boolQuery($query, 2); } - protected function wordMatch(string $q, string $operator = 'or') + protected function wordMatch(string $q, string $operator = 'or'): Query { $query = (new MatchQuery('content', $q)) ->operator($operator); @@ -265,13 +272,6 @@ class SearchController extends ListDiscussionsController return $this->boolQuery($query, $boost); } - protected function partialMatch(string $q) - { - $query = (new MatchQuery('content', $q)); - - return $this->boolQuery($query, .6); - } - protected function getGroups(User $actor): Collection { diff --git a/src/Searchers/CommentPostSearcher.php b/src/Searchers/CommentPostSearcher.php index cd52a5c..a2f575b 100644 --- a/src/Searchers/CommentPostSearcher.php +++ b/src/Searchers/CommentPostSearcher.php @@ -10,7 +10,7 @@ class CommentPostSearcher extends Searcher public function enabled(): bool { - $enabled = $this->setting('blomstra-search.admin.search-post-bodies', true); + $enabled = $this->setting('blomstra-search.search-post-bodies', true); return boolval($enabled); } From 9401b677a316e8719c8c0cd90e486c70f2009f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Klabbers?= Date: Thu, 10 Nov 2022 12:57:46 +0100 Subject: [PATCH 3/3] change weighing for exact words match --- src/Api/Controllers/SearchController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Api/Controllers/SearchController.php b/src/Api/Controllers/SearchController.php index 0239378..bd3fda9 100644 --- a/src/Api/Controllers/SearchController.php +++ b/src/Api/Controllers/SearchController.php @@ -267,7 +267,7 @@ class SearchController extends ListDiscussionsController $query = (new MatchQuery('content', $q)) ->operator($operator); - $boost = $operator === 'and' ? 1 : .8; + $boost = $operator === 'and' ? 1.8 : .8; return $this->boolQuery($query, $boost); }