diff --git a/src/Plugin/search_api/processor/AddHierarchy.php b/src/Plugin/search_api/processor/AddHierarchy.php
new file mode 100644
index 0000000..ea9549f
--- /dev/null
+++ b/src/Plugin/search_api/processor/AddHierarchy.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace Drupal\search_api\Plugin\search_api\processor;
+
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\search_api\Datasource\DatasourceInterface;
+use Drupal\search_api\Item\FieldInterface;
+use Drupal\search_api\Processor\ProcessorPluginBase;
+use Drupal\taxonomy\TermInterface;
+use Drupal\taxonomy\TermStorageInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @SearchApiProcessor(
+ *   id = "add_hierarchy",
+ *   label = @Translation("Hierarchy"),
+ *   description = @Translation("Adds item's hierarchy into the backend"),
+ *   stages = {
+ *     "preprocess_index" = -45
+ *   }
+ * )
+ */
+class AddHierarchy extends ProcessorPluginBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * Term storage service.
+   *
+   * @var \Drupal\taxonomy\TermStorageInterface
+   */
+  protected $termStorage;
+
+  /**
+   * {@inheritdoc}
+   *
+   * Constructs the hierarchy plugin.
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, TermStorageInterface $term_storage = NULL) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->termStorage = $term_storage;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    $entity_type_manager = $container->get('entity_type.manager');
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $entity_type_manager->hasHandler('taxonomy_term', 'storage') ? $entity_type_manager->getStorage('taxonomy_term') : NULL
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function alterPropertyDefinitions(array &$properties, DatasourceInterface $datasource = NULL) {
+
+    if ($datasource === NULL) {
+      return;
+    }
+
+    $fields = $datasource->getIndex()->getFields();
+    foreach ($fields as $field) {
+      if ($this->isEntityReferenceField($field)) {
+        $definition = [
+          'label' => $this->t('Hierarchy of %field', ['%field' => $field->getLabel()]),
+          'description' => $this->t('The complete hierarchy for the entity reference'),
+          'type' => 'string',
+          'locked' => TRUE,
+        ];
+
+        $properties['search_api_hierarchy:' . $field->getFieldIdentifier()] = new DataDefinition($definition);
+      }
+    }
+  }
+
+  /**
+   * Determine if a given field is an entity reference field.
+   *
+   * @param \Drupal\search_api\Item\FieldInterface $field
+   *   The field to check.
+   *
+   * @return bool
+   *   Returns TRUE if the given field is an entity reference field.
+   */
+  protected function isEntityReferenceField(FieldInterface $field) {
+    return $field->getOriginalType() == 'field_item:entity_reference';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preprocessIndexItems(array &$items) {
+    /** @var \Drupal\search_api\Item\ItemInterface $item */
+    foreach ($items as $item) {
+      foreach ($item->getFields() as $field) {
+        if ($this->isEntityReferenceField($field)) {
+          // The field is an entity reference, so we have to traverse up the tree
+          // to find all parents.
+          $hierarchyValues = [];
+
+          $fieldValues = $field->getValues();
+
+          foreach ($fieldValues as $k => $val) {
+            // Special handling if this is a taxonomy field.
+            $field_storage = $field->getDataDefinition()->getFieldDefinition()->getFieldStorageDefinition();
+            if ($field_storage->getSetting('target_type') == 'taxonomy_term') {
+              // Add this term.
+              $hierarchyValues[$k] = $val;
+
+              // Add each parent.
+              foreach ($this->termStorage->loadParents($val) as $parent) {
+                $hierarchyValues[] = $parent->id();
+              }
+            }
+            else {
+              // @todo This just repeats the values, without hierarchy, so
+              // it needs to be re-worked in a general way to find entity
+              // reference 'parents'.
+              $hierarchyValues[$k] = $val;
+            }
+          }
+
+          // The output of all the parents is saved in a new, dynamic field. This
+          // is the list of all parents, concatenated and separated with a slash
+          // as the lucene/solr guidelines suggest:
+          // https://wiki.apache.org/solr/HierarchicalFaceting.
+          $hierarchyField = $item->getField('search_api_hierarchy:' . $field->getFieldIdentifier());
+
+          if (!is_null($hierarchyField)) {
+            $hierarchyField->setValues($hierarchyValues);
+          }
+          $field->setValues($hierarchyValues);
+        }
+      }
+    }
+  }
+
+}
diff --git a/src/Query/Query.php b/src/Query/Query.php
index 4cfc7e6..263a844 100644
--- a/src/Query/Query.php
+++ b/src/Query/Query.php
@@ -549,7 +549,7 @@ class Query implements QueryInterface {
    * Implements the magic __wakeup() method to reload the query's index.
    */
   public function __wakeup() {
-    if (!isset($this->index) && !empty($this->indexId)) {
+    if (!isset($this->index) && !empty($this->indexId) && \Drupal::hasContainer()) {
       $this->index = \Drupal::entityTypeManager()
         ->getStorage('search_api_index')
         ->load($this->indexId);
diff --git a/src/Utility.php b/src/Utility.php
index 1585562..029b02b 100644
--- a/src/Utility.php
+++ b/src/Utility.php
@@ -266,7 +266,6 @@ class Utility {
       }
       $field->addValue($value);
     }
-    $field->setOriginalType($data->getDataDefinition()->getDataType());
   }
 
   /**
diff --git a/tests/src/Kernel/Processor/AddHierarchyTest.php b/tests/src/Kernel/Processor/AddHierarchyTest.php
new file mode 100644
index 0000000..9ccfcdd
--- /dev/null
+++ b/tests/src/Kernel/Processor/AddHierarchyTest.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace Drupal\Tests\search_api\Kernel\Processor;
+
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
+use Drupal\node\Entity\NodeType;
+use Drupal\search_api\Item\Field;
+use Drupal\search_api\Utility;
+use Drupal\simpletest\NodeCreationTrait;
+use Drupal\taxonomy\Tests\TaxonomyTestTrait;
+use Drupal\Tests\search_api\Kernel\ResultsTrait;
+
+/**
+ * Tests the 'add_hierarchy' processor.
+ *
+ * @coversDefaultClass \Drupal\search_api\Plugin\search_api\processor\AddHierarchy
+ */
+class AddHierarchyTest extends ProcessorTestBase {
+
+  use NodeCreationTrait;
+  use EntityReferenceTestTrait;
+  use ResultsTrait;
+  use TaxonomyTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['filter', 'taxonomy'];
+
+  /**
+   * A hierarchy to test.
+   */
+  protected static $hierarchy = [
+    'fruit' => [
+      '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,
+        ]);
+      }
+    }
+  }
+
+}
diff --git a/tests/src/Kernel/Processor/ContentAccessTest.php b/tests/src/Kernel/Processor/ContentAccessTest.php
index 1964845..2b15f09 100644
--- a/tests/src/Kernel/Processor/ContentAccessTest.php
+++ b/tests/src/Kernel/Processor/ContentAccessTest.php
@@ -9,8 +9,8 @@ use Drupal\Core\Database\Database;
 use Drupal\Core\TypedData\DataDefinitionInterface;
 use Drupal\node\Entity\Node;
 use Drupal\node\Entity\NodeType;
-use Drupal\search_api\Query\ResultSetInterface;
 use Drupal\search_api\Utility;
+use Drupal\Tests\search_api\Kernel\ResultsTrait;
 use Drupal\user\Entity\Role;
 use Drupal\user\Entity\User;
 
@@ -24,6 +24,7 @@ use Drupal\user\Entity\User;
 class ContentAccessTest extends ProcessorTestBase {
 
   use CommentTestTrait;
+  use ResultsTrait;
 
   /**
    * The nodes created for testing.
@@ -314,39 +315,6 @@ class ContentAccessTest extends ProcessorTestBase {
   }
 
   /**
-   * Asserts that the search results contain the expected IDs.
-   *
-   * @param \Drupal\search_api\Query\ResultSetInterface $result
-   *   The search results.
-   * @param int[][] $expected
-   *   The expected entity IDs, grouped by entity type and with their indexes in
-   *   this object's respective array properties as the values.
-   */
-  protected function assertResults(ResultSetInterface $result, array $expected) {
-    $results = array_keys($result->getResultItems());
-    sort($results);
-
-    $ids = array();
-    foreach ($expected as $entity_type => $items) {
-      $datasource_id = "entity:$entity_type";
-      foreach ($items as $i) {
-        if ($entity_type == 'user') {
-          $id = $i . ':en';
-        }
-        else {
-          /** @var \Drupal\Core\Entity\EntityInterface $entity */
-          $entity = $this->{"{$entity_type}s"}[$i];
-          $id = $entity->id() . ':en';
-        }
-        $ids[] = Utility::createCombinedId($datasource_id, $id);
-      }
-    }
-    sort($ids);
-
-    $this->assertEquals($ids, $results);
-  }
-
-  /**
    * Creates a new user account.
    *
    * @param string[] $permissions
diff --git a/tests/src/Kernel/ResultsTrait.php b/tests/src/Kernel/ResultsTrait.php
new file mode 100644
index 0000000..7129613
--- /dev/null
+++ b/tests/src/Kernel/ResultsTrait.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Drupal\Tests\search_api\Kernel;
+
+use Drupal\search_api\Query\ResultSetInterface;
+use Drupal\search_api\Utility;
+
+/**
+ * Defines a trait for testing results.
+ */
+trait ResultsTrait {
+
+  /**
+   * Asserts that the search results contain the expected IDs.
+   *
+   * @param \Drupal\search_api\Query\ResultSetInterface $result
+   *   The search results.
+   * @param int[][] $expected
+   *   The expected entity IDs, grouped by entity type and with their indexes in
+   *   this object's respective array properties as the values.
+   */
+  protected function assertResults(ResultSetInterface $result, array $expected) {
+    $results = array_keys($result->getResultItems());
+    sort($results);
+
+    $ids = array();
+    foreach ($expected as $entity_type => $items) {
+      $datasource_id = "entity:$entity_type";
+      foreach ($items as $i) {
+        if ($entity_type == 'user') {
+          $id = $i . ':en';
+        }
+        else {
+          /** @var \Drupal\Core\Entity\EntityInterface $entity */
+          $entity = $this->{"{$entity_type}s"}[$i];
+          $id = $entity->id() . ':en';
+        }
+        $ids[] = Utility::createCombinedId($datasource_id, $id);
+      }
+    }
+    sort($ids);
+
+    $this->assertEquals($ids, $results);
+  }
+
+}
diff --git a/tests/src/Unit/Plugin/Processor/AddHierarchyTest.php b/tests/src/Unit/Plugin/Processor/AddHierarchyTest.php
new file mode 100644
index 0000000..6ef647e
--- /dev/null
+++ b/tests/src/Unit/Plugin/Processor/AddHierarchyTest.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace Drupal\Tests\search_api\Unit\Plugin\Processor;
+
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
+use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\TypedData\DataDefinitionInterface;
+use Drupal\search_api\Datasource\DatasourceInterface;
+use Drupal\search_api\IndexInterface;
+use Drupal\search_api\Item\FieldInterface;
+use Drupal\search_api\Plugin\search_api\processor\AddHierarchy;
+use Drupal\search_api\Utility;
+use Drupal\Tests\UnitTestCase;
+use Drupal\text\Plugin\Field\FieldType\TextItem;
+use Prophecy\Argument;
+use Drupal\search_api\Item\ItemInterface;
+
+/**
+ * Tests for the "Hierarchy" plugin.
+ *
+ * @group search_api
+ *
+ * @coversDefaultClass \Drupal\search_api\Plugin\search_api\processor\AddHierarchy
+ */
+class AddHierarchyTest extends UnitTestCase {
+
+  /**
+   * The processor to be tested.
+   *
+   * @var \Drupal\search_api\Plugin\search_api\processor\AddHierarchy
+   */
+  protected $processor;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    // Test without term storage.
+    $this->processor = new AddHierarchy([], 'hierarchy', []);
+    $this->processor->setStringTranslation($this->getStringTranslationStub());
+  }
+
+  /**
+   * Test data properties alter.
+   *
+   * @covers ::alterPropertyDefinitions
+   * @covers ::isEntityReferenceField
+   */
+  public function testAlterPropertyDefinitions() {
+    // Without a datasource, properties should be unaltered.
+    $properties = [
+      'foo' => 'bar',
+    ];
+    $this->processor->alterPropertyDefinitions($properties);
+    $this->assertArrayEquals(['foo' => 'bar'], $properties);
+
+    // Test with a non entity reference.
+    $field = $this->prophesize(FieldInterface::class);
+    $field->getOriginalType()->willReturn('string');
+    $index = $this->prophesize(IndexInterface::class);
+    $index->getFields()->willReturn([$field->reveal()]);
+    $datasource = $this->prophesize(DatasourceInterface::class);
+    $datasource->getIndex()->willReturn($index->reveal());
+    $this->processor->alterPropertyDefinitions($properties, $datasource->reveal());
+    $this->assertArrayEquals(['foo' => 'bar'], $properties);
+
+    // Test with a field item, but not an entity reference field.
+    $field = $this->prophesize(FieldInterface::class);
+    $field->getOriginalType()->willReturn('field_item:string');
+    $index = $this->prophesize(IndexInterface::class);
+    $index->getFields()->willReturn([$field->reveal()]);
+    $datasource = $this->prophesize(DatasourceInterface::class);
+    $datasource->getIndex()->willReturn($index->reveal());
+    $this->processor->alterPropertyDefinitions($properties, $datasource->reveal());
+    $this->assertArrayEquals(['foo' => 'bar'], $properties);
+
+    // Test with an entity reference field.
+    $field = $this->prophesize(FieldInterface::class);
+    $field->getOriginalType()->willReturn('field_item:entity_reference');
+    $field->getLabel()->willReturn('Foo field');
+    $field->getFieldIdentifier()->willReturn('foo_field');
+    $index = $this->prophesize(IndexInterface::class);
+    $index->getFields()->willReturn([$field->reveal()]);
+    $datasource = $this->prophesize(DatasourceInterface::class);
+    $datasource->getIndex()->willReturn($index->reveal());
+    $this->processor->alterPropertyDefinitions($properties, $datasource->reveal());
+    $definition = [
+      'label' => new TranslatableMarkup('Hierarchy of %field', ['%field' => 'Foo field'], [], $this->getStringTranslationStub()),
+      'description' => new TranslatableMarkup('The complete hierarchy for the entity reference', [], [], $this->getStringTranslationStub()),
+      'type' => 'string',
+      'locked' => TRUE,
+    ];
+    $expected = [
+      'foo' => 'bar',
+      'search_api_hierarchy:foo_field' => new DataDefinition($definition),
+    ];
+    $this->assertArrayEquals($expected, $properties);
+  }
+
+}
