mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-03 11:21:01 +01:00
Use ApplicationTransactions/CustomField to power Differential global search
Summary: Ref T2222. Ref T3886. Ref T418. A few changes: - CustomField can now index into global search. - Use CustomField fields instead of older custom fields for Differential global search. (This slightly breaks any custom fields which exist, but they are presumably very rare, and probably do not exist; this break is also very mild.) - Automatically perform CustomField and Subscribable indexing on applicable object types. Test Plan: Used `bin/search index` to reindex a bunch of stuff, then searched for it. Debug-dumped abstract documents to inspect them. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T418, T3886, T2222 Differential Revision: https://secure.phabricator.com/D8346
This commit is contained in:
parent
bcf255e9c9
commit
cd080b092e
10 changed files with 118 additions and 81 deletions
|
@ -78,4 +78,15 @@ final class DifferentialSummaryField
|
||||||
$xaction->getNewValue());
|
$xaction->getNewValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function shouldAppearInGlobalSearch() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateAbstractDocument(
|
||||||
|
PhabricatorSearchAbstractDocument $document) {
|
||||||
|
if (strlen($this->getValue())) {
|
||||||
|
$document->addField('body', $this->getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,4 +92,16 @@ final class DifferentialTestPlanField
|
||||||
$xaction->getNewValue());
|
$xaction->getNewValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function shouldAppearInGlobalSearch() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateAbstractDocument(
|
||||||
|
PhabricatorSearchAbstractDocument $document) {
|
||||||
|
if (strlen($this->getValue())) {
|
||||||
|
$document->addField('plan', $this->getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
|
||||||
* @group differential
|
|
||||||
*/
|
|
||||||
final class DifferentialSearchIndexer
|
final class DifferentialSearchIndexer
|
||||||
extends PhabricatorSearchDocumentIndexer {
|
extends PhabricatorSearchDocumentIndexer {
|
||||||
|
|
||||||
|
@ -10,6 +7,18 @@ final class DifferentialSearchIndexer
|
||||||
return new DifferentialRevision();
|
return new DifferentialRevision();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function loadDocumentByPHID($phid) {
|
||||||
|
$object = id(new DifferentialRevisionQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withPHIDs(array($phid))
|
||||||
|
->needReviewerStatus(true)
|
||||||
|
->executeOne();
|
||||||
|
if (!$object) {
|
||||||
|
throw new Exception("Unable to load object by phid '{$phid}'!");
|
||||||
|
}
|
||||||
|
return $object;
|
||||||
|
}
|
||||||
|
|
||||||
protected function buildAbstractDocumentByPHID($phid) {
|
protected function buildAbstractDocumentByPHID($phid) {
|
||||||
$rev = $this->loadDocumentByPHID($phid);
|
$rev = $this->loadDocumentByPHID($phid);
|
||||||
|
|
||||||
|
@ -20,24 +29,6 @@ final class DifferentialSearchIndexer
|
||||||
$doc->setDocumentCreated($rev->getDateCreated());
|
$doc->setDocumentCreated($rev->getDateCreated());
|
||||||
$doc->setDocumentModified($rev->getDateModified());
|
$doc->setDocumentModified($rev->getDateModified());
|
||||||
|
|
||||||
$aux_fields = DifferentialFieldSelector::newSelector()
|
|
||||||
->getFieldSpecifications();
|
|
||||||
foreach ($aux_fields as $key => $aux_field) {
|
|
||||||
$aux_field->setUser(PhabricatorUser::getOmnipotentUser());
|
|
||||||
if (!$aux_field->shouldAddToSearchIndex()) {
|
|
||||||
unset($aux_fields[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$aux_fields = DifferentialAuxiliaryField::loadFromStorage(
|
|
||||||
$rev,
|
|
||||||
$aux_fields);
|
|
||||||
foreach ($aux_fields as $aux_field) {
|
|
||||||
$doc->addField(
|
|
||||||
$aux_field->getKeyForSearchIndex(),
|
|
||||||
$aux_field->getValueForSearchIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
$doc->addRelationship(
|
$doc->addRelationship(
|
||||||
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR,
|
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR,
|
||||||
$rev->getAuthorPHID(),
|
$rev->getAuthorPHID(),
|
||||||
|
@ -52,29 +43,16 @@ final class DifferentialSearchIndexer
|
||||||
DifferentialPHIDTypeRevision::TYPECONST,
|
DifferentialPHIDTypeRevision::TYPECONST,
|
||||||
time());
|
time());
|
||||||
|
|
||||||
$comments = id(new DifferentialCommentQuery())
|
$this->indexTransactions(
|
||||||
->withRevisionPHIDs(array($rev->getPHID()))
|
$doc,
|
||||||
->execute();
|
new DifferentialTransactionQuery(),
|
||||||
|
array($rev->getPHID()));
|
||||||
$inlines = id(new DifferentialInlineCommentQuery())
|
|
||||||
->withRevisionIDs(array($rev->getID()))
|
|
||||||
->withNotDraft(true)
|
|
||||||
->execute();
|
|
||||||
|
|
||||||
foreach (array_merge($comments, $inlines) as $comment) {
|
|
||||||
if (strlen($comment->getContent())) {
|
|
||||||
$doc->addField(
|
|
||||||
PhabricatorSearchField::FIELD_COMMENT,
|
|
||||||
$comment->getContent());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$rev->loadRelationships();
|
|
||||||
|
|
||||||
// If a revision needs review, the owners are the reviewers. Otherwise, the
|
// If a revision needs review, the owners are the reviewers. Otherwise, the
|
||||||
// owner is the author (e.g., accepted, rejected, closed).
|
// owner is the author (e.g., accepted, rejected, closed).
|
||||||
if ($rev->getStatus() == ArcanistDifferentialRevisionStatus::NEEDS_REVIEW) {
|
if ($rev->getStatus() == ArcanistDifferentialRevisionStatus::NEEDS_REVIEW) {
|
||||||
$reviewers = $rev->getReviewers();
|
$reviewers = $rev->getReviewerStatus();
|
||||||
|
$reviewers = mpull($reviewers, 'getReviewerPHID', 'getReviewerPHID');
|
||||||
if ($reviewers) {
|
if ($reviewers) {
|
||||||
foreach ($reviewers as $phid) {
|
foreach ($reviewers as $phid) {
|
||||||
$doc->addRelationship(
|
$doc->addRelationship(
|
||||||
|
@ -98,20 +76,6 @@ final class DifferentialSearchIndexer
|
||||||
$rev->getDateCreated());
|
$rev->getDateCreated());
|
||||||
}
|
}
|
||||||
|
|
||||||
$ccphids = $rev->getCCPHIDs();
|
|
||||||
$handles = id(new PhabricatorHandleQuery())
|
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
|
||||||
->withPHIDs($ccphids)
|
|
||||||
->execute();
|
|
||||||
|
|
||||||
foreach ($handles as $phid => $handle) {
|
|
||||||
$doc->addRelationship(
|
|
||||||
PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER,
|
|
||||||
$phid,
|
|
||||||
$handle->getType(),
|
|
||||||
$rev->getDateModified()); // Bogus timestamp.
|
|
||||||
}
|
|
||||||
|
|
||||||
return $doc;
|
return $doc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,8 +81,6 @@ final class ManiphestSearchIndexer
|
||||||
time());
|
time());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->indexCustomFields($doc, $task);
|
|
||||||
|
|
||||||
return $doc;
|
return $doc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,6 @@ final class PhabricatorUserSearchIndexer
|
||||||
PhabricatorPeoplePHIDTypeUser::TYPECONST,
|
PhabricatorPeoplePHIDTypeUser::TYPECONST,
|
||||||
time());
|
time());
|
||||||
|
|
||||||
$this->indexCustomFields($doc, $user);
|
|
||||||
|
|
||||||
return $doc;
|
return $doc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,8 +46,6 @@ final class PonderSearchIndexer
|
||||||
new PonderAnswerTransactionQuery(),
|
new PonderAnswerTransactionQuery(),
|
||||||
mpull($answers, 'getPHID'));
|
mpull($answers, 'getPHID'));
|
||||||
|
|
||||||
$this->indexSubscribers($doc);
|
|
||||||
|
|
||||||
return $doc;
|
return $doc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,6 @@ final class PhabricatorProjectSearchIndexer
|
||||||
$doc->setDocumentCreated($project->getDateCreated());
|
$doc->setDocumentCreated($project->getDateCreated());
|
||||||
$doc->setDocumentModified($project->getDateModified());
|
$doc->setDocumentModified($project->getDateModified());
|
||||||
|
|
||||||
$this->indexSubscribers($doc);
|
|
||||||
$this->indexCustomFields($doc, $project);
|
|
||||||
|
|
||||||
$doc->addRelationship(
|
$doc->addRelationship(
|
||||||
$project->isArchived()
|
$project->isArchived()
|
||||||
? PhabricatorSearchRelationship::RELATIONSHIP_CLOSED
|
? PhabricatorSearchRelationship::RELATIONSHIP_CLOSED
|
||||||
|
|
|
@ -37,6 +37,19 @@ abstract class PhabricatorSearchDocumentIndexer {
|
||||||
try {
|
try {
|
||||||
$document = $this->buildAbstractDocumentByPHID($phid);
|
$document = $this->buildAbstractDocumentByPHID($phid);
|
||||||
|
|
||||||
|
$object = $this->loadDocumentByPHID($phid);
|
||||||
|
|
||||||
|
// Automatically rebuild CustomField indexes if the object uses custom
|
||||||
|
// fields.
|
||||||
|
if ($object instanceof PhabricatorCustomFieldInterface) {
|
||||||
|
$this->indexCustomFields($document, $object);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatically rebuild subscriber indexes if the object is subscribable.
|
||||||
|
if ($object instanceof PhabricatorSubscribableInterface) {
|
||||||
|
$this->indexSubscribers($document);
|
||||||
|
}
|
||||||
|
|
||||||
$engine = PhabricatorSearchEngineSelector::newSelector()->newEngine();
|
$engine = PhabricatorSearchEngineSelector::newSelector()->newEngine();
|
||||||
try {
|
try {
|
||||||
$engine->reindexAbstractDocument($document);
|
$engine->reindexAbstractDocument($document);
|
||||||
|
@ -107,7 +120,7 @@ abstract class PhabricatorSearchDocumentIndexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function indexCustomFields(
|
protected function indexCustomFields(
|
||||||
PhabricatorSearchAbstractDocument $doc,
|
PhabricatorSearchAbstractDocument $document,
|
||||||
PhabricatorCustomFieldInterface $object) {
|
PhabricatorCustomFieldInterface $object) {
|
||||||
|
|
||||||
// Rebuild the ApplicationSearch indexes. These are internal and not part of
|
// Rebuild the ApplicationSearch indexes. These are internal and not part of
|
||||||
|
@ -116,17 +129,16 @@ abstract class PhabricatorSearchDocumentIndexer {
|
||||||
|
|
||||||
$field_list = PhabricatorCustomField::getObjectFields(
|
$field_list = PhabricatorCustomField::getObjectFields(
|
||||||
$object,
|
$object,
|
||||||
PhabricatorCustomField::ROLE_APPLICATIONSEARCH);
|
PhabricatorCustomField::ROLE_DEFAULT);
|
||||||
|
|
||||||
$field_list->setViewer($this->getViewer());
|
$field_list->setViewer($this->getViewer());
|
||||||
$field_list->readFieldsFromStorage($object);
|
$field_list->readFieldsFromStorage($object);
|
||||||
|
|
||||||
|
// Rebuild ApplicationSearch indexes.
|
||||||
$field_list->rebuildIndexes($object);
|
$field_list->rebuildIndexes($object);
|
||||||
|
|
||||||
// We could also allow fields to provide fulltext content, and index it
|
// Rebuild global search indexes.
|
||||||
// here on the document. No one has asked for this yet, though, and the
|
$field_list->updateAbstractDocument($document);
|
||||||
// existing "search" key isn't a good fit to interpret to mean we should
|
|
||||||
// index stuff here, since it can be set on a lot of fields which don't
|
|
||||||
// contain anything resembling fulltext.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function dispatchDidUpdateIndexEvent(
|
private function dispatchDidUpdateIndexEvent(
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @task apps Building Applications with Custom Fields
|
* @task apps Building Applications with Custom Fields
|
||||||
* @task core Core Properties and Field Identity
|
* @task core Core Properties and Field Identity
|
||||||
* @task proxy Field Proxies
|
* @task proxy Field Proxies
|
||||||
* @task context Contextual Data
|
* @task context Contextual Data
|
||||||
* @task storage Field Storage
|
* @task storage Field Storage
|
||||||
* @task appsearch Integration with ApplicationSearch
|
* @task edit Integration with Edit Views
|
||||||
* @task appxaction Integration with ApplicationTransactions
|
* @task view Integration with Property Views
|
||||||
* @task edit Integration with edit views
|
* @task list Integration with List views
|
||||||
* @task view Integration with property views
|
* @task appsearch Integration with ApplicationSearch
|
||||||
* @task list Integration with list views
|
* @task appxaction Integration with ApplicationTransactions
|
||||||
|
* @task globalsearch Integration with Global Search
|
||||||
*/
|
*/
|
||||||
abstract class PhabricatorCustomField {
|
abstract class PhabricatorCustomField {
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@ abstract class PhabricatorCustomField {
|
||||||
const ROLE_EDIT = 'edit';
|
const ROLE_EDIT = 'edit';
|
||||||
const ROLE_VIEW = 'view';
|
const ROLE_VIEW = 'view';
|
||||||
const ROLE_LIST = 'list';
|
const ROLE_LIST = 'list';
|
||||||
|
const ROLE_GLOBALSEARCH = 'GlobalSearch';
|
||||||
|
|
||||||
|
|
||||||
/* -( Building Applications with Custom Fields )--------------------------- */
|
/* -( Building Applications with Custom Fields )--------------------------- */
|
||||||
|
@ -253,6 +255,8 @@ abstract class PhabricatorCustomField {
|
||||||
return $this->shouldAppearInPropertyView();
|
return $this->shouldAppearInPropertyView();
|
||||||
case self::ROLE_LIST:
|
case self::ROLE_LIST:
|
||||||
return $this->shouldAppearInListView();
|
return $this->shouldAppearInListView();
|
||||||
|
case self::ROLE_GLOBALSEARCH:
|
||||||
|
return $this->shouldAppearInGlobalSearch();
|
||||||
case self::ROLE_DEFAULT:
|
case self::ROLE_DEFAULT:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
|
@ -1091,4 +1095,30 @@ abstract class PhabricatorCustomField {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Global Search )------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task globalsearch
|
||||||
|
*/
|
||||||
|
public function shouldAppearInGlobalSearch() {
|
||||||
|
if ($this->proxy) {
|
||||||
|
return $this->proxy->shouldAppearInGlobalSearch();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task globalsearch
|
||||||
|
*/
|
||||||
|
public function updateAbstractDocument(
|
||||||
|
PhabricatorSearchAbstractDocument $document) {
|
||||||
|
if ($this->proxy) {
|
||||||
|
return $this->proxy->updateAbstractDocument($document);
|
||||||
|
}
|
||||||
|
return $document;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ final class PhabricatorCustomFieldList extends Phobject {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$keys) {
|
if (!$keys) {
|
||||||
return;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: We assume all fields share the same storage. This isn't guaranteed
|
// NOTE: We assume all fields share the same storage. This isn't guaranteed
|
||||||
|
@ -79,6 +79,8 @@ final class PhabricatorCustomFieldList extends Phobject {
|
||||||
$field->setValueFromStorage(null);
|
$field->setValueFromStorage(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function appendFieldsToForm(AphrontFormView $form) {
|
public function appendFieldsToForm(AphrontFormView $form) {
|
||||||
|
@ -304,4 +306,19 @@ final class PhabricatorCustomFieldList extends Phobject {
|
||||||
$any_index->saveTransaction();
|
$any_index->saveTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updateAbstractDocument(
|
||||||
|
PhabricatorSearchAbstractDocument $document) {
|
||||||
|
|
||||||
|
$role = PhabricatorCustomField::ROLE_GLOBALSEARCH;
|
||||||
|
foreach ($this->getFields() as $field) {
|
||||||
|
if (!$field->shouldEnableForRole($role)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$field->updateAbstractDocument($document);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $document;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue