Adding an entity reference via a processor

Last updated on
27 April 2022

While it is relatively straight-forward to add new properties for indexing via a processor, this is only completely supported when you want to add scalar properties for direct indexing – strings, numbers, etc. If, however, you want to add an entity reference for indexing, which would also allow you to add that entity's own properties to the index (like it is by default support for all normally defined entity references, like the Content's author), this is a bit harder to do. However, it still is possible, even though there might be issues for specific use cases.

Please see the attached "Soul Mate" example files, which add a node reference to indexed user entities. If you add those files to an existing module (and replace the "MY_MODULE" placeholders), the new "Soul mate" property should become available for indexing, along with all its properties.

The only thing necessary for getting this part to work is this method:

public function getPropertyDefinitions(DatasourceInterface $datasource = NULL) {
  $properties = [];

  if ($datasource && $datasource->getEntityTypeId() === 'user') {
    $definition = [
      'label' => $this->t('Soul mate'),
      'description' => $this->t("The Node with the same NID as the user's UID"),
      'type' => 'entity:node',
      'processor_id' => $this->getPluginId(),
    ];
    $properties['soul_mate'] = new EntityProcessorProperty($definition);
    $properties['soul_mate']->setEntityTypeId('node');
  }

  return $properties;
}

The important part here is that the returned property not only implements \Drupal\search_api\Processor\ProcessorPropertyInterface, but also \Drupal\Core\TypedData\ComplexDataDefinitionInterface – by inheriting from EntityDataDefinition, in this case. This ensures that the property's sub-properties can all be added to the index.

The second part, of actually getting the correct field values when indexing, is a bit trickier. Normally, addFieldValues() can just be used to add values for individual fields, not for adding values for all fields of a certain parent entity. This, therefore, has to work a bit differently:

$to_extract = [];
foreach ($item->getFields() as $field) {
  $datasource = $field->getDatasource();
  $property_path = $field->getPropertyPath();
  list($direct, $nested) = Utility::splitPropertyPath($property_path, FALSE);
  if ($datasource && $datasource->getEntityTypeId() === 'user' && $direct === 'soul_mate') {
    $to_extract[$nested][] = $field;
  }
}

// …
// Get $node for the "soul_mate" field.
// …

$this->getFieldsHelper()->extractFields($node->getTypedData(), $to_extract, $item->getLanguage());

For details and latest code, see https://git.drupalcode.org/project/search_api/-/blob/HEAD/tests/modules/...

In the above code snippet, we find all fields which have our custom property as the first part of their property path and prepare an array with them. Then, we just use the Search API fields helper service to automatically extract the values for those fields.

Ideally, you could just return values for the properties themselves, and Search API would take care of extracting the field values on its own, but this workaround should work pretty well in most cases.

Help improve this page

Page status: No known problems

You can: