Skip to content

Commit

Permalink
Sort threads by last message date in document ThreadManager queries
Browse files Browse the repository at this point in the history
Due to MongoDB limitations and the current schema, we cannot sort by the timestamps present in the metadata fields and match behavior of the ORM's ThreadManager. This would require use of Mongo's positional operator ($) in sorts, but it may be possible with the upcoming aggregation framework in MongoDB 2.2. TODO's have been left to revisit this when possible.

To compromise, we can sort by the creation timestamp of the last message in the thread, which is easy to denormalize and index.
  • Loading branch information
jmikola committed Dec 14, 2011
1 parent 4f24291 commit cef0798
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 6 deletions.
23 changes: 23 additions & 0 deletions Document/Thread.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ abstract class Thread extends AbstractThread
*/
protected $createdAt;

/**
* Date that the last message in this thread was created at
*
* This denormalization field is used for sorting threads in the inbox and
* sent list.
*
* @var \DateTime
*/
protected $lastMessageDate;

/**
* All text contained in the thread messages
* Used for the full text search
Expand Down Expand Up @@ -229,6 +239,7 @@ public function addMetadata(ThreadMetadata $meta)
public function denormalize()
{
$this->doCreatedByAndAt();
$this->doLastMessageDate();
$this->doKeywords();
$this->doSpam();
$this->doMetadataLastMessageDates();
Expand All @@ -251,6 +262,18 @@ protected function doCreatedByAndAt()
$this->setCreatedAt($message->getCreatedAt());
}

/**
* Ensures that the lastMessageDate property is up to date
*/
protected function doLastMessageDate()
{
if (!$message = $this->getLastMessage()) {
return;
}

$this->lastMessageDate = $message->getCreatedAt();
}

/**
* Adds all messages contents to the keywords property
*/
Expand Down
24 changes: 18 additions & 6 deletions DocumentManager/ThreadManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,19 @@ public function getParticipantInboxThreadsQueryBuilder(ParticipantInterface $par
{
$queryBuilder = $this->repository->createQueryBuilder();

// TODO: sort by date of last message written by an other participant
return $queryBuilder
// the participant hasn't deleted the thread, and another user wrote a message
->field('metadata')->elemMatch($this
->getNotDeletedByParticipantExpression($queryBuilder, $participant)
->field('lastMessageDate')->notEqual(null)
)
// the thread does not contain spam or flood
->field('isSpam')->equals(false);
->field('isSpam')->equals(false)
/* TODO: Sort by date of the last message written by another
* participant, as is done for ORM. This is not possible with the
* current schema; compromise by sorting by last message date.
*/
->sort('lastMessageDate', 'desc');
}

/**
Expand Down Expand Up @@ -120,13 +124,17 @@ public function getParticipantSentThreadsQueryBuilder(ParticipantInterface $part
{
$queryBuilder = $this->repository->createQueryBuilder();

// TODO: sort by date of last message written by this participant
return $queryBuilder
// the participant hasn't deleted the thread, and has written a message
->field('metadata')->elemMatch($this
->getNotDeletedByParticipantExpression($queryBuilder, $participant)
->field('lastParticipantMessageDate')->notEqual(null)
);
)
/* TODO: Sort by date of the last message written by this
* participant, as is done for ORM. This is not possible with the
* current schema; compromise by sorting by last message date.
*/
->sort('lastMessageDate', 'desc');
}

/**
Expand Down Expand Up @@ -161,12 +169,16 @@ public function getParticipantThreadsBySearchQueryBuilder(ParticipantInterface $

$queryBuilder = $this->repository->createQueryBuilder();

// TODO: sort by date of last message written by an other participant
return $queryBuilder
// the thread is not deleted by this participant
->field('metadata')->elemMatch($this->getNotDeletedByParticipantExpression($queryBuilder, $participant))
// TODO: this search is not anchored and uses no indexes
->field('keywords')->equals(new \MongoRegex($regex));
->field('keywords')->equals(new \MongoRegex($regex))
/* TODO: Sort by date of the last message written by another
* participant, as is done for ORM. This is not possible with the
* current schema; compromise by sorting by last message date.
*/
->sort('lastMessageDate', 'desc');
}

/**
Expand Down
5 changes: 5 additions & 0 deletions Resources/config/doctrine/Thread.mongodb.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

<field name="createdAt" fieldName="createdAt" type="date" />

<field name="lastMessageDate" fieldName="lastMessageDate" type="date" />

<field name="isSpam" fieldName="isSpam" type="boolean" />

<field name="keywords" fieldName="keywords" type="string" />
Expand All @@ -26,6 +28,9 @@
<index>
<key name="createdAt" order="desc" />
</index>
<index>
<key name="lastMessageDate" order="desc" />
</index>
<!-- ThreadManager::findThreadsCreatedBy() -->
<index>
<key name="createdBy.$id" order="asc" />
Expand Down

0 comments on commit cef0798

Please sign in to comment.