@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Skeleton support for running global fulltext queries via the Ferret engine

Summary:
Ref T12819. Provides a Ferret-engine-based fulltext engine to ultimately replace the InnoDB fulltext engine.

This is still pretty basic (hard-coded and buggy) but technically sort of works.

To activate this, you must explicitly configure it, so it isn't visible to users yet.

Test Plan: Searched for objects with global fulltext search, got a mixture of matching revisions and tasks back.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T12819

Differential Revision: https://secure.phabricator.com/D18548

+162 -1
+2
src/__phutil_library_map__.php
··· 2838 2838 'PhabricatorFerretEngineTestCase' => 'applications/search/ferret/__tests__/PhabricatorFerretEngineTestCase.php', 2839 2839 'PhabricatorFerretField' => 'applications/search/ferret/PhabricatorFerretField.php', 2840 2840 'PhabricatorFerretFulltextEngineExtension' => 'applications/search/engineextension/PhabricatorFerretFulltextEngineExtension.php', 2841 + 'PhabricatorFerretFulltextStorageEngine' => 'applications/search/fulltextstorage/PhabricatorFerretFulltextStorageEngine.php', 2841 2842 'PhabricatorFerretInterface' => 'applications/search/ferret/PhabricatorFerretInterface.php', 2842 2843 'PhabricatorFerretNgrams' => 'applications/search/ferret/PhabricatorFerretNgrams.php', 2843 2844 'PhabricatorFerretSearchEngineExtension' => 'applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php', ··· 8173 8174 'PhabricatorFerretEngineTestCase' => 'PhabricatorTestCase', 8174 8175 'PhabricatorFerretField' => 'PhabricatorSearchDAO', 8175 8176 'PhabricatorFerretFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension', 8177 + 'PhabricatorFerretFulltextStorageEngine' => 'PhabricatorFulltextStorageEngine', 8176 8178 'PhabricatorFerretNgrams' => 'PhabricatorSearchDAO', 8177 8179 'PhabricatorFerretSearchEngineExtension' => 'PhabricatorSearchEngineExtension', 8178 8180 'PhabricatorFile' => array(
+1 -1
src/applications/differential/query/DifferentialRevisionQuery.php
··· 772 772 'column' => 'dateModified', 773 773 'type' => 'int', 774 774 ), 775 - ); 775 + ) + parent::getOrderableColumns(); 776 776 } 777 777 778 778 protected function getPagingValueMap($cursor, array $keys) {
+4
src/applications/differential/search/DifferentialRevisionFerretEngine.php
··· 15 15 return new DifferentialRevisionFerretField(); 16 16 } 17 17 18 + protected function newSearchEngine() { 19 + return new DifferentialRevisionSearchEngine(); 20 + } 21 + 18 22 protected function getFunctionMap() { 19 23 $map = parent::getFunctionMap(); 20 24
+4
src/applications/maniphest/search/ManiphestTaskFerretEngine.php
··· 15 15 return new ManiphestTaskFerretField(); 16 16 } 17 17 18 + protected function newSearchEngine() { 19 + return new ManiphestTaskSearchEngine(); 20 + } 21 + 18 22 protected function getFunctionMap() { 19 23 $map = parent::getFunctionMap(); 20 24
+55
src/applications/search/ferret/PhabricatorFerretEngine.php
··· 5 5 abstract public function newNgramsObject(); 6 6 abstract public function newDocumentObject(); 7 7 abstract public function newFieldObject(); 8 + abstract protected function newSearchEngine(); 8 9 9 10 public function getDefaultFunctionKey() { 10 11 return 'all'; ··· 67 68 68 69 public function newStemmer() { 69 70 return new PhutilSearchStemmer(); 71 + } 72 + 73 + public function newConfiguredFulltextQuery( 74 + $object, 75 + PhabricatorSavedQuery $query, 76 + PhabricatorUser $viewer) { 77 + 78 + $local_query = new PhabricatorSavedQuery(); 79 + $local_query->setParameter('query', $query->getParameter('query')); 80 + 81 + // TODO: Modularize this piece. 82 + $project_phids = $query->getParameter('projectPHIDs'); 83 + if ($project_phids) { 84 + $local_query->setParameter('projectPHIDs', $project_phids); 85 + } 86 + 87 + $subscriber_phids = $query->getParameter('subscriberPHIDs'); 88 + if ($subscriber_phids) { 89 + $local_query->setParameter('subscriberPHIDs', $subscriber_phids); 90 + } 91 + 92 + $author_phids = $query->getParameter('authorPHIDs'); 93 + if ($author_phids) { 94 + $local_query->setParameter('authorPHIDs', $author_phids); 95 + } 96 + 97 + $owner_phids = $query->getParameter('ownerPHIDs'); 98 + if ($owner_phids) { 99 + $local_query->setParameter('ownerPHIDs', $owner_phids); 100 + } 101 + 102 + $rel_open = PhabricatorSearchRelationship::RELATIONSHIP_OPEN; 103 + $rel_closed = PhabricatorSearchRelationship::RELATIONSHIP_CLOSED; 104 + 105 + $statuses = $query->getParameter('statuses'); 106 + if ($statuses) { 107 + $statuses = array_fuse($statuses); 108 + if (count($statuses) == 1) { 109 + if (isset($statuses[$rel_open])) { 110 + $local_query->setParameter('statuses', array('open()')); 111 + } 112 + if (isset($statuses[$rel_closed])) { 113 + $local_query->setParameter('statuses', array('closed()')); 114 + } 115 + } 116 + } 117 + 118 + $search_engine = $this->newSearchEngine() 119 + ->setViewer($viewer); 120 + 121 + $engine_query = $search_engine->buildQueryFromSavedQuery($local_query) 122 + ->setViewer($viewer); 123 + 124 + return $engine_query; 70 125 } 71 126 72 127 public function tokenizeString($value) {
+96
src/applications/search/fulltextstorage/PhabricatorFerretFulltextStorageEngine.php
··· 1 + <?php 2 + 3 + final class PhabricatorFerretFulltextStorageEngine 4 + extends PhabricatorFulltextStorageEngine { 5 + 6 + private $fulltextTokens = array(); 7 + private $engineLimits; 8 + 9 + public function getEngineIdentifier() { 10 + return 'ferret'; 11 + } 12 + 13 + public function getHostType() { 14 + return new PhabricatorMySQLSearchHost($this); 15 + } 16 + 17 + public function reindexAbstractDocument( 18 + PhabricatorSearchAbstractDocument $doc) { 19 + 20 + // NOTE: The Ferret engine indexes are rebuilt by an extension rather than 21 + // by the main fulltext engine, and are always built regardless of 22 + // configuration. 23 + 24 + return; 25 + } 26 + 27 + public function executeSearch(PhabricatorSavedQuery $query) { 28 + $all_objects = id(new PhutilClassMapQuery()) 29 + ->setAncestorClass('PhabricatorFerretInterface') 30 + ->execute(); 31 + 32 + $type_map = array(); 33 + foreach ($all_objects as $object) { 34 + $phid_type = phid_get_type($object->generatePHID()); 35 + 36 + $type_map[$phid_type] = array( 37 + 'object' => $object, 38 + 'engine' => $object->newFerretEngine(), 39 + ); 40 + } 41 + 42 + $types = $query->getParameter('types'); 43 + if ($types) { 44 + $type_map = array_select_keys($type_map, $types); 45 + } 46 + 47 + $offset = (int)$query->getParameter('offset', 0); 48 + $limit = (int)$query->getParameter('limit', 25); 49 + 50 + $viewer = PhabricatorUser::getOmnipotentUser(); 51 + 52 + $type_results = array(); 53 + foreach ($type_map as $type => $spec) { 54 + $engine = $spec['engine']; 55 + $object = $spec['object']; 56 + 57 + // NOTE: For now, it's okay to query with the omnipotent viewer here 58 + // because we're just returning PHIDs which we'll filter later. 59 + 60 + $type_query = $engine->newConfiguredFulltextQuery( 61 + $object, 62 + $query, 63 + $viewer); 64 + 65 + $type_query 66 + ->setOrder('relevance') 67 + ->setLimit($offset + $limit); 68 + 69 + $results = $type_query->execute(); 70 + $results = mpull($results, null, 'getPHID'); 71 + $type_results[$type] = $results; 72 + } 73 + 74 + $list = array(); 75 + foreach ($type_results as $type => $results) { 76 + $list += $results; 77 + } 78 + 79 + $result_slice = array_slice($list, $offset, $limit, true); 80 + return array_keys($result_slice); 81 + } 82 + 83 + public function indexExists() { 84 + return true; 85 + } 86 + 87 + public function getIndexStats() { 88 + return false; 89 + } 90 + 91 + public function getFulltextTokens() { 92 + return $this->fulltextTokens; 93 + } 94 + 95 + 96 + }