سؤال

وكيف يمكنني تحويل استعلام البحث جوجل لشيء يمكن أن تغذي to_tsquery كيو في ()؟

إذا لم يكن هناك مكتبة موجودة هناك، وكيف ينبغي أن تذهب نحو تحليل استعلام البحث جوجل في اللغة مثل PHP؟

وعلى سبيل المثال، كنت أود أن أغتنم هذه جوجل العش استعلام البحث التالية:

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

وتحويله الى to_tsquery () - سلسلة دية:

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

وأستطيع أن يتلاعب هذا مع regexes، ولكن هذا هو أفضل ما يمكن القيام به. هناك بعض القوي أسلوب التحليل المعجمي من الذهاب حول هذا الموضوع؟ أود أن تكون قادرة على دعم مشغلي بحث طويلة جدا (مثل موقع جوجل: وسوف intitle :) التي سيتم تطبيقها على حقول قاعدة البيانات المختلفة، وبالتالي يجب أن يتم فصلها عن سلسلة tsquery

وUPDATE: وأنا أدرك أنه مع مشغلي خاصة هذا يصبح جوجل إلى SQL التحويل حيث بالشرط، بدلا من جوجل لtsquery التحويل. لكن جملة WHERE قد تحتوي على واحد أو أكثر tsqueries.

وعلى سبيل المثال، الاستعلام على غرار جوجل:

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

وإذا ما ينتج WHERE-بند SQL مثل هذا:

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

ولست متأكدا إذا كان ما سبق هو ممكن مع التعابير المنطقية؟

هل كانت مفيدة؟

المحلول

وصادق، وأعتقد أن التعابير العادية هي وسيلة للذهاب مع شيء من هذا القبيل. بنفس الطريقة، وكان هذا ممارسة متعة. رمز أدناه هو نموذجي جدا - في الواقع، سترى أنني لم تنفذ حتى lexer نفسها - أنا فقط مزورة الإخراج. أود أن يستمر ذلك، ولكن أنا فقط لم يكن لديك المزيد من وقت الفراغ اليوم.

وبالإضافة إلى ذلك، هناك بالتأكيد الكثير من العمل الذي يجب القيام به هنا من حيث دعم أنواع أخرى من شركات البحث ومثل.

والأساس، والفكرة هي أن نوع معين من الاستعلام lexed ثم تحليل إلى تنسيق مشترك (في هذه الحالة، مثيل 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();
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top