From 02755f88c9b252b7db540b9d6cf030e569a5d769 Mon Sep 17 00:00:00 2001 From: aletail Date: Wed, 8 May 2024 09:45:14 -0400 Subject: [PATCH 1/6] Improving Search Results - Updated tntsearch to use v4.2.1 - Updated search to perform a boolean search first, if not results it falls back to a default search - Updated getSearchableSummary with an extend point, where more content can be added to the summary --- composer.json | 6 +- src/SearchControllerExtension.php | 135 +++++++++++++++--------------- src/SearchableExtension.php | 9 +- 3 files changed, 79 insertions(+), 71 deletions(-) diff --git a/composer.json b/composer.json index 5cf5d4c..e638ae1 100644 --- a/composer.json +++ b/composer.json @@ -5,9 +5,9 @@ "license": "MIT", "homepage": "https://github.com/werkbot/silverstripe-module-search", "require": { - "php": "^8.1", - "silverstripe/cms": "^5", - "silverstripe/framework": "^5", + "php": "^8.0", + "silverstripe/cms": "^4", + "silverstripe/framework": "^4", "teamtnt/tntsearch": "v4.2.1", "ext-sqlite3": "*", "symbiote/silverstripe-queuedjobs": "^5.1" diff --git a/src/SearchControllerExtension.php b/src/SearchControllerExtension.php index 696901d..29890a1 100644 --- a/src/SearchControllerExtension.php +++ b/src/SearchControllerExtension.php @@ -33,33 +33,33 @@ class SearchControllerExtension extends DataExtension * Site search form * * @return SiteSearchForm - **/ + **/ public function SiteSearchForm() { - $searchText = ''; - if ($this->owner->getRequest() && $this->owner->getRequest()->getVar('Search')) { - $searchText = $this->owner->getRequest()->getVar('Search'); - } - $fields = new FieldList( - TextField::create('Search', _t("Search.INPUT_LABEL", "Search"), $searchText) - ->setAttribute('placeholder', _t("Search.INPUT_PLACEHOLDER", "Enter search terms")) - ->setAttribute('aria-label', _t("Search.INPUT_ARIALABEL", "Enter search terms")) - ->setAttribute('title', _t("Search.INPUT_TITLE", "Search")) - ); - $requried = new RequiredFields('Search'); - $actions = new FieldList( - FormAction::create('SiteSearchFormResults', '') - ->setUseButtonTag(true) - ->setButtonContent(_t("Search.BUTTON_LABEL", 'Search')) - ->setAttribute('aria-label', _t("Search.BUTTON_ARIALABEL", 'Search')) - ); - $form = SearchForm::create($this->owner, 'SiteSearchForm', $fields, $actions, $requried); - $form->setTemplate('Forms\\SiteSearchForm'); - $form->setFormAction('/home/SiteSearchForm'); - - $this->owner->extend("updateSiteSearchForm", $fields, $required, $actions, $form); - - return $form; + $searchText = ''; + if ($this->owner->getRequest() && $this->owner->getRequest()->getVar('Search')) { + $searchText = $this->owner->getRequest()->getVar('Search'); + } + $fields = new FieldList( + TextField::create('Search', _t("Search.INPUT_LABEL", "Search"), $searchText) + ->setAttribute('placeholder', _t("Search.INPUT_PLACEHOLDER", "Enter search terms")) + ->setAttribute('aria-label', _t("Search.INPUT_ARIALABEL", "Enter search terms")) + ->setAttribute('title', _t("Search.INPUT_TITLE", "Search")) + ); + $requried = new RequiredFields('Search'); + $actions = new FieldList( + FormAction::create('SiteSearchFormResults', '') + ->setUseButtonTag(true) + ->setButtonContent(_t("Search.BUTTON_LABEL", 'Search')) + ->setAttribute('aria-label', _t("Search.BUTTON_ARIALABEL", 'Search')) + ); + $form = SearchForm::create($this->owner, 'SiteSearchForm', $fields, $actions, $requried); + $form->setTemplate('Forms\\SiteSearchForm'); + $form->setFormAction('/home/SiteSearchForm'); + + $this->owner->extend("updateSiteSearchForm", $fields, $required, $actions, $form); + + return $form; } /** @@ -68,43 +68,43 @@ public function SiteSearchForm() * @param array $data The raw request data submitted by user * @param SiteSearchForm $form The form instance that was submitted * @param HTTPRequest $request Request generated for this action - **/ + **/ public function SiteSearchFormResults($searchdata, $form) { - $start = ($this->owner->getRequest()->getVar('start')) ? (int)$this->owner->getRequest()->getVar('start') : 0; - $Results = new ArrayList(); - $ErrorMessge = ""; - - if (isset($searchdata['Search'])) { - try { - $Results = $this->getSearchResults($searchdata['Search']); - } catch (IndexNotFoundException $e) { - $validationResult = new ValidationResult(); - $validationResult->addFieldError('Message', 'Search index not found'); - $form->setSessionValidationResult($validationResult); - } - - if ($this->owner->config()->get('save_search_queries')) { - // Store the Search Query - $sq = SearchQuery::create(); - $sq->Query = $searchdata['Search']; - $sq->write(); - } + $start = ($this->owner->getRequest()->getVar('start')) ? (int)$this->owner->getRequest()->getVar('start') : 0; + $Results = new ArrayList(); + $ErrorMessge = ""; + + if (isset($searchdata['Search'])) { + try { + $Results = $this->getSearchResults($searchdata['Search']); + } catch (IndexNotFoundException $e) { + $validationResult = new ValidationResult(); + $validationResult->addFieldError('Message', 'Search index not found'); + $form->setSessionValidationResult($validationResult); + } + + if ($this->owner->config()->get('save_search_queries')) { + // Store the Search Query + $sq = SearchQuery::create(); + $sq->Query = $searchdata['Search']; + $sq->write(); } + } - $pageLength = 10; - $this->owner->extend("updateSiteSearchFormResults", $searchdata, $form, $Results, $pageLength); - - // Pack up the results - $Paged = new PaginatedList($Results, $this->owner->getRequest()); - $Paged->setPageLength($pageLength); - $Paged->setPageStart($start); - $data = array( - 'Results' => $Paged, - 'Query' => DBField::create_field('Text', $form->getSearchQuery()), - 'Title' => _t('SilverStripe\\CMS\\Search\\SiteSearchForm.SearchResults', 'Search Results') - ); - return $this->owner->customise($data)->renderWith(array('SearchableResultsPage', 'Page')); + $pageLength = 10; + $this->owner->extend("updateSiteSearchFormResults", $searchdata, $form, $Results, $pageLength); + + // Pack up the results + $Paged = new PaginatedList($Results, $this->owner->getRequest()); + $Paged->setPageLength($pageLength); + $Paged->setPageStart($start); + $data = array( + 'Results' => $Paged, + 'Query' => DBField::create_field('Text', $form->getSearchQuery()), + 'Title' => _t('SilverStripe\\CMS\\Search\\SiteSearchForm.SearchResults', 'Search Results') + ); + return $this->owner->customise($data)->renderWith(array('SearchableResultsPage', 'Page')); } /** @@ -119,23 +119,26 @@ public function getSearchResults(string $search) $tnt = TNTSearchHelper::Instance()->getTNTSearch(); $tnt->selectIndex('site.index'); - $res = $tnt->search($search); + $res = $tnt->searchBoolean($search); + + //if no results with boolean search, do a regular search + if (empty($res['ids'])) { + $res = $tnt->search($search, 1000); + } $classlist = []; $classes = ClassInfo::classesWithExtension(SearchableExtension::class); foreach ($classes as $key => $value) { - $classlist[ClassInfo::shortName($value)] = $value; + $classlist[ClassInfo::shortName($value)] = $value; } foreach ($res['ids'] as $result) { - $parts = explode("_", $result); - if ($obj = $classlist[$parts[0]]::get()->byID($parts[1])) { - $results->push($obj); - } + $parts = explode("_", $result); + if ($obj = $classlist[$parts[0]]::get()->byID($parts[1])) { + $results->push($obj); + } } return $results; } - } - diff --git a/src/SearchableExtension.php b/src/SearchableExtension.php index 721ca2f..80f0c48 100644 --- a/src/SearchableExtension.php +++ b/src/SearchableExtension.php @@ -81,11 +81,16 @@ public function getSearchableTitleColumnName() **/ public function getSearchableSummary() { + $content = ""; if ($this->owner->SearchableExtension_Summary_ColumnName) { - return $this->owner->{$this->owner->SearchableExtension_Summary_ColumnName}; + $content = $this->owner->{$this->owner->SearchableExtension_Summary_ColumnName}; } else { - return $this->owner->Content; + $content = $this->owner->Content; } + + $this->owner->extend('updateSearchableSummary', $content); + + return $content; } /** * getSearchableContent From 5795c822ae583d1a23c0471535ac853819b9680a Mon Sep 17 00:00:00 2001 From: aletail Date: Wed, 8 May 2024 11:03:25 -0400 Subject: [PATCH 2/6] Updated queded jobs dependency --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e638ae1..8435a7e 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ "silverstripe/framework": "^4", "teamtnt/tntsearch": "v4.2.1", "ext-sqlite3": "*", - "symbiote/silverstripe-queuedjobs": "^5.1" + "symbiote/silverstripe-queuedjobs": "^4.12" }, "autoload": { "psr-4": { From 9f804bfb3cb6a63a94fdf794e5d113a6b682d1b4 Mon Sep 17 00:00:00 2001 From: Jay Richardson Date: Thu, 16 May 2024 10:17:19 -0400 Subject: [PATCH 3/6] Added a getSearchableID function Returns the ID to be used in search results, for objects that are apart of a page this can be overridden to return the Page ID - which can then be used to remove duplicates from search results --- src/SearchableExtension.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/SearchableExtension.php b/src/SearchableExtension.php index 80f0c48..460ba64 100644 --- a/src/SearchableExtension.php +++ b/src/SearchableExtension.php @@ -43,6 +43,17 @@ public function getIndexQuery() { return false; } + /** + * getSearchableID + * Returns the ID to be used in search results, for objects that are apart of a page this can be + * overridden to return the Page ID - which can then be used to remove duplicates from search results + * + * @return int + */ + public function getSearchableID() + { + return $this->owner->ID; + } /** * getSearchableTitle * Returns the title, to be used in search results From 0b2e5ad8ca58c6d042c7d9fe9ef6f36e6eb0d67d Mon Sep 17 00:00:00 2001 From: Jay Richardson Date: Thu, 16 May 2024 10:18:06 -0400 Subject: [PATCH 4/6] Added search settings - Enable boolean search is optional --- _config.php | 7 ++++++ src/SearchControllerExtension.php | 36 +++++++++++++++++++------------ src/SearchSiteConfigExtension.php | 24 +++++++++++++++++++++ 3 files changed, 53 insertions(+), 14 deletions(-) create mode 100644 src/SearchSiteConfigExtension.php diff --git a/_config.php b/_config.php index e69de29..075d547 100644 --- a/_config.php +++ b/_config.php @@ -0,0 +1,7 @@ +getTNTSearch(); $tnt->selectIndex('site.index'); - $res = $tnt->searchBoolean($search); - - //if no results with boolean search, do a regular search - if (empty($res['ids'])) { + if (SiteConfig::current_site_config()->EnableBooleanSearch) { + $res = $tnt->searchBoolean($search); + //if no results with boolean search, do a regular search + if (empty($res['ids'])) { + $res = $tnt->search($search, 1000); + } + } else { $res = $tnt->search($search, 1000); } @@ -133,12 +137,16 @@ public function getSearchResults(string $search) } foreach ($res['ids'] as $result) { - $parts = explode("_", $result); - if ($obj = $classlist[$parts[0]]::get()->byID($parts[1])) { - $results->push($obj); + if ($result) { + $parts = explode("_", $result); + if ($obj = $classlist[$parts[0]]::get()->byID($parts[1])) { + $results->push($obj); + } } } + $results->removeDuplicates('getSearchableID'); + return $results; } } diff --git a/src/SearchSiteConfigExtension.php b/src/SearchSiteConfigExtension.php new file mode 100644 index 0000000..1174eba --- /dev/null +++ b/src/SearchSiteConfigExtension.php @@ -0,0 +1,24 @@ + 'Boolean', + ]; + + public function updateCMSFields(FieldList $fields) + { + $EnableBooleanSearchField = FieldGroup::create( + 'Search Settings', + CheckboxField::create('EnableBooleanSearch', 'Enable boolean search?') + ); + $fields->addFieldToTab("Root.Search", $EnableBooleanSearchField); + } +} From 4da10b2dc75d8d1374bc0e6354d10b530f76d748 Mon Sep 17 00:00:00 2001 From: Jay Richardson Date: Thu, 16 May 2024 13:48:39 -0400 Subject: [PATCH 5/6] Updated getSearchableID to include ClassName Will prevent different objects from returning the same ID --- src/SearchableExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SearchableExtension.php b/src/SearchableExtension.php index 460ba64..e82ea7a 100644 --- a/src/SearchableExtension.php +++ b/src/SearchableExtension.php @@ -52,7 +52,7 @@ public function getIndexQuery() */ public function getSearchableID() { - return $this->owner->ID; + return $this->owner->ClassName . "_" . $this->owner->ID; } /** * getSearchableTitle From c99618b0720be780591a544ab312b75ec7aea5c6 Mon Sep 17 00:00:00 2001 From: Jay Richardson Date: Thu, 16 May 2024 13:49:11 -0400 Subject: [PATCH 6/6] Reverse sorted the docScores with asb value --- src/SearchControllerExtension.php | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/SearchControllerExtension.php b/src/SearchControllerExtension.php index a5aade4..1ab8ccc 100644 --- a/src/SearchControllerExtension.php +++ b/src/SearchControllerExtension.php @@ -136,13 +136,26 @@ public function getSearchResults(string $search) $classlist[ClassInfo::shortName($value)] = $value; } - foreach ($res['ids'] as $result) { - if ($result) { - $parts = explode("_", $result); + if (isset($res["docScores"])) { + $docScores = $res['docScores']; + uasort($docScores, function ($a, $b) { + return abs($a) < abs($b) ? 1 : -1; + }); + foreach ($docScores as $id => $score) { + $parts = explode("_", $id); if ($obj = $classlist[$parts[0]]::get()->byID($parts[1])) { $results->push($obj); } } + } else { + foreach ($res['ids'] as $result) { + if ($result) { + $parts = explode("_", $result); + if ($obj = $classlist[$parts[0]]::get()->byID($parts[1])) { + $results->push($obj); + } + } + } } $results->removeDuplicates('getSearchableID');