Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15445211
D12589.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
10 KB
Referenced Files
None
Subscribers
None
D12589.id.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D12589: Implement very rough message context for Conpherence search
Attached
Detach File
Event Timeline
Log In to Comment