mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-22 05:20: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:
parent
3c80292295
commit
cd7fec1729
3 changed files with 281 additions and 0 deletions
|
@ -4807,6 +4807,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
'PhabricatorMarkupInterface',
|
'PhabricatorMarkupInterface',
|
||||||
'PhabricatorApplicationTransactionInterface',
|
'PhabricatorApplicationTransactionInterface',
|
||||||
|
'PhabricatorSubscribableInterface',
|
||||||
),
|
),
|
||||||
'PhabricatorCalendarEventDeleteController' => 'PhabricatorCalendarController',
|
'PhabricatorCalendarEventDeleteController' => 'PhabricatorCalendarController',
|
||||||
'PhabricatorCalendarEventEditController' => 'PhabricatorCalendarController',
|
'PhabricatorCalendarEventEditController' => 'PhabricatorCalendarController',
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue