From dbada2f72accfb078d006f72e1d618855f8c6c92 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 4 Sep 2020 17:34:59 +0200 Subject: [PATCH] =?UTF-8?q?N=C2=B03238=20-=20Fix=20multi-words=20search=20?= =?UTF-8?q?in=20FilterBrick=20and=20ManageBrick?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/dbobjectsearch.class.php | 21 ++++++- core/dbsearch.class.php | 4 +- core/dbunionsearch.class.php | 10 +++- .../src/Controller/BrowseBrickController.php | 22 ++++--- .../src/Controller/ManageBrickController.php | 58 +++++++------------ pages/UI.php | 8 ++- 6 files changed, 72 insertions(+), 51 deletions(-) diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index 33a6ffc22..fff7c2338 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -624,14 +624,29 @@ class DBObjectSearch extends DBSearch public function AddCondition_FullText($sNeedle) { // Transform the full text condition into additional condition expression - $aFullTextFields = array(); - foreach (MetaModel::ListAttributeDefs($this->GetClass()) as $sAttCode => $oAttDef) - { + $aAttCodes = []; + foreach (MetaModel::ListAttributeDefs($this->GetClass()) as $sAttCode => $oAttDef) { if (!$oAttDef->IsScalar()) continue; if ($oAttDef->IsExternalKey()) continue; if (!$oAttDef->IsSearchable()) continue; + $aAttCodes[] = $sAttCode; + } + $this->AddCondition_FullTextOnAttributes($aAttCodes, $sNeedle); + } + + /** + * @param array $aAttCodes array of attCodes to search into + * @param string $sNeedle one word to be searched + * + * @throws \CoreException + */ + public function AddCondition_FullTextOnAttributes(array $aAttCodes, $sNeedle) + { + $aFullTextFields = []; + foreach ($aAttCodes as $sAttCode) { $aFullTextFields[] = new FieldExpression($sAttCode, $this->GetClassAlias()); } + $oTextFields = new CharConcatWSExpression(' ', $aFullTextFields); $sQueryParam = str_replace('.', '', uniqid('needle_', true)); diff --git a/core/dbsearch.class.php b/core/dbsearch.class.php index 1449122eb..e5ce11999 100644 --- a/core/dbsearch.class.php +++ b/core/dbsearch.class.php @@ -404,7 +404,9 @@ abstract class DBSearch */ abstract public function AddCondition_FullText($sFullText); - /** + abstract public function AddCondition_FullTextOnAttributes(array $aAttCodes, $sNeedle); + + /** * Perform a join, the remote class being matched by the mean of its primary key * * The join is performed diff --git a/core/dbunionsearch.class.php b/core/dbunionsearch.class.php index 28347a580..721191bd3 100644 --- a/core/dbunionsearch.class.php +++ b/core/dbunionsearch.class.php @@ -376,7 +376,15 @@ class DBUnionSearch extends DBSearch } } - /** + public function AddCondition_FullTextOnAttributes(array $aAttCodes, $sNeedle) + { + foreach ($this->aSearches as $oSearch) + { + $oSearch->AddCondition_FullTextOnAttributes($aAttCodes, $sNeedle); + } + } + + /** * @param DBObjectSearch $oFilter * @param $sExtKeyAttCode * @param int $iOperatorCode diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/BrowseBrickController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/BrowseBrickController.php index 8498486f8..de55c9a26 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Controller/BrowseBrickController.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/BrowseBrickController.php @@ -84,8 +84,9 @@ class BrowseBrickController extends BrickController $sDataLoading = ($sDataLoading !== null) ? $sDataLoading : $oRequestManipulator->ReadParam('sDataLoading', $oBrick->GetDataLoading()); // Getting search value - $sSearchValue = $oRequestManipulator->ReadParam('sSearchValue', ''); - if (!empty($sSearchValue)) + $sRawSearchValue = $oRequestManipulator->ReadParam('sSearchValue', ''); + $sSearchValue = html_entity_decode($sRawSearchValue); + if (strlen($sSearchValue) > 0) { $sDataLoading = AbstractBrick::ENUM_DATA_LOADING_LAZY; } @@ -167,13 +168,16 @@ class BrowseBrickController extends BrickController // Adding search clause // Note : For know the search is naive and looks only for the exact match. It doesn't search for words separately - if (!empty($sSearchValue)) + if (strlen($sSearchValue) > 0) { // - Cleaning the search value by exploding and trimming spaces - $aSearchValues = explode(' ', $sSearchValue); - array_walk($aSearchValues, function (&$sSearchValue /*, $sKey*/) { - trim($sSearchValue); - }); + $aExplodedSearchValues = explode(' ', $sSearchValue); + $aSearchValues = []; + foreach ($aExplodedSearchValues as $sValue) { + if (strlen($sValue) > 0) { + $aSearchValues[] = $sValue; + } + } // - Retrieving fields to search $aSearchFields = array($aLevelsProperties[$aLevelsPropertiesKeys[$i]]['name_att']); @@ -257,7 +261,7 @@ class BrowseBrickController extends BrickController { $aLevelsProperties[$aLevelsPropertiesKeys[$i]]['search']->SetSelectedClasses($aLevelsClasses); - if (!empty($sSearchValue)) + if (strlen($sSearchValue) > 0) { // Note : This could be way more simpler if we had a SetInternalParam($sParam, $value) verb $aQueryParams = $aLevelsProperties[$aLevelsPropertiesKeys[$i]]['search']->GetInternalParams(); @@ -452,7 +456,7 @@ class BrowseBrickController extends BrickController 'sBrickId' => $sBrickId, 'sBrowseMode' => $sBrowseMode, 'aBrowseButtons' => $aBrowseButtons, - 'sSearchValue' => $sSearchValue, + 'sSearchValue' => $sRawSearchValue, 'sDataLoading' => $sDataLoading, 'aItems' => json_encode($aItems), 'iItemsCount' => count($aItems), diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/ManageBrickController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/ManageBrickController.php index 3786612b3..615e272f5 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Controller/ManageBrickController.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/ManageBrickController.php @@ -894,64 +894,50 @@ class ManageBrickController extends BrickController $oRequestManipulator = $this->get('request_manipulator'); // Getting search value - $sSearchValue = $oRequestManipulator->ReadParam('sSearchValue', ''); + $sRawSearchValue = trim($oRequestManipulator->ReadParam('sSearchValue', '')); + $sSearchValue = html_entity_decode($sRawSearchValue); // - Adding search clause if necessary // Note : This is a very naive search at the moment - if (!empty($sSearchValue)) - { + if (strlen($sSearchValue) > 0) { // Putting only valid attributes as one can define attributes of leaf classes in the brick definition (), but at this stage we are working on the abstract class. // Note: This won't fix everything as the search will not be looking in all fields. - $aSearchListItems = array(); - foreach ($aColumnsAttrs as $sColumnAttr) - { - // Skip invalid attcodes - if (!MetaModel::IsValidAttCode($sClass, $sColumnAttr)) - { + $aSearchListItems = []; + foreach ($aColumnsAttrs as $sColumnAttr) { + // Skip invalid attCodes + if (!MetaModel::IsValidAttCode($sClass, $sColumnAttr)) { continue; } // For external key, force search on the friendlyname instead of the ID. // This should be addressed more globally with the bigger issue, see N°1970 $oAttDef = MetaModel::GetAttributeDef($sClass, $sColumnAttr); - if($oAttDef instanceof AttributeExternalKey) - { + if ($oAttDef instanceof AttributeExternalKey) { $sColumnAttr .= '_friendlyname'; } $aSearchListItems[] = $sColumnAttr; } - $oFullBinExpr = null; - foreach ($aSearchListItems as $sSearchItemAttr) - { - $oBinExpr = new BinaryExpression(new FieldExpression($sSearchItemAttr, $oQuery->GetClassAlias()), - 'LIKE', new VariableExpression('search_value')); - // At each iteration we build the complete expression for the search like ( (field1 LIKE %search%) OR (field2 LIKE %search%) OR (field3 LIKE %search%) ...) - if (is_null($oFullBinExpr)) - { - $oFullBinExpr = $oBinExpr; - } - else - { - $oFullBinExpr = new BinaryExpression($oFullBinExpr, 'OR', $oBinExpr); + if (preg_match('/^"(.*)"$/', $sSearchValue, $aMatches)) { + // The text is surrounded by double-quotes, remove the quotes and treat it as one single expression + $aSearchNeedles = [$aMatches[1]]; + } else { + // Split the text on the blanks and treat this as a search for AND AND + $aExplodedSearchNeedles = explode(' ', $sSearchValue); + $aSearchNeedles = []; + foreach ($aExplodedSearchNeedles as $sValue) { + if (strlen($sValue) > 0) { + $aSearchNeedles[] = $sValue; + } } } - - // Then add the complete expression to the query - if (!is_null($oFullBinExpr)) - { - // - Adding expression to the query - $oQuery->AddConditionExpression($oFullBinExpr); - // - Setting expression parameters - // Note : This could be way more simpler if we had a SetInternalParam($sParam, $value) verb - $aQueryParams = $oQuery->GetInternalParams(); - $aQueryParams['search_value'] = '%'.$sSearchValue.'%'; - $oQuery->SetInternalParams($aQueryParams); + foreach ($aSearchNeedles as $sSearchWord) { + $oQuery->AddCondition_FullTextOnAttributes($aSearchListItems, $sSearchWord); } } - $aData['sSearchValue'] = $sSearchValue; + $aData['sSearchValue'] = $sRawSearchValue; } /** diff --git a/pages/UI.php b/pages/UI.php index 581059517..96222f666 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -576,7 +576,13 @@ try else { // Split the text on the blanks and treat this as a search for AND AND - $aFullTextNeedles = explode(' ', $sFullText); + $aExplodedFullTextNeedles = explode(' ', $sFullText); + $aFullTextNeedles = []; + foreach ($aExplodedFullTextNeedles as $sValue) { + if (strlen($sValue) > 0) { + $aFullTextNeedles[] = $sValue; + } + } } // Check the needle length