Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15325448
D12372.id29703.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
21 KB
Referenced Files
None
Subscribers
None
D12372.id29703.diff
View Options
diff --git a/src/applications/maniphest/editor/ManiphestTransactionEditor.php b/src/applications/maniphest/editor/ManiphestTransactionEditor.php
--- a/src/applications/maniphest/editor/ManiphestTransactionEditor.php
+++ b/src/applications/maniphest/editor/ManiphestTransactionEditor.php
@@ -612,12 +612,13 @@
$query = id(new ManiphestTaskQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
- ->setOrderBy(ManiphestTaskQuery::ORDER_PRIORITY)
->withPriorities(array($priority))
->setLimit(1);
if ($is_end) {
- $query->setReversePaging(true);
+ $query->setOrderVector(array('-priority', '-subpriority', '-id'));
+ } else {
+ $query->setOrderVector(array('priority', 'subpriority', 'id'));
}
$result = $query->executeOne();
@@ -675,13 +676,18 @@
// Get all of the tasks with the same subpriority as the adjacent
// task, including the adjacent task itself.
$shift_base = $adjacent->getSubpriority();
- $shift_all = id(new ManiphestTaskQuery())
+ $query = id(new ManiphestTaskQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
- ->setOrderBy(ManiphestTaskQuery::ORDER_PRIORITY)
->withPriorities(array($adjacent->getPriority()))
- ->withSubpriorities(array($shift_base))
- ->setReversePaging(!$is_after)
- ->execute();
+ ->withSubpriorities(array($shift_base));
+
+ if (!$is_after) {
+ $query->setOrderVector(array('-priority', '-subpriority', '-id'));
+ } else {
+ $query->setOrderVector(array('priority', 'subpriority', 'id'));
+ }
+
+ $shift_all = $query->execute();
$shift_last = last($shift_all);
// Find the subpriority before or after the task at the end of the
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
@@ -21,7 +21,6 @@
private $dateCreatedBefore;
private $dateModifiedAfter;
private $dateModifiedBefore;
- private $reversePaging;
private $fullTextSearch = '';
@@ -54,14 +53,10 @@
private $needSubscriberPHIDs;
private $needProjectPHIDs;
-
private $blockingTasks;
private $blockedTasks;
-
private $projectPolicyCheckFailed = false;
- const DEFAULT_PAGE_SIZE = 1000;
-
public function withAuthors(array $authors) {
$this->authorPHIDs = $authors;
return $this;
@@ -231,6 +226,10 @@
return $this;
}
+ protected function newResultObject() {
+ return new ManiphestTask();
+ }
+
protected function willExecute() {
// Make sure the user can see any projects specified in this
// query FIRST.
@@ -252,6 +251,67 @@
}
$this->projectPHIDs = array_values($this->projectPHIDs);
}
+
+ // If we already have an order vector, use it as provided.
+ // TODO: This is a messy hack to make setOrderVector() stronger than
+ // setPriority().
+ $vector = $this->getOrderVector();
+ $keys = mpull(iterator_to_array($vector), 'getOrderKey');
+ if (array_values($keys) !== array('id')) {
+ return;
+ }
+
+ $parts = array();
+ switch ($this->groupBy) {
+ case self::GROUP_NONE:
+ break;
+ case self::GROUP_PRIORITY:
+ $parts[] = array('priority');
+ break;
+ case self::GROUP_OWNER:
+ $parts[] = array('owner');
+ break;
+ case self::GROUP_STATUS:
+ $parts[] = array('status');
+ break;
+ case self::GROUP_PROJECT:
+ $parts[] = array('project');
+ break;
+ }
+
+ if ($this->applicationSearchOrders) {
+ $columns = array();
+ foreach ($this->applicationSearchOrders as $order) {
+ $part = 'custom:'.$order['key'];
+ if ($order['ascending']) {
+ $part = '-'.$part;
+ }
+ $columns[] = $part;
+ }
+ $columns[] = 'id';
+ $parts[] = $columns;
+ } else {
+ switch ($this->orderBy) {
+ case self::ORDER_PRIORITY:
+ $parts[] = array('priority', 'subpriority', 'id');
+ break;
+ case self::ORDER_CREATED:
+ $parts[] = array('id');
+ break;
+ case self::ORDER_MODIFIED:
+ $parts[] = array('updated', 'id');
+ break;
+ case self::ORDER_TITLE:
+ $parts[] = array('title', 'id');
+ break;
+ }
+ }
+
+ $parts = array_mergev($parts);
+ // We may have a duplicate column if we are both ordering and grouping
+ // by priority.
+ $parts = array_unique($parts);
+ $this->setOrderVector($parts);
}
protected function loadPage() {
@@ -268,7 +328,6 @@
$where[] = $this->buildTaskPHIDsWhereClause($conn);
$where[] = $this->buildStatusWhereClause($conn);
$where[] = $this->buildStatusesWhereClause($conn);
- $where[] = $this->buildPrioritiesWhereClause($conn);
$where[] = $this->buildDependenciesWhereClause($conn);
$where[] = $this->buildAuthorWhereClause($conn);
$where[] = $this->buildOwnerWhereClause($conn);
@@ -306,6 +365,20 @@
$this->dateModifiedBefore);
}
+ if ($this->priorities) {
+ $where[] = qsprintf(
+ $conn,
+ 'task.priority IN (%Ld)',
+ $this->priorities);
+ }
+
+ if ($this->subpriorities) {
+ $where[] = qsprintf(
+ $conn,
+ 'task.subpriority IN (%Lf)',
+ $this->subpriorities);
+ }
+
$where[] = $this->buildPagingClause($conn);
$where = $this->formatWhereClause($where);
@@ -325,13 +398,6 @@
count($this->projectPHIDs));
}
- $order = $this->buildCustomOrderClause($conn);
-
- // TODO: Clean up this nonstandardness.
- if (!$this->getLimit()) {
- $this->setLimit(self::DEFAULT_PAGE_SIZE);
- }
-
$group_column = '';
switch ($this->groupBy) {
case self::GROUP_PROJECT:
@@ -351,7 +417,7 @@
$where,
$this->buildGroupClause($conn),
$having,
- $order,
+ $this->buildOrderClause($conn),
$this->buildLimitClause($conn));
switch ($this->groupBy) {
@@ -504,24 +570,6 @@
return null;
}
- private function buildPrioritiesWhereClause(AphrontDatabaseConnection $conn) {
- if ($this->priorities) {
- return qsprintf(
- $conn,
- 'task.priority IN (%Ld)',
- $this->priorities);
- }
-
- if ($this->subpriorities) {
- return qsprintf(
- $conn,
- 'task.subpriority IN (%Lf)',
- $this->subpriorities);
- }
-
- return null;
- }
-
private function buildAuthorWhereClause(AphrontDatabaseConnection $conn) {
if (!$this->authorPHIDs) {
return null;
@@ -689,107 +737,6 @@
'xproject.dst IS NULL');
}
- private function buildCustomOrderClause(AphrontDatabaseConnection $conn) {
- $reverse = ($this->getBeforeID() xor $this->getReversePaging());
-
- $order = array();
-
- switch ($this->groupBy) {
- case self::GROUP_NONE:
- break;
- case self::GROUP_PRIORITY:
- $order[] = 'task.priority';
- break;
- case self::GROUP_OWNER:
- $order[] = 'task.ownerOrdering';
- break;
- case self::GROUP_STATUS:
- $order[] = 'task.status';
- break;
- case self::GROUP_PROJECT:
- $order[] = '<group.project>';
- break;
- default:
- throw new Exception("Unknown group query '{$this->groupBy}'!");
- }
-
- $app_order = $this->buildApplicationSearchOrders($conn, $reverse);
-
- if (!$app_order) {
- switch ($this->orderBy) {
- case self::ORDER_PRIORITY:
- $order[] = 'task.priority';
- $order[] = 'task.subpriority';
- $order[] = 'task.dateModified';
- break;
- case self::ORDER_CREATED:
- $order[] = 'task.id';
- break;
- case self::ORDER_MODIFIED:
- $order[] = 'task.dateModified';
- break;
- case self::ORDER_TITLE:
- $order[] = 'task.title';
- break;
- default:
- throw new Exception("Unknown order query '{$this->orderBy}'!");
- }
- }
-
- $order = array_unique($order);
-
- if (empty($order) && empty($app_order)) {
- return null;
- }
-
- foreach ($order as $k => $column) {
- switch ($column) {
- case 'subpriority':
- case 'ownerOrdering':
- case 'title':
- if ($reverse) {
- $order[$k] = "{$column} DESC";
- } else {
- $order[$k] = "{$column} ASC";
- }
- break;
- case '<group.project>':
- // Put "No Project" at the end of the list.
- if ($reverse) {
- $order[$k] =
- 'projectGroupName.indexedObjectName IS NULL DESC, '.
- 'projectGroupName.indexedObjectName DESC';
- } else {
- $order[$k] =
- 'projectGroupName.indexedObjectName IS NULL ASC, '.
- 'projectGroupName.indexedObjectName ASC';
- }
- break;
- default:
- if ($reverse) {
- $order[$k] = "{$column} ASC";
- } else {
- $order[$k] = "{$column} DESC";
- }
- break;
- }
- }
-
- if ($app_order) {
- foreach ($app_order as $order_by) {
- $order[] = $order_by;
- }
- }
-
- if ($reverse) {
- $order[] = 'task.id ASC';
- } else {
- $order[] = 'task.id DESC';
- }
-
- return 'ORDER BY '.implode(', ', $order);
- }
-
private function buildJoinsClause(AphrontDatabaseConnection $conn_r) {
$edge_table = PhabricatorEdgeConfig::TABLE_NAME_EDGE;
@@ -954,13 +901,10 @@
switch ($this->groupBy) {
case self::GROUP_NONE:
- return $id;
+ case self::GROUP_STATUS:
case self::GROUP_PRIORITY:
- return $id.'.'.$result->getPriority();
case self::GROUP_OWNER:
- return rtrim($id.'.'.$result->getOwnerPHID(), '.');
- case self::GROUP_STATUS:
- return $id.'.'.$result->getStatus();
+ return $id;
case self::GROUP_PROJECT:
return rtrim($id.'.'.$result->getGroupByProjectPHID(), '.');
default:
@@ -968,156 +912,95 @@
}
}
- protected function buildPagingClause(AphrontDatabaseConnection $conn_r) {
- $before_id = $this->getBeforeID();
- $after_id = $this->getAfterID();
-
- if (!$before_id && !$after_id) {
- return '';
- }
+ public function getOrderableColumns() {
+ return parent::getOrderableColumns() + array(
+ 'priority' => array(
+ 'table' => 'task',
+ 'column' => 'priority',
+ 'type' => 'int',
+ ),
+ 'owner' => array(
+ 'table' => 'task',
+ 'column' => 'ownerOrdering',
+ 'null' => 'head',
+ 'reverse' => true,
+ 'type' => 'string',
+ ),
+ 'status' => array(
+ 'table' => 'task',
+ 'column' => 'status',
+ 'type' => 'string',
+ 'reverse' => true,
+ ),
+ 'project' => array(
+ 'table' => 'projectGroupName',
+ 'column' => 'indexedObjectName',
+ 'type' => 'string',
+ 'null' => 'head',
+ 'reverse' => true,
+ ),
+ 'title' => array(
+ 'table' => 'task',
+ 'column' => 'title',
+ 'type' => 'string',
+ 'reverse' => true,
+ ),
+ 'subpriority' => array(
+ 'table' => 'task',
+ 'column' => 'subpriority',
+ 'type' => 'float',
+ ),
+ 'updated' => array(
+ 'table' => 'task',
+ 'column' => 'dateModified',
+ 'type' => 'int',
+ ),
+ );
+ }
- $cursor_id = nonempty($before_id, $after_id);
- $cursor_parts = explode('.', $cursor_id, 2);
+ protected function getPagingValueMap($cursor, array $keys) {
+ $cursor_parts = explode('.', $cursor, 2);
$task_id = $cursor_parts[0];
$group_id = idx($cursor_parts, 1);
- $cursor = $this->loadCursorObject($task_id);
- if (!$cursor) {
- // We may loop if we have a cursor and don't build a paging clause; fail
- // instead.
- throw new PhabricatorEmptyQueryException();
- }
-
- $columns = array();
-
- switch ($this->groupBy) {
- case self::GROUP_NONE:
- break;
- case self::GROUP_PRIORITY:
- $columns[] = array(
- 'table' => 'task',
- 'column' => 'priority',
- 'value' => (int)$group_id,
- 'type' => 'int',
- );
- break;
- case self::GROUP_OWNER:
- $value = null;
- if ($group_id) {
- $paging_users = id(new PhabricatorPeopleQuery())
- ->setViewer($this->getViewer())
- ->withPHIDs(array($group_id))
- ->execute();
- if ($paging_users) {
- $value = head($paging_users)->getUsername();
- }
- }
- $columns[] = array(
- 'table' => 'task',
- 'column' => 'ownerOrdering',
- 'value' => $value,
- 'type' => 'string',
- 'null' => 'head',
- 'reverse' => true,
- );
- break;
- case self::GROUP_STATUS:
- $columns[] = array(
- 'table' => 'task',
- 'column' => 'status',
- 'value' => $group_id,
- 'type' => 'string',
- );
- break;
- case self::GROUP_PROJECT:
- $value = null;
- if ($group_id) {
- $paging_projects = id(new PhabricatorProjectQuery())
- ->setViewer($this->getViewer())
- ->withPHIDs(array($group_id))
- ->execute();
- if ($paging_projects) {
- $value = head($paging_projects)->getName();
- }
- }
+ $task = $this->loadCursorObject($task_id);
- $columns[] = array(
- 'table' => 'projectGroupName',
- 'column' => 'indexedObjectName',
- 'value' => $value,
- 'type' => 'string',
- 'null' => 'head',
- 'reverse' => true,
- );
- break;
- default:
- throw new Exception("Unknown group query '{$this->groupBy}'!");
- }
+ $map = array(
+ 'id' => $task->getID(),
+ 'priority' => $task->getPriority(),
+ 'subpriority' => $task->getSubpriority(),
+ 'owner' => $task->getOwnerOrdering(),
+ 'status' => $task->getStatus(),
+ 'title' => $task->getTitle(),
+ 'updated' => $task->getDateModified(),
+ );
- $app_columns = $this->buildApplicationSearchPagination($conn_r, $cursor);
- if ($app_columns) {
- $columns = array_merge($columns, $app_columns);
- } else {
- switch ($this->orderBy) {
- case self::ORDER_PRIORITY:
- if ($this->groupBy != self::GROUP_PRIORITY) {
- $columns[] = array(
- 'table' => 'task',
- 'column' => 'priority',
- 'value' => (int)$cursor->getPriority(),
- 'type' => 'int',
- );
+ foreach ($keys as $key) {
+ switch ($key) {
+ case 'project':
+ $value = null;
+ if ($group_id) {
+ $paging_projects = id(new PhabricatorProjectQuery())
+ ->setViewer($this->getViewer())
+ ->withPHIDs(array($group_id))
+ ->execute();
+ if ($paging_projects) {
+ $value = head($paging_projects)->getName();
+ }
}
- $columns[] = array(
- 'table' => 'task',
- 'column' => 'subpriority',
- 'value' => $cursor->getSubpriority(),
- 'type' => 'float',
- );
- $columns[] = array(
- 'table' => 'task',
- 'column' => 'dateModified',
- 'value' => (int)$cursor->getDateModified(),
- 'type' => 'int',
- );
+ $map[$key] = $value;
break;
- case self::ORDER_CREATED:
- // This just uses the ID column, below.
- break;
- case self::ORDER_MODIFIED:
- $columns[] = array(
- 'table' => 'task',
- 'column' => 'dateModified',
- 'value' => (int)$cursor->getDateModified(),
- 'type' => 'int',
- );
- break;
- case self::ORDER_TITLE:
- $columns[] = array(
- 'table' => 'task',
- 'column' => 'title',
- 'value' => $cursor->getTitle(),
- 'type' => 'string',
- );
- break;
- default:
- throw new Exception("Unknown order query '{$this->orderBy}'!");
}
}
- $columns[] = array(
- 'table' => 'task',
- 'column' => 'id',
- 'value' => $cursor->getID(),
- 'type' => 'int',
- );
+ foreach ($keys as $key) {
+ if ($this->isCustomFieldOrderKey($key)) {
+ $map += $this->getPagingValueMapForCustomFields($task);
+ break;
+ }
+ }
- return $this->buildPagingClauseFromMultipleColumns(
- $conn_r,
- $columns,
- array(
- 'reversed' => (bool)($before_id xor $this->getReversePaging()),
- ));
+ return $map;
}
protected function getPrimaryTableAlias() {
@@ -1128,13 +1011,4 @@
return 'PhabricatorManiphestApplication';
}
- public function setReversePaging($reverse_paging) {
- $this->reversePaging = $reverse_paging;
- return $this;
- }
-
- protected function getReversePaging() {
- return $this->reversePaging;
- }
-
}
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
@@ -14,7 +14,7 @@
private $afterID;
private $beforeID;
private $applicationSearchConstraints = array();
- private $applicationSearchOrders = array();
+ protected $applicationSearchOrders = array();
private $internalPaging;
private $orderVector;
@@ -161,6 +161,10 @@
return null;
}
+ protected function newResultObject() {
+ return null;
+ }
+
/* -( Paging )------------------------------------------------------------- */
@@ -546,7 +550,7 @@
return array();
}
- return array(
+ $columns = array(
'id' => array(
'table' => $this->getPrimaryTableAlias(),
'column' => 'id',
@@ -555,6 +559,32 @@
'unique' => true,
),
);
+
+ $object = $this->newResultObject();
+ if ($object instanceof PhabricatorCustomFieldInterface) {
+ $list = PhabricatorCustomField::getObjectFields(
+ $object,
+ PhabricatorCustomField::ROLE_APPLICATIONSEARCH);
+ foreach ($list->getFields() as $field) {
+ $index = $field->buildOrderIndex();
+ if (!$index) {
+ continue;
+ }
+
+ $key = $field->getFieldKey();
+ $digest = $field->getFieldIndex();
+
+ $full_key = 'custom:'.$key;
+ $columns[$full_key] = array(
+ 'table' => 'appsearch_order_'.$digest,
+ 'column' => 'indexValue',
+ 'type' => $index->getIndexValueType(),
+ 'null' => 'tail',
+ );
+ }
+ }
+
+ return $columns;
}
@@ -961,8 +991,8 @@
foreach ($this->applicationSearchOrders as $key => $order) {
$table = $order['table'];
- $alias = 'appsearch_order_'.$key;
$index = $order['index'];
+ $alias = 'appsearch_order_'.$index;
$phid_column = $this->getApplicationSearchObjectPHIDColumn();
$joins[] = qsprintf(
@@ -980,51 +1010,27 @@
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) {
+ protected function getPagingValueMapForCustomFields(
+ PhabricatorCustomFieldInterface $object) {
// We have to get the current field values on the cursor object.
$fields = PhabricatorCustomField::getObjectFields(
- $cursor,
+ $object,
PhabricatorCustomField::ROLE_APPLICATIONSEARCH);
$fields->setViewer($this->getViewer());
- $fields->readFieldsFromStorage($cursor);
-
- $fields = mpull($fields->getFields(), null, 'getFieldKey');
+ $fields->readFieldsFromStorage($object);
- $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'],
- );
+ $map = array();
+ foreach ($fields->getFields() as $field) {
+ $map['custom:'.$field->getFieldKey()] = $field->getValueForStorage();
}
- return $columns;
+ return $map;
+ }
+
+ protected function isCustomFieldOrderKey($key) {
+ $prefix = 'custom:';
+ return !strncmp($key, $prefix, strlen($prefix));
}
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Mar 8, 2:35 AM (4 d, 1 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7365662
Default Alt Text
D12372.id29703.diff (21 KB)
Attached To
Mode
D12372: Modernize ManiphestTask paging and ordering
Attached
Detach File
Event Timeline
Log In to Comment