3 final class PhabricatorFeedTransactionQuery
4 extends PhabricatorCursorPagedPolicyAwareQuery
{
12 public function withPHIDs(array $phids) {
13 $this->phids
= $phids;
17 public function withAuthorPHIDs(array $phids) {
18 $this->authorPHIDs
= $phids;
22 public function withObjectTypes(array $types) {
23 $this->objectTypes
= $types;
27 public function withDateCreatedBetween($min, $max) {
28 $this->createdMin
= $min;
29 $this->createdMax
= $max;
33 public function newResultObject() {
34 // Return an arbitrary valid transaction object. The actual query may
35 // return objects of any subclass of "ApplicationTransaction" when it is
36 // executed, but we need to pick something concrete here to make some
37 // integrations work (like automatic handling of PHIDs in data export).
38 return new PhabricatorUserTransaction();
41 protected function loadPage() {
42 $queries = $this->newTransactionQueries();
46 if ($this->shouldLimitResults()) {
47 $limit = $this->getRawResultLimit();
55 // We're doing a bit of manual work to get paging working, because this
56 // query aggregates the results of a large number of subqueries.
58 // Overall, we're ordering transactions by "<dateCreated, phid>". Ordering
59 // by PHID is not very meaningful, but we don't need the ordering to be
60 // especially meaningful, just consistent. Using PHIDs is easy and does
61 // everything we need it to technically.
63 // To actually configure paging, if we have an external cursor, we load
64 // the internal cursor first. Then we pass it to each subquery and the
65 // subqueries pretend they just loaded a page where it was the last object.
66 // This configures their queries properly and we can aggregate a cohesive
67 // set of results by combining all the queries.
69 $cursor = $this->getExternalCursorString();
70 if ($cursor !== null) {
71 $cursor_object = $this->newInternalCursorFromExternalCursor($cursor);
73 $cursor_object = null;
76 $is_reversed = $this->getIsQueryOrderReversed();
78 $created_min = $this->createdMin
;
79 $created_max = $this->createdMax
;
81 $xaction_phids = $this->phids
;
82 $author_phids = $this->authorPHIDs
;
84 foreach ($queries as $query) {
85 $query->withDateCreatedBetween($created_min, $created_max);
87 if ($xaction_phids !== null) {
88 $query->withPHIDs($xaction_phids);
91 if ($author_phids !== null) {
92 $query->withAuthorPHIDs($author_phids);
95 if ($limit !== null) {
96 $query->setLimit($limit);
99 if ($cursor_object !== null) {
101 ->setAggregatePagingCursor($cursor_object)
102 ->setIsQueryOrderReversed($is_reversed);
105 $query->setOrder('global');
107 $query_xactions = $query->execute();
108 foreach ($query_xactions as $query_xaction) {
109 $xactions[] = $query_xaction;
112 $xactions = msortv($xactions, 'newGlobalSortVector');
114 $xactions = array_reverse($xactions);
117 if ($limit !== null) {
118 $xactions = array_slice($xactions, 0, $limit);
120 // If we've found enough transactions to fill up the entire requested
121 // page size, we can narrow the search window: transactions after the
122 // last transaction we've found so far can't possibly be part of the
125 if (count($xactions) === $limit) {
126 $last_date = last($xactions)->getDateCreated();
128 if ($created_max === null) {
129 $created_max = $last_date;
131 $created_max = min($created_max, $last_date);
134 if ($created_min === null) {
135 $created_min = $last_date;
137 $created_min = max($created_min, $last_date);
147 public function getQueryApplicationClass() {
148 return 'PhabricatorFeedApplication';
151 private function newTransactionQueries() {
152 $viewer = $this->getViewer();
154 $queries = id(new PhutilClassMapQuery())
155 ->setAncestorClass('PhabricatorApplicationTransactionQuery')
160 // If we're querying for specific transaction PHIDs, we only need to
161 // consider queries which may load transactions with subtypes present
164 // For example, if we're loading Maniphest Task transaction PHIDs, we know
165 // we only have to look at Maniphest Task transactions, since other types
166 // of objects will never have the right transaction PHIDs.
168 $xaction_phids = $this->phids
;
169 if ($xaction_phids) {
170 foreach ($xaction_phids as $xaction_phid) {
171 $type_map[phid_get_subtype($xaction_phid)] = true;
175 $object_types = $this->objectTypes
;
177 $object_types = array_fuse($object_types);
181 foreach ($queries as $query) {
182 $query_type = $query->getTemplateApplicationTransaction()
183 ->getApplicationTransactionType();
186 if (!isset($type_map[$query_type])) {
192 if (!isset($object_types[$query_type])) {
197 $results[] = id(clone $query)
199 ->setParentQuery($this);
205 protected function newExternalCursorStringForResult($object) {
206 return (string)$object->getPHID();
209 protected function applyExternalCursorConstraintsToQuery(
210 PhabricatorCursorPagedPolicyAwareQuery
$subquery,
212 $subquery->withPHIDs(array($cursor));