Problem/Motivation

There are cases when we want to index/reindex a given content entity but that entity is not in an update process, hence is not indexed by search_api_entity_update() hook implementation. Also we don't want to re-save the entity just to get it reindexed. We workaround this by running this code:

$entity = ...
$entity->original = $entity;
search_api_entity_update($entity);

This is bad for several reasons:

  • search_api_entity_update(), being a hook implementation, is not meant to be called directly, it's a kind of magic function that is called only by the entity save process.
  • The $entity->original = $entity; trick is there to avoid an error in search_api_entity_update(), line $old_translations = $entity->original->getTranslationLanguages();. The code assumes that the entity is indexed ONLY as part of entity save process, where the original public property is set.
  • search_api_entity_update() and search_api_entity_insert() are duplicating almost the same code.

Proposed resolution

  1. Create a new static method: ContentEntity::indexEntity() that replicates most of the code from search_api_entity_update() but handles also the case when $entity->original is not set.
  2. From search_api_entity_insert() and search_api_entity_update(), just call the new method.

Remaining tasks

None.

User interface changes

None.

API changes

New static method: ContentEntity::indexEntity().

Data model changes

None.

Release notes snippet

New helper method to index a content entity: ContentEntity::indexEntity($entity).

Comments

claudiu.cristea created an issue. See original summary.

claudiu.cristea’s picture

Issue summary: View changes
claudiu.cristea’s picture

Issue summary: View changes
Status: Active » Needs review
StatusFileSize
new5.82 KB

Patch.

claudiu.cristea’s picture

StatusFileSize
new2.61 KB
new5.92 KB

An improved version that is enforcing a content entity by properly typing the function/method parameter. Also static:: should be used to call other static methods inside the new method.

claudiu.cristea’s picture

StatusFileSize
new1.02 KB
new5.87 KB

Ooops, the hooks are called for all kind of entities.

claudiu.cristea’s picture

StatusFileSize
new692 bytes
new5.87 KB

Oh, the skip check should be reverted.

claudiu.cristea’s picture

Issue summary: View changes
claudiu.cristea’s picture

Issue summary: View changes
claudiu.cristea’s picture

Assigned: claudiu.cristea » Unassigned
dimilias’s picture

I can also vote for this as I have already ran across this. Does this needs tests?

claudiu.cristea’s picture

Does this needs tests?

Well, we are only moving the code around. The current implementation has already test coverage.

dimilias’s picture

Status: Needs review » Reviewed & tested by the community

It is a good addition nonetheless. RTBC by me.

claudiu.cristea’s picture

StatusFileSize
new3.34 KB
new5.67 KB

The variable names are confusing. Simplified the code by using array_diff() instead of iterating.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 13: 3106342-13.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new1.03 KB
new5.7 KB

Fixes the case when the entity is new.

Status: Needs review » Needs work

The last submitted patch, 15: 3106342-15.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new1.03 KB
new5.7 KB

Oops!

Status: Needs review » Needs work

The last submitted patch, 17: 3106342-17.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new1.82 KB
new5.63 KB
borisson_’s picture

Status: Needs review » Reviewed & tested by the community

This makes the code simpler to reason about, it has more deletions than additions and I don't think we can apply this anywhere else already.
Big fan!

drunken monkey’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new5.22 KB

Thanks a lot for this patch, really looks like a great idea. And sorry I didn’t reply any sooner – for some reason, it seems the notification for this issue slipped through the cracks in my Inbox. I only noticed this now, when searching for RTBC issues I might have missed.
I remember that you already pinged me about this issue a few months ago (which I always recommend doing when you don’t get a reaction from me), but seems I didn’t realize at that time that it wasn’t in my stack of unread issues. I’ll take more care in the future – sorry again!

Anyways, unfortunately the patch became outdated in the meantime, the code in question was already moved to ContentEntityTrackingManager. However, an indexEntity() method still sounds like a good idea there, so why not?
Re-rolled the patch – please test/review and I can commit it.
And thanks again!

jeroen_vreuls’s picture

The last patch initializes $original_translations as follows:

$original_translations = [];

Shouldn't this be:

$original_translations = $current_translations;

Because when I index an existing entity it now calls trackItemsInserted() instead of trackItemsUpdated(). (although my change might break indexing new entities)

claudiu.cristea’s picture

