Differential D16944 Diff 40787 src/applications/search/fulltextstorage/PhabricatorMySQLFulltextStorageEngine.php
Changeset View
Changeset View
Standalone View
Standalone View
src/applications/search/fulltextstorage/PhabricatorMySQLFulltextStorageEngine.php
Show First 20 Lines • Show All 147 Lines • ▼ Show 20 Lines | foreach ($relationships as $relationship) { | ||||
$relationship['relatedType'], | $relationship['relatedType'], | ||||
$relationship['relatedTime']); | $relationship['relatedTime']); | ||||
} | } | ||||
return $adoc; | return $adoc; | ||||
} | } | ||||
public function executeSearch(PhabricatorSavedQuery $query) { | public function executeSearch(PhabricatorSavedQuery $query) { | ||||
$where = array(); | $table = new PhabricatorSearchDocument(); | ||||
$join = array(); | $document_table = $table->getTableName(); | ||||
$order = 'ORDER BY documentCreated DESC'; | $conn = $table->establishConnection('r'); | ||||
$dao_doc = new PhabricatorSearchDocument(); | $subquery = $this->newFulltextSubquery($query, $conn); | ||||
$dao_field = new PhabricatorSearchDocumentField(); | |||||
$t_doc = $dao_doc->getTableName(); | $offset = (int)$query->getParameter('offset', 0); | ||||
$t_field = $dao_field->getTableName(); | $limit = (int)$query->getParameter('limit', 25); | ||||
$conn_r = $dao_doc->establishConnection('r'); | // NOTE: We must JOIN the subquery in order to apply a limit. | ||||
$results = queryfx_all( | |||||
$conn, | |||||
'SELECT | |||||
documentPHID, | |||||
MAX(fieldScore) AS documentScore | |||||
FROM (%Q) query | |||||
JOIN %T root ON query.documentPHID = root.phid | |||||
GROUP BY documentPHID | |||||
ORDER BY documentScore DESC | |||||
LIMIT %d, %d', | |||||
$subquery, | |||||
$document_table, | |||||
$offset, | |||||
$limit); | |||||
return ipull($results, 'documentPHID'); | |||||
} | |||||
private function newFulltextSubquery( | |||||
PhabricatorSavedQuery $query, | |||||
AphrontDatabaseConnection $conn) { | |||||
$field = new PhabricatorSearchDocumentField(); | |||||
$field_table = $field->getTableName(); | |||||
$document = new PhabricatorSearchDocument(); | |||||
$document_table = $document->getTableName(); | |||||
$select = array(); | |||||
$select[] = 'document.phid AS documentPHID'; | |||||
$join = array(); | |||||
$where = array(); | |||||
$raw_query = $query->getParameter('query'); | $raw_query = $query->getParameter('query'); | ||||
$q = $this->compileQuery($raw_query); | $compiled_query = $this->compileQuery($raw_query); | ||||
if (strlen($compiled_query)) { | |||||
$select[] = qsprintf( | |||||
$conn, | |||||
'MATCH(corpus) AGAINST (%s IN BOOLEAN MODE) AS fieldScore', | |||||
$compiled_query); | |||||
if (strlen($q)) { | |||||
$join[] = qsprintf( | $join[] = qsprintf( | ||||
$conn_r, | $conn, | ||||
'%T field ON field.phid = document.phid', | '%T field ON field.phid = document.phid', | ||||
$t_field); | $field_table); | ||||
$where[] = qsprintf( | $where[] = qsprintf( | ||||
$conn_r, | $conn, | ||||
'MATCH(corpus) AGAINST (%s IN BOOLEAN MODE)', | 'MATCH(corpus) AGAINST (%s IN BOOLEAN MODE)', | ||||
$q); | $compiled_query); | ||||
// When searching for a string, promote user listings above other | |||||
// listings. | |||||
$order = qsprintf( | |||||
$conn_r, | |||||
'ORDER BY | |||||
IF(documentType = %s, 0, 1) ASC, | |||||
MAX(MATCH(corpus) AGAINST (%s)) DESC', | |||||
'USER', | |||||
$q); | |||||
$field = $query->getParameter('field'); | if ($query->getParameter('field')) { | ||||
if ($field) { | |||||
$where[] = qsprintf( | $where[] = qsprintf( | ||||
$conn_r, | $conn, | ||||
'field.field = %s', | 'field.field = %s', | ||||
$field); | $field); | ||||
} | } | ||||
} else { | |||||
$select[] = qsprintf( | |||||
$conn, | |||||
'document.dateCreated AS fieldScore'); | |||||
} | } | ||||
$exclude = $query->getParameter('exclude'); | $exclude = $query->getParameter('exclude'); | ||||
if ($exclude) { | if ($exclude) { | ||||
$where[] = qsprintf($conn_r, 'document.phid != %s', $exclude); | $where[] = qsprintf( | ||||
$conn, | |||||
'document.phid != %s', | |||||
$exclude); | |||||
} | } | ||||
$types = $query->getParameter('types'); | $types = $query->getParameter('types'); | ||||
if ($types) { | if ($types) { | ||||
if (strlen($q)) { | if (strlen($compiled_query)) { | ||||
$where[] = qsprintf( | $where[] = qsprintf( | ||||
$conn_r, | $conn, | ||||
'field.phidType IN (%Ls)', | 'field.phidType IN (%Ls)', | ||||
$types); | $types); | ||||
} | } | ||||
$where[] = qsprintf( | $where[] = qsprintf( | ||||
$conn_r, | $conn, | ||||
'document.documentType IN (%Ls)', | 'document.documentType IN (%Ls)', | ||||
$types); | $types); | ||||
} | } | ||||
$join[] = $this->joinRelationship( | $join[] = $this->joinRelationship( | ||||
$conn_r, | $conn, | ||||
$query, | $query, | ||||
'authorPHIDs', | 'authorPHIDs', | ||||
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR); | PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR); | ||||
$statuses = $query->getParameter('statuses', array()); | $statuses = $query->getParameter('statuses', array()); | ||||
$statuses = array_fuse($statuses); | $statuses = array_fuse($statuses); | ||||
$open_rel = PhabricatorSearchRelationship::RELATIONSHIP_OPEN; | $open_rel = PhabricatorSearchRelationship::RELATIONSHIP_OPEN; | ||||
$closed_rel = PhabricatorSearchRelationship::RELATIONSHIP_CLOSED; | $closed_rel = PhabricatorSearchRelationship::RELATIONSHIP_CLOSED; | ||||
$include_open = !empty($statuses[$open_rel]); | $include_open = !empty($statuses[$open_rel]); | ||||
$include_closed = !empty($statuses[$closed_rel]); | $include_closed = !empty($statuses[$closed_rel]); | ||||
if ($include_open && !$include_closed) { | if ($include_open && !$include_closed) { | ||||
$join[] = $this->joinRelationship( | $join[] = $this->joinRelationship( | ||||
$conn_r, | $conn, | ||||
$query, | $query, | ||||
'statuses', | 'statuses', | ||||
$open_rel, | $open_rel, | ||||
true); | true); | ||||
} else if ($include_closed && !$include_open) { | } else if ($include_closed && !$include_open) { | ||||
$join[] = $this->joinRelationship( | $join[] = $this->joinRelationship( | ||||
$conn_r, | $conn, | ||||
$query, | $query, | ||||
'statuses', | 'statuses', | ||||
$closed_rel, | $closed_rel, | ||||
true); | true); | ||||
} | } | ||||
if ($query->getParameter('withAnyOwner')) { | if ($query->getParameter('withAnyOwner')) { | ||||
$join[] = $this->joinRelationship( | $join[] = $this->joinRelationship( | ||||
$conn_r, | $conn, | ||||
$query, | $query, | ||||
'withAnyOwner', | 'withAnyOwner', | ||||
PhabricatorSearchRelationship::RELATIONSHIP_OWNER, | PhabricatorSearchRelationship::RELATIONSHIP_OWNER, | ||||
true); | true); | ||||
} else if ($query->getParameter('withUnowned')) { | } else if ($query->getParameter('withUnowned')) { | ||||
$join[] = $this->joinRelationship( | $join[] = $this->joinRelationship( | ||||
$conn_r, | $conn, | ||||
$query, | $query, | ||||
'withUnowned', | 'withUnowned', | ||||
PhabricatorSearchRelationship::RELATIONSHIP_UNOWNED, | PhabricatorSearchRelationship::RELATIONSHIP_UNOWNED, | ||||
true); | true); | ||||
} else { | } else { | ||||
$join[] = $this->joinRelationship( | $join[] = $this->joinRelationship( | ||||
$conn_r, | $conn, | ||||
$query, | $query, | ||||
'ownerPHIDs', | 'ownerPHIDs', | ||||
PhabricatorSearchRelationship::RELATIONSHIP_OWNER); | PhabricatorSearchRelationship::RELATIONSHIP_OWNER); | ||||
} | } | ||||
$join[] = $this->joinRelationship( | $join[] = $this->joinRelationship( | ||||
$conn_r, | $conn, | ||||
$query, | $query, | ||||
'subscriberPHIDs', | 'subscriberPHIDs', | ||||
PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER); | PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER); | ||||
$join[] = $this->joinRelationship( | $join[] = $this->joinRelationship( | ||||
$conn_r, | $conn, | ||||
$query, | $query, | ||||
'projectPHIDs', | 'projectPHIDs', | ||||
PhabricatorSearchRelationship::RELATIONSHIP_PROJECT); | PhabricatorSearchRelationship::RELATIONSHIP_PROJECT); | ||||
$join[] = $this->joinRelationship( | $join[] = $this->joinRelationship( | ||||
$conn_r, | $conn, | ||||
$query, | $query, | ||||
'repository', | 'repository', | ||||
PhabricatorSearchRelationship::RELATIONSHIP_REPOSITORY); | PhabricatorSearchRelationship::RELATIONSHIP_REPOSITORY); | ||||
$join = array_filter($join); | $select = implode(', ', $select); | ||||
$join = array_filter($join); | |||||
foreach ($join as $key => $clause) { | foreach ($join as $key => $clause) { | ||||
$join[$key] = ' JOIN '.$clause; | $join[$key] = ' JOIN '.$clause; | ||||
} | } | ||||
$join = implode(' ', $join); | $join = implode(' ', $join); | ||||
if ($where) { | if ($where) { | ||||
$where = 'WHERE '.implode(' AND ', $where); | $where = 'WHERE '.implode(' AND ', $where); | ||||
} else { | } else { | ||||
$where = ''; | $where = ''; | ||||
} | } | ||||
$offset = (int)$query->getParameter('offset', 0); | return qsprintf( | ||||
$limit = (int)$query->getParameter('limit', 25); | $conn, | ||||
'SELECT %Q FROM %T document %Q %Q LIMIT 1000', | |||||
$hits = queryfx_all( | $select, | ||||
$conn_r, | $document_table, | ||||
'SELECT | |||||
document.phid | |||||
FROM %T document | |||||
%Q | |||||
%Q | |||||
GROUP BY document.phid | |||||
%Q | |||||
LIMIT %d, %d', | |||||
$t_doc, | |||||
$join, | $join, | ||||
$where, | $where); | ||||
$order, | |||||
$offset, | |||||
$limit); | |||||
return ipull($hits, 'phid'); | |||||
} | } | ||||
protected function joinRelationship( | protected function joinRelationship( | ||||
AphrontDatabaseConnection $conn, | AphrontDatabaseConnection $conn, | ||||
PhabricatorSavedQuery $query, | PhabricatorSavedQuery $query, | ||||
$field, | $field, | ||||
$type, | $type, | ||||
$is_existence = false) { | $is_existence = false) { | ||||
Show All 37 Lines |