Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Numerical sorting #226

Merged
merged 1 commit into from
Jan 5, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 103 additions & 38 deletions src/Jackalope/Transport/DoctrineDBAL/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ public function __construct(FactoryInterface $factory, Connection $conn)
private function registerSqliteFunctions(PDOConnection $sqliteConnection)
{
$sqliteConnection->sqliteCreateFunction('EXTRACTVALUE', function ($string, $expression) {
if (null === $string) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

numerical_prop can be nullable.

return null;
}

$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->loadXML($string);
$xpath = new \DOMXPath($dom);
Expand Down Expand Up @@ -571,10 +575,20 @@ public function copyNode($srcAbsPath, $dstAbsPath, $srcWorkspace = null)
foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
$newPath = str_replace($srcAbsPath, $dstAbsPath, $row['path']);

$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->loadXML($row['props']);
$stringDom = new \DOMDocument('1.0', 'UTF-8');
$stringDom->loadXML($row['props']);

$numericalDom = null;

$propsData = array('dom' => $dom);
if ($row['numerical_props']) {
$numericalDom = new \DOMDocument('1.0', 'UTF-8');
$numericalDom->loadXML($row['numerical_props']);
}

$propsData = array(
'stringDom' => $stringDom,
'numericalDom' => $numericalDom
);
// when copying a node, the copy is always a new node. set $isNewNode to true
$newNodeId = $this->syncNode(null, $newPath, $row['type'], true, array(), $propsData);

Expand Down Expand Up @@ -646,25 +660,27 @@ private function syncNode($uuid, $path, $type, $isNewNode, $props = array(), $pr

$qb = $this->conn->createQueryBuilder();

$qb->select(':identifier, :type, :path, :local_name, :namespace, :parent, :workspace_name, :props, :depth, COALESCE(MAX(n.sort_order), 0) + 1')
$qb->select(':identifier, :type, :path, :local_name, :namespace, :parent, :workspace_name, :props, :numerical_props, :depth, COALESCE(MAX(n.sort_order), 0) + 1')
->from('phpcr_nodes', 'n')
->where('n.parent = :parent_a');

$sql = $qb->getSql();

try {
$insert = "INSERT INTO phpcr_nodes (identifier, type, path, local_name, namespace, parent, workspace_name, props, depth, sort_order) " . $sql;
$this->conn->executeUpdate($insert, array(
'identifier' => $uuid,
'type' => $type,
'path' => $path,
'local_name' => $localName,
'namespace' => $namespace,
'parent' => PathHelper::getParentPath($path),
$insert = "INSERT INTO phpcr_nodes (identifier, type, path, local_name, namespace, parent, workspace_name, props, numerical_props, depth, sort_order) " . $sql;

$this->conn->executeUpdate($insert, $data = array(
'identifier' => $uuid,
'type' => $type,
'path' => $path,
'local_name' => $localName,
'namespace' => $namespace,
'parent' => PathHelper::getParentPath($path),
'workspace_name' => $this->workspaceName,
'props' => $propsData['dom']->saveXML(),
'depth' => PathHelper::getPathDepth($path),
'parent_a' => PathHelper::getParentPath($path),
'props' => $propsData['stringDom']->saveXML(),
'numerical_props' => $propsData['numericalDom'] ? $propsData['numericalDom']->saveXML() : null,
'depth' => PathHelper::getPathDepth($path),
'parent_a' => PathHelper::getParentPath($path),
));
} catch(\Exception $e) {
if ($e instanceof \PDOException || $e instanceof DBALException) {
Expand All @@ -684,7 +700,13 @@ private function syncNode($uuid, $path, $type, $isNewNode, $props = array(), $pr
if (!$nodeId) {
throw new RepositoryException("nodeId for $path not found");
}
$this->conn->update('phpcr_nodes', array('props' => $propsData['dom']->saveXML()), array('id' => $nodeId));

$this->conn->update('phpcr_nodes', array(
'props' => $propsData['stringDom']->saveXML(),
'numerical_props' => $propsData['numericalDom'] ? $propsData['numericalDom']->saveXML() : null,
),
array('id' => $nodeId)
);
}

$this->nodeIdentifiers[$path] = $uuid;
Expand Down Expand Up @@ -853,6 +875,7 @@ public static function xmlToProps($xml, ValueConverter $valueConverter, $filter
}

$values = array();

$type = PropertyType::valueFromName($propertyNode->getAttribute('sv:type'));
foreach ($propertyNode->childNodes as $valueNode) {
switch ($type) {
Expand Down Expand Up @@ -912,7 +935,11 @@ public static function xmlToProps($xml, ValueConverter $valueConverter, $filter
* @param array $properties
* @param boolean $inlineBinaries
*
* @return array ('dom' => $dom, 'binaryData' => streams, 'references' => array('type' => INT, 'values' => array(UUIDs)))
* @return array (
* 'stringDom' => $stringDom,
* 'numericalDom' => $numericalDom',
* 'binaryData' => streams,
* 'references' => array('type' => INT, 'values' => array(UUIDs)))
*/
private function propsToXML($properties, $inlineBinaries = false)
{
Expand All @@ -925,20 +952,16 @@ private function propsToXML($properties, $inlineBinaries = false)
'rep' => "internal",
);

$dom = new \DOMDocument('1.0', 'UTF-8');
$rootNode = $dom->createElement('sv:node');
foreach ($namespaces as $namespace => $uri) {
$rootNode->setAttribute('xmlns:' . $namespace, $uri);
}
$dom->appendChild($rootNode);
$doms = array(
'stringDom' => array(),
'numericalDom' => array(),
);

$binaryData = $references = array();

foreach ($properties as $property) {
/* @var $property Property */
$propertyNode = $dom->createElement('sv:property');
$propertyNode->setAttribute('sv:name', $property->getName());
$propertyNode->setAttribute('sv:type', PropertyType::nameFromValue($property->getType()));
$propertyNode->setAttribute('sv:multi-valued', $property->isMultiple() ? '1' : '0');

$targetDoms = array('stringDom');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always populate props column


switch ($property->getType()) {
case PropertyType::WEAKREFERENCE:
Expand All @@ -955,12 +978,14 @@ private function propsToXML($properties, $inlineBinaries = false)
break;
case PropertyType::DECIMAL:
$values = $property->getDecimal();
$targetDoms[] = 'numericalDom';
break;
case PropertyType::BOOLEAN:
$values = array_map('intval', (array) $property->getBoolean());
break;
case PropertyType::LONG:
$values = $property->getLong();
$targetDoms[] = 'numericalDom';
break;
case PropertyType::BINARY:
if ($property->isNew() || $property->isModified()) {
Expand Down Expand Up @@ -998,26 +1023,66 @@ private function propsToXML($properties, $inlineBinaries = false)
break;
case PropertyType::DOUBLE:
$values = $property->getDouble();
$targetDoms[] = 'numericalDom';
break;
default:
throw new RepositoryException('unknown type '.$property->getType());
}

$lengths = (array) $property->getLength();
foreach ((array) $values as $key => $value) {
$element = $propertyNode->appendChild($dom->createElement('sv:value'));
$element->appendChild($dom->createTextNode($value));
if (isset($lengths[$key])) {
$lengthAttribute = $dom->createAttribute('length');
$lengthAttribute->value = $lengths[$key];
$element->appendChild($lengthAttribute);
foreach ($targetDoms as $targetDom) {
$doms[$targetDom][] = array(
'name' => $property->getName(),
'type' => PropertyType::nameFromValue($property->getType()),
'multiple' => $property->isMultiple(),
'lengths' => (array) $property->getLength(),
'values' => $values,
);
}
}

$ret = array(
'stringDom' => null,
'numericalDom' => null,
'binaryData' => $binaryData,
'references' => $references
);

foreach ($doms as $targetDom => $properties) {

$dom = new \DOMDocument('1.0', 'UTF-8');
$rootNode = $dom->createElement('sv:node');
foreach ($namespaces as $namespace => $uri) {
$rootNode->setAttribute('xmlns:' . $namespace, $uri);
}
$dom->appendChild($rootNode);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doh. wtf.

foreach ($properties as $property) {

/* @var $property Property */
$propertyNode = $dom->createElement('sv:property');
$propertyNode->setAttribute('sv:name', $property['name']);
$propertyNode->setAttribute('sv:type', $property['type']);
$propertyNode->setAttribute('sv:multi-valued', $property['multiple'] ? '1' : '0');
$lengths = (array) $property['lengths'];
foreach ((array) $property['values'] as $key => $value) {
$element = $propertyNode->appendChild($dom->createElement('sv:value'));
$element->appendChild($dom->createTextNode($value));
if (isset($lengths[$key])) {
$lengthAttribute = $dom->createAttribute('length');
$lengthAttribute->value = $lengths[$key];
$element->appendChild($lengthAttribute);
}
}

$rootNode->appendChild($propertyNode);
}

$rootNode->appendChild($propertyNode);
if (count($properties)) {
$ret[$targetDom] = $dom;
}
}

return array('dom' => $dom, 'binaryData' => $binaryData, 'references' => $references);
return $ret;
}

/**
Expand Down
50 changes: 24 additions & 26 deletions src/Jackalope/Transport/DoctrineDBAL/Query/QOMWalker.php
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,26 @@ public function walkOrdering(QOM\OrderingInterface $ordering)
$direction = 'DESC';
}

return $this->walkOperand($ordering->getOperand()) . " " . $direction;
$sql = $this->walkOperand($ordering->getOperand());

if ($ordering->getOperand() instanceof QOM\PropertyValueInterface) {
$operand = $ordering->getOperand();
$property = $ordering->getOperand()->getPropertyName();
if ($property !== 'jcr:path' && $property !== 'jcr:uuid') {
$alias = $this->getTableAlias($operand->getSelectorName() . '.' . $property);

$numericalSelector = $this->sqlXpathExtractValue($alias, $property, 'numerical_props');

$sql = sprintf('CAST(%s AS DECIMAL), %s',
$numericalSelector,
$sql
);
}
}

$sql .= ' ' .$direction;

return $sql;
}

/**
Expand Down Expand Up @@ -791,16 +810,16 @@ private function sqlXpathValueExists($alias, $property)
*
* @return string
*/
private function sqlXpathExtractValue($alias, $property)
private function sqlXpathExtractValue($alias, $property, $column = 'props')
{
if ($this->platform instanceof MySqlPlatform) {
return "EXTRACTVALUE($alias.props, '//sv:property[@sv:name=\"" . $property . "\"]/sv:value[1]')";
return "EXTRACTVALUE($alias.$column, '//sv:property[@sv:name=\"" . $property . "\"]/sv:value[1]')";
}
if ($this->platform instanceof PostgreSqlPlatform) {
return "(xpath('//sv:property[@sv:name=\"" . $property . "\"]/sv:value[1]/text()', CAST($alias.props AS xml), ".$this->sqlXpathPostgreSQLNamespaces()."))[1]::text";
return "(xpath('//sv:property[@sv:name=\"" . $property . "\"]/sv:value[1]/text()', CAST($alias.$column AS xml), ".$this->sqlXpathPostgreSQLNamespaces()."))[1]::text";
}
if ($this->platform instanceof SqlitePlatform) {
return "EXTRACTVALUE($alias.props, '//sv:property[@sv:name=\"" . $property . "\"]/sv:value[1]')";
return "EXTRACTVALUE($alias.$column, '//sv:property[@sv:name=\"" . $property . "\"]/sv:value[1]')";
}

throw new NotImplementedException("Xpath evaluations cannot be executed with '" . $this->platform->getName() . "' yet.");
Expand Down Expand Up @@ -868,27 +887,6 @@ private function sqlXpathPostgreSQLNamespaces()
return "ARRAY[ARRAY['sv', 'http://www.jcp.org/jcr/sv/1.0']]";
}

/**
* Returns the SQL part to select the given property
*
* @param string $alias
* @param string $propertyName
*
* @return string
*/
private function sqlProperty($alias, $propertyName)
{
if ('jcr:uuid' === $propertyName) {
return "$alias.identifier";
}

if ('jcr:path' === $propertyName) {
return "$alias.path";
}

return $this->sqlXpathExtractValue($alias, $propertyName);
}

/**
* @param QOM\SelectorInterface $source
* @param string $alias
Expand Down
1 change: 1 addition & 0 deletions src/Jackalope/Transport/DoctrineDBAL/RepositorySchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ protected function addNodesTable()
$nodes->addColumn('identifier', 'string');
$nodes->addColumn('type', 'string');
$nodes->addColumn('props', 'text');
$nodes->addColumn('numerical_props', 'text', array('notnull' => false));
$nodes->addColumn('depth', 'integer');
$nodes->addColumn('sort_order', 'integer', array('notnull' => false));
$nodes->setPrimaryKey(array('id'));
Expand Down
Loading