Puncte:2

Cum pot indexa datele de entitate legate în mod arbitrar într-un index API de căutare la fel ca și cum ar fi o referință de entitate?

drapel in

Am un site cu trei tipuri de conținut:

  • Colecție (se referă la un paragraf care face referire la articole)
  • Articol (referințe Autori)
  • Autor

Am afișări de pagini de listă pentru fiecare dintre aceste tipuri de conținut. Ecranele au fațete bazate pe relațiile de mai sus.

  • Pagina de listare a colecțiilor are un filtru după autor. Această relație nu este o relație directă, mai degrabă un lanț de referințe: Colecție > Paragraf > Articol > Autor.
  • Pagina de listare a articolelor are un filtru în funcție de colecție. Această relație nu este, de asemenea, o referință directă, mai degrabă un lanț de referințe inverse: Articolul < Paragraf < Colecție.
  • Pagina de listare a autorului are, de asemenea, un filtru după colecție, care este și un lanț de referințe inverse: Autor < Articolul < Paragraf < Colecție.

Din câte știu, deoarece aceste fațete nu se bazează pe relații directe, nu pot indexa datele / crea fațete cu ceea ce search_api oferă din cutie.

Aș dori să indexez aceste date legate de entitate cu entitatea. De exemplu, indexați articolele unei colecții ca și cum ar fi o referință directă la entitate. Doresc să indexez colecțiile unui articol și colecțiile unui autor în același mod. Aș dori să o fac în acest fel, deoarece ar trebui să se joace frumos cu fațete și alte componente. În plus, aceasta va pune cea mai mare parte a procesării în fundal în timp ce conținutul este indexat, mai degrabă decât atunci când se construiește un afișaj sau se execută o interogare.

Cum se poate realiza acest lucru?

Puncte:3
drapel in

Acest lucru este posibil cu procesoare personalizate search_api.

Mai întâi, am creat o clasă abstractă pe care să o folosesc ca bază pentru funcționalitatea partajată. i.e. o metodă de indexare a datelor unei entități arbitrare cu o bucată de conținut.

spațiu de nume Drupal\my_module\Plugin\search_api\processor;

utilizați Drupal\Core\Entity\ContentEntityInterface;
utilizați Drupal\search_api\Datasource\DatasourceInterface;
utilizați Drupal\search_api\Item\ItemInterface;
utilizați Drupal\search_api\Processor\EntityProcessorProperty;
utilizați Drupal\search_api\Processor\ProcessorPluginBase;
utilizați Drupal\search_api\Utility\Utility;

/**
 * Clasa de plugin de bază pentru indexarea datelor despre entități legate în mod arbitrar.
 *
 * Acest lucru poate fi util pentru a indexa proprietățile entităților care fac referire la o entitate sau
 * entități legate într-un alt mod arbitrar.
 *
 * @pachet Drupal\my_module\Plugin\search_api\processor
 */
clasa abstractă RelatedEntityBase extinde ProcessorPluginBase {

  /**
   * {@inheritdoc}
   */
  funcția publică getPropertyDefinitions(DatasourceInterface $datasource = NULL) {
    $plugin_definition = $this->getPluginDefinition();
    $proprietati = [];

    if (!$datasource || $datasource->getEntityTypeId() !== $this->getIndexedEntityTypeId()) {
      returnează $proprietăți;
    }

    $definiție = [
      'label' => $plugin_definition['label'],
      'description' => $plugin_definition['descriere'],
      'type' => 'entitate:' . $this->getRelatedEntityTypeId(),
      'processor_id' => $this->getPluginId(),
      'is_list' => TRUE,
    ];
    $property = new EntityProcessorProperty($definition);
    $property->setEntityTypeId($this->getRelatedEntityTypeId());
    $properties[$this->getPluginId()] = $proprietate;

    returnează $proprietăți;
  }

  /**
   * {@inheritdoc}
   */
  funcția publică addFieldValues(ItemInterface $item) {
    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
    $entity = $item->getOriginalObject()->getValue();

    $pentru_extrage = [];
    foreach ($item->getFields() ca $câmp) {
      $datasource = $field->getDatasource();
      $cale_proprietate = $câmp->getPropertyPath();
      [$direct, $imbricat] = Utility::splitPropertyPath($property_path, FALSE);
      if ($datasource && $datasource->getEntityTypeId() === $entity->getEntityTypeId() && $direct === $this->getPluginId()) {
        $to_extract[$imbricat][] = $câmp;
      }
    }

    foreach ($this->getRelatedEntities($entity) ca $relation) {
      $this->getFieldsHelper()
        ->extractFields($relation->getTypedData(), $to_extract, $item->getLanguage());
    }
  }

  /**
   * Obțineți o serie de entități conexe.
   *
   * Aceasta ar trebui să returneze o serie de entități complet încărcate care se referă la
   * $entity este indexat.
   *
   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
   * Entitatea care este indexată.
   *
   * @return matrice
   * O serie de entități legate de $entity.
   */
  funcţie abstractă protejată getRelatedEntities(ContentEntityInterface $entity): matrice;

  /**
   * Obțineți ID-ul tipului de entitate al entității care este indexată.
   *
   * Acesta este tipul de entitate al entității $ căreia i-a fost transmis
   * $this->getRelatedEntities().
   *
   * @return șir
   * Un șir de id de tip de entitate, de ex. „nod”, „media” sau „termen_taxonomie”.
   */
  funcție abstractă protejată getIndexedEntityTypeId(): șir;

  /**
   * Obțineți ID-ul tipului de entitate al entităților aferente.
   *
   * Acesta este tipul de entitate de la articolele returnate
   * $this->getRelatedEntities().
   *
   * @return șir
   * Un șir de id de tip de entitate, de ex. „nod”, „media” sau „termen_taxonomie”.
   */
  funcție abstractă protejată getRelatedEntityTypeId(): șir;

}

