1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-23 14:00:56 +01:00

Support some QueryConstraint operations against generic ApplicationSearch query logic

Summary: Ref T13090. Currently, it isn't possible to query custom fields for complex constraints. Lay the groundwork to implement some of the easy ones (none(), any()) for Datasource/PHID fields.

Test Plan: Hard-coded some constraints and queried with them; see next change for more substantial testing.

Maniphest Tasks: T13090

Differential Revision: https://secure.phabricator.com/D19126
This commit is contained in:
epriestley 2018-02-22 12:16:11 -08:00
parent 4cb62ca0d6
commit d0591f3680
2 changed files with 126 additions and 32 deletions

View file

@ -9,6 +9,7 @@ final class PhabricatorQueryConstraint extends Phobject {
const OPERATOR_ANCESTOR = 'ancestor';
const OPERATOR_EMPTY = 'empty';
const OPERATOR_ONLY = 'only';
const OPERATOR_ANY = 'any';
private $operator;
private $value;

View file

@ -342,6 +342,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
$where[] = $this->buildSpacesWhereClause($conn);
$where[] = $this->buildNgramsWhereClause($conn);
$where[] = $this->buildFerretWhereClause($conn);
$where[] = $this->buildApplicationSearchWhereClause($conn);
return $where;
}
@ -1158,12 +1159,29 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
PhabricatorCustomFieldIndexStorage $index,
$value) {
$values = (array)$value;
$data_values = array();
$constraint_values = array();
foreach ($values as $value) {
if ($value instanceof PhabricatorQueryConstraint) {
$constraint_values[] = $value;
} else {
$data_values[] = $value;
}
}
$alias = 'appsearch_'.count($this->applicationSearchConstraints);
$this->applicationSearchConstraints[] = array(
'type' => $index->getIndexValueType(),
'cond' => '=',
'table' => $index->getTableName(),
'index' => $index->getIndexKey(),
'value' => $value,
'alias' => $alias,
'value' => $values,
'data' => $data_values,
'constraints' => $constraint_values,
);
return $this;
@ -1203,11 +1221,14 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
'int'));
}
$alias = 'appsearch_'.count($this->applicationSearchConstraints);
$this->applicationSearchConstraints[] = array(
'type' => $index->getIndexValueType(),
'cond' => 'range',
'table' => $index->getTableName(),
'index' => $index->getIndexKey(),
'alias' => $alias,
'value' => array($min, $max),
);
@ -1256,7 +1277,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
switch ($type) {
case 'string':
case 'int':
if (count((array)$value) > 1) {
if (count($value) > 1) {
return true;
}
break;
@ -1309,49 +1330,39 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
* @task appsearch
*/
protected function buildApplicationSearchJoinClause(
AphrontDatabaseConnection $conn_r) {
AphrontDatabaseConnection $conn) {
$joins = array();
foreach ($this->applicationSearchConstraints as $key => $constraint) {
$table = $constraint['table'];
$alias = 'appsearch_'.$key;
$alias = $constraint['alias'];
$index = $constraint['index'];
$cond = $constraint['cond'];
$phid_column = $this->getApplicationSearchObjectPHIDColumn();
switch ($cond) {
case '=':
$type = $constraint['type'];
switch ($type) {
case 'string':
$constraint_clause = qsprintf(
$conn_r,
'%T.indexValue IN (%Ls)',
$alias,
(array)$constraint['value']);
// Figure out whether we need to do a LEFT JOIN or not. We need to
// LEFT JOIN if we're going to select "IS NULL" rows.
$join_type = 'JOIN';
foreach ($constraint['constraints'] as $query_constraint) {
$op = $query_constraint->getOperator();
if ($op === PhabricatorQueryConstraint::OPERATOR_NULL) {
$join_type = 'LEFT JOIN';
break;
case 'int':
$constraint_clause = qsprintf(
$conn_r,
'%T.indexValue IN (%Ld)',
$alias,
(array)$constraint['value']);
break;
default:
throw new Exception(pht('Unknown index type "%s"!', $type));
}
}
$joins[] = qsprintf(
$conn_r,
'JOIN %T %T ON %T.objectPHID = %Q
AND %T.indexKey = %s
AND (%Q)',
$conn,
'%Q %T %T ON %T.objectPHID = %Q
AND %T.indexKey = %s',
$join_type,
$table,
$alias,
$alias,
$phid_column,
$alias,
$index,
$constraint_clause);
$index);
break;
case 'range':
list($min, $max) = $constraint['value'];
@ -1362,19 +1373,19 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
if ($min === null) {
$constraint_clause = qsprintf(
$conn_r,
$conn,
'%T.indexValue <= %d',
$alias,
$max);
} else if ($max === null) {
$constraint_clause = qsprintf(
$conn_r,
$conn,
'%T.indexValue >= %d',
$alias,
$min);
} else {
$constraint_clause = qsprintf(
$conn_r,
$conn,
'%T.indexValue BETWEEN %d AND %d',
$alias,
$min,
@ -1382,7 +1393,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
}
$joins[] = qsprintf(
$conn_r,
$conn,
'JOIN %T %T ON %T.objectPHID = %Q
AND %T.indexKey = %s
AND (%Q)',
@ -1414,7 +1425,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
$key = $spec['customfield.index.key'];
$joins[] = qsprintf(
$conn_r,
$conn,
'LEFT JOIN %T %T ON %T.objectPHID = %Q
AND %T.indexKey = %s',
$table,
@ -1428,6 +1439,88 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
return implode(' ', $joins);
}
/**
* Construct a WHERE clause appropriate for applying ApplicationSearch
* constraints.
*
* @param AphrontDatabaseConnection Connection executing the query.
* @return list<string> Where clause parts.
* @task appsearch
*/
protected function buildApplicationSearchWhereClause(
AphrontDatabaseConnection $conn) {
$where = array();
foreach ($this->applicationSearchConstraints as $key => $constraint) {
$alias = $constraint['alias'];
$cond = $constraint['cond'];
$type = $constraint['type'];
$data_values = $constraint['data'];
$constraint_values = $constraint['constraints'];
$constraint_parts = array();
switch ($cond) {
case '=':
if ($data_values) {
switch ($type) {
case 'string':
$constraint_parts[] = qsprintf(
$conn,
'%T.indexValue IN (%Ls)',
$alias,
$data_values);
break;
case 'int':
$constraint_parts[] = qsprintf(
$conn,
'%T.indexValue IN (%Ld)',
$alias,
$data_values);
break;
default:
throw new Exception(pht('Unknown index type "%s"!', $type));
}
}
if ($constraint_values) {
foreach ($constraint_values as $value) {
$op = $value->getOperator();
switch ($op) {
case PhabricatorQueryConstraint::OPERATOR_NULL:
$constraint_parts[] = qsprintf(
$conn,
'%T.indexValue IS NULL',
$alias);
break;
case PhabricatorQueryConstraint::OPERATOR_ANY:
$constraint_parts[] = qsprintf(
$conn,
'%T.indexValue IS NOT NULL',
$alias);
break;
default:
throw new Exception(
pht(
'No support for applying operator "%s" against '.
'index of type "%s".',
$op,
$type));
}
}
}
if ($constraint_parts) {
$where[] = '('.implode(') OR (', $constraint_parts).')';
}
break;
}
}
return $where;
}
/* -( Integration with CustomField )--------------------------------------- */