Skip to content

Commit

Permalink
Merge branch 'release/2.3.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Jacob Christensen authored and Jacob Christensen committed May 19, 2017
2 parents 7e3cdb8 + ec276de commit fd76a8b
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 34 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ require 'vendor/autoload.php';
$rql = 'or(eq(name,foo)&eq(name,bar))';

/** @var \Doctrine\ODM\MongoDB\Query\Builder $builder */
$visitor = new \Graviton\Rql\Visitor\MongoOdm($builder);
$visitor = new \Graviton\Rql\Visitor\MongoOdm();
$visitor->setBuilder($builder);
$lexer = new \Xiag\Rql\Parser\Lexer;
$parser = \Xiag\Rql\Parser\Parser::createDefault();

Expand Down
65 changes: 61 additions & 4 deletions src/Node/SearchNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

namespace Graviton\Rql\Node;

use Xiag\Rql\Parser\AbstractNode;
use Xiag\Rql\Parser\Node\AbstractQueryNode;

/**
Expand All @@ -15,19 +14,41 @@
*/
class SearchNode extends AbstractQueryNode
{
/**
* Singleton search param
*/
private static $instance;

/** @var bool */
private $visited = false;

/**
* @var array
*/
protected $searchTerms;
protected $searchTerms = [];

/**
* @param array $searchTerms What search parameters should be added on custurction
* SearchNode constructor.
* @param array $searchTerms list of search items.
*/
public function __construct(array $searchTerms = [])
{
$this->searchTerms = $searchTerms;
}

/**
* Singleton implementation
* @return SearchNode
*/
public static function getInstance()
{
if (!self::$instance) {
self::$instance = new SearchNode();
}

return self::$instance;
}

/**
* @inheritdoc
*
Expand All @@ -44,14 +65,50 @@ public function getNodeName()
*/
public function addSearchTerm($searchTerm)
{
$this->searchTerms[$searchTerm] = $searchTerm;
$string = trim($searchTerm);
if ($string && !in_array($string, $this->searchTerms)) {
$this->searchTerms[] = $string;
}
}

/**
* Elements to be searched for
*
* @return array
*/
public function getSearchTerms()
{
return $this->searchTerms;
}

/**
* if visited
*
* @return bool
*/
public function isVisited()
{
return $this->visited;
}

/**
* Set visited
*
* @param bool $visited Change visit status
* @return void
*/
public function setVisited($visited)
{
$this->visited = $visited;
}

/**
* Enable TESTs so the singleton can be reset
* @return void
*/
public function resetSearchTerms()
{
$this->searchTerms = [];
$this->visited = false;
}
}
16 changes: 10 additions & 6 deletions src/TokenParser/SearchTokenParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

use Graviton\Rql\Node\SearchNode;
use Xiag\Rql\Parser\AbstractTokenParser;
use Xiag\Rql\Parser\Exception\UnknownTokenException;
use Xiag\Rql\Parser\Token;
use Xiag\Rql\Parser\TokenStream;

