diff --git a/src/applications/people/query/PhabricatorPeopleQuery.php b/src/applications/people/query/PhabricatorPeopleQuery.php --- a/src/applications/people/query/PhabricatorPeopleQuery.php +++ b/src/applications/people/query/PhabricatorPeopleQuery.php @@ -15,6 +15,7 @@ private $isDisabled; private $isApproved; private $nameLike; + private $nameTokens; private $needPrimaryEmail; private $needProfile; @@ -81,6 +82,11 @@ return $this; } + public function withNameTokens(array $tokens) { + $this->nameTokens = array_values($tokens); + return $this; + } + public function needPrimaryEmail($need) { $this->needPrimaryEmail = $need; return $this; @@ -111,7 +117,7 @@ $table->getTableName(), $this->buildJoinsClause($conn_r), $this->buildWhereClause($conn_r), - $this->buildApplicationSearchGroupClause($conn_r), + $this->buildGroupClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); @@ -180,6 +186,16 @@ return $users; } + private function buildGroupClause(AphrontDatabaseConnection $conn) { + if ($this->nameTokens) { + return qsprintf( + $conn, + 'GROUP BY user.id'); + } else { + return $this->buildApplicationSearchGroupClause($conn); + } + } + private function buildJoinsClause($conn_r) { $joins = array(); @@ -191,6 +207,20 @@ $email_table->getTableName()); } + if ($this->nameTokens) { + foreach ($this->nameTokens as $key => $token) { + $token_table = 'token_'.$key; + $joins[] = qsprintf( + $conn_r, + 'JOIN %T %T ON %T.userID = user.id AND %T.token LIKE %>', + PhabricatorUser::NAMETOKEN_TABLE, + $token_table, + $token_table, + $token_table, + $token); + } + } + $joins[] = $this->buildApplicationSearchJoinClause($conn_r); $joins = implode(' ', $joins); @@ -296,4 +326,25 @@ return 'PhabricatorPeopleApplication'; } + public function getOrderableColumns() { + return parent::getOrderableColumns() + array( + 'username' => array( + 'table' => 'user', + 'column' => 'username', + 'type' => 'string', + 'reverse' => true, + 'unique' => true, + ), + ); + } + + public function getPagingValueMap($cursor, array $keys) { + $user = $this->loadCursorObject($cursor); + return array( + 'id' => $user->getID(), + 'username' => $user->getUsername(), + ); + } + + } diff --git a/src/applications/people/typeahead/PhabricatorPeopleDatasource.php b/src/applications/people/typeahead/PhabricatorPeopleDatasource.php --- a/src/applications/people/typeahead/PhabricatorPeopleDatasource.php +++ b/src/applications/people/typeahead/PhabricatorPeopleDatasource.php @@ -25,64 +25,17 @@ public function loadResults() { $viewer = $this->getViewer(); - $raw_query = $this->getRawQuery(); + $tokens = $this->getTokens(); - $results = array(); - - $users = array(); - if (strlen($raw_query)) { - // This is an arbitrary limit which is just larger than any limit we - // actually use in the application. - - // TODO: The datasource should pass this in the query. - $limit = 15; - - $user_table = new PhabricatorUser(); - $conn_r = $user_table->establishConnection('r'); - $ids = queryfx_all( - $conn_r, - 'SELECT id FROM %T WHERE username LIKE %> - ORDER BY username ASC LIMIT %d', - $user_table->getTableName(), - $raw_query, - $limit); - $ids = ipull($ids, 'id'); - - if (count($ids) < $limit) { - // If we didn't find enough username hits, look for real name hits. - // We need to pull the entire pagesize so that we end up with the - // right number of items if this query returns many duplicate IDs - // that we've already selected. - - $realname_ids = queryfx_all( - $conn_r, - 'SELECT DISTINCT userID FROM %T WHERE token LIKE %> - ORDER BY token ASC LIMIT %d', - PhabricatorUser::NAMETOKEN_TABLE, - $raw_query, - $limit); - $realname_ids = ipull($realname_ids, 'userID'); - $ids = array_merge($ids, $realname_ids); - - $ids = array_unique($ids); - $ids = array_slice($ids, 0, $limit); - } - - // Always add the logged-in user because some tokenizers autosort them - // first. They'll be filtered out on the client side if they don't - // match the query. - if ($viewer->getID()) { - $ids[] = $viewer->getID(); - } + $query = id(new PhabricatorPeopleQuery()) + ->setOrderVector(array('username')); - if ($ids) { - $users = id(new PhabricatorPeopleQuery()) - ->setViewer($viewer) - ->withIDs($ids) - ->execute(); - } + if ($tokens) { + $query->withNameTokens($tokens); } + $users = $this->executeQuery($query); + if ($this->enrichResults && $users) { $phids = mpull($users, 'getPHID'); $handles = id(new PhabricatorHandleQuery()) @@ -91,6 +44,7 @@ ->execute(); } + $results = array(); foreach ($users as $user) { $closed = null; if ($user->getIsDisabled()) { diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php @@ -28,12 +28,14 @@ } $results = array_mergev($results); - $results = msort($results, 'getName'); + $results = msort($results, 'getSortKey'); + $count = count($results); if ($offset || $limit) { if (!$limit) { $limit = count($results); } + $results = array_slice($results, $offset, $limit, $preserve_keys = true); } diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php @@ -88,4 +88,18 @@ return array_unique($tokens); } + public function getTokens() { + return self::tokenizeString($this->getRawQuery()); + } + + protected function executeQuery( + PhabricatorCursorPagedPolicyAwareQuery $query) { + + return $query + ->setViewer($this->getViewer()) + ->setOffset($this->getOffset()) + ->setLimit($this->getLimit()) + ->execute(); + } + } diff --git a/src/applications/typeahead/storage/PhabricatorTypeaheadResult.php b/src/applications/typeahead/storage/PhabricatorTypeaheadResult.php --- a/src/applications/typeahead/storage/PhabricatorTypeaheadResult.php +++ b/src/applications/typeahead/storage/PhabricatorTypeaheadResult.php @@ -85,6 +85,10 @@ return $this->phid; } + public function getSortKey() { + return phutil_utf8_strtolower($this->getName()); + } + public function getWireFormat() { $data = array( $this->name,