2008-10-16 2 views
4

Google 검색 쿼리를 PostgreSQL의 to_tsquery() 피드로 변환 할 수있는 방법은 무엇입니까?Google 검색 쿼리를 PostgreSQL "tsquery"로 변환

밖에있는 라이브러리가 없다면 PHP와 같은 언어로 Google 검색 쿼리를 구문 분석하려면 어떻게해야합니까?

예를 들어, 나는 다음 Google 틱 검색 쿼리 가지고 싶습니다

("used cars" OR "new cars") -ford -mistubishi 

을 그리고 to_tsquery()로 돌려 - 친화적 인 문자열 :

('used cars' | 'new cars') & !ford & !mistubishi 

내가 꾸며 낼 수 있습니다 regexes와 함께,하지만 그게 내가 할 수있는 최선입니다. 이 문제에 대한 강력한 어휘 분석 방법이 있습니까? 다른 데이터베이스 필드에도 적용 할 수있는 확장 검색 연산자 (Google 사이트 및 intit :)를 지원할 수 있기를 원하며 tsquery 문자열과 구분해야합니다.

업데이트 : 특수 연산자를 사용하면 Google에서 tsquery로 변환하는 대신 Google에서 SQL로 변환하는 WHERE 절이됩니다. 그러나 WHERE 절은 하나 이상의 tsqueries를 포함 할 수 있습니다.

예를 들어

, 구글 스타일의 쿼리

WHERE to_tsvector(description) MATCH to_tsquery('used') 
    AND color <> 'red' 
    AND ((color = 'blue' OR to_tsvector(description) MATCH to_tsquery('4x4')) 
    OR style = 'coupe' 
); 

나는 위의 정규식 가능합니다 있는지 확실하지 않습니다 :

((color:blue OR "4x4") OR style:coupe) -color:red used 

이 같은 SQL WHERE 절을 생산해야 하는가?

답변

2

정직하게 말하자면 정규 표현식은 이와 같은 방식으로 진행되는 방법이라고 생각합니다. 똑같은, 이것은 재미있는 운동이었다. 아래의 코드는 매우 프로토 타입입니다. 사실, 내가 렉서 자체를 구현하지 않았다는 것을 알게 될 것입니다. 계속하고 싶지만 오늘은 여가 시간이 없습니다.

또한 다른 유형의 검색 연산자 등을 지원하는 측면에서 여기서해야 할 일이 많이 있습니다.

기본적으로 특정 유형의 쿼리는 렉싱되어 다른 형식의 쿼리로 다시 렌더링되는 공통 형식 (이 경우 QueryExpression 인스턴스)으로 구문 분석됩니다.

<?php 

ini_set("display_errors", "on"); 
error_reporting(E_ALL); 

interface ILexer 
{ 
    public function execute($str); 
    public function getTokens(); 
} 

interface IParser 
{ 
    public function __construct(iLexer $lexer); 
    public function parse($input); 
    public function addToken($token); 
} 

class GoogleQueryLexer implements ILexer 
{ 
    private $tokenStack = array(); 

    public function execute($str) 
    { 
     $chars = str_split($str); 
     foreach ($chars as $char) 
     { 
      // add to self::$tokenStack per your rules 
     } 

     //'("used cars" OR "new cars") -ford -mistubishi' 
     $this->tokenStack = array(
       '(' 
      , 'used cars' 
      , 'or new cars' 
      , ')' 
      , '-ford' 
      , '-mitsubishi' 
     ); 
    } 

    public function getTokens() 
    { 
     return $this->tokenStack; 
    } 
} 

class GoogleQueryParser implements IParser 
{ 
    protected $lexer; 

    public function __construct(iLexer $lexer) 
    { 
     $this->lexer = $lexer; 
    } 

    public function addToken($token) 
    { 
     $this->tokenStack[] = $token; 
    } 

    public function parse($input) 
    { 
     $this->lexer->execute($input); 
     $tokens = $this->lexer->getTokens(); 

     $expression = new QueryExpression(); 

     foreach ($tokens as $token) 
     { 
      $expression = $this->processToken($token, $expression); 
     } 

     return $expression; 
    } 

    protected function processToken($token, QueryExpression $expression) 
    { 
     switch ($token) 
     { 
      case '(': 
       return $expression->initiateSubExpression(); 
       break; 
      case ')': 
       return $expression->getParentExpression(); 
       break; 
      default: 
       $modifier = $token[0]; 
       $phrase  = substr($token, 1); 
       switch ($modifier) 
       { 
        case '-': 
         $expression->addExclusionPhrase($phrase); 
         break; 
        case '+': 
         $expression->addPhrase($phrase); 
         break; 
        default: 
         $operator = trim(substr($token, 0, strpos($token, ' '))); 
         $phrase  = trim(substr($token, strpos($token, ' '))); 
         switch (strtolower($operator)) 
         { 
          case 'and': 
           $expression->addAndPhrase($phrase); 
           break; 
          case 'or': 
           $expression->addOrPhrase($phrase); 
           break; 
          default: 
           $expression->addPhrase($token); 
         } 
       } 
     } 
     return $expression; 
    } 
} 

