PostgreSQL "tsquery" için bir Google arama sorgusu dönüştürme

1 Cevap php

Nasıl (Ben PostgreSQL'in to_tsquery besleyebilir şey için bir Google arama sorgusu dönüştürebilirsiniz?)

Hiçbir mevcut kütüphane orada varsa, nasıl PHP gibi bir dilde bir Google arama sorgusu ayrıştırma hakkında gitmeli?

Örneğin, ben aşağıdaki Google-ish arama sorgusu almak istiyorum:

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

Ve bir to_tsquery çevirmek ()-dostu dize:

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

Ben regexes ile geçiştirmek, ama ben yapabilirim en iyisi. Bu konuda giderek bazı sağlam sözcüksel analiz yöntemi var mı? Ve intitle :) farklı veritabanı alanları için geçerli olacak ve böylece tsquery dize ayrılması gerekir: Google'ın site gibi (çok genişletilmiş arama operatörlerini desteklemek mümkün olmak istiyorum.

GÜNCELLEME: Ben özel operatörlerin bu oldukça tsquery dönüşüm için bir Google daha, NEREDE fıkra dönüşüm SQL Google haline biliyoruz. Ancak WHERE yan tümce, bir veya daha fazla tsqueries içerebilir.

Örneğin, Google tarzı sorgu:

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

Bir SQL böyle NEREDE fıkra üretmek gerekir:

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'
  );

Ben yukarıdaki regex ile mümkün olup olmadığından emin değilim?

1 Cevap

Dürüst, ben düzenli ifadeler böyle bir şey ile gitmek için yol olduğunu düşünüyorum. Sadece aynı, bu eğlenceli bir egzersiz oldu. Aşağıdaki kodu çok Prototypal - aslında, ben bile lexer kendisini uygulamak olmadığını göreceksiniz - Ben sadece çıkışını sahte. Ben devam etmek istiyorum ama ben sadece bugün daha boş vaktim yok.

Ayrıca, kesinlikle çok daha fazla arama operatörleri ve benzeri diğer tür destek açısından burada yapılması gereken işler.

Temel olarak, fikir sorgu belirli bir tür daha sonra sorgu başka bir türü olarak geri dışarı işlenen ortak bir formatta (bu durumda, bir QueryExpression örneği) ayrıştırılır sonra lexed olmasıdır.

<?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();