Skip to content

Commit

Permalink
Add full text search
Browse files Browse the repository at this point in the history
  • Loading branch information
ornicar committed Jun 27, 2011
1 parent 1bde7d8 commit 1cc1353
Show file tree
Hide file tree
Showing 13 changed files with 433 additions and 60 deletions.
18 changes: 17 additions & 1 deletion Controller/MessageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,27 @@ public function newThreadAction()
public function deleteAction($threadId)
{
$thread = $this->container->get('ornicar_message.provider')->getThread($threadId);
$this->container->get('ornicar_message.thread_manager')->deleteThread($thread);
$this->container->get('ornicar_message.deleter')->delete($thread);

return new RedirectResponse($this->container->get('router')->generate('ornicar_message_inbox'));
}

/**
* Searches for messages in the inbox and sentbox
*
* @return Response
*/
public function searchAction()
{
$query = $this->container->get('ornicar_message.search_query_factory')->createFromRequest();
$threads = $this->container->get('ornicar_message.search_finder')->find($query);

return $this->container->get('templating')->renderResponse('OrnicarMessageBundle:Message:search.html.twig', array(
'query' => $query,
'threads' => $threads
));
}

/**
* Gets the current authenticated participant
*
Expand Down
8 changes: 8 additions & 0 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ public function getConfigTreeBuilder()
->scalarNode('message_reader')->defaultValue('ornicar_message.message_reader.default')->cannotBeEmpty()->end()
->scalarNode('thread_reader')->defaultValue('ornicar_message.thread_reader.default')->cannotBeEmpty()->end()
->scalarNode('deleter')->defaultValue('ornicar_message.deleter.default')->cannotBeEmpty()->end()
->arrayNode('search')
->addDefaultsIfNotSet()
->children()
->scalarNode('query_factory')->defaultValue('ornicar_message.search_query_factory.default')->cannotBeEmpty()->end()
->scalarNode('finder')->defaultValue('ornicar_message.search_finder.default')->cannotBeEmpty()->end()
->scalarNode('query_parameter')->defaultValue('q')->cannotBeEmpty()->end()
->end()
->end()
->arrayNode('new_thread_form')
->addDefaultsIfNotSet()
->children()
Expand Down
5 changes: 5 additions & 0 deletions DependencyInjection/OrnicarMessageExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,10 @@ public function load(array $configs, ContainerBuilder $container)
$container->setAlias('ornicar_message.reply_form.type', $config['reply_form']['type']);
$container->setAlias('ornicar_message.reply_form.factory', $config['reply_form']['factory']);
$container->setAlias('ornicar_message.reply_form.handler', $config['reply_form']['handler']);

$container->setAlias('ornicar_message.search_query_factory', $config['search']['query_factory']);
$container->setAlias('ornicar_message.search_finder', $config['search']['finder']);
$container->getDefinition('ornicar_message.search_query_factory.default')
->replaceArgument(1, $config['search']['query_parameter']);
}
}
21 changes: 21 additions & 0 deletions Document/Thread.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ abstract class Thread extends AbstractThread
*/
protected $datesOfLastMessageWrittenByParticipant = array();

/**
* All text contained in the thread messages
* Used for the full text search
*
* @var string
*/
protected $keywords = '';

/**
* Initializes the collections
*/
Expand Down Expand Up @@ -174,5 +182,18 @@ protected function denormalize(MessageInterface $message)
// having theses sorted by user does not harm, and it makes unit testing easier
ksort($this->datesOfLastMessageWrittenByParticipant);
ksort($this->datesOfLastMessageWrittenByOtherParticipant);

$this->denormalizeKeywords();
}

