Page MenuHomePhabricator

D12589.id.diff
No OneTemporary

D12589.id.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -4807,6 +4807,7 @@
'PhabricatorPolicyInterface',
'PhabricatorMarkupInterface',
'PhabricatorApplicationTransactionInterface',
+ 'PhabricatorSubscribableInterface',
),
'PhabricatorCalendarEventDeleteController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventEditController' => 'PhabricatorCalendarController',
diff --git a/src/applications/conpherence/query/ConpherenceFulltextQuery.php b/src/applications/conpherence/query/ConpherenceFulltextQuery.php
--- a/src/applications/conpherence/query/ConpherenceFulltextQuery.php
+++ b/src/applications/conpherence/query/ConpherenceFulltextQuery.php
@@ -4,6 +4,7 @@
extends PhabricatorOffsetPagedQuery {
private $threadPHIDs;
+ private $previousTransactionPHIDs;
private $fulltext;
public function withThreadPHIDs(array $phids) {
@@ -11,6 +12,11 @@
return $this;
}
+ public function withPreviousTransactionPHIDs(array $phids) {
+ $this->previousTransactionPHIDs = $phids;
+ return $this;
+ }
+
public function withFulltext($fulltext) {
$this->fulltext = $fulltext;
return $this;
@@ -42,6 +48,13 @@
$this->threadPHIDs);
}
+ if ($this->previousTransactionPHIDs !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'i.previousTransactionPHID IN (%Ls)',
+ $this->previousTransactionPHIDs);
+ }
+
if (strlen($this->fulltext)) {
$where[] = qsprintf(
$conn_r,
diff --git a/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php b/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php
--- a/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php
+++ b/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php
@@ -149,6 +149,27 @@
$viewer,
$conpherences);
+ $fulltext = $query->getParameter('fulltext');
+ if (strlen($fulltext) && $conpherences) {
+ $context = $this->loadContextMessages($conpherences, $fulltext);
+
+ $author_phids = array();
+ foreach ($context as $messages) {
+ foreach ($messages as $group) {
+ foreach ($group as $message) {
+ $xaction = $message['xaction'];
+ if ($xaction) {
+ $author_phids[] = $xaction->getAuthorPHID();
+ }
+ }
+ }
+ }
+
+ $handles = $viewer->loadHandles($author_phids);
+ } else {
+ $context = array();
+ }
+
$list = new PHUIObjectItemListView();
$list->setUser($viewer);
foreach ($conpherences as $conpherence) {
@@ -181,6 +202,47 @@
phabricator_datetime($conpherence->getDateModified(), $viewer)),
));
+ $messages = idx($context, $conpherence->getPHID());
+ if ($messages) {
+
+ // TODO: This is egregiously under-designed.
+
+ foreach ($messages as $group) {
+ $rows = array();
+ $rowc = array();
+ foreach ($group as $message) {
+ $xaction = $message['xaction'];
+ if (!$xaction) {
+ continue;
+ }
+
+ $rowc[] = ($message['match'] ? 'highlighted' : null);
+ $rows[] = array(
+ $handles->renderHandle($xaction->getAuthorPHID()),
+ $xaction->getComment()->getContent(),
+ phabricator_datetime($xaction->getDateCreated(), $viewer),
+ );
+ }
+ $table = id(new AphrontTableView($rows))
+ ->setHeaders(
+ array(
+ pht('User'),
+ pht('Message'),
+ pht('At'),
+ ))
+ ->setRowClasses($rowc)
+ ->setColumnClasses(
+ array(
+ '',
+ 'wide',
+ ));
+ $box = id(new PHUIBoxView())
+ ->appendChild($table)
+ ->addMargin(PHUI::MARGIN_SMALL);
+ $item->appendChild($box);
+ }
+ }
+
$list->addItem($item);
}
@@ -195,4 +257,209 @@
);
}
+ private function loadContextMessages(array $threads, $fulltext) {
+ $phids = mpull($threads, 'getPHID');
+
+ // We want to load a few messages for each thread in the result list, to
+ // show some of the actual content hits to help the user find what they
+ // are looking for.
+
+ // This method is trying to batch this lookup in most cases, so we do
+ // between one and "a handful" of queries instead of one per thread in
+ // most cases. To do this:
+ //
+ // - Load a big block of results for all of the threads.
+ // - If we didn't get a full block back, we have everything that matches
+ // the query. Sort it out and exit.
+ // - Otherwise, some threads had a ton of hits, so we might not be
+ // getting everything we want (we could be getting back 1,000 hits for
+ // the first thread). Remove any threads which we have enough results
+ // for and try again.
+ // - Repeat until we have everything or every thread has enough results.
+ //
+ // In the worst case, we could end up degrading to one query per thread,
+ // but this is incredibly unlikely on real data.
+
+ // Size of the result blocks we're going to load.
+ $limit = 1000;
+
+ // Number of messages we want for each thread.
+ $want = 3;
+
+ $need = $phids;
+ $hits = array();
+ while ($need) {
+ $rows = id(new ConpherenceFulltextQuery())
+ ->withThreadPHIDs($need)
+ ->withFulltext($fulltext)
+ ->setLimit($limit)
+ ->execute();
+
+ foreach ($rows as $row) {
+ $hits[$row['threadPHID']][] = $row;
+ }
+
+ if (count($rows) < $limit) {
+ break;
+ }
+
+ foreach ($need as $key => $phid) {
+ if (count($hits[$phid]) >= $want) {
+ unset($need[$key]);
+ }
+ }
+ }
+
+ // Now that we have all the fulltext matches, throw away any extras that we
+ // aren't going to render so we don't need to do lookups on them.
+ foreach ($hits as $phid => $rows) {
+ if (count($rows) > $want) {
+ $hits[$phid] = array_slice($rows, 0, $want);
+ }
+ }
+
+ // For each fulltext match, we want to render a message before and after
+ // the match to give it some context. We already know the transactions
+ // before each match because the rows have a "previousTransactionPHID",
+ // but we need to do one more query to figure out the transactions after
+ // each match.
+
+ // Collect the transactions we want to find the next transactions for.
+ $after = array();
+ foreach ($hits as $phid => $rows) {
+ foreach ($rows as $row) {
+ $after[] = $row['transactionPHID'];
+ }
+ }
+
+ // Look up the next transactions.
+ if ($after) {
+ $after_rows = id(new ConpherenceFulltextQuery())
+ ->withPreviousTransactionPHIDs($after)
+ ->execute();
+ } else {
+ $after_rows = array();
+ }
+
+ // Build maps from PHIDs to the previous and next PHIDs.
+ $prev_map = array();
+ $next_map = array();
+ foreach ($after_rows as $row) {
+ $next_map[$row['previousTransactionPHID']] = $row['transactionPHID'];
+ }
+
+ foreach ($hits as $phid => $rows) {
+ foreach ($rows as $row) {
+ $prev = $row['previousTransactionPHID'];
+ if ($prev) {
+ $prev_map[$row['transactionPHID']] = $prev;
+ $next_map[$prev] = $row['transactionPHID'];
+ }
+ }
+ }
+
+ // Now we're going to collect the actual transaction PHIDs, in order, that
+ // we want to show for each thread.
+ $groups = array();
+ foreach ($hits as $thread_phid => $rows) {
+ $rows = ipull($rows, null, 'transactionPHID');
+ foreach ($rows as $phid => $row) {
+ unset($rows[$phid]);
+
+ $group = array();
+
+ // Walk backward, finding all the previous results. We can just keep
+ // going until we run out of results because we've only loaded things
+ // that we want to show.
+ $prev = $phid;
+ while (true) {
+ if (!isset($prev_map[$prev])) {
+ // No previous transaction, so we're done.
+ break;
+ }
+
+ $prev = $prev_map[$prev];
+
+ if (isset($rows[$prev])) {
+ $match = true;
+ unset($rows[$prev]);
+ } else {
+ $match = false;
+ }
+
+ $group[] = array(
+ 'phid' => $prev,
+ 'match' => $match,
+ );
+ }
+
+ if (count($group) > 1) {
+ $group = array_reverse($group);
+ }
+
+ $group[] = array(
+ 'phid' => $phid,
+ 'match' => true,
+ );
+
+ $next = $phid;
+ while (true) {
+ if (!isset($next_map[$next])) {
+ break;
+ }
+
+ $next = $next_map[$next];
+
+ if (isset($rows[$next])) {
+ $match = true;
+ unset($rows[$next]);
+ } else {
+ $match = false;
+ }
+
+ $group[] = array(
+ 'phid' => $next,
+ 'match' => $match,
+ );
+ }
+
+ $groups[$thread_phid][] = $group;
+ }
+ }
+
+ // Load all the actual transactions we need.
+ $xaction_phids = array();
+ foreach ($groups as $thread_phid => $group) {
+ foreach ($group as $list) {
+ foreach ($list as $item) {
+ $xaction_phids[] = $item['phid'];
+ }
+ }
+ }
+
+ if ($xaction_phids) {
+ $xactions = id(new ConpherenceTransactionQuery())
+ ->setViewer($this->requireViewer())
+ ->withPHIDs($xaction_phids)
+ ->needComments(true)
+ ->execute();
+ $xactions = mpull($xactions, null, 'getPHID');
+ } else {
+ $xactions = array();
+ }
+
+ foreach ($groups as $thread_phid => $group) {
+ foreach ($group as $key => $list) {
+ foreach ($list as $lkey => $item) {
+ $xaction = idx($xactions, $item['phid']);
+ $groups[$thread_phid][$key][$lkey]['xaction'] = $xaction;
+ }
+ }
+ }
+
+ // TODO: Sort the groups chronologically?
+
+ return $groups;
+ }
+
}

File Metadata

Mime Type
text/plain
Expires
Fri, Mar 28, 12:54 PM (3 w, 4 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7223778
Default Alt Text
D12589.id.diff (10 KB)

Event Timeline