class QueryExpression 
{ 
    protected $phrases = array(); 
    protected $subExpressions = array(); 
    protected $parent; 

    public function __construct($parent=null) 
    { 
     $this->parent = $parent; 
    } 

    public function initiateSubExpression() 
    { 
     $expression = new self($this); 
     $this->subExpressions[] = $expression; 
     return $expression; 
    } 

    public function getPhrases() 
    { 
     return $this->phrases; 
    } 

    public function getSubExpressions() 
    { 
     return $this->subExpressions; 
    } 

    public function getParentExpression() 
    { 
     return $this->parent; 
    } 

    protected function addQueryPhrase(QueryPhrase $phrase) 
    { 
     $this->phrases[] = $phrase; 
    } 

    public function addPhrase($input) 
    { 
     $this->addQueryPhrase(new QueryPhrase($input)); 
    } 

    public function addOrPhrase($input) 
    { 
     $this->addQueryPhrase(new QueryPhrase($input, QueryPhrase::MODE_OR)); 
    } 

    public function addAndPhrase($input) 
    { 
     $this->addQueryPhrase(new QueryPhrase($input, QueryPhrase::MODE_AND)); 
    } 

    public function addExclusionPhrase($input) 
    { 
     $this->addQueryPhrase(new QueryPhrase($input, QueryPhrase::MODE_EXCLUDE)); 
    } 
} 

class QueryPhrase 
{ 
    const MODE_DEFAULT = 1; 
    const MODE_OR = 2; 
    const MODE_AND = 3; 
    const MODE_EXCLUDE = 4; 

    protected $phrase; 
    protected $mode; 

    public function __construct($input, $mode=self::MODE_DEFAULT) 
    { 
     $this->phrase = $input; 
     $this->mode = $mode; 
    } 

    public function getMode() 
    { 
     return $this->mode; 
    } 

    public function __toString() 
    { 
     return $this->phrase; 
    } 
} 

class TsqueryBuilder 
{ 
    protected $expression; 
    protected $query; 

    public function __construct(QueryExpression $expression) 
    { 
     $this->query = trim($this->processExpression($expression), ' &|'); 
    } 

    public function getResult() 
    { 
     return $this->query; 
    } 

    protected function processExpression(QueryExpression $expression) 
    { 
     $query = ''; 
     $phrases = $expression->getPhrases(); 
     $subExpressions = $expression->getSubExpressions(); 

     foreach ($phrases as $phrase) 
     { 
      $format = "'%s' "; 
      switch ($phrase->getMode()) 
      { 
       case QueryPhrase::MODE_AND : 
        $format = "& '%s' "; 
        break; 
       case QueryPhrase::MODE_OR : 
        $format = "| '%s' "; 
        break; 
       case QueryPhrase::MODE_EXCLUDE : 
        $format = "& !'%s' "; 
        break; 
      } 
      $query .= sprintf($format, str_replace("'", "\\'", $phrase)); 
     } 

     foreach ($subExpressions as $subExpression) 
     { 
      $query .= "& (" . $this->processExpression($subExpression) . ")"; 
     } 
     return $query; 
    } 
} 

$parser = new GoogleQueryParser(new GoogleQueryLexer()); 

$queryBuilder = new TsqueryBuilder($parser->parse('("used cars" OR "new cars") -ford -mistubishi')); 

echo $queryBuilder->getResult(); 
+0

멋진 답변입니다. :-) 나는 정규식이 그것을 할 수있는 방법이라고 생각하기 때문에 당신은 어떤 정규식을 사용할 것인지 알고 싶다. 내가 몇 가지 정규식을 작성하려고했지만 중첩 괄호를 처리하는 방법에 붙어있어? –

+0

글쎄, 나는 당신이 원하는 입출력 포맷 사이에서 변경하지 않을 것이기 때문에 그렇게 생각하지 않는다. 정말로 연산자 만 변환하면된다. 원한다면 정규식 기반 솔루션으로 응답을 게시 할 것입니다. –

+0

게시하는 데 문제가 없다면 정규식 솔루션을 보는 것이 좋습니다. 일단 특수 연산자가 관련되면 더 이상 google-to-tsquery 상황이 아니지만 google-to-sql-where-clause 상황입니다. 게시물을 편집하여 예제를 보여 드리겠습니다. –

관련 문제