Add indexing support

Using the index keyword, lists can now have an alphabetical Index at the
top level.
This commit is contained in:
Andreas Gohr
2023-06-26 14:11:18 +02:00
parent 1ee0a2dc29
commit ce44c6393f
6 changed files with 144 additions and 4 deletions

View File

@ -201,4 +201,53 @@ class NestedResultTest extends StructTest
$this->assertCount(3, $tree, '3 root nodes of material expected');
$this->assertCount(1, $tree[0]->getChildren()[0]->getResultRows(), '1 metal black row expected');
}
/**
* Nest by two multi value levels with indexing
*/
public function testMultiMultiTwoLevelsIndex()
{
$result = $this->makeResult($this->multiMultiItems);
$nestedResult = new NestedResult($result);
$root = $nestedResult->getRoot(2, 1);
$tree = $root->getChildren(); // nest: index, material, color, *
$this->assertCount(3, $tree, '3 root index nodes expected');
$this->assertEquals('M', $tree[0]->getValueObject()->getValue(), 'M expected');
$this->assertCount(1, $tree[0]->getChildren(), '1 metal sub node under M expected');
}
/**
* Index a flat result with no multi values
*/
public function testSimpleIndex()
{
$result = $this->makeResult($this->simpleItems);
$nestedResult = new NestedResult($result);
$root = $nestedResult->getRoot(0, 2);
$tree = $root->getChildren();
$this->assertCount(2, $tree, '2 root index nodes expected');
$this->assertEquals('CA', $tree[0]->getValueObject()->getValue(), 'CA(r) index expected');
$this->assertEquals('LA', $tree[1]->getValueObject()->getValue(), 'LA(ptop) index expected');
$this->assertCount(6, $tree[0]->getResultRows(), '6 rows under CA expected');
}
/**
* Index a flat result with multi values
*/
public function testMultiIndex()
{
$result = $this->makeResult($this->multiItems);
$nestedResult = new NestedResult($result);
$root = $nestedResult->getRoot(0, 2);
$tree = $root->getChildren();
$this->assertCount(4, $tree, '4 root index nodes expected');
$this->assertEquals('BL', $tree[0]->getValueObject()->getValue(), 'BL(ack|blue) index expected');
$this->assertCount(4, $tree[0]->getResultRows(), '4 rows under BL expected');
}
}

View File

@ -26,7 +26,7 @@ class AggregationList extends Aggregation
$this->startScope();
if ($this->result) {
$nestedResult = new NestedResult($this->result);
$root = $nestedResult->getRoot($this->data['nesting']);
$root = $nestedResult->getRoot($this->data['nesting'], $this->data['index']);
$this->renderNode($root);
} elseif ($showNotFound) {
$this->renderer->cdata($this->helper->getLang('none'));

View File

@ -40,6 +40,7 @@ class ConfigParser
'sort' => array(),
'csv' => true,
'nesting' => 0,
'index' => 0,
);
// parse info
foreach ($lines as $line) {
@ -121,6 +122,9 @@ class ConfigParser
case 'nest':
$this->config['nesting'] = (int) $val;
break;
case 'index':
$this->config['index'] = (int) $val;
break;
default:
$data = array('config' => &$this->config, 'key' => $key, 'val' => $val);
$ev = new \Doku_Event('PLUGIN_STRUCT_CONFIGPARSER_UNKNOWNKEY', $data);

View File

@ -2,6 +2,9 @@
namespace dokuwiki\plugin\struct\meta;
use dokuwiki\plugin\struct\types\Text;
use dokuwiki\Utf8\PhpString;
/**
* This class builds a nested tree from a search result
*
@ -13,6 +16,9 @@ class NestedResult
/** @var NestedValue[] */
protected $nodes = [];
/** @var NestedValue[] */
protected $indexNodes = [];
/** @var Value[][] */
protected $result;
@ -29,20 +35,63 @@ class NestedResult
* Get the nested result
*
* @param int $nesting the nesting level to use
* @param int $index the number of characters to use for indexing
* @return NestedValue the root node of the nested tree
*/
public function getRoot($nesting) {
public function getRoot($nesting, $index = 0)
{
$this->nodes = [];
$root = new NestedValue(null, -1);
if(!$this->result) return $root;
if (!$this->result) return $root;
foreach ($this->result as $row) {
$this->nestBranch($root, $row, $nesting);
}
$root = $this->createIndex($root, $index);
return $root;
}
/**
* Add a top level index to the tree
*
* @param NestedValue $root Root node of the tree
* @param int $index Number of characters to use for indexing
* @return NestedValue new root node
*/
protected function createIndex(NestedValue $root, $index)
{
if (!$index) return $root;
$this->indexNodes = [];
$children = $root->getChildren();
$resultRows = $root->getResultRows();
if ($children) {
// there are children, so we are a nested result
foreach ($children as $child) {
$indexValue = $child->getValueObject();
$indexNode = $this->getIndexNode($indexValue, $index);
$indexNode->addChild($child);
$child->setDepth(1); // increase child's depth from 0 to 1
}
} elseif ($resultRows) {
// no children, so we are currently a flat result
foreach ($resultRows as $row) {
$indexValue = $row[0];
$indexNode = $this->getIndexNode($indexValue, $index);
$indexNode->addResultRow($row);
}
}
// now all results are added to index nodes - use them as children
$newRoot = new NestedValue(null, -1);
foreach ($this->indexNodes as $node) {
$newRoot->addChild($node);
}
return $newRoot;
}
/**
* Creates nested nodes for a given result row
*
@ -96,6 +145,33 @@ class NestedResult
}
return $this->nodes[$key];
}
/**
* Create or get an existing Node for indexing
*
* @param Value $value
* @param int $index
* @return NestedValue
*/
protected function getIndexNode(Value $value, $index)
{
$compare = $value->getCompareValue();
if (is_array($compare)) $compare = $compare[0];
$key = PhpString::strtoupper(PhpString::substr($compare, 0, $index));
if (!isset($this->indexNodes[$key])) {
$col = new Column(
0,
new Text([], '%index%', false),
-1,
true,
$value->getColumn()->getTable()
);
$this->indexNodes[$key] = new NestedValue(new Value($col, $key), 0);
}
return $this->indexNodes[$key];
}
}

View File

@ -44,6 +44,17 @@ class NestedValue
return $this->depth;
}
/**
* @param int $depth
*/
public function setDepth($depth)
{
$this->depth = $depth;
foreach ($this->children as $child) {
$child->setDepth($depth + 1);
}
}
/**
* Access the stored value
*

View File

@ -19,7 +19,7 @@ class syntax_plugin_struct_table extends DokuWiki_Syntax_Plugin
protected $tableclass = AggregationTable::class;
/** @var string Config options that are not allowed for this syntax mode */
protected $illegalOptions = ['nesting'];
protected $illegalOptions = ['nesting', 'index'];
/**
* @return string Syntax mode type