/**
* Adds all messages contents to the keywords property
*/
public function denormalizeKeywords()
{
$this->keywords = $this->getSubject();
foreach ($this->getMessages() as $message) {
$this->keywords .= ' '.$message->getBody();
}
}
}
117 changes: 84 additions & 33 deletions DocumentManager/ThreadManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Ornicar\MessageBundle\Model\ReadableInterface;
use Ornicar\MessageBundle\ModelManager\ThreadManager as BaseThreadManager;
use Ornicar\MessageBundle\Model\ParticipantInterface;
use Doctrine\ODM\MongoDB\Query\Builder;

/**
* Default MongoDB ThreadManager.
Expand Down Expand Up @@ -66,22 +67,22 @@ public function findThreadById($id)
}

/**
* Finds not deleted threads for a user,
* containing at least one message not written by this user,
* ordered by last message not written by this user in reverse order.
* Finds not deleted threads for a participant,
* containing at least one message not written by this participant,
* ordered by last message not written by this participant in reverse order.
* In one word: an inbox.
*
* @param ParticipantInterface $user
* @param ParticipantInterface $participant
* @return Builder a query builder suitable for pagination
*/
public function getParticipantInboxThreadsQueryBuilder(ParticipantInterface $user)
public function getParticipantInboxThreadsQueryBuilder(ParticipantInterface $participant)
{
$isDeletedByParticipantFieldName = sprintf('isDeletedByParticipant.%s', $user->getId());
$datesOfLastMessageWrittenByOtherParticipantFieldName = sprintf('datesOfLastMessageWrittenByOtherParticipant.%s', $user->getId());
$isDeletedByParticipantFieldName = sprintf('isDeletedByParticipant.%s', $participant->getId());
$datesOfLastMessageWrittenByOtherParticipantFieldName = sprintf('datesOfLastMessageWrittenByOtherParticipant.%s', $participant->getId());

return $this->repository->createQueryBuilder()
// the participant is in the thread participants
->field('participants.$id')->equals(new \MongoId($user->getId()))
->field('participants.$id')->equals(new \MongoId($participant->getId()))
// the thread is not deleted by this participant
->field($isDeletedByParticipantFieldName)->equals(false)
// there is at least one message written by an other participant
Expand All @@ -91,49 +92,99 @@ public function getParticipantInboxThreadsQueryBuilder(ParticipantInterface $use
}

/**
* Finds not deleted threads for a user,
* containing at least one message not written by this user,
* ordered by last message not written by this user in reverse order.
* Finds not deleted threads for a participant,
* containing at least one message not written by this participant,
* ordered by last message not written by this participant in reverse order.
* In one word: an inbox.
*
* @param ParticipantInterface $user
* @param ParticipantInterface $participant
* @return array of ThreadInterface
*/
public function findParticipantInboxThreads(ParticipantInterface $user)
public function findParticipantInboxThreads(ParticipantInterface $participant)
{
return $this->getParticipantInboxThreadsQueryBuilder($user)->getQuery()->execute();
return $this->getParticipantInboxThreadsQueryBuilder($participant)->getQuery()->execute();
}

/**
* Finds threads from a user,
* containing at least one message written by this user,
* ordered by last message written by this user in reverse order.
* Finds not deleted threads from a participant,
* containing at least one message written by this participant,
* ordered by last message written by this participant in reverse order.
* In one word: an sentbox.
*
* @param ParticipantInterface $user
* @param ParticipantInterface $participant
* @return Builder a query builder suitable for pagination
*/
public function getParticipantSentThreadsQueryBuilder(ParticipantInterface $user)
public function getParticipantSentThreadsQueryBuilder(ParticipantInterface $participant)
{
$datesOfLastMessageWrittenByParticipantFieldName = sprintf('datesOfLastMessageWrittenByParticipant.%s', $user->getId());
$isDeletedByParticipantFieldName = sprintf('isDeletedByParticipant.%s', $participant->getId());
$datesOfLastMessageWrittenByParticipantFieldName = sprintf('datesOfLastMessageWrittenByParticipant.%s', $participant->getId());

return $this->repository->createQueryBuilder()
->field('participants.$id')->equals(new \MongoId($user->getId()))
// the participant is in the thread participants
->field('participants.$id')->equals(new \MongoId($participant->getId()))
// the thread is not deleted by this participant
->field($isDeletedByParticipantFieldName)->equals(false)
// there is at least one message written by this participant
->field($datesOfLastMessageWrittenByParticipantFieldName)->exists(true)
// sort by date of last message written by this participant
->sort($datesOfLastMessageWrittenByParticipantFieldName, 'desc');
}

/**
* Finds threads from a user,
* containing at least one message written by this user,
* ordered by last message written by this user in reverse order.
* Finds not deleted threads from a participant,
* containing at least one message written by this participant,
* ordered by last message written by this participant in reverse order.
* In one word: an sentbox.
*
* @param ParticipantInterface $user
* @param ParticipantInterface $participant
* @return array of ThreadInterface
*/
public function findParticipantSentThreads(ParticipantInterface $participant)
{
return $this->getParticipantSentThreadsQueryBuilder($participant)->getQuery()->execute();
}

/**
* Finds not deleted threads for a participant,
* matching the given search term
* ordered by last message not written by this participant in reverse order.
*
* @param ParticipantInterface $participant
* @param string $search
* @return Builder a query builder suitable for pagination
*/
public function getParticipantThreadsBySearchQueryBuilder(ParticipantInterface $participant, $search)
{
// remove all non-word chars
$search = preg_replace('/[^\w]/', ' ', trim($search));
// build a regex like (term1|term2)
$regex = sprintf('/(%s)/', implode('|', explode(' ', $search)));

$isDeletedByParticipantFieldName = sprintf('isDeletedByParticipant.%s', $participant->getId());
$datesOfLastMessageWrittenByOtherParticipantFieldName = sprintf('datesOfLastMessageWrittenByOtherParticipant.%s', $participant->getId());

return $this->repository->createQueryBuilder()
// the participant is in the thread participants
->field('participants.$id')->equals(new \MongoId($participant->getId()))
// the thread is not deleted by this participant
->field($isDeletedByParticipantFieldName)->equals(false)
// sort by date of last message written by an other participant
->sort($datesOfLastMessageWrittenByOtherParticipantFieldName, 'desc')
->field('keywords')->equals(new \MongoRegex($regex));
}

/**
* Finds not deleted threads for a participant,
* matching the given search term
* ordered by last message not written by this participant in reverse order.
*
* @param ParticipantInterface $participant
* @param string $search
* @return array of ThreadInterface
*/
public function findParticipantSentThreads(ParticipantInterface $user)
public function findParticipantThreadsBySearch(participantinterface $participant, $search)
{
return $this->getParticipantSentThreadsQueryBuilder($user)->getQuery()->execute();
return $this->getParticipantThreadsBySearchQueryBuilder($participant, $search)->getQuery()->execute();
}

/**
Expand All @@ -144,22 +195,22 @@ public function findParticipantSentThreads(ParticipantInterface $user)
* as well as marking the as read.
*
* @param ReadableInterface $readable
* @param ParticipantInterface $user
* @param ParticipantInterface $participant
*/
public function markAsReadByParticipant(ReadableInterface $readable, ParticipantInterface $user)
public function markAsReadByParticipant(ReadableInterface $readable, ParticipantInterface $participant)
{
return $this->messageManager->markIsReadByThreadAndParticipant($readable, $user, true);
return $this->messageManager->markIsReadByThreadAndParticipant($readable, $participant, true);
}

/**
* Marks the readable as unread by this participant
*
* @param ReadableInterface $readable
* @param ParticipantInterface $user
* @param ParticipantInterface $participant
*/
public function markAsUnreadByParticipant(ReadableInterface $readable, ParticipantInterface $user)
public function markAsUnreadByParticipant(ReadableInterface $readable, ParticipantInterface $participant)
{
return $this->messageManager->markIsReadByThreadAndParticipant($readable, $user, false);
return $this->messageManager->markIsReadByThreadAndParticipant($readable, $participant, false);
}

/**
Expand Down
74 changes: 48 additions & 26 deletions ModelManager/ThreadManagerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,62 +23,77 @@ interface ThreadManagerInterface extends ReadableManagerInterface
function findThreadById($id);

/**
* Finds not deleted threads for a user,
* containing at least one message not written by this user,
* ordered by last message not written by this user in reverse order.
* Finds not deleted threads for a participant,
* containing at least one message not written by this participant,
* ordered by last message not written by this participant in reverse order.
* In one word: an inbox.
*
* @param ParticipantInterface $user
* @param ParticipantInterface $participant
* @return Builder a query builder suitable for pagination
*/
function getParticipantInboxThreadsQueryBuilder(ParticipantInterface $user);
function getParticipantInboxThreadsQueryBuilder(ParticipantInterface $participant);

/**
* Finds not deleted threads for a user,
* containing at least one message not written by this user,
* ordered by last message not written by this user in reverse order.
* Finds not deleted threads for a participant,
* containing at least one message not written by this participant,
* ordered by last message not written by this participant in reverse order.
* In one word: an inbox.
*
* @param ParticipantInterface $user
* @param ParticipantInterface $participant
* @return array of ThreadInterface
*/
function findParticipantInboxThreads(ParticipantInterface $user);
function findParticipantInboxThreads(ParticipantInterface $participant);

/**
* Finds threads from a user,
* containing at least one message written by this user,
* ordered by last message written by this user in reverse order.
* Finds not deleted threads from a participant,
* containing at least one message written by this participant,
* ordered by last message written by this participant in reverse order.
* In one word: an sentbox.
*
* @param ParticipantInterface $user
* @param ParticipantInterface $participant
* @return Builder a query builder suitable for pagination
*/
function getParticipantSentThreadsQueryBuilder(ParticipantInterface $user);
function getParticipantSentThreadsQueryBuilder(ParticipantInterface $participant);

/**
* Finds threads from a user,
* containing at least one message written by this user,
* ordered by last message written by this user in reverse order.
* Finds not deleted threads from a participant,
* containing at least one message written by this participant,
* ordered by last message written by this participant in reverse order.
* In one word: an sentbox.
*
* @param ParticipantInterface $user
* @param ParticipantInterface $participant
* @return array of ThreadInterface
*/
function findParticipantSentThreads(ParticipantInterface $user);
function findParticipantSentThreads(ParticipantInterface $participant);

/**
* Creates an empty comment thread instance
* Finds not deleted threads for a participant,
* matching the given search term
* ordered by last message not written by this participant in reverse order.
*
* @return ThreadInterface
* @param ParticipantInterface $participant
* @param string $search
* @return Builder a query builder suitable for pagination
*/
function createThread();
function getParticipantThreadsBySearchQueryBuilder(ParticipantInterface $participant, $search);

/**
* Deletes a thread
* Finds not deleted threads for a participant,
* matching the given search term
* ordered by last message not written by this participant in reverse order.
*
* @param ThreadInterface $thread the thread to delete
* @param ParticipantInterface $participant
* @param string $search
* @return array of ThreadInterface
*/
function deleteThread(ThreadInterface $thread);
function findparticipantthreadsbysearch(participantinterface $participant, $search);

/**
* Creates an empty comment thread instance
*
* @return ThreadInterface
*/
function createThread();

/**
* Saves a thread
Expand All @@ -87,4 +102,11 @@ function deleteThread(ThreadInterface $thread);
* @param Boolean $andFlush Whether to flush the changes (default true)
*/
function updateThread(ThreadInterface $thread, $andFlush = true);

/**
* Deletes a thread
*
* @param ThreadInterface $thread the thread to delete
*/
function deleteThread(ThreadInterface $thread);
}
Loading

0 comments on commit 1cc1353

Please sign in to comment.