Apoi, am creat clase de plugin care mi-au extins clasa abstractă pentru fiecare caz (Autorii colecției, Colecțiile articolelor, Colecțiile autorului). De exemplu, pentru a indexa datele din colecțiile unui articol ca parte a datelor indexate ale articolului:

spațiu de nume Drupal\my_module\Plugin\search_api\processor;

utilizați Drupal\Core\Entity\ContentEntityInterface;
utilizați Drupal\my_module\Plugin\search_api\processor\RelatedEntityBase;

/**
 * Indexați proprietățile din colecții care fac referire la un articol.
 *
 * @SearchApiProcessor(
 * id = „colecțiile_mii_module_articole”,
 * label = @Translation ("Colecțiile articolului"),
 * description = @Translation(„Proprietățile indexate din colecții care fac referire la acest articol.”),
 * etape = {
 * „add_properties” = 0,
 *},
 * )
 */
clasa ArticleCollections extinde RelatedEntityBase {

  /**
   * {@inheritdoc}
   */
  funcția protejată getRelatedEntities(ContentEntityInterface $entity): array {
    returnează my_function_to_get_article_collections($entity)
  }

  /**
   * {@inheritdoc}
   */
  funcție protejată getIndexedEntityTypeId(): șir {
    returnează „nod”;
  }

  /**
   * {@inheritdoc}
   */
  funcție protejată getRelatedEntityTypeId(): șir {
    returnează „nod”;
  }

}

Acest lucru mi-a permis să indexez datele dintr-o colecție ca parte a datelor unui articol, de exemplu ID-urile colecției articolului (adică ID-urile colecțiilor care fac referire la articol). Pot indexa orice câmp din Colecție - selectând câmpul pe care îl doresc în UI - la fel ca și când articolul ar avea un câmp de referință de entitate care face referire la Colecție. (Notă: înainte de a putea indexa orice câmp cu procesorul personalizat, trebuie mai întâi să îl activați în fila Procesor pentru indexul dvs.)

Toate acestea au funcționat excelent, cu toate acestea, datele mele indexate nu au rămas sincronizate cu realitatea. De exemplu, dacă aș adăuga un articol nou la o colecție, datele indexate pentru acel articol nou nu vor fi actualizate cu informații pentru noua colecție. i.e.articolul nu a fost reindexat dacă o colecție care face referire la el a fost actualizată. Am rezolvat asta cu a hook_ENTITY_TYPE_update() implementare care marchează articolele dependente pentru a fi reindexate atunci când o colecție este salvată.

utilizați Drupal\node\NodeInterface;

/*
 * Implementează hook_ENTITY_TYPE_update().
 */
funcția my_module_node_update(NodeInterface $nod) {
  if ($nod->bundle() == 'colecție') {
    $articole = [];

    // Adună toate articolele la care se referă această colecție.
    $articole = my_function_to_get_collection_articles($nod);
    // Adunați, de asemenea, orice articole la care au fost menționate înainte de această salvare, dar sunt
    // nu se mai face referire.
    $original_node = isset($node->original) ? $nod->original : NULL;
    if ($original_node instanceof NodeInterface) {
      $articole += my_function_to_get_collection_articles($original_node);
    }

    // Marcați articolele care urmează să fie reindexate.
    foreach ($articole ca $articol) {
      /** @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($articol);
      dacă (!gol ($indexuri)) {
        $item_ids = [];
        foreach ($articol->getTranslationLanguages() ca $langcode => $language) {
          $articol_ids[] = $articol->id() . ':' . $langcode;
        }
        foreach ($indexuri ca $index) {
          $index->trackItemsUpdated('entity:node', $item_ids);
        }
      }
    }
  }
}

După toate acestea, pot indexa în siguranță datele de la entități legate în mod arbitrar.

Postează un răspuns

Majoritatea oamenilor nu înțeleg că a pune multe întrebări deblochează învățarea și îmbunătățește legătura interpersonală. În studiile lui Alison, de exemplu, deși oamenii își puteau aminti cu exactitate câte întrebări au fost puse în conversațiile lor, ei nu au intuit legătura dintre întrebări și apreciere. În patru studii, în care participanții au fost implicați în conversații ei înșiși sau au citit transcrieri ale conversațiilor altora, oamenii au avut tendința să nu realizeze că întrebarea ar influența – sau ar fi influențat – nivelul de prietenie dintre conversatori.