You are here

class Basic in Search API 8

Provides a tracker implementation which uses a FIFO-like processing order.

Plugin annotation


@SearchApiTracker(
  id = "default",
  label = @Translation("Default"),
  description = @Translation("Default index tracker which uses a simple database table for tracking items.")
)

Hierarchy

Expanded class hierarchy of Basic

2 files declare their use of Basic
IntegrationTest.php in tests/src/Functional/IntegrationTest.php
NoUi.php in tests/search_api_test_no_ui/src/Plugin/search_api/tracker/NoUi.php

File

src/Plugin/search_api/tracker/Basic.php, line 24

Namespace

Drupal\search_api\Plugin\search_api\tracker
View source
class Basic extends TrackerPluginBase implements PluginFormInterface {
  use LoggerTrait;
  use PluginFormTrait;

  /**
   * Status value that represents items which are indexed in their latest form.
   */
  const STATUS_INDEXED = 0;

  /**
   * Status value that represents items which still need to be indexed.
   */
  const STATUS_NOT_INDEXED = 1;

  /**
   * The database connection used by this plugin.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $connection;

  /**
   * The time service.
   *
   * @var \Drupal\Component\Datetime\TimeInterface|null
   */
  protected $timeService;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {

    /** @var static $tracker */
    $tracker = parent::create($container, $configuration, $plugin_id, $plugin_definition);
    $tracker
      ->setDatabaseConnection($container
      ->get('database'));
    $tracker
      ->setTimeService($container
      ->get('datetime.time'));
    return $tracker;
  }

  /**
   * Retrieves the database connection.
   *
   * @return \Drupal\Core\Database\Connection
   *   The database connection used by this plugin.
   */
  public function getDatabaseConnection() {
    return $this->connection ?: \Drupal::database();
  }

  /**
   * Sets the database connection.
   *
   * @param \Drupal\Core\Database\Connection $connection
   *   The database connection to use.
   *
   * @return $this
   */
  public function setDatabaseConnection(Connection $connection) {
    $this->connection = $connection;
    return $this;
  }

  /**
   * Retrieves the time service.
   *
   * @return \Drupal\Component\Datetime\TimeInterface
   *   The time service.
   */
  public function getTimeService() {
    return $this->timeService ?: \Drupal::time();
  }

  /**
   * Sets the time service.
   *
   * @param \Drupal\Component\Datetime\TimeInterface $time_service
   *   The new time service.
   *
   * @return $this
   */
  public function setTimeService(TimeInterface $time_service) {
    $this->timeService = $time_service;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    return [
      'indexing_order' => 'fifo',
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    $form['indexing_order'] = [
      '#type' => 'radios',
      '#title' => $this
        ->t('Indexing order'),
      '#description' => $this
        ->t('The order in which items will be indexed.'),
      '#options' => [
        'fifo' => $this
          ->t('Index items in the same order in which they were saved'),
        'lifo' => $this
          ->t('Index the most recent items first'),
      ],
      '#default_value' => $this->configuration['indexing_order'],
    ];
    return $form;
  }

  /**
   * Creates a SELECT statement for this tracker.
   *
   * @return \Drupal\Core\Database\Query\SelectInterface
   *   A SELECT statement.
   */
  protected function createSelectStatement() {
    $select = $this
      ->getDatabaseConnection()
      ->select('search_api_item', 'sai');
    $select
      ->condition('index_id', $this
      ->getIndex()
      ->id());
    return $select;
  }

  /**
   * Creates an INSERT statement for this tracker.
   *
   * @return \Drupal\Core\Database\Query\Insert
   *   An INSERT statement.
   */
  protected function createInsertStatement() {
    return $this
      ->getDatabaseConnection()
      ->insert('search_api_item')
      ->fields([
      'index_id',
      'datasource',
      'item_id',
      'changed',
      'status',
    ]);
  }

  /**
   * Creates an UPDATE statement for this tracker.
   *
   * @return \Drupal\Core\Database\Query\Update
   *   An UPDATE statement.
   */
  protected function createUpdateStatement() {
    return $this
      ->getDatabaseConnection()
      ->update('search_api_item')
      ->condition('index_id', $this
      ->getIndex()
      ->id());
  }

  /**
   * Creates a DELETE statement for this tracker.
   *
   * @return \Drupal\Core\Database\Query\Delete
   *   A DELETE Statement.
   */
  protected function createDeleteStatement() {
    return $this
      ->getDatabaseConnection()
      ->delete('search_api_item')
      ->condition('index_id', $this
      ->getIndex()
      ->id());
  }

  /**
   * Creates a SELECT statement which filters on the not indexed items.
   *
   * @param string|null $datasource_id
   *   (optional) If specified, only items of the datasource with that ID are
   *   retrieved.
   *
   * @return \Drupal\Core\Database\Query\SelectInterface
   *   A SELECT statement.
   */
  protected function createRemainingItemsStatement($datasource_id = NULL) {
    $select = $this
      ->createSelectStatement();
    $select
      ->fields('sai', [
      'item_id',
    ]);
    if ($datasource_id) {
      $select
        ->condition('datasource', $datasource_id);
    }
    $select
      ->condition('sai.status', $this::STATUS_NOT_INDEXED, '=');

    // Use the same direction for both sorts to avoid performance problems.
    $order = $this->configuration['indexing_order'] === 'lifo' ? 'DESC' : 'ASC';
    $select
      ->orderBy('sai.changed', $order);

    // Add a secondary sort on item ID to make the order completely predictable.
    $select
      ->orderBy('sai.item_id', $order);
    return $select;
  }

  /**
   * {@inheritdoc}
   */
  public function trackItemsInserted(array $ids) {
    try {
      $index_id = $this
        ->getIndex()
        ->id();

      // Process the IDs in chunks so we don't create an overly large INSERT
      // statement.
      foreach (array_chunk($ids, 1000) as $ids_chunk) {

        // We have to make sure we don't try to insert duplicate items.
        $select = $this
          ->createSelectStatement()
          ->fields('sai', [
          'item_id',
        ]);
        $select
          ->condition('item_id', $ids_chunk, 'IN');
        $existing = $select
          ->execute()
          ->fetchCol();
        $existing = array_flip($existing);
        $insert = $this
          ->createInsertStatement();
        foreach ($ids_chunk as $item_id) {
          if (isset($existing[$item_id])) {
            continue;
          }
          list($datasource_id) = Utility::splitCombinedId($item_id);
          $insert
            ->values([
            'index_id' => $index_id,
            'datasource' => $datasource_id,
            'item_id' => $item_id,
            'changed' => $this
              ->getTimeService()
              ->getRequestTime(),
            'status' => $this::STATUS_NOT_INDEXED,
          ]);
        }
        if ($insert
          ->count()) {
          $insert
            ->execute();
        }
      }
    } catch (\Exception $e) {
      $this
        ->logException($e);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function trackItemsUpdated(array $ids = NULL) {
    try {

      // Process the IDs in chunks so we don't create an overly large UPDATE
      // statement.
      $ids_chunks = $ids !== NULL ? array_chunk($ids, 1000) : [
        NULL,
      ];
      foreach ($ids_chunks as $ids_chunk) {
        $update = $this
          ->createUpdateStatement();
        $update
          ->fields([
          'changed' => $this
            ->getTimeService()
            ->getRequestTime(),
          'status' => $this::STATUS_NOT_INDEXED,
        ]);
        if ($ids_chunk) {
          $update
            ->condition('item_id', $ids_chunk, 'IN');
        }

        // Update the status of unindexed items only if the item order is LIFO.
        // (Otherwise, an item that's regularly being updated might never get
        // indexed.)
        if ($this->configuration['indexing_order'] === 'fifo') {
          $update
            ->condition('status', self::STATUS_INDEXED);
        }
        $update
          ->execute();
      }
    } catch (\Exception $e) {
      $this
        ->logException($e);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function trackAllItemsUpdated($datasource_id = NULL) {
    try {
      $update = $this
        ->createUpdateStatement();
      $update
        ->fields([
        'changed' => $this
          ->getTimeService()
          ->getRequestTime(),
        'status' => $this::STATUS_NOT_INDEXED,
      ]);
      if ($datasource_id) {
        $update
          ->condition('datasource', $datasource_id);
      }
      $update
        ->execute();
    } catch (\Exception $e) {
      $this
        ->logException($e);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function trackItemsIndexed(array $ids) {
    try {

      // Process the IDs in chunks so we don't create an overly large UPDATE
      // statement.
      $ids_chunks = array_chunk($ids, 1000);
      foreach ($ids_chunks as $ids_chunk) {
        $update = $this
          ->createUpdateStatement();
        $update
          ->fields([
          'status' => $this::STATUS_INDEXED,
        ]);
        $update
          ->condition('item_id', $ids_chunk, 'IN');
        $update
          ->execute();
      }
    } catch (\Exception $e) {
      $this
        ->logException($e);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function trackItemsDeleted(array $ids = NULL) {
    try {

      // Process the IDs in chunks so we don't create an overly large DELETE
      // statement.
      $ids_chunks = $ids !== NULL ? array_chunk($ids, 1000) : [
        NULL,
      ];
      foreach ($ids_chunks as $ids_chunk) {
        $delete = $this
          ->createDeleteStatement();
        if ($ids_chunk) {
          $delete
            ->condition('item_id', $ids_chunk, 'IN');
        }
        $delete
          ->execute();
      }
    } catch (\Exception $e) {
      $this
        ->logException($e);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function trackAllItemsDeleted($datasource_id = NULL) {
    try {
      $delete = $this
        ->createDeleteStatement();
      if ($datasource_id) {
        $delete
          ->condition('datasource', $datasource_id);
      }
      $delete
        ->execute();
    } catch (\Exception $e) {
      $this
        ->logException($e);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getRemainingItems($limit = -1, $datasource_id = NULL) {
    try {
      $select = $this
        ->createRemainingItemsStatement($datasource_id);
      if ($limit >= 0) {
        $select
          ->range(0, $limit);
      }
      return $select
        ->execute()
        ->fetchCol();
    } catch (\Exception $e) {
      $this
        ->logException($e);
      return [];
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getTotalItemsCount($datasource_id = NULL) {
    try {
      $select = $this
        ->createSelectStatement();
      if ($datasource_id) {
        $select
          ->condition('datasource', $datasource_id);
      }
      return (int) $select
        ->countQuery()
        ->execute()
        ->fetchField();
    } catch (\Exception $e) {
      $this
        ->logException($e);
      return 0;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getIndexedItemsCount($datasource_id = NULL) {
    try {
      $select = $this
        ->createSelectStatement();
      $select
        ->condition('sai.status', $this::STATUS_INDEXED);
      if ($datasource_id) {
        $select
          ->condition('datasource', $datasource_id);
      }
      return (int) $select
        ->countQuery()
        ->execute()
        ->fetchField();
    } catch (\Exception $e) {
      $this
        ->logException($e);
      return 0;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getRemainingItemsCount($datasource_id = NULL) {
    try {
      $select = $this
        ->createRemainingItemsStatement();
      if ($datasource_id) {
        $select
          ->condition('datasource', $datasource_id);
      }
      return (int) $select
        ->countQuery()
        ->execute()
        ->fetchField();
    } catch (\Exception $e) {
      $this
        ->logException($e);
      return 0;
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
Basic::$connection protected property The database connection used by this plugin.
Basic::$timeService protected property The time service.
Basic::buildConfigurationForm public function Form constructor. Overrides PluginFormInterface::buildConfigurationForm
Basic::create public static function Creates an instance of the plugin. Overrides ConfigurablePluginBase::create
Basic::createDeleteStatement protected function Creates a DELETE statement for this tracker.
Basic::createInsertStatement protected function Creates an INSERT statement for this tracker.
Basic::createRemainingItemsStatement protected function Creates a SELECT statement which filters on the not indexed items.
Basic::createSelectStatement protected function Creates a SELECT statement for this tracker.
Basic::createUpdateStatement protected function Creates an UPDATE statement for this tracker.
Basic::defaultConfiguration public function Gets default configuration for this plugin. Overrides ConfigurablePluginBase::defaultConfiguration
Basic::getDatabaseConnection public function Retrieves the database connection.
Basic::getIndexedItemsCount public function Retrieves the number of indexed items for this index. Overrides TrackerInterface::getIndexedItemsCount
Basic::getRemainingItems public function Retrieves a list of item IDs that need to be indexed. Overrides TrackerInterface::getRemainingItems
Basic::getRemainingItemsCount public function Retrieves the total number of pending items for this index. Overrides TrackerInterface::getRemainingItemsCount
Basic::getTimeService public function Retrieves the time service.
Basic::getTotalItemsCount public function Retrieves the total number of items that are being tracked for this index. Overrides TrackerInterface::getTotalItemsCount
Basic::setDatabaseConnection public function Sets the database connection.
Basic::setTimeService public function Sets the time service.
Basic::STATUS_INDEXED constant Status value that represents items which are indexed in their latest form.
Basic::STATUS_NOT_INDEXED constant Status value that represents items which still need to be indexed.
Basic::trackAllItemsDeleted public function Removes all items from the tracker, or only those of a specific datasource. Overrides TrackerInterface::trackAllItemsDeleted
Basic::trackAllItemsUpdated public function Marks all items as updated, or only those of a specific datasource. Overrides TrackerInterface::trackAllItemsUpdated
Basic::trackItemsDeleted public function Removes items from the tracking system for this index. Overrides TrackerInterface::trackItemsDeleted
Basic::trackItemsIndexed public function Marks items as indexed for this index. Overrides TrackerInterface::trackItemsIndexed
Basic::trackItemsInserted public function Inserts new items into the tracking system for this index. Overrides TrackerInterface::trackItemsInserted
Basic::trackItemsUpdated public function Marks the given items as updated for this index. Overrides TrackerInterface::trackItemsUpdated
ConfigurablePluginBase::calculateDependencies public function Calculates dependencies for the configured plugin. Overrides DependentPluginInterface::calculateDependencies 6
ConfigurablePluginBase::calculatePluginDependencies Deprecated protected function Calculates and adds dependencies of a specific plugin instance.
ConfigurablePluginBase::getConfiguration public function Gets this plugin's configuration. Overrides ConfigurableInterface::getConfiguration
ConfigurablePluginBase::getDescription public function Returns the plugin's description. Overrides ConfigurablePluginInterface::getDescription
ConfigurablePluginBase::getPluginDependencies Deprecated protected function Calculates and returns dependencies of a specific plugin instance.
ConfigurablePluginBase::label public function Returns the label for use on the administration pages. Overrides ConfigurablePluginInterface::label
ConfigurablePluginBase::moduleHandler Deprecated protected function Wraps the module handler.
ConfigurablePluginBase::onDependencyRemoval public function Informs the plugin that some of its dependencies are being removed. Overrides ConfigurablePluginInterface::onDependencyRemoval 5
ConfigurablePluginBase::setConfiguration public function Sets the configuration for this plugin instance. Overrides ConfigurableInterface::setConfiguration 3
ConfigurablePluginBase::themeHandler Deprecated protected function Wraps the theme handler.
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
DependencyTrait::$dependencies protected property The object's dependencies.
DependencyTrait::addDependencies protected function Adds multiple dependencies.
DependencyTrait::addDependency protected function Adds a dependency.
HideablePluginBase::isHidden public function Determines whether this plugin should be hidden in the UI. Overrides HideablePluginInterface::isHidden 1
IndexPluginBase::$index protected property The index this processor is configured for.
IndexPluginBase::getIndex public function Retrieves the index this plugin is configured for. Overrides IndexPluginInterface::getIndex
IndexPluginBase::setIndex public function Sets the index this plugin is configured for. Overrides IndexPluginInterface::setIndex
IndexPluginBase::__construct public function Constructs a \Drupal\Component\Plugin\PluginBase object. Overrides ConfigurablePluginBase::__construct 2
LoggerTrait::$logger protected property The logging channel to use.
LoggerTrait::getLogger public function Retrieves the logger.
LoggerTrait::logException protected function Logs an exception.
LoggerTrait::setLogger public function Sets the logger.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
PluginDependencyTrait::calculatePluginDependencies protected function Calculates and adds dependencies of a specific plugin instance. Aliased as: traitCalculatePluginDependencies 1
PluginDependencyTrait::getPluginDependencies protected function Calculates and returns dependencies of a specific plugin instance. Aliased as: traitGetPluginDependencies
PluginDependencyTrait::moduleHandler protected function Wraps the module handler. Aliased as: traitModuleHandler 1
PluginDependencyTrait::themeHandler protected function Wraps the theme handler. Aliased as: traitThemeHandler 1
PluginFormTrait::submitConfigurationForm public function Form submission handler. 7
PluginFormTrait::validateConfigurationForm public function Form validation handler. 2
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.