Search code examples
phpsymfonydoctrinedql

MATCH AGAINST script is not working with Symfony2


I try to set this class in my Symfony2 project : http://xlab.pl/en/full-text-searching/ (Match against part)

This class is supposed to allow me to use the Match against with doctrine like this :

$searchResult =      $em->createQueryBuilder('uvaluo_user')
                                ->addSelect("MATCH_AGAINST (uvaluo_user.firstname, uvaluo_user.lastname, :name 'IN NATURAL MODE') as score")
                                ->add('where', 'MATCH_AGAINST (uvaluo_user.firstname, uvaluo_user.lastname, :name) > 0.8')
                                ->setParameter('name', $name)
                                ->getQuery()
                                ->getResult();

As they said in the website I put that in my config.yml of my app :

orm:
    auto_generate_proxy_classes: %kernel.debug%
    auto_mapping: true
    dql:
      string_functions:
        match_against: Uvaluo\UserBundle\Extension\Doctrine\MatchAgainstFunction

And I have the class MatchAgainstFunction like this

<?

namespace Uvaluo\UserBundle\Extension\Doctrine;

use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;

/**
 * @example by https://gist.github.com/1234419 Jérémy Hubert 
 * "MATCH_AGAINST" "(" {StateFieldPathExpression ","}* InParameter {Literal}? ")"
 */
class MatchAgainstFunction extends FunctionNode {
//Content that I got in the website
}

And finally when I try my code I get that following error :

[Semantical Error] line 0, col 107 near 'MATCH_AGAINST': Error: Class 'MATCH_AGAINST' is not defined. 500 Internal Server Error - QueryException 1 linked Exception: QueryException »

Do you know what can be the reason of that ?


Solution

  • Based on this article Extending DQL in Doctrine 2: User-Defined Functions:

    config.yml:

    doctrine:
      orm:
        dql:
          string_functions:
            MATCH: My\Bundle\DQL\MatchAgainstFunction
    

    MatchAgainstFunction.php

    <?php
    
    namespace My\Bundle\DQL;
    
    use Doctrine\ORM\Query\AST\Functions\FunctionNode;
    use Doctrine\ORM\Query\Lexer;
    use Doctrine\ORM\Query\Parser;
    use Doctrine\ORM\Query\SqlWalker;
    
    /**
     * MatchAgainstFunction ::=
     *  "MATCH" "(" StateFieldPathExpression {"," StateFieldPathExpression}* ")" "AGAINST" "("
     *      StringPrimary ["BOOLEAN"] ["EXPAND"] ")"
     */
    class MatchAgainstFunction extends FunctionNode {
    
      /** @var array list of \Doctrine\ORM\Query\AST\PathExpression */
      protected $pathExp = null;
    
      /** @var string */
      protected $against = null;
    
      /** @var boolean */
      protected $booleanMode = false;
    
      /** @var boolean */
      protected $queryExpansion = false;
    
      public function parse(Parser $parser) {
        // match
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);
    
        // first Path Expression is mandatory
        $this->pathExp = array();
        $this->pathExp[] = $parser->StateFieldPathExpression();
    
        // Subsequent Path Expressions are optional
        $lexer = $parser->getLexer();
        while ($lexer->isNextToken(Lexer::T_COMMA)) { 
          $parser->match(Lexer::T_COMMA); 
          $this->pathExp[] = $parser->StateFieldPathExpression(); 
        }
    
        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    
        // against
        if (strtolower($lexer->lookahead['value']) !== 'against') {
          $parser->syntaxError('against');
        }
    
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);
        $this->against = $parser->StringPrimary();
    
        if (strtolower($lexer->lookahead['value']) === 'boolean') {
          $parser->match(Lexer::T_IDENTIFIER);
          $this->booleanMode = true;
        }
    
        if (strtolower($lexer->lookahead['value']) === 'expand') {
          $parser->match(Lexer::T_IDENTIFIER);
          $this->queryExpansion = true;
        }
    
        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
      }
    
      public function getSql(SqlWalker $walker) {
        $fields = array();
        foreach ($this->pathExp as $pathExp) {
          $fields[] = $pathExp->dispatch($walker);
        }
    
        $against = $walker->walkStringPrimary($this->against)
            . ($this->booleanMode ? ' IN BOOLEAN MODE' : '')
            . ($this->queryExpansion ? ' WITH QUERY EXPANSION' : '');
        
        return sprintf('MATCH (%s) AGAINST (%s)', implode(', ', $fields), $against);
      }
    }
    

    In your repository:

    class MyRepository extends EntityRepository
    {
        
        public function getUsingAgainstMatch() {
            $qb = $this->getEntityManager()->createQueryBuilder();
            $qb
                ->select('m')
                ->from('MyBundle:MyEntity', 'm')
                ->andWhere('MATCH (m.field) AGAINST (:field) > 1')
                ->setParameter('field', 'value')
            ;
            return $qb;
        }
    
        //...
    
    }
    

    You can find on this repository a lot of Doctrine Extensions.

    Hope this helps