Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15396964
D10106.id24326.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Referenced Files
None
Subscribers
None
D10106.id24326.diff
View Options
diff --git a/src/applications/maniphest/query/ManiphestTaskQuery.php b/src/applications/maniphest/query/ManiphestTaskQuery.php
--- a/src/applications/maniphest/query/ManiphestTaskQuery.php
+++ b/src/applications/maniphest/query/ManiphestTaskQuery.php
@@ -572,8 +572,10 @@
}
private function buildCustomOrderClause(AphrontDatabaseConnection $conn) {
- $order = array();
+ $reverse = ($this->getBeforeID() xor $this->getReversePaging());
+ $order = array();
+
switch ($this->groupBy) {
case self::GROUP_NONE:
break;
@@ -593,33 +595,35 @@
throw new Exception("Unknown group query '{$this->groupBy}'!");
}
- switch ($this->orderBy) {
- case self::ORDER_PRIORITY:
- $order[] = 'priority';
- $order[] = 'subpriority';
- $order[] = 'dateModified';
- break;
- case self::ORDER_CREATED:
- $order[] = 'id';
- break;
- case self::ORDER_MODIFIED:
- $order[] = 'dateModified';
- break;
- case self::ORDER_TITLE:
- $order[] = 'title';
- break;
- default:
- throw new Exception("Unknown order query '{$this->orderBy}'!");
+ $app_order = $this->buildApplicationSearchOrders($conn, $reverse);
+
+ if (!$app_order) {
+ switch ($this->orderBy) {
+ case self::ORDER_PRIORITY:
+ $order[] = 'priority';
+ $order[] = 'subpriority';
+ $order[] = 'dateModified';
+ break;
+ case self::ORDER_CREATED:
+ $order[] = 'id';
+ break;
+ case self::ORDER_MODIFIED:
+ $order[] = 'dateModified';
+ break;
+ case self::ORDER_TITLE:
+ $order[] = 'title';
+ break;
+ default:
+ throw new Exception("Unknown order query '{$this->orderBy}'!");
+ }
}
$order = array_unique($order);
- if (empty($order)) {
+ if (empty($order) && empty($app_order)) {
return null;
}
- $reverse = ($this->getBeforeID() xor $this->getReversePaging());
-
foreach ($order as $k => $column) {
switch ($column) {
case 'subpriority':
@@ -653,6 +657,18 @@
}
}
+ if ($app_order) {
+ foreach ($app_order as $order_by) {
+ array_push($order, $order_by);
+ }
+
+ if ($reverse) {
+ $order[] = 'task.id ASC';
+ } else {
+ $order[] = 'task.id DESC';
+ }
+ }
+
return 'ORDER BY '.implode(', ', $order);
}
@@ -903,55 +919,65 @@
throw new Exception("Unknown group query '{$this->groupBy}'!");
}
- switch ($this->orderBy) {
- case self::ORDER_PRIORITY:
- if ($this->groupBy != self::GROUP_PRIORITY) {
+ $app_columns = $this->buildApplicationSearchPagination($conn_r, $cursor);
+ if ($app_columns) {
+ $columns = array_merge($columns, $app_columns);
+ $columns[] = array(
+ 'name' => 'task.id',
+ 'value' => (int)$cursor->getID(),
+ 'type' => 'int',
+ );
+ } else {
+ switch ($this->orderBy) {
+ case self::ORDER_PRIORITY:
+ if ($this->groupBy != self::GROUP_PRIORITY) {
+ $columns[] = array(
+ 'name' => 'task.priority',
+ 'value' => (int)$cursor->getPriority(),
+ 'type' => 'int',
+ );
+ }
$columns[] = array(
- 'name' => 'task.priority',
- 'value' => (int)$cursor->getPriority(),
+ 'name' => 'task.subpriority',
+ 'value' => (int)$cursor->getSubpriority(),
'type' => 'int',
+ 'reverse' => true,
);
- }
- $columns[] = array(
- 'name' => 'task.subpriority',
- 'value' => (int)$cursor->getSubpriority(),
- 'type' => 'int',
- 'reverse' => true,
- );
- $columns[] = array(
- 'name' => 'task.dateModified',
- 'value' => (int)$cursor->getDateModified(),
- 'type' => 'int',
- );
- break;
- case self::ORDER_CREATED:
- $columns[] = array(
- 'name' => 'task.id',
- 'value' => (int)$cursor->getID(),
- 'type' => 'int',
- );
- break;
- case self::ORDER_MODIFIED:
- $columns[] = array(
- 'name' => 'task.dateModified',
- 'value' => (int)$cursor->getDateModified(),
- 'type' => 'int',
- );
- break;
- case self::ORDER_TITLE:
- $columns[] = array(
- 'name' => 'task.title',
- 'value' => $cursor->getTitle(),
- 'type' => 'string',
- );
- $columns[] = array(
- 'name' => 'task.id',
- 'value' => $cursor->getID(),
- 'type' => 'int',
- );
- break;
- default:
- throw new Exception("Unknown order query '{$this->orderBy}'!");
+ $columns[] = array(
+ 'name' => 'task.dateModified',
+ 'value' => (int)$cursor->getDateModified(),
+ 'type' => 'int',
+ );
+ break;
+ case self::ORDER_CREATED:
+ $columns[] = array(
+ 'name' => 'task.id',
+ 'value' => (int)$cursor->getID(),
+ 'type' => 'int',
+ );
+ break;
+ case self::ORDER_MODIFIED:
+ $columns[] = array(
+ 'name' => 'task.dateModified',
+ 'value' => (int)$cursor->getDateModified(),
+ 'type' => 'int',
+ );
+ break;
+ case self::ORDER_TITLE:
+ $columns[] = array(
+ 'name' => 'task.title',
+ 'value' => $cursor->getTitle(),
+ 'type' => 'string',
+ );
+ $columns[] = array(
+ 'name' => 'task.id',
+ 'value' => $cursor->getID(),
+ 'type' => 'int',
+ );
+ break;
+ default:
+ throw new Exception("Unknown order query '{$this->orderBy}'!");
+ }
}
return $this->buildPagingClauseFromMultipleColumns(
diff --git a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php
--- a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php
+++ b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php
@@ -151,13 +151,10 @@
$query->withPriorities($priorities);
}
- $order = $saved->getParameter('order');
- $order = idx($this->getOrderValues(), $order);
- if ($order) {
- $query->setOrderBy($order);
- } else {
- $query->setOrderBy(head($this->getOrderValues()));
- }
+ $this->applyOrderByToQuery(
+ $query,
+ $this->getOrderValues(),
+ $saved->getParameter('order'));
$group = $saved->getParameter('group');
$group = idx($this->getGroupValues(), $group);
@@ -306,6 +303,10 @@
$ids = $saved->getParameter('ids', array());
+ $builtin_orders = $this->getOrderOptions();
+ $custom_orders = $this->getCustomFieldOrderOptions();
+ $all_orders = $builtin_orders + $custom_orders;
+
$form
->appendChild(
id(new AphrontFormTokenizerControl())
@@ -385,7 +386,7 @@
->setName('order')
->setLabel(pht('Order By'))
->setValue($saved->getParameter('order'))
- ->setOptions($this->getOrderOptions()));
+ ->setOptions($all_orders));
}
$form
diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php
--- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php
+++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php
@@ -764,6 +764,65 @@
}
}
+ protected function applyOrderByToQuery(
+ PhabricatorCursorPagedPolicyAwareQuery $query,
+ array $standard_values,
+ $order) {
+
+ if (substr($order, 0, 7) === 'custom:') {
+ $list = $this->getCustomFieldList();
+ if (!$list) {
+ $query->setOrderBy(head($standard_values));
+ return;
+ }
+
+ foreach ($list->getFields() as $field) {
+ $key = $this->getKeyForCustomField($field);
+
+ if ($key === $order) {
+ $index = $field->buildOrderIndex();
+
+ if ($index === null) {
+ $query->setOrderBy(head($standard_values));
+ return;
+ }
+
+ $query->withApplicationSearchOrder(
+ $field,
+ $index,
+ false);
+ break;
+ }
+ }
+ } else {
+ $order = idx($standard_values, $order);
+ if ($order) {
+ $query->setOrderBy($order);
+ } else {
+ $query->setOrderBy(head($standard_values));
+ }
+ }
+ }
+
+
+ protected function getCustomFieldOrderOptions() {
+ $list = $this->getCustomFieldList();
+ if (!$list) {
+ return;
+ }
+
+ $custom_order = array();
+ foreach ($list->getFields() as $field) {
+ if ($field->shouldAppearInApplicationSearch()) {
+ if ($field->buildOrderIndex() !== null) {
+ $key = $this->getKeyForCustomField($field);
+ $custom_order[$key] = $field->getFieldName();
+ }
+ }
+ }
+
+ return $custom_order;
+ }
/**
* Get a unique key identifying a field.
diff --git a/src/infrastructure/customfield/field/PhabricatorCustomField.php b/src/infrastructure/customfield/field/PhabricatorCustomField.php
--- a/src/infrastructure/customfield/field/PhabricatorCustomField.php
+++ b/src/infrastructure/customfield/field/PhabricatorCustomField.php
@@ -621,6 +621,28 @@
/**
+ * Return an index against which this field can be meaningfully ordered
+ * against to implement ApplicationSearch.
+ *
+ * This should be a single index, normally built using
+ * @{method:newStringIndex} and @{method:newNumericIndex}.
+ *
+ * The value of the index is not used.
+ *
+ * Return null from this method if the field can not be ordered.
+ *
+ * @return PhabricatorCustomFieldIndexStorage A single index to order by.
+ * @task appsearch
+ */
+ public function buildOrderIndex() {
+ if ($this->proxy) {
+ return $this->proxy->buildOrderIndex();
+ }
+ return null;
+ }
+
+
+ /**
* Build a new empty storage object for storing string indexes. Normally,
* this should be a concrete subclass of
* @{class:PhabricatorCustomFieldStringIndexStorage}.
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php
@@ -260,6 +260,10 @@
return array();
}
+ public function buildOrderIndex() {
+ return null;
+ }
+
public function readApplicationSearchValueFromRequest(
PhabricatorApplicationSearchEngine $engine,
AphrontRequest $request) {
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php
@@ -18,6 +18,10 @@
return $indexes;
}
+ public function buildOrderIndex() {
+ return $this->newNumericIndex(0);
+ }
+
public function getValueForStorage() {
$value = $this->getFieldValue();
if (strlen($value)) {
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php
@@ -18,6 +18,10 @@
return $indexes;
}
+ public function buildOrderIndex() {
+ return $this->newNumericIndex(0);
+ }
+
public function getValueForStorage() {
$value = $this->getFieldValue();
if (strlen($value)) {
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php
@@ -18,6 +18,10 @@
return $indexes;
}
+ public function buildOrderIndex() {
+ return $this->newNumericIndex(0);
+ }
+
public function getValueForStorage() {
$value = $this->getFieldValue();
if (strlen($value)) {
diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
--- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
+++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
@@ -12,6 +12,7 @@
private $afterID;
private $beforeID;
private $applicationSearchConstraints = array();
+ private $applicationSearchOrders = array();
private $internalPaging;
protected function getPagingColumn() {
@@ -360,6 +361,32 @@
/**
+ * Order the results by an ApplicationSearch index.
+ *
+ * @param PhabricatorCustomField Field to which the index belongs.
+ * @param PhabricatorCustomFieldIndexStorage Table where the index is stored.
+ * @param bool True to sort ascending.
+ * @return this
+ * @task appsearch
+ */
+ public function withApplicationSearchOrder(
+ PhabricatorCustomField $field,
+ PhabricatorCustomFieldIndexStorage $index,
+ $ascending) {
+
+ $this->applicationSearchOrders[] = array(
+ 'key' => $field->getFieldKey(),
+ 'type' => $index->getIndexValueType(),
+ 'table' => $index->getTableName(),
+ 'index' => $index->getIndexKey(),
+ 'ascending' => $ascending,
+ );
+
+ return $this;
+ }
+
+
+ /**
* Get the name of the query's primary object PHID column, for constructing
* JOIN clauses. Normally (and by default) this is just `"phid"`, but if the
* query construction requires a table alias it may be something like
@@ -535,7 +562,72 @@
}
}
+ foreach ($this->applicationSearchOrders as $key => $order) {
+ $table = $order['table'];
+ $alias = 'appsearch_order_'.$key;
+ $index = $order['index'];
+ $phid_column = $this->getApplicationSearchObjectPHIDColumn();
+
+ $joins[] = qsprintf(
+ $conn_r,
+ 'JOIN %T %T ON %T.objectPHID = %Q
+ AND %T.indexKey = %s',
+ $table,
+ $alias,
+ $alias,
+ $phid_column,
+ $alias,
+ $index);
+ }
+
return implode(' ', $joins);
}
+ protected function buildApplicationSearchOrders(
+ AphrontDatabaseConnection $conn_r,
+ $reverse) {
+
+ $orders = array();
+ foreach ($this->applicationSearchOrders as $key => $order) {
+ $alias = 'appsearch_order_'.$key;
+
+ if ($order['ascending'] xor $reverse) {
+ $orders[] = qsprintf($conn_r, '%T.indexValue ASC', $alias);
+ } else {
+ $orders[] = qsprintf($conn_r, '%T.indexValue DESC', $alias);
+ }
+ }
+
+ return $orders;
+ }
+
+ protected function buildApplicationSearchPagination(
+ AphrontDatabaseConnection $conn_r,
+ $cursor) {
+
+ // We have to get the current field values on the cursor object.
+ $fields = PhabricatorCustomField::getObjectFields(
+ $cursor,
+ PhabricatorCustomField::ROLE_APPLICATIONSEARCH);
+ $fields->setViewer($this->getViewer());
+ $fields->readFieldsFromStorage($cursor);
+
+ $fields = mpull($fields->getFields(), null, 'getFieldKey');
+
+ $columns = array();
+ foreach ($this->applicationSearchOrders as $key => $order) {
+ $alias = 'appsearch_order_'.$key;
+
+ $field = idx($fields, $order['key']);
+
+ $columns[] = array(
+ 'name' => $alias.'.indexValue',
+ 'value' => $field->getValueForStorage(),
+ 'type' => $order['type'],
+ );
+ }
+
+ return $columns;
+ }
+
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Mar 17, 5:15 PM (1 w, 1 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7534838
Default Alt Text
D10106.id24326.diff (15 KB)
Attached To
Mode
D10106: Support custom fields in "Order By" for Maniphest
Attached
Detach File
Event Timeline
Log In to Comment