diff --git a/config/schema/search_api.processor.schema.yml b/config/schema/search_api.processor.schema.yml
index efb2da1..96d2a51 100644
--- a/config/schema/search_api.processor.schema.yml
+++ b/config/schema/search_api.processor.schema.yml
@@ -28,6 +28,10 @@ plugin.plugin_configuration.search_api_processor.*:
 
 # Definitions for individual processors
 
+plugin.plugin_configuration.search_api_processor.add_hierarchy:
+  type: search_api.fields_processor_configuration
+  label: 'Hierarchy processor configuration'
+
 plugin.plugin_configuration.search_api_processor.highlight:
   type: search_api.default_processor_configuration
   label: 'Highlight processor configuration'
diff --git a/src/Plugin/search_api/processor/AddHierarchy.php b/src/Plugin/search_api/processor/AddHierarchy.php
new file mode 100644
index 0000000..c0de577
--- /dev/null
+++ b/src/Plugin/search_api/processor/AddHierarchy.php
@@ -0,0 +1,210 @@
+<?php
+
+namespace Drupal\search_api\Plugin\search_api\processor;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\Plugin\DataType\EntityAdapter;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\search_api\Item\FieldInterface;
+use Drupal\search_api\Item\ItemInterface;
+use Drupal\search_api\Processor\ProcessorPluginBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @SearchApiProcessor(
+ *   id = "hierarchy",
+ *   label = @Translation("Index hierarchy"),
+ *   description = @Translation("Allows the indexing of values along with all their ancestors for hierarchical fields (like taxonomy term references)"),
+ *   stages = {
+ *     "preprocess_index" = -45
+ *   }
+ * )
+ */
+class AddHierarchy extends ProcessorPluginBase {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    /** @var static $processor */
+    $processor = parent::create($container, $configuration, $plugin_id, $plugin_definition);
+
+    $processor->setEntityTypeManager($container->get('entity_type.manager'));
+
+    return $processor;
+  }
+
+  /**
+   * Retrieves the entity type manager service.
+   *
+   * @return \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager service.
+   */
+  public function getEntityTypeManager() {
+    return $this->entityTypeManager ?: \Drupal::entityTypeManager();
+  }
+
+  /**
+   * Sets the entity type manager service.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager service.
+   *
+   * @return $this
+   */
+  public function setEntityTypeManager(EntityTypeManagerInterface $entity_type_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return array(
+      'fields' => array(),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+
+    $field_options = array();
+    foreach ($this->index->getFields() as $field_id => $field) {
+      if ($this->isEntityReferenceField($field)) {
+        $field_options[$field_id] = Html::escape($field->getPrefixedLabel());
+      }
+    }
+
+    $form['fields'] = array(
+      '#type' => 'checkboxes',
+      '#title' => $this->t('Enable this processor on the following fields'),
+      '#description' => $this->t("These are the fields on the index which might contain hierarchical data. Enable those for which you want to include all values' ancestors in the indexed values."),
+      '#options' => $field_options,
+      '#default_value' => $this->configuration['fields'],
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
+    parent::validateConfigurationForm($form, $form_state);
+
+    $fields = array_filter($form_state->getValues()['fields']);
+    if ($fields) {
+      $fields = array_keys($fields);
+    }
+    $form_state->setValue('fields', $fields);
+  }
+
+  /**
+   * {@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) && in_array($field->getFieldIdentifier(), $this->configuration['fields'])) {
+          // The field is an entity reference, so we have to traverse up the
+          // tree to find all parents.
+          $hierarchyValues = array();
+          $this->extractHierarchy($item, $field, $hierarchyValues);
+          $field->setValues(array_unique($hierarchyValues));
+        }
+      }
+    }
+  }
+
+  /**
+   * Determines 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';
+  }
+
+  /**
+   * Extract the hierarchy of a given field.
+   *
+   * This method calls itself recursively and extracts the hierarchy by
+   * appending the `$values` array.
+   *
+   * Taxonomy references are handled differently since the hierarchy is not on
+   * the given item, but rather on the referenced vocabulary.
+   *
+   * @param \Drupal\search_api\Item\ItemInterface $item
+   *   The item being indexed.
+   * @param \Drupal\search_api\Item\FieldInterface $field
+   *   The specific entity reference field that defines the hierarchy.
+   * @param array $values
+   *   The values array.
+   */
+  protected function extractHierarchy(ItemInterface $item, FieldInterface $field, array &$values) {
+    // Load the entity storage for the given field.
+    $field_storage = $field->getDataDefinition()->getFieldDefinition()->getFieldStorageDefinition();
+    $taxonomy_hierarchy = $field_storage->getSetting('target_type') == 'taxonomy_term';
+    foreach ($field->getValues() as $id) {
+      // Initial value is always included.
+      $values[] = $id;
+
+      // Special handling if this is a taxonomy field.
+      if ($taxonomy_hierarchy) {
+        // Add each parent.
+        foreach ($this->entityTypeManager->getStorage('taxonomy_term')->loadParents($id) as $parent) {
+          $values[] = $parent->id();
+        }
+
+      }
+      else {
+        $this->extractEntityReferenceHierarchy($id, $values, $field_storage);
+      }
+    }
+
+    // For non-taxonomy hierarchies, add the item itself to complete the
+    // hierarchy. Top-level items (or bottom-level items depending on the
+    // direction) have no entity reference value.
+    if (!$taxonomy_hierarchy && $item->getOriginalObject() instanceof EntityAdapter) {
+      $values[] = $item->getOriginalObject()->getValue()->id();
+    }
+  }
+
+  /**
+   * Specific hierarchy extraction for non-taxonomy references.
+   *
+   * @param mixed $id
+   *   The entity ID.
+   * @param array $values
+   *   The values array to modify with hierarchy.
+   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage
+   *   The entity storage.
+   */
+  protected function extractEntityReferenceHierarchy($id, array &$values, FieldStorageDefinitionInterface $field_storage) {
+    $child = $this->entityTypeManager->getStorage($field_storage->getTargetEntityTypeId())->load($id);
+    foreach ($child->{$field_storage->getName()} as $value) {
+      $values[] = $value->target_id;
+      // @todo Check for circular references to avoid infinite recursion.
+      $this->extractEntityReferenceHierarchy($value->target_id, $values, $field_storage);
+    }
+  }
+
+}
diff --git a/tests/src/Kernel/Processor/AddHierarchyTest.php b/tests/src/Kernel/Processor/AddHierarchyTest.php
new file mode 100644
index 0000000..4775f3c
--- /dev/null
+++ b/tests/src/Kernel/Processor/AddHierarchyTest.php
@@ -0,0 +1,268 @@
+<?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 "Hierarchy" processor.
+ *
+ * @see \Drupal\search_api\Plugin\search_api\processor\AddHierarchy
+ *
+ * @group search_api
+ *
+ * @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 = array(
+    'filter',
+    'taxonomy',
+  );
+
+  /**
+   * A hierarchy to test.
+   */
+  protected static $hierarchy = array(
+    'fruit' => array(
+      'apple',
+      'pear'
+    ),
+    'vegetable' => array(
+      'radish',
+      'turnip',
+    ),
+  );
+
+  /**
+   * The nodes created for testing.
+   *
+   * @var \Drupal\node\NodeInterface[]
+   */
+  protected $nodes = array();
+
+  /**
+   * Hierarchical taxonomy terms.
+   *
+   * This is keyed by "type.item", for example: "fruit.pear".
+   *
+   * @var \Drupal\taxonomy\TermInterface[]
+   */
+  protected $terms = array();
+
+  /**
+   * Vocabulary to test with when using taxonomy for the hierarchy.
+   *
+   * @var \Drupal\taxonomy\VocabularyInterface
+   */
+  protected $vocabulary;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp($processor = NULL) {
+    parent::setUp('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',
+      array(),
+      FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
+    );
+
+    // Add a generic entity reference field.
+    $this->createEntityReferenceField(
+      'node',
+      'page',
+      'parent_reference',
+      NULL,
+      'node',
+      'default',
+      array()
+    );
+
+    // 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();
+
+    // Index the entity reference field.
+    $reference_field = new Field($this->index, 'parent_reference');
+    $reference_field->setType('integer');
+    $reference_field->setPropertyPath('parent_reference');
+    $reference_field->setDatasourceId('entity:node');
+    $reference_field->setLabel('Parent page');
+    $this->index->addField($reference_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());
+  }
+
+  /**
+   * 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, array(
+        'name' => $type,
+      ));
+      foreach ($items as $item) {
+        $this->terms["$type.$item"] = $this->createTerm($this->vocabulary, array(
+          'name' => $item,
+          'parent' => $type_term,
+        ));
+      }
+    }
+  }
+
+  /**
+   * Tests taxonomy-based hierarchy indexing.
+   *
+   * @covers ::preprocessIndexItems
+   */
+  public function testPreprocessIndexItemsTaxonomy() {
+    // Add hierarchical terms to 3 nodes.
+    foreach (array('vegetable.turnip', 'vegetable', 'fruit.pear') as $i => $term) {
+      $this->nodes[$i] = $this->createNode(array(
+        'type' => 'page',
+        'term_field' => array(
+          'target_id' => $this->terms[$term]->id(),
+        ),
+      ));
+    }
+    $this->index->reindex();
+    $this->index->indexItems();
+
+    // By default, hierarchy is not indexed, so a search for 'vegetable' should
+    // only return node 2.
+    $query = Utility::createQuery($this->index);
+    $query->addCondition('term_field', $this->terms['vegetable']->id());
+    $result = $query->execute();
+    $expected = array('node' => array(1));
+    $this->assertResults($result, $expected);
+
+    // Enable hierarchical indexing.
+    $processor = $this->index->getProcessors()['add_hierarchy'];
+    $processor->setConfiguration(array('fields' => array('term_field')));
+    $this->index->addProcessor($processor);
+    $this->index->save();
+    $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 = array('node' => array(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 = array('node' => array(0));
+    $this->assertResults($result, $expected);
+  }
+
+  /**
+   * Tests non-taxonomy-based hierarchy.
+   *
+   * @covers ::preprocessIndexItems
+   * @covers ::extractHierarchy
+   * @covers ::extractEntityReferenceHierarchy
+   */
+  public function testPreprocessIndexItems() {
+    // Setup the nodes to follow the hierarchy.
+    foreach (static::$hierarchy as $type => $items) {
+      $this->nodes[] = $type_node = $this->createNode(array(
+        'title' => $type,
+      ));
+      foreach ($items as $item) {
+        $this->nodes[] = $this->createNode(array(
+          'title' => $item,
+          'parent_reference' => array('target_id' => $type_node->id()),
+        ));
+      }
+    }
+    // Add a third tier of hierarchy for specific types of radishes.
+    foreach (array('Cherry Belle', 'Snow Belle', 'Daikon') as $item) {
+      $this->nodes[] = $this->createNode(array(
+        'title' => $item,
+        'parent_reference' => array('target_id' => $this->nodes[5]->id()),
+      ));
+    }
+    $this->index->reindex();
+    $this->index->indexItems();
+
+    // Initially hierarchy is excluded, so "vegetable" should only return nodes
+    // 5 and 6.
+    $query = Utility::createQuery($this->index);
+    $query->addCondition('parent_reference', $this->nodes[3]->id());
+    $result = $query->execute();
+    $expected = array('node' => array(4, 5));
+    $this->assertResults($result, $expected);
+
+    // Enable hierarchical indexing.
+    $processor = $this->index->getProcessors()['add_hierarchy'];
+    $processor->setConfiguration(array('fields' => array('parent_reference')));
+    $this->index->addProcessor($processor);
+    $this->index->save();
+    $this->index->reindex();
+    $this->index->indexItems();
+
+    // A search for "vegetable" should now include the hierarchy.
+    $query = Utility::createQuery($this->index);
+    $query->addCondition('parent_reference', $this->nodes[3]->id());
+    $result = $query->execute();
+    $expected = array('node' => array(3, 4, 5, 6, 7, 8));
+    $this->assertResults($result, $expected);
+  }
+
+}
