Это возможно с помощью пользовательских процессоров 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);
}
}
}
}
}
После всего этого я могу безопасно индексировать данные из произвольно связанных сущностей.