Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2891447
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
9 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/applications/feed/query/PhabricatorFeedQuery.php b/src/applications/feed/query/PhabricatorFeedQuery.php
index f8f67f7a3e..ae1da3aba0 100644
--- a/src/applications/feed/query/PhabricatorFeedQuery.php
+++ b/src/applications/feed/query/PhabricatorFeedQuery.php
@@ -1,139 +1,165 @@
<?php
final class PhabricatorFeedQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $filterPHIDs;
private $chronologicalKeys;
+ private $rangeMin;
+ private $rangeMax;
public function withFilterPHIDs(array $phids) {
$this->filterPHIDs = $phids;
return $this;
}
public function withChronologicalKeys(array $keys) {
$this->chronologicalKeys = $keys;
return $this;
}
+ public function withEpochInRange($range_min, $range_max) {
+ $this->rangeMin = $range_min;
+ $this->rangeMax = $range_max;
+ return $this;
+ }
+
public function newResultObject() {
return new PhabricatorFeedStoryData();
}
protected function loadPage() {
// NOTE: We return raw rows from this method, which is a little unusual.
return $this->loadStandardPageRows($this->newResultObject());
}
protected function willFilterPage(array $data) {
return PhabricatorFeedStory::loadAllFromRows($data, $this->getViewer());
}
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = parent::buildJoinClauseParts($conn);
// NOTE: We perform this join unconditionally (even if we have no filter
// PHIDs) to omit rows which have no story references. These story data
// rows are notifications or realtime alerts.
$ref_table = new PhabricatorFeedStoryReference();
$joins[] = qsprintf(
$conn,
'JOIN %T ref ON ref.chronologicalKey = story.chronologicalKey',
$ref_table->getTableName());
return $joins;
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->filterPHIDs !== null) {
$where[] = qsprintf(
$conn,
'ref.objectPHID IN (%Ls)',
$this->filterPHIDs);
}
if ($this->chronologicalKeys !== null) {
// NOTE: We want to use integers in the query so we can take advantage
// of keys, but can't use %d on 32-bit systems. Make sure all the keys
// are integers and then format them raw.
$keys = $this->chronologicalKeys;
foreach ($keys as $key) {
if (!ctype_digit($key)) {
throw new Exception(
pht("Key '%s' is not a valid chronological key!", $key));
}
}
$where[] = qsprintf(
$conn,
'ref.chronologicalKey IN (%Q)',
implode(', ', $keys));
}
+ // NOTE: We may not have 64-bit PHP, so do the shifts in MySQL instead.
+ // From EXPLAIN, it appears like MySQL is smart enough to compute the
+ // result and make use of keys to execute the query.
+
+ if ($this->rangeMin !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'ref.chronologicalKey >= (%d << 32)',
+ $this->rangeMin);
+ }
+
+ if ($this->rangeMax !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'ref.chronologicalKey < (%d << 32)',
+ $this->rangeMax);
+ }
+
return $where;
}
protected function buildGroupClause(AphrontDatabaseConnection $conn) {
if ($this->filterPHIDs !== null) {
return qsprintf($conn, 'GROUP BY ref.chronologicalKey');
} else {
return qsprintf($conn, 'GROUP BY story.chronologicalKey');
}
}
protected function getDefaultOrderVector() {
return array('key');
}
public function getBuiltinOrders() {
return array(
'newest' => array(
'vector' => array('key'),
'name' => pht('Creation (Newest First)'),
'aliases' => array('created'),
),
'oldest' => array(
'vector' => array('-key'),
'name' => pht('Creation (Oldest First)'),
),
);
}
public function getOrderableColumns() {
$table = ($this->filterPHIDs ? 'ref' : 'story');
return array(
'key' => array(
'table' => $table,
'column' => 'chronologicalKey',
'type' => 'string',
'unique' => true,
),
);
}
protected function getPagingValueMap($cursor, array $keys) {
return array(
'key' => $cursor,
);
}
protected function getResultCursor($item) {
if ($item instanceof PhabricatorFeedStory) {
return $item->getChronologicalKey();
}
return $item['chronologicalKey'];
}
protected function getPrimaryTableAlias() {
return 'story';
}
public function getQueryApplicationClass() {
return 'PhabricatorFeedApplication';
}
}
diff --git a/src/applications/feed/query/PhabricatorFeedSearchEngine.php b/src/applications/feed/query/PhabricatorFeedSearchEngine.php
index 6dae9f9c37..d17c756524 100644
--- a/src/applications/feed/query/PhabricatorFeedSearchEngine.php
+++ b/src/applications/feed/query/PhabricatorFeedSearchEngine.php
@@ -1,132 +1,162 @@
<?php
final class PhabricatorFeedSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Feed Stories');
}
public function getApplicationClassName() {
return 'PhabricatorFeedApplication';
}
public function newQuery() {
return new PhabricatorFeedQuery();
}
protected function shouldShowOrderField() {
return false;
}
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorUsersSearchField())
->setLabel(pht('Include Users'))
->setKey('userPHIDs'),
// NOTE: This query is not executed with EdgeLogic, so we can't use
// a fancy logical datasource.
id(new PhabricatorSearchDatasourceField())
->setDatasource(new PhabricatorProjectDatasource())
->setLabel(pht('Include Projects'))
->setKey('projectPHIDs'),
+ id(new PhabricatorSearchDateControlField())
+ ->setLabel(pht('Occurs After'))
+ ->setKey('rangeStart'),
+ id(new PhabricatorSearchDateControlField())
+ ->setLabel(pht('Occurs Before'))
+ ->setKey('rangeEnd'),
// NOTE: This is a legacy field retained only for backward
// compatibility. If the projects field used EdgeLogic, we could use
// `viewerprojects()` to execute an equivalent query.
id(new PhabricatorSearchCheckboxesField())
->setKey('viewerProjects')
->setOptions(
array(
'self' => pht('Include stories about projects I am a member of.'),
)),
);
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
$phids = array();
if ($map['userPHIDs']) {
$phids += array_fuse($map['userPHIDs']);
}
if ($map['projectPHIDs']) {
$phids += array_fuse($map['projectPHIDs']);
}
// NOTE: This value may be `true` for older saved queries, or
// `array('self')` for newer ones.
$viewer_projects = $map['viewerProjects'];
if ($viewer_projects) {
$viewer = $this->requireViewer();
$projects = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withMemberPHIDs(array($viewer->getPHID()))
->execute();
$phids += array_fuse(mpull($projects, 'getPHID'));
}
if ($phids) {
$query->withFilterPHIDs($phids);
}
+ $range_min = $map['rangeStart'];
+ if ($range_min) {
+ $range_min = $range_min->getEpoch();
+ }
+
+ $range_max = $map['rangeEnd'];
+ if ($range_max) {
+ $range_max = $range_max->getEpoch();
+ }
+
+ if ($range_min && $range_max) {
+ if ($range_min > $range_max) {
+ throw new PhabricatorSearchConstraintException(
+ pht(
+ 'The specified "Occurs Before" date is earlier in time than the '.
+ 'specified "Occurs After" date, so this query can never match '.
+ 'any results.'));
+ }
+ }
+
+ if ($range_min || $range_max) {
+ $query->withEpochInRange($range_min, $range_max);
+ }
+
return $query;
}
protected function getURI($path) {
return '/feed/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'all' => pht('All Stories'),
);
if ($this->requireViewer()->isLoggedIn()) {
$names['projects'] = pht('Tags');
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'projects':
return $query->setParameter('viewerProjects', array('self'));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $objects,
PhabricatorSavedQuery $query,
array $handles) {
$builder = new PhabricatorFeedBuilder($objects);
if ($this->isPanelContext()) {
$builder->setShowHovercards(false);
} else {
$builder->setShowHovercards(true);
}
$builder->setUser($this->requireViewer());
$view = $builder->buildView();
$list = phutil_tag_div('phabricator-feed-frame', $view);
$result = new PhabricatorApplicationSearchResultView();
$result->setContent($list);
return $result;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 15:13 (3 w, 1 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1125862
Default Alt Text
(9 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment