diff --git a/src/Plugin/search_api/processor/AddHierarchy.php b/src/Plugin/search_api/processor/AddHierarchy.php index 0a5f5d4..ea9549f 100644 --- a/src/Plugin/search_api/processor/AddHierarchy.php +++ b/src/Plugin/search_api/processor/AddHierarchy.php @@ -108,10 +108,12 @@ class AddHierarchy extends ProcessorPluginBase implements ContainerFactoryPlugin // Special handling if this is a taxonomy field. $field_storage = $field->getDataDefinition()->getFieldDefinition()->getFieldStorageDefinition(); if ($field_storage->getSetting('target_type') == 'taxonomy_term') { - /** @var \Drupal\taxonomy\TermInterface $term */ - $term = $this->termStorage->load($val); - if ($parent = $this->getTermParent($term)) { - $hierarchyValues[$k] = $parent->id(); + // Add this term. + $hierarchyValues[$k] = $val; + + // Add each parent. + foreach ($this->termStorage->loadParents($val) as $parent) { + $hierarchyValues[] = $parent->id(); } } else { @@ -137,16 +139,4 @@ class AddHierarchy extends ProcessorPluginBase implements ContainerFactoryPlugin } } - /** - * Gets the parent of a term. - * - * @param \Drupal\taxonomy\TermInterface $term - * - * @return \Drupal\taxonomy\TermInterface $term|null - */ - protected function getTermParent(TermInterface $term) { - $parents = $this->termStorage->loadParents($term->id()); - return array_pop($parents); - } - } diff --git a/tests/src/Kernel/Processor/AddHierarchyTest.php b/tests/src/Kernel/Processor/AddHierarchyTest.php new file mode 100644 index 0000000..2bc46ee --- /dev/null +++ b/tests/src/Kernel/Processor/AddHierarchyTest.php @@ -0,0 +1,155 @@ + [ + 'apple', + 'pear' + ], + 'vegetable' => [ + 'radish', + 'turnip', + ], + ]; + + /** + * The nodes created for testing. + * + * @var \Drupal\node\NodeInterface[] + */ + protected $nodes; + + /** + * Hierarchical taxonomy terms. + * + * This is keyed by `type.item`, for example: `fruit.pear`. + * + * @var \Drupal\taxonomy\TermInterface[] + */ + protected $terms; + + /** + * Vocabulary to test with when using taxonomy for the hierarchy. + * + * @var \Drupal\taxonomy\VocabularyInterface + */ + protected $vocabulary; + + /** + * {@inheritdoc} + */ + public function setUp($processor = NULL) { + parent::setUp('add_hierarchy'); + $this->installConfig(['filter']); + $this->installEntitySchema('taxonomy_term'); + $this->createTaxonomyHierarchy(); + + // Create a node type for testing. + $type = NodeType::create(array('type' => 'page', 'name' => 'page')); + $type->save(); + + // Add the taxonomy field to page type. + $this->createEntityReferenceField('node', 'page', 'term_field', NULL, 'taxonomy_term', 'default', [], FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED); + + // Index the taxonomy field. + $term_field = new Field($this->index, 'term_field'); + $term_field->setType('integer'); + $term_field->setPropertyPath('term_field'); + $term_field->setDatasourceId('entity:node'); + $term_field->setLabel('Terms'); + $this->index->addField($term_field); + $this->index->save(); + + // Setup a node index. + $manager = $this->container->get('plugin.manager.search_api.datasource'); + $datasources['entity:node'] = $manager->createInstance('entity:node', ['index' => $this->index]); + $this->index->setDatasources($datasources); + $this->index->save(); + $this->container->get('search_api.index_task_manager')->addItemsAll($this->index); + $index_storage = $this->container->get('entity_type.manager')->getStorage('search_api_index'); + $index_storage->resetCache([$this->index->id()]); + $this->index = $index_storage->load($this->index->id()); + } + + /** + * Test taxonomy-based hierarchy indexing. + * + * @covers ::preprocessIndexItems + */ + public function testPreprocessIndexItems() { + // Add hierarchical terms to 3 nodes. + foreach (['vegetable.turnip', 'vegetable', 'fruit.pear'] as $i => $term) { + $this->nodes[$i] = $this->createNode([ + 'type' => 'page', + 'term_field' => ['target_id' => $this->terms[$term]->id()], + ]); + } + $this->index->reindex(); + $this->index->indexItems(); + + // Query for 'vegetable' should return 2 items: + // Node 1 is 'vegetable.turnip' and node 2 is just 'vegetable'. + $query = Utility::createQuery($this->index); + $query->addCondition('term_field', $this->terms['vegetable']->id()); + $result = $query->execute(); + $expected = ['node' => [0, 1]]; + $this->assertResults($result, $expected); + + // A search for just turnips should return node 1 only. + $query = Utility::createQuery($this->index); + $query->addCondition('term_field', $this->terms['vegetable.turnip']->id()); + $result = $query->execute(); + $expected = ['node' => [0]]; + $this->assertResults($result, $expected); + } + + /** + * Helper function to create the hierarchy with taxonomy terms. + */ + protected function createTaxonomyHierarchy() { + $this->vocabulary = $this->createVocabulary(); + + foreach (static::$hierarchy as $type => $items) { + // Add the 'type' item, and nest items underneath. + $this->terms[$type] = $type_term = $this->createTerm($this->vocabulary, [ + 'name' => $type, + ]); + foreach ($items as $item) { + $this->terms["$type.$item"] = $this->createTerm($this->vocabulary, [ + 'name' => $item, + 'parent' => $type_term, + ]); + } + } + } + +}