mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-09 22:31:03 +01:00
Make Portals indexable with Ferret
Summary: Ref T13275. Add portals to the search index so that: - they show up in fulltext global search; and - the typeahead actually uses an index. Also make them taggable with projects as an organizational aid. Test Plan: Indexed portals with `bin/serach index`, searched for a portal with "Query", with fulltext search in main menu, with typehead on "Install Dashboard...", changed the name of a portal and searched again to check that the index updates properly. Reviewers: amckinley Reviewed By: amckinley Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T13275 Differential Revision: https://secure.phabricator.com/D20389
This commit is contained in:
parent
26ee9274ca
commit
89d038f53e
13 changed files with 157 additions and 39 deletions
|
@ -0,0 +1,9 @@
|
||||||
|
CREATE TABLE {$NAMESPACE}_dashboard.dashboard_portal_fdocument (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
objectPHID VARBINARY(64) NOT NULL,
|
||||||
|
isClosed BOOL NOT NULL,
|
||||||
|
authorPHID VARBINARY(64),
|
||||||
|
ownerPHID VARBINARY(64),
|
||||||
|
epochCreated INT UNSIGNED NOT NULL,
|
||||||
|
epochModified INT UNSIGNED NOT NULL
|
||||||
|
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,8 @@
|
||||||
|
CREATE TABLE {$NAMESPACE}_dashboard.dashboard_portal_ffield (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
documentID INT UNSIGNED NOT NULL,
|
||||||
|
fieldKey VARCHAR(4) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||||
|
rawCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT},
|
||||||
|
termCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT},
|
||||||
|
normalCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT}
|
||||||
|
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,5 @@
|
||||||
|
CREATE TABLE {$NAMESPACE}_dashboard.dashboard_portal_fngrams (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
documentID INT UNSIGNED NOT NULL,
|
||||||
|
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT}
|
||||||
|
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,7 @@
|
||||||
|
CREATE TABLE {$NAMESPACE}_dashboard.dashboard_portal_fngrams_common (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||||
|
needsCollection BOOL NOT NULL,
|
||||||
|
UNIQUE KEY `key_ngram` (ngram),
|
||||||
|
KEY `key_collect` (needsCollection)
|
||||||
|
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -2957,6 +2957,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorDashboardPortalEditController' => 'applications/dashboard/controller/portal/PhabricatorDashboardPortalEditController.php',
|
'PhabricatorDashboardPortalEditController' => 'applications/dashboard/controller/portal/PhabricatorDashboardPortalEditController.php',
|
||||||
'PhabricatorDashboardPortalEditEngine' => 'applications/dashboard/editor/PhabricatorDashboardPortalEditEngine.php',
|
'PhabricatorDashboardPortalEditEngine' => 'applications/dashboard/editor/PhabricatorDashboardPortalEditEngine.php',
|
||||||
'PhabricatorDashboardPortalEditor' => 'applications/dashboard/editor/PhabricatorDashboardPortalEditor.php',
|
'PhabricatorDashboardPortalEditor' => 'applications/dashboard/editor/PhabricatorDashboardPortalEditor.php',
|
||||||
|
'PhabricatorDashboardPortalFerretEngine' => 'applications/dashboard/engine/PhabricatorDashboardPortalFerretEngine.php',
|
||||||
|
'PhabricatorDashboardPortalFulltextEngine' => 'applications/dashboard/engine/PhabricatorDashboardPortalFulltextEngine.php',
|
||||||
'PhabricatorDashboardPortalInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardPortalInstallWorkflow.php',
|
'PhabricatorDashboardPortalInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardPortalInstallWorkflow.php',
|
||||||
'PhabricatorDashboardPortalListController' => 'applications/dashboard/controller/portal/PhabricatorDashboardPortalListController.php',
|
'PhabricatorDashboardPortalListController' => 'applications/dashboard/controller/portal/PhabricatorDashboardPortalListController.php',
|
||||||
'PhabricatorDashboardPortalMenuItem' => 'applications/dashboard/menuitem/PhabricatorDashboardPortalMenuItem.php',
|
'PhabricatorDashboardPortalMenuItem' => 'applications/dashboard/menuitem/PhabricatorDashboardPortalMenuItem.php',
|
||||||
|
@ -8938,6 +8940,9 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorApplicationTransactionInterface',
|
'PhabricatorApplicationTransactionInterface',
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
'PhabricatorDestructibleInterface',
|
'PhabricatorDestructibleInterface',
|
||||||
|
'PhabricatorProjectInterface',
|
||||||
|
'PhabricatorFulltextInterface',
|
||||||
|
'PhabricatorFerretInterface',
|
||||||
),
|
),
|
||||||
'PhabricatorDashboardPortalController' => 'PhabricatorDashboardController',
|
'PhabricatorDashboardPortalController' => 'PhabricatorDashboardController',
|
||||||
'PhabricatorDashboardPortalDatasource' => 'PhabricatorTypeaheadDatasource',
|
'PhabricatorDashboardPortalDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
|
@ -8945,6 +8950,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorDashboardPortalEditController' => 'PhabricatorDashboardPortalController',
|
'PhabricatorDashboardPortalEditController' => 'PhabricatorDashboardPortalController',
|
||||||
'PhabricatorDashboardPortalEditEngine' => 'PhabricatorEditEngine',
|
'PhabricatorDashboardPortalEditEngine' => 'PhabricatorEditEngine',
|
||||||
'PhabricatorDashboardPortalEditor' => 'PhabricatorApplicationTransactionEditor',
|
'PhabricatorDashboardPortalEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||||
|
'PhabricatorDashboardPortalFerretEngine' => 'PhabricatorFerretEngine',
|
||||||
|
'PhabricatorDashboardPortalFulltextEngine' => 'PhabricatorFulltextEngine',
|
||||||
'PhabricatorDashboardPortalInstallWorkflow' => 'PhabricatorDashboardObjectInstallWorkflow',
|
'PhabricatorDashboardPortalInstallWorkflow' => 'PhabricatorDashboardObjectInstallWorkflow',
|
||||||
'PhabricatorDashboardPortalListController' => 'PhabricatorDashboardPortalController',
|
'PhabricatorDashboardPortalListController' => 'PhabricatorDashboardPortalController',
|
||||||
'PhabricatorDashboardPortalMenuItem' => 'PhabricatorProfileMenuItem',
|
'PhabricatorDashboardPortalMenuItem' => 'PhabricatorProfileMenuItem',
|
||||||
|
|
|
@ -28,4 +28,8 @@ final class PhabricatorDashboardPortalEditor
|
||||||
return $types;
|
return $types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function supportsSearch() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorDashboardPortalFerretEngine
|
||||||
|
extends PhabricatorFerretEngine {
|
||||||
|
|
||||||
|
public function getApplicationName() {
|
||||||
|
return 'dashboard';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getScopeName() {
|
||||||
|
return 'portal';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newSearchEngine() {
|
||||||
|
return new PhabricatorDashboardPortalSearchEngine();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorDashboardPortalFulltextEngine
|
||||||
|
extends PhabricatorFulltextEngine {
|
||||||
|
|
||||||
|
protected function buildAbstractDocument(
|
||||||
|
PhabricatorSearchAbstractDocument $document,
|
||||||
|
$object) {
|
||||||
|
|
||||||
|
$portal = $object;
|
||||||
|
|
||||||
|
$document->setDocumentTitle($portal->getName());
|
||||||
|
|
||||||
|
$document->addRelationship(
|
||||||
|
$portal->isArchived()
|
||||||
|
? PhabricatorSearchRelationship::RELATIONSHIP_CLOSED
|
||||||
|
: PhabricatorSearchRelationship::RELATIONSHIP_OPEN,
|
||||||
|
$portal->getPHID(),
|
||||||
|
PhabricatorDashboardPortalPHIDType::TYPECONST,
|
||||||
|
PhabricatorTime::getNow());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -36,21 +36,21 @@ final class PhabricatorDashboardPortalQuery
|
||||||
if ($this->ids !== null) {
|
if ($this->ids !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'id IN (%Ld)',
|
'portal.id IN (%Ld)',
|
||||||
$this->ids);
|
$this->ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->phids !== null) {
|
if ($this->phids !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'phid IN (%Ls)',
|
'portal.phid IN (%Ls)',
|
||||||
$this->phids);
|
$this->phids);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->statuses !== null) {
|
if ($this->statuses !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'status IN (%Ls)',
|
'portal.status IN (%Ls)',
|
||||||
$this->statuses);
|
$this->statuses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,4 +61,8 @@ final class PhabricatorDashboardPortalQuery
|
||||||
return 'PhabricatorDashboardApplication';
|
return 'PhabricatorDashboardApplication';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getPrimaryTableAlias() {
|
||||||
|
return 'portal';
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,10 @@ final class PhabricatorDashboardPortal
|
||||||
implements
|
implements
|
||||||
PhabricatorApplicationTransactionInterface,
|
PhabricatorApplicationTransactionInterface,
|
||||||
PhabricatorPolicyInterface,
|
PhabricatorPolicyInterface,
|
||||||
PhabricatorDestructibleInterface {
|
PhabricatorDestructibleInterface,
|
||||||
|
PhabricatorProjectInterface,
|
||||||
|
PhabricatorFulltextInterface,
|
||||||
|
PhabricatorFerretInterface {
|
||||||
|
|
||||||
protected $name;
|
protected $name;
|
||||||
protected $viewPolicy;
|
protected $viewPolicy;
|
||||||
|
@ -55,6 +58,11 @@ final class PhabricatorDashboardPortal
|
||||||
return '/portal/view/'.$this->getID().'/';
|
return '/portal/view/'.$this->getID().'/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isArchived() {
|
||||||
|
$status_archived = PhabricatorDashboardPortalStatus::STATUS_ARCHIVED;
|
||||||
|
return ($this->getStatus() === $status_archived);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||||
|
|
||||||
|
@ -100,5 +108,16 @@ final class PhabricatorDashboardPortal
|
||||||
$this->delete();
|
$this->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -( PhabricatorFulltextInterface )--------------------------------------- */
|
||||||
|
|
||||||
|
public function newFulltextEngine() {
|
||||||
|
return new PhabricatorDashboardPortalFulltextEngine();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -( PhabricatorFerretInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
public function newFerretEngine() {
|
||||||
|
return new PhabricatorDashboardPortalFerretEngine();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,11 @@ final class PhabricatorDashboardPortalDatasource
|
||||||
public function buildResults() {
|
public function buildResults() {
|
||||||
$query = new PhabricatorDashboardPortalQuery();
|
$query = new PhabricatorDashboardPortalQuery();
|
||||||
|
|
||||||
// TODO: Actually query by name so this scales past 100 portals.
|
$this->applyFerretConstraints(
|
||||||
|
$query,
|
||||||
|
id(new PhabricatorDashboardPortal())->newFerretEngine(),
|
||||||
|
'title',
|
||||||
|
$this->getRawQuery());
|
||||||
|
|
||||||
$portals = $this->executeQuery($query);
|
$portals = $this->executeQuery($query);
|
||||||
|
|
||||||
|
|
|
@ -18,41 +18,17 @@ final class PhrictionDocumentDatasource
|
||||||
public function loadResults() {
|
public function loadResults() {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$raw_query = $this->getRawQuery();
|
$query = id(new PhrictionDocumentQuery())
|
||||||
|
|
||||||
$engine = id(new PhrictionDocument())
|
|
||||||
->newFerretEngine();
|
|
||||||
|
|
||||||
$compiler = id(new PhutilSearchQueryCompiler())
|
|
||||||
->setEnableFunctions(true);
|
|
||||||
|
|
||||||
$raw_tokens = $compiler->newTokens($raw_query);
|
|
||||||
|
|
||||||
$fulltext_tokens = array();
|
|
||||||
foreach ($raw_tokens as $raw_token) {
|
|
||||||
|
|
||||||
// This is a little hacky and could maybe be cleaner. We're treating
|
|
||||||
// every search term as though the user had entered "title:dog" insead
|
|
||||||
// of "dog".
|
|
||||||
|
|
||||||
$alternate_token = PhutilSearchQueryToken::newFromDictionary(
|
|
||||||
array(
|
|
||||||
'quoted' => $raw_token->isQuoted(),
|
|
||||||
'value' => $raw_token->getValue(),
|
|
||||||
'operator' => PhutilSearchQueryCompiler::OPERATOR_SUBSTRING,
|
|
||||||
'function' => 'title',
|
|
||||||
));
|
|
||||||
|
|
||||||
$fulltext_token = id(new PhabricatorFulltextToken())
|
|
||||||
->setToken($alternate_token);
|
|
||||||
$fulltext_tokens[] = $fulltext_token;
|
|
||||||
}
|
|
||||||
|
|
||||||
$documents = id(new PhrictionDocumentQuery())
|
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withFerretConstraint($engine, $fulltext_tokens)
|
->needContent(true);
|
||||||
->needContent(true)
|
|
||||||
->execute();
|
$this->applyFerretConstraints(
|
||||||
|
$query,
|
||||||
|
id(new PhrictionDocument())->newFerretEngine(),
|
||||||
|
'title',
|
||||||
|
$this->getRawQuery());
|
||||||
|
|
||||||
|
$documents = $query->execute();
|
||||||
|
|
||||||
$results = array();
|
$results = array();
|
||||||
foreach ($documents as $document) {
|
foreach ($documents as $document) {
|
||||||
|
|
|
@ -604,4 +604,38 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
|
||||||
return mpull($tokens, 'getWireFormat', 'getPHID');
|
return mpull($tokens, 'getWireFormat', 'getPHID');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final protected function applyFerretConstraints(
|
||||||
|
PhabricatorCursorPagedPolicyAwareQuery $query,
|
||||||
|
PhabricatorFerretEngine $engine,
|
||||||
|
$ferret_function,
|
||||||
|
$raw_query) {
|
||||||
|
|
||||||
|
$compiler = id(new PhutilSearchQueryCompiler())
|
||||||
|
->setEnableFunctions(true);
|
||||||
|
|
||||||
|
$raw_tokens = $compiler->newTokens($raw_query);
|
||||||
|
|
||||||
|
$fulltext_tokens = array();
|
||||||
|
foreach ($raw_tokens as $raw_token) {
|
||||||
|
// This is a little hacky and could maybe be cleaner. We're treating
|
||||||
|
// every search term as though the user had entered "title:dog" instead
|
||||||
|
// of "dog".
|
||||||
|
|
||||||
|
$alternate_token = PhutilSearchQueryToken::newFromDictionary(
|
||||||
|
array(
|
||||||
|
'quoted' => $raw_token->isQuoted(),
|
||||||
|
'value' => $raw_token->getValue(),
|
||||||
|
'operator' => PhutilSearchQueryCompiler::OPERATOR_SUBSTRING,
|
||||||
|
'function' => $ferret_function,
|
||||||
|
));
|
||||||
|
|
||||||
|
$fulltext_token = id(new PhabricatorFulltextToken())
|
||||||
|
->setToken($alternate_token);
|
||||||
|
$fulltext_tokens[] = $fulltext_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->withFerretConstraint($engine, $fulltext_tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue