Рейтинг:2

Как я могу индексировать произвольно связанные данные объекта в индексе API поиска так же, как если бы это была ссылка на объект?

флаг in

У меня есть сайт с тремя типами контента:

  • Сборник (ссылается на абзац со ссылкой на статьи)
  • Статья (ссылки на авторов)
  • Автор

У меня есть страницы со списком для каждого из этих типов контента. Отображения имеют аспекты, основанные на вышеуказанных отношениях.

  • На странице списка коллекций есть фильтр по автору. Эта связь не является прямой связью, а представляет собой цепочку ссылок: Сборник > Абзац > Статья > Автор.
  • На странице со списком статей есть фильтр по коллекции. Эта связь также не является прямой ссылкой, а представляет собой цепочку обратных ссылок: Статья < Абзац < Сборник.
  • На странице списка авторов также есть фильтр по коллекции, которая также представляет собой цепочку обратных ссылок: Автор < Статья < Абзац < Коллекция.

Насколько я знаю, поскольку эти фасеты не полагаются на прямые отношения, я не могу индексировать данные/создавать фасеты с тем, что предоставляет search_api из коробки.

Я хотел бы проиндексировать эти связанные данные сущности с сущностью. Например, проиндексируйте статьи коллекции, как если бы это была прямая ссылка на объект. Я хочу индексировать коллекции статей и коллекции авторов одинаково. Я хотел бы сделать это таким образом, поскольку он должен естественно хорошо сочетаться с гранями и другими компонентами. Кроме того, это переведет большую часть обработки в фоновый режим во время индексации контента, а не во время построения отображения или выполнения запроса.

Как этого добиться?

Рейтинг:3
флаг in

Это возможно с помощью пользовательских процессоров search_api.

Во-первых, я создал абстрактный класс, чтобы использовать его в качестве основы для общих функций. т.е. метод для индексации произвольных данных объекта с частью контента.

пространство имен Drupal\my_module\Plugin\search_api\processor;

используйте Drupal\Core\Entity\ContentEntityInterface;
используйте Drupal\search_api\Datasource\DatasourceInterface;
используйте Drupal\search_api\Item\ItemInterface;
используйте Drupal\search_api\Processor\EntityProcessorProperty;
используйте Drupal\search_api\Processor\ProcessorPluginBase;
используйте Drupal\search_api\Utility\Utility;

/**
 * Базовый класс плагина для индексации произвольно связанных данных объекта.
 *
 * Это может быть полезно для индексации свойств сущностей, ссылающихся на сущность или
 * объекты, связанные каким-либо другим произвольным образом.
 *
 * @package Drupal\my_module\Plugin\search_api\processor
 */
абстрактный класс RelatedEntityBase расширяет ProcessorPluginBase {

  /**
   * {@inheritdoc}
   */
  общедоступная функция getPropertyDefinitions (DatasourceInterface $ datasource = NULL) {
    $plugin_definition = $this->getPluginDefinition();
    $Свойства = [];

    if (!$datasource || $datasource->getEntityTypeId() !== $this->getIndexedEntityTypeId()) {
      вернуть $свойства;
    }

    $определение = [
      'метка' => $plugin_definition['метка'],
      'описание' => $plugin_definition['описание'],
      'тип' => 'сущность:' . $this->getRelatedEntityTypeId(),
      'processor_id' => $this->getPluginId(),
      'is_list' => ИСТИНА,
    ];
    $ свойство = новое свойство EntityProcessorProperty ($ определение);
    $property->setEntityTypeId($this->getRelatedEntityTypeId());
    $properties[$this->getPluginId()] = $property;

    вернуть $свойства;
  }

  /**
   * {@inheritdoc}
   */
  общедоступная функция addFieldValues ​​(ItemInterface $ item) {
    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
    $entity = $item->getOriginalObject()->getValue();

    $to_extract = [];
    foreach ($item->getFields() как $field) {
      $datasource = $field->getDatasource();
      $property_path = $field->getPropertyPath();
      [$direct, $nested] = Utility::splitPropertyPath($property_path, FALSE);
      if ($datasource && $datasource->getEntityTypeId() === $entity->getEntityTypeId() && $direct === $this->getPluginId()) {
        $to_extract[$nested][] = $поле;
      }
    }

    foreach ($this->getRelatedEntities($entity) как $отношение) {
      $this->getFieldsHelper()
        ->extractFields($relation->getTypedData(), $to_extract, $item->getLanguage());
    }
  }

  /**
   * Получить массив связанных сущностей.
   *
   * Это должно вернуть массив полностью загруженных сущностей, относящихся к
   * Индексируемая сущность $.
   *
   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
   * Индексируемая сущность.
   *
   * @возвратный массив
   * Массив сущностей, связанных с $entity.
   */
  абстрактная защищенная функция getRelatedEntities(ContentEntityInterface $entity): array;

  /**
   * Получить идентификатор типа объекта индексируемого объекта.
   *
   * Это тип объекта $entity, переданный в
   * $this->getRelatedEntities().
   *
   * @возвратная строка
   * Строка идентификатора типа сущности, например. «узел», «носитель» или «taxonomy_term».
   */
  абстрактная защищенная функция getIndexedEntityTypeId(): string;

  /**
   * Получить идентификатор типа объекта связанных объектов.
   *
   * Это тип сущности элементов, возвращенных из
   * $this->getRelatedEntities().
   *
   * @возвратная строка
   * Строка идентификатора типа сущности, например. «узел», «носитель» или «taxonomy_term».
   */
  абстрактная защищенная функция getRelatedEntityTypeId(): string;

}

Затем я создал классы плагинов, которые расширяли мой абстрактный класс для каждого случая (авторы коллекции, коллекции статей, коллекции авторов). Например, чтобы проиндексировать данные из коллекций статей как часть проиндексированных данных статьи:

пространство имен Drupal\my_module\Plugin\search_api\processor;

используйте Drupal\Core\Entity\ContentEntityInterface;
используйте Drupal\my_module\Plugin\search_api\processor\RelatedEntityBase;

/**
 * Свойства индекса из коллекций, ссылающихся на статью.
 *
 * @SearchApiProcessor(
 * id = "мой_модуль_статьи_коллекции",
 * label = @Translation("Подборки статей"),
 * description = @Translation("Индексировать свойства из коллекций, ссылающихся на эту статью."),
 * этапы = {
 * "добавить_свойства" = 0,
 * },
 * )
 */
класс ArticleCollections расширяет RelatedEntityBase {

  /**
   * {@inheritdoc}
   */
  защищенная функция getRelatedEntities (ContentEntityInterface $entity): массив {
    вернуть my_function_to_get_article_collections($entity)
  }

  /**
   * {@inheritdoc}
   */
  защищенная функция getIndexedEntityTypeId(): строка {
    вернуть «узел»;
  }

  /**
   * {@inheritdoc}
   */
  защищенная функция getRelatedEntityTypeId(): строка {
    вернуть «узел»;
  }

}

Это позволило мне индексировать данные из коллекции как часть данных статьи, например идентификаторы коллекции статей (т. е. идентификаторы коллекций, ссылающихся на статью). Я могу проиндексировать любое поле из коллекции — выбрав нужное поле в пользовательском интерфейсе — так же, как если бы в статье было поле ссылки на объект, ссылающееся на коллекцию. (Примечание: прежде чем вы сможете индексировать какие-либо поля с помощью пользовательского процессора, вы должны сначала включить его на вкладке «Процессор» для вашего индекса.)

Все это отлично работало, однако мои проиндексированные данные не синхронизировались с реальностью. Например, если я добавлю новую статью в коллекцию, проиндексированные данные для этой новой статьи не будут обновлены информацией для новой коллекции. т.е.статья не переиндексировалась, если коллекция, ссылающаяся на нее, была обновлена. Я решил это с помощью крючок_ENTITY_TYPE_update() реализация, которая помечает зависимые статьи для переиндексации при сохранении коллекции.

используйте Drupal\node\NodeInterface;

/*
 * Реализует hook_ENTITY_TYPE_update().
 */
функция my_module_node_update(NodeInterface $node) {
  если ($node->bundle() == 'коллекция') {
    $ статьи = [];

    // Соберите все статьи, на которые ссылается эта коллекция.
    $articles = my_function_to_get_collection_articles($node);
    // Также соберите все статьи, на которые ссылались до этого сохранения, но
    // больше не упоминается.
    $original_node = isset($node->original)? $узел->оригинал: NULL;
    если ($original_node instanceof NodeInterface) {
      $articles += my_function_to_get_collection_articles($original_node);
    }

    // Отмечаем статьи для переиндексации.
    foreach ($ статьи как $ статья) {
      /** @var \Drupal\search_api\Plugin\search_api\datasource\ContentEntityTrackingManager $tracking_manager */
      $search_api_tracking_manager = \Drupal::service('search_api.entity_datasource.tracking_manager');

      $indexes = $search_api_tracking_manager->getIndexesForEntity($article);
      если (! пусто ($ индексы)) {
        $item_ids = [];
        foreach ($article->getTranslationLanguages() as $langcode => $language) {
          $item_ids[] = $статья->id() . ':' . $лангкод;
        }
        foreach ($ индексирует как $ index) {
          $index->trackItemsUpdated('entity:node', $item_ids);
        }
      }
    }
  }
}

После всего этого я могу безопасно индексировать данные из произвольно связанных сущностей.

Ответить или комментировать

Большинство людей не понимают, что склонность к познанию нового открывает путь к обучению и улучшает межличностные связи. В исследованиях Элисон, например, хотя люди могли точно вспомнить, сколько вопросов было задано в их разговорах, они не чувствовали интуитивно связи между вопросами и симпатиями. В четырех исследованиях, в которых участники сами участвовали в разговорах или читали стенограммы чужих разговоров, люди, как правило, не осознавали, что задаваемый вопрос повлияет — или повлиял — на уровень дружбы между собеседниками.