@drunken monkey, thank you for reviving this. Unfortunately I cannot test this on a real project because on our project we're using an old pinned version of Search API, where only #19 applies. And that works as expected. So, I only did a formal review and I see nothing wrong. Still some nits:

  1. +++ b/src/Plugin/search_api/datasource/ContentEntityTrackingManager.php
    @@ -116,8 +99,20 @@ public function entityInsert(EntityInterface $entity) {
    +  public function indexEntity(ContentEntityInterface $entity) {
    

    As this is new code, we can strict type also the method's return, void in this case.

  2. +++ b/src/Plugin/search_api/datasource/ContentEntityTrackingManager.php
    @@ -129,39 +124,33 @@ public function entityUpdate(EntityInterface $entity) {
    +    $get_ids = function ($langcode) use ($entity_id) {
    

    Let's strict type the closure parameter and return type.

drunken monkey’s picture

StatusFileSize
new3.56 KB
new5.9 KB

@ jeroen_vreuls: No, it shouldn’t, checking $entity->original is in general correct here.
However, you do have a point: when calling indexEntity() directly, which is what we want to do here, $entity->original will always be empty, so the code will always think it’s dealing with a new entity. So, I guess we probably need a public wrapper method that retrieves the original as appropriate, and then a protected method with the actual (current) code.

Hm. But how do we “retrieve the original”? In the general case, we’d have to pass the version of the entity that was last tracked – something which we can’t achieve. Off the top of my head, I can’t even think of a way to reliably determine the known translations of the entity, which is really the only thing we need the original for. So if you make changes to the entities translations and want them indexed via this mechanism, you’d have to set $entity->original yourself before calling this.
However, I think for the more traditional use case of having other changes (not new or removed translations) you want indexed, I’ve now got this fixed. Please see the attached patch revision.

Also, is it only me or is “current” and “original” a bit confusing here? The change is just happening, so “current” doesn’t seem very clear to me.
I now renamed the variables to $new_translations and $old_translations. Would you agree that’s better (@everyone)?

Finally, I think indexEntity() is also a misnomer for that method. It won’t index an entity, it will mark it as needing to be indexed (either because it’s new or changed). Only if the search index option “Index items immediately” is enabled will the entity also be indexed right away (or at least in the same page request).
So, I also renamed the method, though I’m not sure what would actually be a good name for it. In any case, I also corrected/expanded its documentation.

If you really want a method for indexing an entity directly, you should at this point probably create a new issue. For that, no interaction with the tracker, etc., would be necessary – just create an item for each translation of the entity and send them to the server for indexing.

As this is new code, we can strict type also the method's return, void in this case.

Unfortunately, no – we still want to support Drupal 8 while it is maintained, and that only requires PHP 7.0 – so void can’t be used as a return type hint.

Let's strict type the closure parameter and return type.

You’re right, yes. Also included in the attached patch.

drunken monkey’s picture

Any more input on this? This has grown quite complex by now, so would like some feedback before committing.

claudiu.cristea’s picture

As I told in #23, I cannot test on real use case. However....

So if you make changes to the entities translations and want them indexed via this mechanism, you’d have to set $entity->original yourself before calling this.

... this concerns me. The whole idea of this feature is to make it simple to index an entity. Without this feature we already did:

$entity = ...
$entity->original = $entity;
search_api_entity_update($entity);

So, we set $entity->original. Then what's the point to have this feature, except code reusability?

drunken monkey’s picture

So, we set $entity->original. Then what's the point to have this feature, except code reusability?

Well, it still works correctly when not doing that, as long as the set of translations didn’t change (none added/removed). From the IS, it seems that’s all you wanted?
Anyways, feel free to propose any solution to this if you can think of one. I can’t, so it would be either this or nothing.
Would you prefer passing $original as a separate parameter?

drunken monkey’s picture

Any more feedback?

claudiu.cristea’s picture

Unfortunately, I didn't had time to test and, as I told before, we're using a pinned SAPI version which only works with the initial patch. But it seems that #27 has already clarified my concerns.

drunken monkey’s picture

Status: Needs review » Fixed

OK, thanks for your feedback. Then let’s just commit this, I guess. It’s definitely an improvement, even if it can’t provide all of the functionality we’d like to see.
Committed. Thanks again, everyone!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.