MATCH AGAINST script is not working with Symfony2

doctrine, dql, php, symfony

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

Problem

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 ?

Original source