diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2838,6 +2838,7 @@ 'PhabricatorFerretEngineTestCase' => 'applications/search/ferret/__tests__/PhabricatorFerretEngineTestCase.php', 'PhabricatorFerretField' => 'applications/search/ferret/PhabricatorFerretField.php', 'PhabricatorFerretFulltextEngineExtension' => 'applications/search/engineextension/PhabricatorFerretFulltextEngineExtension.php', + 'PhabricatorFerretFulltextStorageEngine' => 'applications/search/fulltextstorage/PhabricatorFerretFulltextStorageEngine.php', 'PhabricatorFerretInterface' => 'applications/search/ferret/PhabricatorFerretInterface.php', 'PhabricatorFerretNgrams' => 'applications/search/ferret/PhabricatorFerretNgrams.php', 'PhabricatorFerretSearchEngineExtension' => 'applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php', @@ -8173,6 +8174,7 @@ 'PhabricatorFerretEngineTestCase' => 'PhabricatorTestCase', 'PhabricatorFerretField' => 'PhabricatorSearchDAO', 'PhabricatorFerretFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension', + 'PhabricatorFerretFulltextStorageEngine' => 'PhabricatorFulltextStorageEngine', 'PhabricatorFerretNgrams' => 'PhabricatorSearchDAO', 'PhabricatorFerretSearchEngineExtension' => 'PhabricatorSearchEngineExtension', 'PhabricatorFile' => array( diff --git a/src/applications/differential/query/DifferentialRevisionQuery.php b/src/applications/differential/query/DifferentialRevisionQuery.php --- a/src/applications/differential/query/DifferentialRevisionQuery.php +++ b/src/applications/differential/query/DifferentialRevisionQuery.php @@ -772,7 +772,7 @@ 'column' => 'dateModified', 'type' => 'int', ), - ); + ) + parent::getOrderableColumns(); } protected function getPagingValueMap($cursor, array $keys) { diff --git a/src/applications/differential/search/DifferentialRevisionFerretEngine.php b/src/applications/differential/search/DifferentialRevisionFerretEngine.php --- a/src/applications/differential/search/DifferentialRevisionFerretEngine.php +++ b/src/applications/differential/search/DifferentialRevisionFerretEngine.php @@ -15,6 +15,10 @@ return new DifferentialRevisionFerretField(); } + protected function newSearchEngine() { + return new DifferentialRevisionSearchEngine(); + } + protected function getFunctionMap() { $map = parent::getFunctionMap(); diff --git a/src/applications/maniphest/search/ManiphestTaskFerretEngine.php b/src/applications/maniphest/search/ManiphestTaskFerretEngine.php --- a/src/applications/maniphest/search/ManiphestTaskFerretEngine.php +++ b/src/applications/maniphest/search/ManiphestTaskFerretEngine.php @@ -15,6 +15,10 @@ return new ManiphestTaskFerretField(); } + protected function newSearchEngine() { + return new ManiphestTaskSearchEngine(); + } + protected function getFunctionMap() { $map = parent::getFunctionMap(); diff --git a/src/applications/search/ferret/PhabricatorFerretEngine.php b/src/applications/search/ferret/PhabricatorFerretEngine.php --- a/src/applications/search/ferret/PhabricatorFerretEngine.php +++ b/src/applications/search/ferret/PhabricatorFerretEngine.php @@ -5,6 +5,7 @@ abstract public function newNgramsObject(); abstract public function newDocumentObject(); abstract public function newFieldObject(); + abstract protected function newSearchEngine(); public function getDefaultFunctionKey() { return 'all'; @@ -69,6 +70,60 @@ return new PhutilSearchStemmer(); } + public function newConfiguredFulltextQuery( + $object, + PhabricatorSavedQuery $query, + PhabricatorUser $viewer) { + + $local_query = new PhabricatorSavedQuery(); + $local_query->setParameter('query', $query->getParameter('query')); + + // TODO: Modularize this piece. + $project_phids = $query->getParameter('projectPHIDs'); + if ($project_phids) { + $local_query->setParameter('projectPHIDs', $project_phids); + } + + $subscriber_phids = $query->getParameter('subscriberPHIDs'); + if ($subscriber_phids) { + $local_query->setParameter('subscriberPHIDs', $subscriber_phids); + } + + $author_phids = $query->getParameter('authorPHIDs'); + if ($author_phids) { + $local_query->setParameter('authorPHIDs', $author_phids); + } + + $owner_phids = $query->getParameter('ownerPHIDs'); + if ($owner_phids) { + $local_query->setParameter('ownerPHIDs', $owner_phids); + } + + $rel_open = PhabricatorSearchRelationship::RELATIONSHIP_OPEN; + $rel_closed = PhabricatorSearchRelationship::RELATIONSHIP_CLOSED; + + $statuses = $query->getParameter('statuses'); + if ($statuses) { + $statuses = array_fuse($statuses); + if (count($statuses) == 1) { + if (isset($statuses[$rel_open])) { + $local_query->setParameter('statuses', array('open()')); + } + if (isset($statuses[$rel_closed])) { + $local_query->setParameter('statuses', array('closed()')); + } + } + } + + $search_engine = $this->newSearchEngine() + ->setViewer($viewer); + + $engine_query = $search_engine->buildQueryFromSavedQuery($local_query) + ->setViewer($viewer); + + return $engine_query; + } + public function tokenizeString($value) { $value = trim($value, ' '); $value = preg_split('/ +/', $value); diff --git a/src/applications/search/fulltextstorage/PhabricatorFerretFulltextStorageEngine.php b/src/applications/search/fulltextstorage/PhabricatorFerretFulltextStorageEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/search/fulltextstorage/PhabricatorFerretFulltextStorageEngine.php @@ -0,0 +1,96 @@ +setAncestorClass('PhabricatorFerretInterface') + ->execute(); + + $type_map = array(); + foreach ($all_objects as $object) { + $phid_type = phid_get_type($object->generatePHID()); + + $type_map[$phid_type] = array( + 'object' => $object, + 'engine' => $object->newFerretEngine(), + ); + } + + $types = $query->getParameter('types'); + if ($types) { + $type_map = array_select_keys($type_map, $types); + } + + $offset = (int)$query->getParameter('offset', 0); + $limit = (int)$query->getParameter('limit', 25); + + $viewer = PhabricatorUser::getOmnipotentUser(); + + $type_results = array(); + foreach ($type_map as $type => $spec) { + $engine = $spec['engine']; + $object = $spec['object']; + + // NOTE: For now, it's okay to query with the omnipotent viewer here + // because we're just returning PHIDs which we'll filter later. + + $type_query = $engine->newConfiguredFulltextQuery( + $object, + $query, + $viewer); + + $type_query + ->setOrder('relevance') + ->setLimit($offset + $limit); + + $results = $type_query->execute(); + $results = mpull($results, null, 'getPHID'); + $type_results[$type] = $results; + } + + $list = array(); + foreach ($type_results as $type => $results) { + $list += $results; + } + + $result_slice = array_slice($list, $offset, $limit, true); + return array_keys($result_slice); + } + + public function indexExists() { + return true; + } + + public function getIndexStats() { + return false; + } + + public function getFulltextTokens() { + return $this->fulltextTokens; + } + + +}