/**
  * Search for a line
  *
  * @param string $q The search query
  * @param integer $page The page of results to retrieve
  * @param integer $perPage How many results to retrieve
  * @return SearchResult The search results
  * @see ChaosTangent\FansubEbooks\Bundle\AppBundle\DataFixtures\ORM\CreateSearchIndex
  */
 public function search($q, $page = 1, $perPage = 30, Series $series = null)
 {
     if (empty(trim($q))) {
         return new SearchResult($q, [], 0, $page, $perPage);
     }
     // default query parameters
     $defaultParams = [':query' => trim($q), ':config' => 'english'];
     // default where clause
     $whereClause = 'WHERE to_tsvector(:config, l.line) @@ to_tsquery(:query)';
     if ($series !== null) {
         $whereClause .= ' AND f.series_id = :series';
         $defaultParams['series'] = $series->getId();
     }
     // count total results from search query
     $countSql = 'SELECT COUNT(l.id)
         FROM lines l
         JOIN files f ON f.id = l.file_id ' . $whereClause;
     $total = $this->_em->getConnection()->fetchColumn($countSql, $defaultParams, 0);
     // get the selected page of results from search query
     $rsm = new ResultSetMappingBuilder($this->_em);
     $rsm->addRootEntityFromClassMetadata('ChaosTangent\\FansubEbooks\\Entity\\Line', 'l');
     $rsm->addScalarResult('positive_votes', 'positive_votes', 'integer');
     $rsm->addScalarResult('negative_votes', 'negative_votes', 'integer');
     $rsm->addScalarResult('tweet_id', 'tweet_id');
     $sql = 'SELECT ' . $rsm->generateSelectClause() . ',
             SUM(CASE WHEN v.positive = true THEN 1 ELSE 0 END) AS positive_votes,
             SUM(CASE WHEN v.positive = false THEN 1 ELSE 0 END) AS negative_votes,
             t.tweet_id
         FROM lines l
         JOIN files f ON f.id = l.file_id
         LEFT JOIN votes v ON v.line_id = l.id
         LEFT JOIN tweets t ON t.line_id = l.id ' . $whereClause . '
         GROUP BY l.id, t.tweet_id
         ORDER BY ts_rank(to_tsvector(:config, l.line), to_tsquery(:query))
         LIMIT :limit OFFSET :offset';
     $query = $this->_em->createNativeQuery($sql, $rsm);
     $query->setParameters(array_merge($defaultParams, ['limit' => $perPage, 'offset' => ($page - 1) * $perPage]));
     $result = $query->getResult();
     $ret = [];
     foreach ($result as $row) {
         $ret[] = $row[0]->setPositiveVoteCount($row['positive_votes'])->setNegativeVoteCount($row['negative_votes'])->setTweetId($row['tweet_id']);
     }
     // bundle it all into a searchresult object
     return new SearchResult($q, $ret, $total, $page, $perPage);
 }