@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<?php
2
3final class PhabricatorFerretFulltextStorageEngine
4 extends PhabricatorFulltextStorageEngine {
5
6 private $fulltextTokens = array();
7 private $engineLimits;
8
9 /**
10 * @return string Engine identifier string: "mysql"
11 */
12 public function getEngineIdentifier() {
13 return 'mysql';
14 }
15
16 public function getHostType() {
17 return new PhabricatorMySQLSearchHost($this);
18 }
19
20 public function reindexAbstractDocument(
21 PhabricatorSearchAbstractDocument $doc) {
22
23 // NOTE: The Ferret engine indexes are rebuilt by an extension rather than
24 // by the main fulltext engine, and are always built regardless of
25 // configuration.
26
27 return;
28 }
29
30 public function executeSearch(PhabricatorSavedQuery $query) {
31 $all_objects = id(new PhutilClassMapQuery())
32 ->setAncestorClass(PhabricatorFerretInterface::class)
33 ->execute();
34
35 $type_map = array();
36 foreach ($all_objects as $object) {
37 $phid_type = phid_get_type($object->generatePHID());
38
39 $type_map[$phid_type] = array(
40 'object' => $object,
41 'engine' => $object->newFerretEngine(),
42 );
43 }
44
45 $types = $query->getParameter('types');
46 if ($types) {
47 $type_map = array_select_keys($type_map, $types);
48 }
49
50 $offset = (int)$query->getParameter('offset', 0);
51 $limit = (int)$query->getParameter('limit', 25);
52
53 // NOTE: For now, it's okay to query with the omnipotent viewer here
54 // because we're just returning PHIDs which we'll filter later.
55 $viewer = PhabricatorUser::getOmnipotentUser();
56
57 $type_results = array();
58 $metadata = array();
59 foreach ($type_map as $type => $spec) {
60 $engine = $spec['engine'];
61 $object = $spec['object'];
62
63 $search_engine = $engine->newSearchEngine()
64 ->setViewer($viewer);
65
66 // Ignore result objects from SearchEngines belonging to disabled apps
67 $app_class = $search_engine->getApplicationClassName();
68 $app = PhabricatorApplication::isClassInstalled($app_class);
69 if (!$app) {
70 continue;
71 }
72 $local_query = new PhabricatorSavedQuery();
73 $local_query->setParameter('query', $query->getParameter('query'));
74
75 $project_phids = $query->getParameter('projectPHIDs');
76 if ($project_phids) {
77 $local_query->setParameter('projectPHIDs', $project_phids);
78 }
79
80 $subscriber_phids = $query->getParameter('subscriberPHIDs');
81 if ($subscriber_phids) {
82 $local_query->setParameter('subscriberPHIDs', $subscriber_phids);
83 }
84
85 $engine_query = $search_engine->buildQueryFromSavedQuery($local_query)
86 ->setViewer($viewer);
87
88 $engine_query
89 ->withFerretQuery($engine, $query)
90 ->setOrder('relevance')
91 ->setLimit($offset + $limit);
92
93 $results = $engine_query->execute();
94 $results = mpull($results, null, 'getPHID');
95 $type_results[$type] = $results;
96
97 $metadata += $engine_query->getFerretMetadata();
98
99 if (!$this->fulltextTokens) {
100 $this->fulltextTokens = $engine_query->getFerretTokens();
101 }
102 }
103
104 $list = array();
105 foreach ($type_results as $type => $results) {
106 $list += $results;
107 }
108
109 // Currently, the list is grouped by object type. For example, all the
110 // tasks might be first, then all the revisions, and so on. In each group,
111 // the results are ordered properly.
112
113 // Reorder the results so that the highest-ranking results come first,
114 // no matter which object types they belong to.
115
116 $metadata = msortv($metadata, 'getRelevanceSortVector');
117 $list = array_select_keys($list, array_keys($metadata)) + $list;
118
119 $result_slice = array_slice($list, $offset, $limit, true);
120 return array_keys($result_slice);
121 }
122
123 public function indexExists() {
124 return true;
125 }
126
127 public function getIndexStats() {
128 return false;
129 }
130
131 public function getFulltextTokens() {
132 return $this->fulltextTokens;
133 }
134
135
136}