1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-11 15:21:03 +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_ANCESTOR = 'ancestor';
const OPERATOR_EMPTY = 'empty'; const OPERATOR_EMPTY = 'empty';
const OPERATOR_ONLY = 'only'; const OPERATOR_ONLY = 'only';
const OPERATOR_ANY = 'any';
private $operator; private $operator;
private $value; private $value;

View file

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