Skip to content

Commit

Permalink
Issue 88: Support Multivalue Comparisons
Browse files Browse the repository at this point in the history
  • Loading branch information
Willem-Jan Zijderveld committed Jun 21, 2013
1 parent e632a43 commit c5441f8
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 25 deletions.
125 changes: 101 additions & 24 deletions src/Jackalope/Transport/DoctrineDBAL/Query/QOMWalker.php
Expand Up @@ -411,12 +411,48 @@ public function walkNotConstraint(QOM\NotInterface $constraint)

/**
* @param QOM\ComparisonInterface $constraint
* @return string
*/
public function walkComparisonConstraint(QOM\ComparisonInterface $constraint)
{
return $this->walkOperand($constraint->getOperand1()) . " " .
$this->walkOperator($constraint->getOperator()) . " " .
$this->walkOperand($constraint->getOperand2());
// When we encouter equal/not-equal do some checks to catch text comparison
if ($constraint->getOperator() == QOM\QueryObjectModelConstantsInterface::JCR_OPERATOR_EQUAL_TO || $constraint->getOperator() == QOM\QueryObjectModelConstantsInterface::JCR_OPERATOR_NOT_EQUAL_TO) {
// Check if we have a property and a literal value (in random order)
if (
($constraint->getOperand1() instanceOf QOM\PropertyValueInterface || $constraint->getOperand2() instanceOf QOM\PropertyValueInterface) &&
($constraint->getOperand1() instanceOf QOM\LiteralInterface || $constraint->getOperand2() instanceOf QOM\LiteralInterface)
) {
if ($constraint->getOperand1() instanceOf QOM\PropertyValueInterface) {
$propertyOperand = $constraint->getOperand1();
$literalOperand = $constraint->getOperand2();
} else {
$propertyOperand = $constraint->getOperand2();
$literalOperand = $constraint->getOperand1();
}

if ('jcr:path' !== $propertyOperand->getPropertyName() && 'jcr:uuid' !== $propertyOperand->getPropertyName()) {
return $this->walkTextComparisonConstraint($propertyOperand, $literalOperand, $constraint->getOperator());
}
}
}

// None of the checks above returned a constraint, return a simple constraint
return
$this->walkOperand($constraint->getOperand1()) . " " .
$this->walkOperator($constraint->getOperator()) . " " .
$this->walkOperand($constraint->getOperand2());
}

/**
* @param QOM\ComparisonInterface $constraint
* @return string
*/
public function walkTextComparisonConstraint(QOM\PropertyValueInterface $propertyOperand, QOM\LiteralInterface $literalOperand, $operator)
{
$alias = $this->getTableAlias($propertyOperand->getSelectorName() . '.' . $propertyOperand->getPropertyName());
$property = $propertyOperand->getPropertyName();

return $this->sqlXpathComparePropertyValue($alias, $property, $this->getLiteralValue($literalOperand), $operator);
}

/**
Expand Down Expand Up @@ -474,28 +510,8 @@ public function walkOperand(QOM\OperandInterface $operand)
return $this->platform->getUpperExpression($this->walkOperand($operand->getOperand()));
}
if ($operand instanceof QOM\LiteralInterface) {
$namespace = '';

$value = $operand->getLiteralValue();

if ($value instanceof \DateTime) {
$literal = $value->format('c');
} else {
$literal = trim($value, '"');
if (($aliasLength = strpos($literal, ':')) !== false) {
$alias = substr($literal, 0, $aliasLength);
if (!isset($this->namespaces[$alias])) {
throw new NamespaceException('the namespace ' . $alias . ' was not registered.');
}
if (!empty($this->namespaces[$alias])) {
$namespace = $this->namespaces[$alias].':';
}

$literal = substr($literal, $aliasLength + 1);
}
}

return $this->conn->quote($namespace.$literal);
return $this->conn->quote($this->getLiteralValue($operand));
}
if ($operand instanceof QOM\PropertyValueInterface) {
$alias = $this->getTableAlias($operand->getSelectorName() . '.' . $operand->getPropertyName());
Expand Down Expand Up @@ -543,6 +559,37 @@ public function walkOrdering(QOM\OrderingInterface $ordering)
(($ordering->getOrder() == QOM\QueryObjectModelConstantsInterface::JCR_ORDER_ASCENDING) ? "ASC" : "DESC");
}

/**
* @param QOM\LiteralInterface $operand
* @return string
* @throws \PHPCR\NamespaceException
*/
private function getLiteralValue(QOM\LiteralInterface $operand)
{
$namespace = '';

$value = $operand->getLiteralValue();

if ($value instanceof \DateTime) {
$literal = $value->format('c');
} else {
$literal = trim($value, '"');
if (($aliasLength = strpos($literal, ':')) !== false) {
$alias = substr($literal, 0, $aliasLength);
if (!isset($this->namespaces[$alias])) {
throw new NamespaceException('the namespace ' . $alias . ' was not registered.');
}
if (!empty($this->namespaces[$alias])) {
$namespace = $this->namespaces[$alias].':';
}

$literal = substr($literal, $aliasLength + 1);
}
}

return $namespace.$literal;
}

/**
* SQL to execute an XPATH expression checking if the property exist on the node with the given alias.
*
Expand Down Expand Up @@ -587,6 +634,36 @@ private function sqlXpathExtractValue($alias, $property)
throw new NotImplementedException("Xpath evaluations cannot be executed with '" . $this->platform->getName() . "' yet.");
}

/**
* @param $alias
* @param $property
* @param $value
* @param string $operator
* @return string
* @throws \Jackalope\NotImplementedException
*/
private function sqlXpathComparePropertyValue($alias, $property, $value, $operator)
{
$expression = null;

if ($this->platform instanceof MySqlPlatform) {
$expression = "EXTRACTVALUE($alias.props, 'count(//sv:property[@sv:name=\"" . $property . "\"]/sv:value[text()%s\"%s\"]) > 0')";
}

if ($this->platform instanceof PostgreSqlPlatform) {
$expression = "(xpath('//sv:property[@sv:name=\"" . $property . "\"]/sv:value[text()%s\"%s\"]', CAST($alias.props AS xml), ".$this->sqlXpathPostgreSQLNamespaces()."))[1]::text";
}
if ($this->platform instanceof SqlitePlatform) {
$expression = "EXTRACTVALUE($alias.props, 'count(//sv:property[@sv:name=\"" . $property . "\"]/sv:value[text()%s\"%s\"]) > 0')";
}

if (null === $expression) {
throw new NotImplementedException("Xpath evaluations cannot be executed with '" . $this->platform->getName() . "' yet.");
}

return sprintf($expression, $this->walkOperator($operator), $value);
}

/**
* @return string
*/
Expand Down
2 changes: 1 addition & 1 deletion tests/ImplementationLoader.php
Expand Up @@ -70,7 +70,7 @@ protected function __construct(Connection $connection, $fixturePath)
'Query\\QueryManagerTest::testGetQueryInvalid',
'Query\\QueryObjectSql2Test::testGetStoredQueryPath',
// TODO https://github.com/jackalope/jackalope-doctrine-dbal/issues/88
'Query\QuerySql2OperationsTest::testQueryMultiValuedProperty',
// 'Query\QuerySql2OperationsTest::testQueryMultiValuedProperty',
// TODO https://github.com/jackalope/jackalope-doctrine-dbal/issues/89 (next two tests)
'Query\QueryResultsTest::testCompareNumberFields',
'Query\QueryResultsTest::testBooleanField',
Expand Down

0 comments on commit c5441f8

Please sign in to comment.