1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-21 21:10:56 +01:00

Implement very rough message context for Conpherence search

Summary: Ref T3165. This is pretty awful looking, but should pull the correct data.

Test Plan: {F387567}

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T3165

Differential Revision: https://secure.phabricator.com/D12589
This commit is contained in:
epriestley 2015-04-28 11:43:56 -07:00
parent 3c80292295
commit cd7fec1729
3 changed files with 281 additions and 0 deletions

View file

@ -4807,6 +4807,7 @@ phutil_register_library_map(array(
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',
'PhabricatorMarkupInterface', 'PhabricatorMarkupInterface',
'PhabricatorApplicationTransactionInterface', 'PhabricatorApplicationTransactionInterface',
'PhabricatorSubscribableInterface',
), ),
'PhabricatorCalendarEventDeleteController' => 'PhabricatorCalendarController', 'PhabricatorCalendarEventDeleteController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventEditController' => 'PhabricatorCalendarController', 'PhabricatorCalendarEventEditController' => 'PhabricatorCalendarController',

View file

@ -4,6 +4,7 @@ final class ConpherenceFulltextQuery
extends PhabricatorOffsetPagedQuery { extends PhabricatorOffsetPagedQuery {
private $threadPHIDs; private $threadPHIDs;
private $previousTransactionPHIDs;
private $fulltext; private $fulltext;
public function withThreadPHIDs(array $phids) { public function withThreadPHIDs(array $phids) {
@ -11,6 +12,11 @@ final class ConpherenceFulltextQuery
return $this; return $this;
} }
public function withPreviousTransactionPHIDs(array $phids) {
$this->previousTransactionPHIDs = $phids;
return $this;
}
public function withFulltext($fulltext) { public function withFulltext($fulltext) {
$this->fulltext = $fulltext; $this->fulltext = $fulltext;
return $this; return $this;
@ -42,6 +48,13 @@ final class ConpherenceFulltextQuery
$this->threadPHIDs); $this->threadPHIDs);
} }
if ($this->previousTransactionPHIDs !== null) {
$where[] = qsprintf(
$conn_r,
'i.previousTransactionPHID IN (%Ls)',
$this->previousTransactionPHIDs);
}
if (strlen($this->fulltext)) { if (strlen($this->fulltext)) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn_r,

View file

@ -149,6 +149,27 @@ final class ConpherenceThreadSearchEngine
$viewer, $viewer,
$conpherences); $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 = new PHUIObjectItemListView();
$list->setUser($viewer); $list->setUser($viewer);
foreach ($conpherences as $conpherence) { foreach ($conpherences as $conpherence) {
@ -181,6 +202,47 @@ final class ConpherenceThreadSearchEngine
phabricator_datetime($conpherence->getDateModified(), $viewer)), 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); $list->addItem($item);
} }
@ -195,4 +257,209 @@ final class ConpherenceThreadSearchEngine
); );
} }
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;
}
} }