Expand All @@ -21,7 +22,6 @@
*/
class SearchTokenParser extends AbstractTokenParser
{

/**
* @param TokenStream $tokenStream token stream
* @return SearchNode
Expand All @@ -30,13 +30,17 @@ public function parse(TokenStream $tokenStream)
{
$tokenStream->expect(Token::T_OPERATOR, 'search');
$tokenStream->expect(Token::T_OPEN_PARENTHESIS);

$searchTermsImploded = $this->getParser()->getExpressionParser()->parseScalar($tokenStream);
$searchTerms = explode(" ", $searchTermsImploded);

$searchTerm = $this->getParser()->getExpressionParser()->parseScalar($tokenStream);
if (!is_string($searchTerm)) {
throw new UnknownTokenException('RQL Search only allows strings');
}
$tokenStream->expect(Token::T_CLOSE_PARENTHESIS);

return new SearchNode($searchTerms);
$searchNode = SearchNode::getInstance();
foreach (explode(" ", $searchTerm) as $string) {
$searchNode->addSearchTerm($string);
}
return $searchNode;
}

/**
Expand Down
78 changes: 59 additions & 19 deletions src/Visitor/MongoOdm.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,24 @@

namespace Graviton\Rql\Visitor;

use Graviton\Rql\Node\SearchNode;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Doctrine\ODM\MongoDB\Query\Builder;
use Doctrine\MongoDB\Query\Builder as MongoBuilder;
use Doctrine\MongoDB\Query\Expr as MongoExpr;
use Doctrine\ODM\MongoDB\Query\Expr;
use Graviton\Rql\QueryBuilderAwareInterface;
use Graviton\Rql\Events;
use Graviton\Rql\Event\VisitNodeEvent;
use Graviton\Rql\Node\ElemMatchNode;
use Xiag\Rql\Parser\AbstractNode;
use Xiag\Rql\Parser\Node\AbstractQueryNode;
use Xiag\Rql\Parser\Node\LimitNode;
use Xiag\Rql\Parser\Node\Query\AbstractScalarOperatorNode;
use Xiag\Rql\Parser\Node\Query\AbstractLogicOperatorNode;
use Xiag\Rql\Parser\Node\Query\AbstractArrayOperatorNode;
use Xiag\Rql\Parser\Node\Query\ScalarOperator\LikeNode;
use Xiag\Rql\Parser\Node\SelectNode;
use Xiag\Rql\Parser\Query;

/**
Expand Down Expand Up @@ -79,10 +85,11 @@ final class MongoOdm implements VisitorInterface, QueryBuilderAwareInterface
/**
* map classes with an internal implementation to methods
*
* @var string<string>
* @var array<string>
*/
private $internalMap = [
'Xiag\Rql\Parser\Node\Query\ScalarOperator\LikeNode' => 'visitLike',
'Graviton\Rql\Node\SearchNode' => 'visitSearch',
'Graviton\Rql\Node\ElemMatchNode' => 'visitElemMatch',
];

Expand Down Expand Up @@ -180,14 +187,17 @@ private function recurse($query, $expr = false)
private function dispatchNodeEvent(AbstractNode $node = null)
{
$builder = $this->builder;
if (!empty($this->dispatcher) && $node instanceof AbstractQueryNode) {
$event = $this->dispatcher
->dispatch(
Events::VISIT_NODE,
new VisitNodeEvent($node, $this->builder, $this->context)
);
$node = $event->getNode();
$builder = $event->getBuilder();
if (!empty($this->dispatcher)) {
if ($node instanceof AbstractQueryNode) {
/** @var VisitNodeEvent $event */
$event = $this->dispatcher
->dispatch(
Events::VISIT_NODE,
new VisitNodeEvent($node, $this->builder, $this->context)
);
$node = $event->getNode();
$builder = $event->getBuilder();
}
}
return [$node, $builder];
}
Expand Down Expand Up @@ -244,7 +254,7 @@ private function visitArray(AbstractArrayOperatorNode $node, $expr = false)
* @param string $field name of field to get
* @param bool $expr should i wrap this in expr()
*
* @return Builder|Expr
* @return MongoBuilder|MongoExpr
*/
private function getField($field, $expr)
{
Expand Down Expand Up @@ -293,12 +303,12 @@ private function visitSort(\Xiag\Rql\Parser\Node\SortNode $node)
}

/**
* @param \Xiag\Rql\Parser\Node\Query\ScalarOperator\LikeNode $node like node
* @param boolean $expr should i wrap this in expr
* @param LikeNode $node like node
* @param boolean $expr should i wrap this in expr
*
* @return Builder|Expr
* @return MongoBuilder||Expr
*/
private function visitLike(\Xiag\Rql\Parser\Node\Query\ScalarOperator\LikeNode $node, $expr = false)
private function visitLike(LikeNode $node, $expr = false)
{
$query = $node->getValue();
if ($query instanceof \Xiag\Rql\Parser\DataType\Glob) {
Expand All @@ -312,7 +322,7 @@ private function visitLike(\Xiag\Rql\Parser\Node\Query\ScalarOperator\LikeNode $
*
* @param ElemMatchNode $node elemMatch() node
* @param bool $expr should i wrap this in expr()
* @return Builder|Expr
* @return MongoBuilder|MongoExpr
*/
private function visitElemMatch(ElemMatchNode $node, $expr = false)
{
Expand All @@ -321,26 +331,56 @@ private function visitElemMatch(ElemMatchNode $node, $expr = false)
->elemMatch($this->recurse($node->getQuery(), true));
}

/**
* Visit visitSearch() node
*
* @param SearchNode $node elemMatch() node
* @param bool $expr should i wrap this in expr()
* @return MongoBuilder|MongoExpr
*/
private function visitSearch(SearchNode $node, $expr = false)
{
if ($node->isVisited()) {
if ($expr) {
return $this->builder->expr()->addAnd($this->builder->expr()->field('_id')->exists(true));
} else {
return $this->builder;
}
}
$node->setVisited(true);
$searchArr = [];
foreach ($node->getSearchTerms() as $string) {
$searchArr[] = "\"{$string}\"";
}

$this->builder->sortMeta('score', 'textScore');
if ($expr) {
return $this->builder->expr()->text(implode(' ', $searchArr));
} else {
return $this->builder->addAnd($this->builder->expr()->text(implode(' ', $searchArr)));
}
}

/**
* add limit condition to builder
*
* @param \Xiag\Rql\Parser\Node\LimitNode $node limit node
* @param LimitNode $node limit node
*
* @return void
*/
private function visitLimit(\Xiag\Rql\Parser\Node\LimitNode $node)
private function visitLimit(LimitNode $node)
{
$this->builder->limit($node->getLimit())->skip($node->getOffset());
}

/**
* add selects to builder
*
* @param \Xiag\Rql\Parser\Node\SelectNode $node select node
* @param SelectNode $node select node
*
* @return void
*/
private function visitSelect(\Xiag\Rql\Parser\Node\SelectNode $node)
private function visitSelect(SelectNode $node)
{
array_map(
function ($field) {
Expand Down
15 changes: 11 additions & 4 deletions test/TokenParser/SearchTokenParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
use Graviton\Rql\Parser as GravitonParser;
use Xiag\Rql\Parser\Node\Query\LogicOperator\AndNode;
use Xiag\Rql\Parser\Node\SortNode;
use Xiag\Rql\Parser\Token;
use Xiag\Rql\Parser\TokenStream;

/**
* @author List of contributors <https://github.com/libgraviton/php-rql-parser/graphs/contributors>
Expand All @@ -20,6 +18,14 @@
*/
class SearchTokenParserTest extends \PHPUnit_Framework_TestCase
{
/**
* Setup, clear search params
* @return void
*/
public function setup()
{
SearchNode::getInstance()->resetSearchTerms();
}

/**
* Test SearchTokenParser::parse()
Expand Down Expand Up @@ -136,7 +142,7 @@ public function testDashReplaceMultipleParse()

$expectedSearchNodes = [];
foreach ($terms as $item) {
$expectedSearchNodes[] = new SearchNode([$item]);
$expectedSearchNodes[] = new SearchNode($terms);
}
$expectedNode = new AndNode($expectedSearchNodes);

Expand All @@ -160,6 +166,7 @@ public function testDashReplaceMultipleParse()

/**
* Test SearchTokenParser::parse()
* Multiple searches should be only one visited
* @return void
*/
public function testDashReplaceMultipleAndSortParse()
Expand All @@ -170,7 +177,7 @@ public function testDashReplaceMultipleAndSortParse()

$expectedSearchNodes = [];
foreach ($terms as $item) {
$expectedSearchNodes[] = new SearchNode([$item]);
$expectedSearchNodes[] = new SearchNode($terms);
}
$expectedNode = new AndNode($expectedSearchNodes);
$expectedSortNode = new SortNode($sort);
Expand Down

0 comments on commit fd76a8b

Please sign in to comment.