mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 00:32:42 +01:00
Add an "only()" edge logic constraint, meaning "only the other constraints, exactly"
Summary: See PHI57. For example, a query for "ios, only()" finds tags tasked with iOS, exactly, and no other tags. I called this "only()" instead of "exact()" because we use the term/function "Exact" elsewhere with a different meaning, e.g. in Differential. Test Plan: Basic query for a tag: {F5168857} Same query with "only", finds tasks tagged with only that tag: {F5168858} Reviewers: chad Reviewed By: chad Differential Revision: https://secure.phabricator.com/D18543
This commit is contained in:
parent
2abbb59cb4
commit
395a2ed6d1
6 changed files with 146 additions and 1 deletions
|
@ -3686,6 +3686,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectLockTransaction' => 'applications/project/xaction/PhabricatorProjectLockTransaction.php',
|
||||
'PhabricatorProjectLogicalAncestorDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalAncestorDatasource.php',
|
||||
'PhabricatorProjectLogicalDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalDatasource.php',
|
||||
'PhabricatorProjectLogicalOnlyDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalOnlyDatasource.php',
|
||||
'PhabricatorProjectLogicalOrNotDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php',
|
||||
'PhabricatorProjectLogicalUserDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php',
|
||||
'PhabricatorProjectLogicalViewerDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php',
|
||||
|
@ -9179,6 +9180,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectLockTransaction' => 'PhabricatorProjectTransactionType',
|
||||
'PhabricatorProjectLogicalAncestorDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorProjectLogicalDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorProjectLogicalOnlyDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorProjectLogicalOrNotDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorProjectLogicalUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorProjectLogicalViewerDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
|
|
|
@ -21,6 +21,7 @@ final class PhabricatorProjectLogicalDatasource
|
|||
new PhabricatorProjectLogicalAncestorDatasource(),
|
||||
new PhabricatorProjectLogicalOrNotDatasource(),
|
||||
new PhabricatorProjectLogicalViewerDatasource(),
|
||||
new PhabricatorProjectLogicalOnlyDatasource(),
|
||||
new PhabricatorProjectLogicalUserDatasource(),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorProjectLogicalOnlyDatasource
|
||||
extends PhabricatorTypeaheadDatasource {
|
||||
|
||||
public function getBrowseTitle() {
|
||||
return pht('Browse Only');
|
||||
}
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type only()...');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return 'PhabricatorProjectApplication';
|
||||
}
|
||||
|
||||
public function getDatasourceFunctions() {
|
||||
return array(
|
||||
'only' => array(
|
||||
'name' => pht('Only Match Other Constraints'),
|
||||
'summary' => pht(
|
||||
'Find results with only the specified tags.'),
|
||||
'description' => pht(
|
||||
"This function is used with other tags, and causes the query to ".
|
||||
"match only results with exactly those tags. For example, to find ".
|
||||
"tasks tagged only iOS:".
|
||||
"\n\n".
|
||||
"> ios, only()".
|
||||
"\n\n".
|
||||
"This will omit results with any other project tag."),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function loadResults() {
|
||||
$results = array(
|
||||
$this->renderOnlyFunctionToken(),
|
||||
);
|
||||
return $this->filterResultsAgainstTokens($results);
|
||||
}
|
||||
|
||||
protected function evaluateFunction($function, array $argv_list) {
|
||||
$results = array();
|
||||
|
||||
$results[] = new PhabricatorQueryConstraint(
|
||||
PhabricatorQueryConstraint::OPERATOR_ONLY,
|
||||
null);
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function renderFunctionTokens(
|
||||
$function,
|
||||
array $argv_list) {
|
||||
|
||||
$tokens = array();
|
||||
foreach ($argv_list as $argv) {
|
||||
$tokens[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult(
|
||||
$this->renderOnlyFunctionToken());
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
private function renderOnlyFunctionToken() {
|
||||
return $this->newFunctionResult()
|
||||
->setName(pht('Only'))
|
||||
->setPHID('only()')
|
||||
->setIcon('fa-asterisk')
|
||||
->setUnique(true)
|
||||
->addAttribute(
|
||||
pht('Select only results with exactly the other specified tags.'));
|
||||
}
|
||||
|
||||
}
|
|
@ -29,7 +29,7 @@ final class PhabricatorProjectLogicalViewerDatasource
|
|||
"\n\n".
|
||||
"This normally means //your// projects, but if you save a query ".
|
||||
"using this function and send it to someone else, it will mean ".
|
||||
"//their// projects when they run it (they become the currnet ".
|
||||
"//their// projects when they run it (they become the current ".
|
||||
"viewer). This can be useful for building dashboard panels."),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -8,6 +8,7 @@ final class PhabricatorQueryConstraint extends Phobject {
|
|||
const OPERATOR_NULL = 'null';
|
||||
const OPERATOR_ANCESTOR = 'ancestor';
|
||||
const OPERATOR_EMPTY = 'empty';
|
||||
const OPERATOR_ONLY = 'only';
|
||||
|
||||
private $operator;
|
||||
private $value;
|
||||
|
|
|
@ -2069,6 +2069,27 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
|||
$op_null = PhabricatorQueryConstraint::OPERATOR_NULL;
|
||||
$has_null = isset($constraints[$op_null]);
|
||||
|
||||
// If we're going to process an only() operator, build a list of the
|
||||
// acceptable set of PHIDs first. We'll only match results which have
|
||||
// no edges to any other PHIDs.
|
||||
$all_phids = array();
|
||||
if (isset($constraints[PhabricatorQueryConstraint::OPERATOR_ONLY])) {
|
||||
foreach ($constraints as $operator => $list) {
|
||||
switch ($operator) {
|
||||
case PhabricatorQueryConstraint::OPERATOR_ANCESTOR:
|
||||
case PhabricatorQueryConstraint::OPERATOR_AND:
|
||||
case PhabricatorQueryConstraint::OPERATOR_OR:
|
||||
foreach ($list as $constraint) {
|
||||
$value = (array)$constraint->getValue();
|
||||
foreach ($value as $v) {
|
||||
$all_phids[$v] = $v;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($constraints as $operator => $list) {
|
||||
$alias = $this->getEdgeLogicTableAlias($operator, $type);
|
||||
|
||||
|
@ -2133,6 +2154,20 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
|||
$alias,
|
||||
$type);
|
||||
break;
|
||||
case PhabricatorQueryConstraint::OPERATOR_ONLY:
|
||||
$joins[] = qsprintf(
|
||||
$conn,
|
||||
'LEFT JOIN %T %T ON %Q = %T.src AND %T.type = %d
|
||||
AND %T.dst NOT IN (%Ls)',
|
||||
$edge_table,
|
||||
$alias,
|
||||
$phid_column,
|
||||
$alias,
|
||||
$alias,
|
||||
$type,
|
||||
$alias,
|
||||
$all_phids);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2159,6 +2194,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
|||
$alias = $this->getEdgeLogicTableAlias($operator, $type);
|
||||
switch ($operator) {
|
||||
case PhabricatorQueryConstraint::OPERATOR_NOT:
|
||||
case PhabricatorQueryConstraint::OPERATOR_ONLY:
|
||||
$full[] = qsprintf(
|
||||
$conn,
|
||||
'%T.dst IS NULL',
|
||||
|
@ -2258,6 +2294,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
|||
// discussion, see T12753.
|
||||
return true;
|
||||
case PhabricatorQueryConstraint::OPERATOR_NULL:
|
||||
case PhabricatorQueryConstraint::OPERATOR_ONLY:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -2375,6 +2412,34 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
|||
}
|
||||
}
|
||||
|
||||
$op_and = PhabricatorQueryConstraint::OPERATOR_AND;
|
||||
$op_or = PhabricatorQueryConstraint::OPERATOR_OR;
|
||||
$op_ancestor = PhabricatorQueryConstraint::OPERATOR_ANCESTOR;
|
||||
|
||||
foreach ($this->edgeLogicConstraints as $type => $constraints) {
|
||||
foreach ($constraints as $operator => $list) {
|
||||
switch ($operator) {
|
||||
case PhabricatorQueryConstraint::OPERATOR_ONLY:
|
||||
if (count($list) > 1) {
|
||||
throw new PhabricatorEmptyQueryException(
|
||||
pht(
|
||||
'This query specifies only() more than once.'));
|
||||
}
|
||||
|
||||
$have_and = idx($constraints, $op_and);
|
||||
$have_or = idx($constraints, $op_or);
|
||||
$have_ancestor = idx($constraints, $op_ancestor);
|
||||
if (!$have_and && !$have_or && !$have_ancestor) {
|
||||
throw new PhabricatorEmptyQueryException(
|
||||
pht(
|
||||
'This query specifies only(), but no other constraints '.
|
||||
'which it can apply to.'));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->edgeLogicConstraintsAreValid = true;
|
||||
|
||||
return $this;
|
||||
|
|
Loading…
Reference in a new issue