Summary: Ref T9979. This uses ngrams (specifically, trigrams) to build a reasonably efficient index for substring matching. Specifically, for a package like "Example", with ID 123, we store rows like this: ``` < ex, 123> <exa, 123> <xam, 123> <amp, 123> <mpl, 123> <ple, 123> <le , 123> ``` When the user searches for `exam`, we join this table for packages with tokens `exa` and `xam`. MySQL can do this a lot more efficiently than it can process a `LIKE "%exam%"` query against a huge table. When the user searches for a one-letter or two-letter string, we only search the beginnings of words. This is probably what they want, the only thing we can do quickly, and a reasonable/expected behavior for typeaheads. Test Plan: - Ran storage upgrades and search indexer. - Searched for stuff with "name contains". - Used typehaead and got sensible results. - Searched for `aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz` and saw only 16 joins. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9979 Differential Revision: https://secure.phabricator.com/D14846
179 lines
4.8 KiB
PHP
179 lines
4.8 KiB
PHP
<?php
|
|
|
|
final class PhabricatorOwnersPackageSearchEngine
|
|
extends PhabricatorApplicationSearchEngine {
|
|
|
|
public function getResultTypeDescription() {
|
|
return pht('Owners Packages');
|
|
}
|
|
|
|
public function getApplicationClassName() {
|
|
return 'PhabricatorOwnersApplication';
|
|
}
|
|
|
|
public function newQuery() {
|
|
return new PhabricatorOwnersPackageQuery();
|
|
}
|
|
|
|
protected function buildCustomSearchFields() {
|
|
return array(
|
|
id(new PhabricatorSearchDatasourceField())
|
|
->setLabel(pht('Authority'))
|
|
->setKey('authorityPHIDs')
|
|
->setAliases(array('authority', 'authorities'))
|
|
->setConduitKey('owners')
|
|
->setDescription(
|
|
pht('Search for packages with specific owners.'))
|
|
->setDatasource(new PhabricatorProjectOrUserDatasource()),
|
|
id(new PhabricatorSearchTextField())
|
|
->setLabel(pht('Name Contains'))
|
|
->setKey('name')
|
|
->setDescription(pht('Search for packages by name substrings.')),
|
|
id(new PhabricatorSearchDatasourceField())
|
|
->setLabel(pht('Repositories'))
|
|
->setKey('repositoryPHIDs')
|
|
->setConduitKey('repositories')
|
|
->setAliases(array('repository', 'repositories'))
|
|
->setDescription(
|
|
pht('Search for packages by included repositories.'))
|
|
->setDatasource(new DiffusionRepositoryDatasource()),
|
|
id(new PhabricatorSearchStringListField())
|
|
->setLabel(pht('Paths'))
|
|
->setKey('paths')
|
|
->setAliases(array('path'))
|
|
->setDescription(
|
|
pht('Search for packages affecting specific paths.')),
|
|
id(new PhabricatorSearchCheckboxesField())
|
|
->setKey('statuses')
|
|
->setLabel(pht('Status'))
|
|
->setDescription(
|
|
pht('Search for active or archived packages.'))
|
|
->setOptions(
|
|
id(new PhabricatorOwnersPackage())
|
|
->getStatusNameMap()),
|
|
);
|
|
}
|
|
|
|
protected function buildQueryFromParameters(array $map) {
|
|
$query = $this->newQuery();
|
|
|
|
if ($map['authorityPHIDs']) {
|
|
$query->withAuthorityPHIDs($map['authorityPHIDs']);
|
|
}
|
|
|
|
if ($map['repositoryPHIDs']) {
|
|
$query->withRepositoryPHIDs($map['repositoryPHIDs']);
|
|
}
|
|
|
|
if ($map['paths']) {
|
|
$query->withPaths($map['paths']);
|
|
}
|
|
|
|
if ($map['statuses']) {
|
|
$query->withStatuses($map['statuses']);
|
|
}
|
|
|
|
if (strlen($map['name'])) {
|
|
$query->withNameNgrams($map['name']);
|
|
}
|
|
|
|
return $query;
|
|
}
|
|
|
|
protected function getURI($path) {
|
|
return '/owners/'.$path;
|
|
}
|
|
|
|
protected function getBuiltinQueryNames() {
|
|
$names = array();
|
|
|
|
if ($this->requireViewer()->isLoggedIn()) {
|
|
$names['authority'] = pht('Owned');
|
|
}
|
|
|
|
$names += array(
|
|
'active' => pht('Active Packages'),
|
|
'all' => pht('All Packages'),
|
|
);
|
|
|
|
return $names;
|
|
}
|
|
|
|
public function buildSavedQueryFromBuiltin($query_key) {
|
|
$query = $this->newSavedQuery();
|
|
$query->setQueryKey($query_key);
|
|
|
|
switch ($query_key) {
|
|
case 'all':
|
|
return $query;
|
|
case 'active':
|
|
return $query->setParameter(
|
|
'statuses',
|
|
array(
|
|
PhabricatorOwnersPackage::STATUS_ACTIVE,
|
|
));
|
|
case 'authority':
|
|
return $query->setParameter(
|
|
'authorityPHIDs',
|
|
array($this->requireViewer()->getPHID()));
|
|
}
|
|
|
|
return parent::buildSavedQueryFromBuiltin($query_key);
|
|
}
|
|
|
|
protected function renderResultList(
|
|
array $packages,
|
|
PhabricatorSavedQuery $query,
|
|
array $handles) {
|
|
assert_instances_of($packages, 'PhabricatorOwnersPackage');
|
|
|
|
$viewer = $this->requireViewer();
|
|
|
|
$list = id(new PHUIObjectItemListView())
|
|
->setUser($viewer);
|
|
foreach ($packages as $package) {
|
|
$id = $package->getID();
|
|
|
|
$item = id(new PHUIObjectItemView())
|
|
->setObject($package)
|
|
->setObjectName(pht('Package %d', $id))
|
|
->setHeader($package->getName())
|
|
->setHref('/owners/package/'.$id.'/');
|
|
|
|
if ($package->isArchived()) {
|
|
$item->setDisabled(true);
|
|
}
|
|
|
|
$list->addItem($item);
|
|
}
|
|
|
|
$result = new PhabricatorApplicationSearchResultView();
|
|
$result->setObjectList($list);
|
|
$result->setNoDataString(pht('No packages found.'));
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
protected function getNewUserBody() {
|
|
$create_button = id(new PHUIButtonView())
|
|
->setTag('a')
|
|
->setText(pht('Create a Package'))
|
|
->setHref('/owners/edit/')
|
|
->setColor(PHUIButtonView::GREEN);
|
|
|
|
$icon = $this->getApplication()->getFontIcon();
|
|
$app_name = $this->getApplication()->getName();
|
|
$view = id(new PHUIBigInfoView())
|
|
->setIcon($icon)
|
|
->setTitle(pht('Welcome to %s', $app_name))
|
|
->setDescription(
|
|
pht('Group sections of a codebase into packages for re-use in other '.
|
|
'areas of Phabricator, like Herald rules.'))
|
|
->addAction($create_button);
|
|
|
|
return $view;
|
|
}
|
|
|
|
}
|