mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 14:52:41 +01:00
Improve search functionality.
Summary: Test Plan: Reviewers: CC:
This commit is contained in:
parent
eec3e8e3aa
commit
505c82236d
14 changed files with 232 additions and 22 deletions
|
@ -26,6 +26,20 @@ class ManiphestTaskSelectorSearchController extends ManiphestController {
|
||||||
$query->setQuery($request->getStr('query'));
|
$query->setQuery($request->getStr('query'));
|
||||||
$query->setParameter('type', 'TASK');
|
$query->setParameter('type', 'TASK');
|
||||||
|
|
||||||
|
switch ($request->getStr('filter')) {
|
||||||
|
case 'assigned':
|
||||||
|
$query->setParameter('owner', array($user->getPHID()));
|
||||||
|
$query->setParameter('open', 1);
|
||||||
|
break;
|
||||||
|
case 'created';
|
||||||
|
$query->setParameter('author', array($user->getPHID()));
|
||||||
|
$query->setParameter('open', 1);
|
||||||
|
break;
|
||||||
|
case 'open':
|
||||||
|
$query->setParameter('open', 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$exec = new PhabricatorSearchMySQLExecutor();
|
$exec = new PhabricatorSearchMySQLExecutor();
|
||||||
$results = $exec->executeSearch($query);
|
$results = $exec->executeSearch($query);
|
||||||
$results = ipull($results, 'phid');
|
$results = ipull($results, 'phid');
|
||||||
|
|
|
@ -29,7 +29,7 @@ class PhabricatorHandleObjectSelectorDataView {
|
||||||
return array(
|
return array(
|
||||||
'phid' => $handle->getPHID(),
|
'phid' => $handle->getPHID(),
|
||||||
'name' => $handle->getFullName(),
|
'name' => $handle->getFullName(),
|
||||||
'href' => $handle->getURI(),
|
'uri' => $handle->getURI(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,5 +21,6 @@ final class PhabricatorSearchField {
|
||||||
const FIELD_TITLE = 'titl';
|
const FIELD_TITLE = 'titl';
|
||||||
const FIELD_BODY = 'body';
|
const FIELD_BODY = 'body';
|
||||||
const FIELD_TEST_PLAN = 'tpln';
|
const FIELD_TEST_PLAN = 'tpln';
|
||||||
|
const FIELD_COMMENT = 'cmnt';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,5 +22,9 @@ final class PhabricatorSearchRelationship {
|
||||||
const RELATIONSHIP_REVIEWER = 'revw';
|
const RELATIONSHIP_REVIEWER = 'revw';
|
||||||
const RELATIONSHIP_SUBSCRIBER = 'subs';
|
const RELATIONSHIP_SUBSCRIBER = 'subs';
|
||||||
const RELATIONSHIP_COMMENTER = 'comm';
|
const RELATIONSHIP_COMMENTER = 'comm';
|
||||||
|
const RELATIONSHIP_OWNER = 'ownr';
|
||||||
|
|
||||||
|
const RELATIONSHIP_OPEN = 'open';
|
||||||
|
const RELATIONSHIP_TOUCH = 'poke';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,12 +38,47 @@ class PhabricatorSearchController extends PhabricatorSearchBaseController {
|
||||||
|
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
$query->setQuery($request->getStr('query'));
|
$query->setQuery($request->getStr('query'));
|
||||||
|
|
||||||
|
if (strlen($request->getStr('type'))) {
|
||||||
|
$query->setParameter('type', $request->getStr('type'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->getArr('author')) {
|
||||||
|
$query->setParameter('author', $request->getArr('author'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->getInt('open')) {
|
||||||
|
$query->setParameter('open', $request->getInt('open'));
|
||||||
|
}
|
||||||
|
|
||||||
$query->save();
|
$query->save();
|
||||||
return id(new AphrontRedirectResponse())
|
return id(new AphrontRedirectResponse())
|
||||||
->setURI('/search/'.$query->getID().'/');
|
->setURI('/search/'.$query->getID().'/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$options = array(
|
||||||
|
'' => 'All Documents',
|
||||||
|
'DREV' => 'Differential Revisions',
|
||||||
|
'TASK' => 'Maniphest Tasks',
|
||||||
|
);
|
||||||
|
|
||||||
|
$status_options = array(
|
||||||
|
0 => 'Open and Closed Documents',
|
||||||
|
1 => 'Open Documents',
|
||||||
|
);
|
||||||
|
|
||||||
|
$phids = array_merge(
|
||||||
|
$query->getParameter('author', array())
|
||||||
|
);
|
||||||
|
|
||||||
|
$handles = id(new PhabricatorObjectHandleData($phids))
|
||||||
|
->loadHandles();
|
||||||
|
|
||||||
|
$author_value = array_select_keys(
|
||||||
|
$handles,
|
||||||
|
$query->getParameter('author', array()));
|
||||||
|
$author_value = mpull($author_value, 'getFullName', 'getPHID');
|
||||||
|
|
||||||
$search_form = new AphrontFormView();
|
$search_form = new AphrontFormView();
|
||||||
$search_form
|
$search_form
|
||||||
|
@ -54,6 +89,24 @@ class PhabricatorSearchController extends PhabricatorSearchBaseController {
|
||||||
->setLabel('Search')
|
->setLabel('Search')
|
||||||
->setName('query')
|
->setName('query')
|
||||||
->setValue($query->getQuery()))
|
->setValue($query->getQuery()))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSelectControl())
|
||||||
|
->setLabel('Document Type')
|
||||||
|
->setName('type')
|
||||||
|
->setOptions($options)
|
||||||
|
->setValue($query->getParameter('type')))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormTokenizerControl())
|
||||||
|
->setName('author')
|
||||||
|
->setLabel('Author')
|
||||||
|
->setDatasource('/typeahead/common/users/')
|
||||||
|
->setValue($author_value))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSelectControl())
|
||||||
|
->setLabel('Document Status')
|
||||||
|
->setName('open')
|
||||||
|
->setOptions($status_options)
|
||||||
|
->setValue($query->getParameter('open')))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
->setValue('Search'));
|
->setValue('Search'));
|
||||||
|
|
|
@ -60,18 +60,33 @@ class PhabricatorSearchMySQLExecutor extends PhabricatorSearchExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($query->getParameter('type')) {
|
if ($query->getParameter('type')) {
|
||||||
|
if (strlen($q)) {
|
||||||
|
// TODO: verify that this column actually does something useful in query
|
||||||
|
// plans once we have nontrivial amounts of data.
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'field.phidType = %s',
|
||||||
|
$query->getParameter('type'));
|
||||||
|
}
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
'document.documentType = %s',
|
'document.documentType = %s',
|
||||||
$query->getParameter('type'));
|
$query->getParameter('type'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
$join[] = $this->joinRelationship(
|
$join[] = $this->joinRelationship(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
$query,
|
$query,
|
||||||
'author',
|
'author',
|
||||||
AdjutantRelationship::RELATIONSHIP_AUTHOR);
|
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR);
|
||||||
|
|
||||||
|
$join[] = $this->joinRelationship(
|
||||||
|
$conn_r,
|
||||||
|
$query,
|
||||||
|
'open',
|
||||||
|
PhabricatorSearchRelationship::RELATIONSHIP_OPEN);
|
||||||
|
|
||||||
|
/*
|
||||||
$join[] = $this->joinRelationship(
|
$join[] = $this->joinRelationship(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
$query,
|
$query,
|
||||||
|
@ -118,20 +133,36 @@ class PhabricatorSearchMySQLExecutor extends PhabricatorSearchExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function joinRelationship($conn, $query, $field, $type) {
|
protected function joinRelationship($conn, $query, $field, $type) {
|
||||||
$fbids = $query->getParameter($field, array());
|
$phids = $query->getParameter($field, array());
|
||||||
if (!$fbids) {
|
if (!$phids) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return qsprintf(
|
|
||||||
|
$is_existence = false;
|
||||||
|
switch ($type) {
|
||||||
|
case PhabricatorSearchRelationship::RELATIONSHIP_OPEN:
|
||||||
|
$is_existence = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'relationship AS %C ON %C.fbid = data.fbid AND %C.relation = %s
|
'%T AS %C ON %C.phid = document.phid AND %C.relation = %s',
|
||||||
AND %C.relatedFBID in (%Ld)',
|
id(new PhabricatorSearchDocumentRelationship())->getTableName(),
|
||||||
$field,
|
$field,
|
||||||
$field,
|
$field,
|
||||||
$field,
|
$field,
|
||||||
$type,
|
$type);
|
||||||
$field,
|
|
||||||
$fbids);
|
if (!$is_existence) {
|
||||||
|
$sql .= qsprintf(
|
||||||
|
$conn,
|
||||||
|
' AND %C.relatedPHID in (%Ls)',
|
||||||
|
$field,
|
||||||
|
$phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,15 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/search/constants/relationship');
|
||||||
phutil_require_module('phabricator', 'applications/search/execute/base');
|
phutil_require_module('phabricator', 'applications/search/execute/base');
|
||||||
phutil_require_module('phabricator', 'applications/search/storage/document/document');
|
phutil_require_module('phabricator', 'applications/search/storage/document/document');
|
||||||
phutil_require_module('phabricator', 'applications/search/storage/document/field');
|
phutil_require_module('phabricator', 'applications/search/storage/document/field');
|
||||||
|
phutil_require_module('phabricator', 'applications/search/storage/document/relationship');
|
||||||
phutil_require_module('phabricator', 'storage/qsprintf');
|
phutil_require_module('phabricator', 'storage/qsprintf');
|
||||||
phutil_require_module('phabricator', 'storage/queryfx');
|
phutil_require_module('phabricator', 'storage/queryfx');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
phutil_require_source('PhabricatorSearchMySQLExecutor.php');
|
phutil_require_source('PhabricatorSearchMySQLExecutor.php');
|
||||||
|
|
|
@ -47,8 +47,8 @@ final class PhabricatorSearchAbstractDocument {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addRelationship($type, $related_phid) {
|
public function addRelationship($type, $related_phid, $rtype, $time) {
|
||||||
$this->relationships[] = array($type, $related_phid);
|
$this->relationships[] = array($type, $related_phid, $rtype, $time);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,9 @@ class PhabricatorSearchDifferentialIndexer
|
||||||
|
|
||||||
$doc->addRelationship(
|
$doc->addRelationship(
|
||||||
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR,
|
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR,
|
||||||
$rev->getAuthorPHID());
|
$rev->getAuthorPHID(),
|
||||||
|
'USER',
|
||||||
|
$rev->getDateCreated());
|
||||||
|
|
||||||
PhabricatorSearchDocument::reindexAbstractDocument($doc);
|
PhabricatorSearchDocument::reindexAbstractDocument($doc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,87 @@ class PhabricatorSearchManiphestIndexer
|
||||||
|
|
||||||
$doc->addRelationship(
|
$doc->addRelationship(
|
||||||
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR,
|
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR,
|
||||||
$task->getAuthorPHID());
|
$task->getAuthorPHID(),
|
||||||
|
'USER',
|
||||||
|
$task->getDateCreated());
|
||||||
|
|
||||||
|
if ($task->getStatus() == ManiphestTaskStatus::STATUS_OPEN) {
|
||||||
|
$doc->addRelationship(
|
||||||
|
PhabricatorSearchRelationship::RELATIONSHIP_OPEN,
|
||||||
|
$task->getPHID(),
|
||||||
|
'TASK',
|
||||||
|
time());
|
||||||
|
}
|
||||||
|
|
||||||
|
$transactions = id(new ManiphestTransaction())->loadAllWhere(
|
||||||
|
'taskID = %d',
|
||||||
|
$task->getID());
|
||||||
|
|
||||||
|
$current_ccs = $task->getCCPHIDs();
|
||||||
|
$touches = array();
|
||||||
|
$owner = null;
|
||||||
|
$ccs = array();
|
||||||
|
foreach ($transactions as $transaction) {
|
||||||
|
if ($transaction->hasComments()) {
|
||||||
|
$doc->addField(
|
||||||
|
PhabricatorSearchField::FIELD_COMMENT,
|
||||||
|
$transaction->getComments());
|
||||||
|
}
|
||||||
|
|
||||||
|
$author = $transaction->getAuthorPHID();
|
||||||
|
|
||||||
|
// Record the most recent time they touched this object.
|
||||||
|
$touches[$author] = $transaction->getDateCreated();
|
||||||
|
|
||||||
|
switch ($transaction->getTransactionType()) {
|
||||||
|
case ManiphestTransactionType::TYPE_OWNER:
|
||||||
|
$owner = $transaction;
|
||||||
|
break;
|
||||||
|
case ManiphestTransactionType::TYPE_CCS:
|
||||||
|
// For users who are still CC'd, record the first time they were
|
||||||
|
// added to CC.
|
||||||
|
foreach ($transaction->getNewValue() as $added_cc) {
|
||||||
|
if (in_array($added_cc, $current_ccs)) {
|
||||||
|
if (empty($ccs[$added_cc])) {
|
||||||
|
$ccs[$added_cc] = $transaction->getDateCreated();
|
||||||
|
|
||||||
|
// CCs count as touches, even if you didn't technically
|
||||||
|
// interact with the object directly.
|
||||||
|
$touches[$added_cc] = $transaction->getDateCreated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($owner && $owner->getNewValue()) {
|
||||||
|
$doc->addRelationship(
|
||||||
|
PhabricatorSearchRelationship::RELATIONSHIP_OWNER,
|
||||||
|
$owner->getNewValue(),
|
||||||
|
'USER',
|
||||||
|
$owner->getDateCreated());
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($touches as $touch => $time) {
|
||||||
|
$doc->addRelationship(
|
||||||
|
PhabricatorSearchRelationship::RELATIONSHIP_TOUCH,
|
||||||
|
$touch,
|
||||||
|
'USER',
|
||||||
|
$time);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to load handles here since non-users may subscribe (mailing
|
||||||
|
// lists, e.g.)
|
||||||
|
$handles = id(new PhabricatorObjectHandleData(array_keys($ccs)))
|
||||||
|
->loadHandles();
|
||||||
|
foreach ($ccs as $cc => $time) {
|
||||||
|
$doc->addRelationship(
|
||||||
|
PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER,
|
||||||
|
$handles[$cc]->getPHID(),
|
||||||
|
$handles[$cc]->getType(),
|
||||||
|
$time);
|
||||||
|
}
|
||||||
|
|
||||||
PhabricatorSearchDocument::reindexAbstractDocument($doc);
|
PhabricatorSearchDocument::reindexAbstractDocument($doc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,17 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/maniphest/constants/status');
|
||||||
|
phutil_require_module('phabricator', 'applications/maniphest/constants/transactiontype');
|
||||||
|
phutil_require_module('phabricator', 'applications/maniphest/storage/transaction');
|
||||||
|
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||||
phutil_require_module('phabricator', 'applications/search/constants/field');
|
phutil_require_module('phabricator', 'applications/search/constants/field');
|
||||||
phutil_require_module('phabricator', 'applications/search/constants/relationship');
|
phutil_require_module('phabricator', 'applications/search/constants/relationship');
|
||||||
phutil_require_module('phabricator', 'applications/search/index/abstractdocument');
|
phutil_require_module('phabricator', 'applications/search/index/abstractdocument');
|
||||||
phutil_require_module('phabricator', 'applications/search/index/indexer/base');
|
phutil_require_module('phabricator', 'applications/search/index/indexer/base');
|
||||||
phutil_require_module('phabricator', 'applications/search/storage/document/document');
|
phutil_require_module('phabricator', 'applications/search/storage/document/document');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
phutil_require_source('PhabricatorSearchManiphestIndexer.php');
|
phutil_require_source('PhabricatorSearchManiphestIndexer.php');
|
||||||
|
|
|
@ -63,10 +63,11 @@ class PhabricatorSearchDocument extends PhabricatorSearchDAO {
|
||||||
list($ftype, $corpus, $aux_phid) = $field;
|
list($ftype, $corpus, $aux_phid) = $field;
|
||||||
queryfx(
|
queryfx(
|
||||||
$conn_w,
|
$conn_w,
|
||||||
'INSERT INTO %T (phid, field, auxPHId, corpus) '.
|
'INSERT INTO %T (phid, phidType, field, auxPHID, corpus) '.
|
||||||
' VALUES (%s, %s, %ns, %s)',
|
' VALUES (%s, %s, %s, %ns, %s)',
|
||||||
$field_dao->getTableName(),
|
$field_dao->getTableName(),
|
||||||
$phid,
|
$phid,
|
||||||
|
$doc->getDocumentType(),
|
||||||
$ftype,
|
$ftype,
|
||||||
$aux_phid,
|
$aux_phid,
|
||||||
$corpus);
|
$corpus);
|
||||||
|
@ -75,13 +76,15 @@ class PhabricatorSearchDocument extends PhabricatorSearchDAO {
|
||||||
|
|
||||||
$sql = array();
|
$sql = array();
|
||||||
foreach ($doc->getRelationshipData() as $relationship) {
|
foreach ($doc->getRelationshipData() as $relationship) {
|
||||||
list($rtype, $toPHID) = $relationship;
|
list($rtype, $to_phid, $to_type, $time) = $relationship;
|
||||||
$sql[] = qsprintf(
|
$sql[] = qsprintf(
|
||||||
$conn_w,
|
$conn_w,
|
||||||
'(%s, %s, %s)',
|
'(%s, %s, %s, %s, %d)',
|
||||||
$phid,
|
$phid,
|
||||||
$toPHID,
|
$to_phid,
|
||||||
$rtype);
|
$rtype,
|
||||||
|
$to_type,
|
||||||
|
$time);
|
||||||
}
|
}
|
||||||
|
|
||||||
$rship_dao = new PhabricatorSearchDocumentRelationship();
|
$rship_dao = new PhabricatorSearchDocumentRelationship();
|
||||||
|
@ -93,7 +96,8 @@ class PhabricatorSearchDocument extends PhabricatorSearchDAO {
|
||||||
if ($sql) {
|
if ($sql) {
|
||||||
queryfx(
|
queryfx(
|
||||||
$conn_w,
|
$conn_w,
|
||||||
'INSERT INTO %T (phid, relatedPHID, relation) '.
|
'INSERT INTO %T'.
|
||||||
|
' (phid, relatedPHID, relation, relatedType, relatedTime) '.
|
||||||
' VALUES %Q',
|
' VALUES %Q',
|
||||||
$rship_dao->getTableName(),
|
$rship_dao->getTableName(),
|
||||||
implode(', ', $sql));
|
implode(', ', $sql));
|
||||||
|
|
|
@ -21,6 +21,8 @@ class PhabricatorSearchDocumentRelationship extends PhabricatorSearchDAO {
|
||||||
protected $phid;
|
protected $phid;
|
||||||
protected $relatedPHID;
|
protected $relatedPHID;
|
||||||
protected $relation;
|
protected $relation;
|
||||||
|
protected $relatedType;
|
||||||
|
protected $relatedTime;
|
||||||
|
|
||||||
public function getConfiguration() {
|
public function getConfiguration() {
|
||||||
return array(
|
return array(
|
||||||
|
|
|
@ -111,6 +111,15 @@ JX.behavior('phabricator-object-selector', function(config) {
|
||||||
e.kill();
|
e.kill();
|
||||||
sendQuery();
|
sendQuery();
|
||||||
});
|
});
|
||||||
|
JX.DOM.listen(
|
||||||
|
JX.$(config.search),
|
||||||
|
'click',
|
||||||
|
'tag:button',
|
||||||
|
function(e) {
|
||||||
|
e.kill();
|
||||||
|
sendQuery();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
JX.DOM.listen(
|
JX.DOM.listen(
|
||||||
JX.$(config.results),
|
JX.$(config.results),
|
||||||
|
|
Loading…
Reference in a new issue