From 959bb16d0f6780c2691fd2ea7d2e724143ea8425 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 21 Feb 2016 10:52:19 -0800 Subject: [PATCH] Allow Almanac services to be searched by substring Summary: Ref T10246. Build an ngram index for Almanac services, and use it to support improved search. Test Plan: {F1121725} Reviewers: chad Reviewed By: chad Maniphest Tasks: T10246 Differential Revision: https://secure.phabricator.com/D15321 --- .../20160221.almanac.3.servicen.sql | 7 +++++++ .../20160221.almanac.4.servicei.php | 11 ++++++++++ src/__phutil_library_map__.php | 3 +++ .../almanac/editor/AlmanacServiceEditor.php | 4 ++++ .../almanac/query/AlmanacServiceQuery.php | 20 ++++++++++++++++++- .../query/AlmanacServiceSearchEngine.php | 16 ++++++++++----- .../almanac/storage/AlmanacService.php | 14 ++++++++++++- .../storage/AlmanacServiceNameNgrams.php | 18 +++++++++++++++++ 8 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 resources/sql/autopatches/20160221.almanac.3.servicen.sql create mode 100644 resources/sql/autopatches/20160221.almanac.4.servicei.php create mode 100644 src/applications/almanac/storage/AlmanacServiceNameNgrams.php diff --git a/resources/sql/autopatches/20160221.almanac.3.servicen.sql b/resources/sql/autopatches/20160221.almanac.3.servicen.sql new file mode 100644 index 0000000000..2e48cd60c8 --- /dev/null +++ b/resources/sql/autopatches/20160221.almanac.3.servicen.sql @@ -0,0 +1,7 @@ +CREATE TABLE {$NAMESPACE}_almanac.almanac_servicename_ngrams ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + objectID INT UNSIGNED NOT NULL, + ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT}, + KEY `key_object` (objectID), + KEY `key_ngram` (ngram, objectID) +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20160221.almanac.4.servicei.php b/resources/sql/autopatches/20160221.almanac.4.servicei.php new file mode 100644 index 0000000000..97211ca7b5 --- /dev/null +++ b/resources/sql/autopatches/20160221.almanac.4.servicei.php @@ -0,0 +1,11 @@ +getPHID(), + array( + 'force' => true, + )); +} diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 2a175303fe..7488ddcff6 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -89,6 +89,7 @@ phutil_register_library_map(array( 'AlmanacServiceEditController' => 'applications/almanac/controller/AlmanacServiceEditController.php', 'AlmanacServiceEditor' => 'applications/almanac/editor/AlmanacServiceEditor.php', 'AlmanacServiceListController' => 'applications/almanac/controller/AlmanacServiceListController.php', + 'AlmanacServiceNameNgrams' => 'applications/almanac/storage/AlmanacServiceNameNgrams.php', 'AlmanacServicePHIDType' => 'applications/almanac/phid/AlmanacServicePHIDType.php', 'AlmanacServiceQuery' => 'applications/almanac/query/AlmanacServiceQuery.php', 'AlmanacServiceSearchEngine' => 'applications/almanac/query/AlmanacServiceSearchEngine.php', @@ -4082,12 +4083,14 @@ phutil_register_library_map(array( 'PhabricatorProjectInterface', 'AlmanacPropertyInterface', 'PhabricatorDestructibleInterface', + 'PhabricatorNgramsInterface', ), 'AlmanacServiceController' => 'AlmanacController', 'AlmanacServiceDatasource' => 'PhabricatorTypeaheadDatasource', 'AlmanacServiceEditController' => 'AlmanacServiceController', 'AlmanacServiceEditor' => 'PhabricatorApplicationTransactionEditor', 'AlmanacServiceListController' => 'AlmanacServiceController', + 'AlmanacServiceNameNgrams' => 'PhabricatorSearchNgrams', 'AlmanacServicePHIDType' => 'PhabricatorPHIDType', 'AlmanacServiceQuery' => 'AlmanacQuery', 'AlmanacServiceSearchEngine' => 'PhabricatorApplicationSearchEngine', diff --git a/src/applications/almanac/editor/AlmanacServiceEditor.php b/src/applications/almanac/editor/AlmanacServiceEditor.php index 868f31d026..178621fee0 100644 --- a/src/applications/almanac/editor/AlmanacServiceEditor.php +++ b/src/applications/almanac/editor/AlmanacServiceEditor.php @@ -11,6 +11,10 @@ final class AlmanacServiceEditor return pht('Almanac Service'); } + protected function supportsSearch() { + return true; + } + public function getTransactionTypes() { $types = parent::getTransactionTypes(); diff --git a/src/applications/almanac/query/AlmanacServiceQuery.php b/src/applications/almanac/query/AlmanacServiceQuery.php index 3701e850e9..f1e6630736 100644 --- a/src/applications/almanac/query/AlmanacServiceQuery.php +++ b/src/applications/almanac/query/AlmanacServiceQuery.php @@ -54,6 +54,12 @@ final class AlmanacServiceQuery return $this; } + public function withNameNgrams($ngrams) { + return $this->withNgramsConstraint( + new AlmanacServiceNameNgrams(), + $ngrams); + } + public function needBindings($need_bindings) { $this->needBindings = $need_bindings; return $this; @@ -66,7 +72,7 @@ final class AlmanacServiceQuery protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); - if ($this->devicePHIDs !== null) { + if ($this->shouldJoinBindingTable()) { $joins[] = qsprintf( $conn, 'JOIN %T binding ON service.phid = binding.servicePHID', @@ -178,6 +184,18 @@ final class AlmanacServiceQuery return parent::didFilterPage($services); } + private function shouldJoinBindingTable() { + return ($this->devicePHIDs !== null); + } + + protected function shouldGroupQueryResultRows() { + if ($this->shouldJoinBindingTable()) { + return true; + } + + return parent::shouldGroupQueryResultRows(); + } + protected function getPrimaryTableAlias() { return 'service'; } diff --git a/src/applications/almanac/query/AlmanacServiceSearchEngine.php b/src/applications/almanac/query/AlmanacServiceSearchEngine.php index 51d97729fe..2f7d6cf58f 100644 --- a/src/applications/almanac/query/AlmanacServiceSearchEngine.php +++ b/src/applications/almanac/query/AlmanacServiceSearchEngine.php @@ -16,21 +16,27 @@ final class AlmanacServiceSearchEngine } public function newResultObject() { - // NOTE: We need to attach a service type in order to generate custom - // field definitions. - return AlmanacService::initializeNewService() - ->attachServiceType(new AlmanacCustomServiceType()); + return AlmanacService::initializeNewService(); } protected function buildQueryFromParameters(array $map) { $query = $this->newQuery(); + if ($map['match'] !== null) { + $query->withNameNgrams($map['match']); + } + return $query; } protected function buildCustomSearchFields() { - return array(); + return array( + id(new PhabricatorSearchTextField()) + ->setLabel(pht('Name Contains')) + ->setKey('match') + ->setDescription(pht('Search for services by name substring.')), + ); } protected function getURI($path) { diff --git a/src/applications/almanac/storage/AlmanacService.php b/src/applications/almanac/storage/AlmanacService.php index b351f0d3fc..c16f85c8ee 100644 --- a/src/applications/almanac/storage/AlmanacService.php +++ b/src/applications/almanac/storage/AlmanacService.php @@ -8,7 +8,8 @@ final class AlmanacService PhabricatorApplicationTransactionInterface, PhabricatorProjectInterface, AlmanacPropertyInterface, - PhabricatorDestructibleInterface { + PhabricatorDestructibleInterface, + PhabricatorNgramsInterface { protected $name; protected $nameIndex; @@ -231,4 +232,15 @@ final class AlmanacService $this->delete(); } + +/* -( PhabricatorNgramInterface )------------------------------------------ */ + + + public function newNgrams() { + return array( + id(new AlmanacServiceNameNgrams()) + ->setValue($this->getName()), + ); + } + } diff --git a/src/applications/almanac/storage/AlmanacServiceNameNgrams.php b/src/applications/almanac/storage/AlmanacServiceNameNgrams.php new file mode 100644 index 0000000000..4ad6a9639d --- /dev/null +++ b/src/applications/almanac/storage/AlmanacServiceNameNgrams.php @@ -0,0 +1,18 @@ +