@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.

Modularize fulltext indexing of Projects, Subscriptions and Custom Fields

Summary: Ref T9979. This is going to become `FulltextEngine`, but pave the way for that by pulling extensions out of it.

Test Plan:
{F1036624}

- Used `bin/search index Txxx`, saw projects, subscribers and custom fields rebuild in the index.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9979

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

+206 -71
+10
src/__phutil_library_map__.php
··· 2017 2017 'PhabricatorCustomFieldEditEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldEditEngineExtension.php', 2018 2018 'PhabricatorCustomFieldEditField' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php', 2019 2019 'PhabricatorCustomFieldEditType' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditType.php', 2020 + 'PhabricatorCustomFieldFulltextEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldFulltextEngineExtension.php', 2020 2021 'PhabricatorCustomFieldHeraldField' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldField.php', 2021 2022 'PhabricatorCustomFieldHeraldFieldGroup' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldFieldGroup.php', 2022 2023 'PhabricatorCustomFieldImplementationIncompleteException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldImplementationIncompleteException.php', ··· 2326 2327 'PhabricatorFlaggableInterface' => 'applications/flag/interface/PhabricatorFlaggableInterface.php', 2327 2328 'PhabricatorFlagsApplication' => 'applications/flag/application/PhabricatorFlagsApplication.php', 2328 2329 'PhabricatorFlagsUIEventListener' => 'applications/flag/events/PhabricatorFlagsUIEventListener.php', 2330 + 'PhabricatorFulltextEngineExtension' => 'applications/search/index/PhabricatorFulltextEngineExtension.php', 2331 + 'PhabricatorFulltextEngineExtensionModule' => 'applications/search/index/PhabricatorFulltextEngineExtensionModule.php', 2329 2332 'PhabricatorFundApplication' => 'applications/fund/application/PhabricatorFundApplication.php', 2330 2333 'PhabricatorGDSetupCheck' => 'applications/config/check/PhabricatorGDSetupCheck.php', 2331 2334 'PhabricatorGarbageCollector' => 'infrastructure/daemon/garbagecollector/PhabricatorGarbageCollector.php', ··· 2870 2873 'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php', 2871 2874 'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php', 2872 2875 'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php', 2876 + 'PhabricatorProjectsFulltextEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsFulltextEngineExtension.php', 2873 2877 'PhabricatorProjectsPolicyRule' => 'applications/project/policyrule/PhabricatorProjectsPolicyRule.php', 2874 2878 'PhabricatorProjectsSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsSearchEngineAttachment.php', 2875 2879 'PhabricatorProjectsSearchEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsSearchEngineExtension.php', ··· 3168 3172 'PhabricatorSubscriptionsEditController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php', 3169 3173 'PhabricatorSubscriptionsEditEngineExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php', 3170 3174 'PhabricatorSubscriptionsEditor' => 'applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php', 3175 + 'PhabricatorSubscriptionsFulltextEngineExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsFulltextEngineExtension.php', 3171 3176 'PhabricatorSubscriptionsHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsHeraldAction.php', 3172 3177 'PhabricatorSubscriptionsListController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsListController.php', 3173 3178 'PhabricatorSubscriptionsRemoveSelfHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsRemoveSelfHeraldAction.php', ··· 6177 6182 'PhabricatorCustomFieldEditEngineExtension' => 'PhabricatorEditEngineExtension', 6178 6183 'PhabricatorCustomFieldEditField' => 'PhabricatorEditField', 6179 6184 'PhabricatorCustomFieldEditType' => 'PhabricatorEditType', 6185 + 'PhabricatorCustomFieldFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension', 6180 6186 'PhabricatorCustomFieldHeraldField' => 'HeraldField', 6181 6187 'PhabricatorCustomFieldHeraldFieldGroup' => 'HeraldFieldGroup', 6182 6188 'PhabricatorCustomFieldImplementationIncompleteException' => 'Exception', ··· 6543 6549 'PhabricatorFlaggableInterface' => 'PhabricatorPHIDInterface', 6544 6550 'PhabricatorFlagsApplication' => 'PhabricatorApplication', 6545 6551 'PhabricatorFlagsUIEventListener' => 'PhabricatorEventListener', 6552 + 'PhabricatorFulltextEngineExtension' => 'Phobject', 6553 + 'PhabricatorFulltextEngineExtensionModule' => 'PhabricatorConfigModule', 6546 6554 'PhabricatorFundApplication' => 'PhabricatorApplication', 6547 6555 'PhabricatorGDSetupCheck' => 'PhabricatorSetupCheck', 6548 6556 'PhabricatorGarbageCollector' => 'Phobject', ··· 7176 7184 'PhabricatorProjectWatchController' => 'PhabricatorProjectController', 7177 7185 'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension', 7178 7186 'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField', 7187 + 'PhabricatorProjectsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension', 7179 7188 'PhabricatorProjectsPolicyRule' => 'PhabricatorPolicyRule', 7180 7189 'PhabricatorProjectsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 7181 7190 'PhabricatorProjectsSearchEngineExtension' => 'PhabricatorSearchEngineExtension', ··· 7530 7539 'PhabricatorSubscriptionsEditController' => 'PhabricatorController', 7531 7540 'PhabricatorSubscriptionsEditEngineExtension' => 'PhabricatorEditEngineExtension', 7532 7541 'PhabricatorSubscriptionsEditor' => 'PhabricatorEditor', 7542 + 'PhabricatorSubscriptionsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension', 7533 7543 'PhabricatorSubscriptionsHeraldAction' => 'HeraldAction', 7534 7544 'PhabricatorSubscriptionsListController' => 'PhabricatorController', 7535 7545 'PhabricatorSubscriptionsRemoveSelfHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
+37
src/applications/project/engineextension/PhabricatorProjectsFulltextEngineExtension.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectsFulltextEngineExtension 4 + extends PhabricatorFulltextEngineExtension { 5 + 6 + const EXTENSIONKEY = 'projects'; 7 + 8 + public function getExtensionName() { 9 + return pht('Projects'); 10 + } 11 + 12 + public function shouldIndexFulltextObject($object) { 13 + return ($object instanceof PhabricatorProjectInterface); 14 + } 15 + 16 + public function indexFulltextObject( 17 + $object, 18 + PhabricatorSearchAbstractDocument $document) { 19 + 20 + $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( 21 + $object->getPHID(), 22 + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); 23 + 24 + if (!$project_phids) { 25 + return; 26 + } 27 + 28 + foreach ($project_phids as $project_phid) { 29 + $document->addRelationship( 30 + PhabricatorSearchRelationship::RELATIONSHIP_PROJECT, 31 + $project_phid, 32 + PhabricatorProjectProjectPHIDType::TYPECONST, 33 + $document->getDocumentModified()); // Bogus timestamp. 34 + } 35 + } 36 + 37 + }
+28
src/applications/search/index/PhabricatorFulltextEngineExtension.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorFulltextEngineExtension extends Phobject { 4 + 5 + final public function getExtensionKey() { 6 + return $this->getPhobjectClassConstant('EXTENSIONKEY'); 7 + } 8 + 9 + final protected function getViewer() { 10 + return PhabricatorUser::getOmnipotentUser(); 11 + } 12 + 13 + abstract public function getExtensionName(); 14 + 15 + abstract public function shouldIndexFulltextObject($object); 16 + 17 + abstract public function indexFulltextObject( 18 + $object, 19 + PhabricatorSearchAbstractDocument $document); 20 + 21 + final public static function getAllExtensions() { 22 + return id(new PhutilClassMapQuery()) 23 + ->setAncestorClass(__CLASS__) 24 + ->setUniqueMethod('getExtensionKey') 25 + ->execute(); 26 + } 27 + 28 + }
+44
src/applications/search/index/PhabricatorFulltextEngineExtensionModule.php
··· 1 + <?php 2 + 3 + final class PhabricatorFulltextEngineExtensionModule 4 + extends PhabricatorConfigModule { 5 + 6 + public function getModuleKey() { 7 + return 'fulltextengine'; 8 + } 9 + 10 + public function getModuleName() { 11 + return pht('Engine: Fulltext'); 12 + } 13 + 14 + public function renderModuleStatus(AphrontRequest $request) { 15 + $viewer = $request->getViewer(); 16 + 17 + $extensions = PhabricatorFulltextEngineExtension::getAllExtensions(); 18 + 19 + $rows = array(); 20 + foreach ($extensions as $extension) { 21 + $rows[] = array( 22 + get_class($extension), 23 + $extension->getExtensionName(), 24 + ); 25 + } 26 + 27 + $table = id(new AphrontTableView($rows)) 28 + ->setHeaders( 29 + array( 30 + pht('Class'), 31 + pht('Name'), 32 + )) 33 + ->setColumnClasses( 34 + array( 35 + null, 36 + 'wide pri', 37 + )); 38 + 39 + return id(new PHUIObjectBoxView()) 40 + ->setHeaderText(pht('FulltextEngine Extensions')) 41 + ->setTable($table); 42 + } 43 + 44 + }
+7 -71
src/applications/search/index/PhabricatorSearchDocumentIndexer.php
··· 52 52 53 53 $object = $this->loadDocumentByPHID($phid); 54 54 55 - // Automatically rebuild CustomField indexes if the object uses custom 56 - // fields. 57 - if ($object instanceof PhabricatorCustomFieldInterface) { 58 - $this->indexCustomFields($document, $object); 55 + $extensions = PhabricatorFulltextEngineExtension::getAllExtensions(); 56 + foreach ($extensions as $key => $extension) { 57 + if (!$extension->shouldIndexFulltextObject($object)) { 58 + unset($extensions[$key]); 59 + } 59 60 } 60 61 61 - // Automatically rebuild subscriber indexes if the object is subscribable. 62 - if ($object instanceof PhabricatorSubscribableInterface) { 63 - $this->indexSubscribers($document); 64 - } 65 - 66 - // Automatically build project relationships 67 - if ($object instanceof PhabricatorProjectInterface) { 68 - $this->indexProjects($document, $object); 62 + foreach ($extensions as $extension) { 63 + $extension->indexFulltextObject($object, $document); 69 64 } 70 65 71 66 $engine = PhabricatorSearchEngine::loadEngine(); ··· 82 77 ->setDocumentType(phid_get_type($phid)); 83 78 } 84 79 85 - protected function indexSubscribers( 86 - PhabricatorSearchAbstractDocument $doc) { 87 - 88 - $subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID( 89 - $doc->getPHID()); 90 - $handles = id(new PhabricatorHandleQuery()) 91 - ->setViewer($this->getViewer()) 92 - ->withPHIDs($subscribers) 93 - ->execute(); 94 - 95 - foreach ($handles as $phid => $handle) { 96 - $doc->addRelationship( 97 - PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER, 98 - $phid, 99 - $handle->getType(), 100 - $doc->getDocumentModified()); // Bogus timestamp. 101 - } 102 - } 103 - 104 - protected function indexProjects( 105 - PhabricatorSearchAbstractDocument $doc, 106 - PhabricatorProjectInterface $object) { 107 - 108 - $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( 109 - $object->getPHID(), 110 - PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); 111 - if ($project_phids) { 112 - foreach ($project_phids as $project_phid) { 113 - $doc->addRelationship( 114 - PhabricatorSearchRelationship::RELATIONSHIP_PROJECT, 115 - $project_phid, 116 - PhabricatorProjectProjectPHIDType::TYPECONST, 117 - $doc->getDocumentModified()); // Bogus timestamp. 118 - } 119 - } 120 - } 121 - 122 80 protected function indexTransactions( 123 81 PhabricatorSearchAbstractDocument $doc, 124 82 PhabricatorApplicationTransactionQuery $query, ··· 139 97 PhabricatorSearchDocumentFieldType::FIELD_COMMENT, 140 98 $comment->getContent()); 141 99 } 142 - } 143 - 144 - protected function indexCustomFields( 145 - PhabricatorSearchAbstractDocument $document, 146 - PhabricatorCustomFieldInterface $object) { 147 - 148 - // Rebuild the ApplicationSearch indexes. These are internal and not part of 149 - // the fulltext search, but putting them in this workflow allows users to 150 - // use the same tools to rebuild the indexes, which is easy to understand. 151 - 152 - $field_list = PhabricatorCustomField::getObjectFields( 153 - $object, 154 - PhabricatorCustomField::ROLE_DEFAULT); 155 - 156 - $field_list->setViewer($this->getViewer()); 157 - $field_list->readFieldsFromStorage($object); 158 - 159 - // Rebuild ApplicationSearch indexes. 160 - $field_list->rebuildIndexes($object); 161 - 162 - // Rebuild global search indexes. 163 - $field_list->updateAbstractDocument($document); 164 100 } 165 101 166 102 private function dispatchDidUpdateIndexEvent(
+41
src/applications/subscriptions/engineextension/PhabricatorSubscriptionsFulltextEngineExtension.php
··· 1 + <?php 2 + 3 + final class PhabricatorSubscriptionsFulltextEngineExtension 4 + extends PhabricatorFulltextEngineExtension { 5 + 6 + const EXTENSIONKEY = 'subscriptions'; 7 + 8 + public function getExtensionName() { 9 + return pht('Subscribers'); 10 + } 11 + 12 + public function shouldIndexFulltextObject($object) { 13 + return ($object instanceof PhabricatorSubscribableInterface); 14 + } 15 + 16 + public function indexFulltextObject( 17 + $object, 18 + PhabricatorSearchAbstractDocument $document) { 19 + 20 + $subscriber_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID( 21 + $object->getPHID()); 22 + 23 + if (!$subscriber_phids) { 24 + return; 25 + } 26 + 27 + $handles = id(new PhabricatorHandleQuery()) 28 + ->setViewer($this->getViewer()) 29 + ->withPHIDs($subscriber_phids) 30 + ->execute(); 31 + 32 + foreach ($handles as $phid => $handle) { 33 + $document->addRelationship( 34 + PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER, 35 + $phid, 36 + $handle->getType(), 37 + $document->getDocumentModified()); // Bogus timestamp. 38 + } 39 + } 40 + 41 + }
+39
src/infrastructure/customfield/engineextension/PhabricatorCustomFieldFulltextEngineExtension.php
··· 1 + <?php 2 + 3 + final class PhabricatorCustomFieldFulltextEngineExtension 4 + extends PhabricatorFulltextEngineExtension { 5 + 6 + const EXTENSIONKEY = 'customfield.fields'; 7 + 8 + public function getExtensionName() { 9 + return pht('Custom Fields'); 10 + } 11 + 12 + public function shouldIndexFulltextObject($object) { 13 + return ($object instanceof PhabricatorCustomFieldInterface); 14 + } 15 + 16 + public function indexFulltextObject( 17 + $object, 18 + PhabricatorSearchAbstractDocument $document) { 19 + 20 + // Rebuild the ApplicationSearch indexes. These are internal and not part 21 + // of the fulltext search, but putting them in this workflow allows users 22 + // to use the same tools to rebuild the indexes, which is easy to 23 + // understand. 24 + 25 + $field_list = PhabricatorCustomField::getObjectFields( 26 + $object, 27 + PhabricatorCustomField::ROLE_DEFAULT); 28 + 29 + $field_list->setViewer($this->getViewer()); 30 + $field_list->readFieldsFromStorage($object); 31 + 32 + // Rebuild ApplicationSearch indexes. 33 + $field_list->rebuildIndexes($object); 34 + 35 + // Rebuild global search indexes. 36 + $field_list->updateAbstractDocument($document); 37 + } 38 + 39 + }