Currently, adding new fields is a kind of second-class operation, half living in alterProperties(), half in preprocessIndexItems(), and not really reflected in the UI in any kind, or aided by the framework (beyond providing these methods).

I propose (at least for discussion) making this first-class, in the sense that processors like "URL Field" or "Rendered Item" would define themselves as just adding an optional field to the index, nothing more. (Same for "Aggregated fields", but that's a much more complicated subject because it's a configurable number of fields, each with its own configuration.) There would then be a method just for getting that field's values for a certain item, which the index would call automatically. Views could more easily display such computed properties (which should properly be marked as such, of course), and the enabling (and possible configuration) of those processors could move to the "Fields" tab (especially in combination with #2574969: Add a Views-like UI for adding fields), hiding them from the "Processors" tab (unless they also work at other stages).
However, I think it should also be possible to add fields with the current, more "hidden" way, since I don't think users will understand why the "Content access" processor has to be added on the "Fields" tab.

And, as said, incorporating the "Aggregated fields" processor into this plan would of course be quite a challenge. Having this actually work like any other "Add field" operation would of course be a huge win, usability-wise, but it will have to remain to be seen whether we can manage that (#2044287: Introduce field aliases would help a lot, I guess).

Estimated Value and Story Points

This issue was identified as a Beta Blocker for Drupal 8. We sat down and figured out the value proposition and amount of work (story points) for this issue.

Value and Story points are in the scale of fibonacci. Our minimum is 1, our maximum is 21. The higher, the more value or work a certain issue has.

Value : 5
Story Points: 8

Comments

drunken monkey created an issue. See original summary.

nick_vh’s picture

Issue summary: View changes
Issue tags: +beta blocker
borisson_’s picture

Should we postpone this issue on #2574969: Add a Views-like UI for adding fields? I think we should.

drunken monkey’s picture

Status: Active » Postponed

Yes, definitely.

drunken monkey’s picture

Assigned: Unassigned » drunken monkey

Might be I'll implement it together with #2574969: Add a Views-like UI for adding fields, so assigning to myself, too.

drunken monkey’s picture

Assigned: drunken monkey » Unassigned
Status: Postponed » Active

Did not get implemented together with #2574969: Add a Views-like UI for adding fields, but since that's committed now this issue is not postponed anymore.

drunken monkey’s picture

Assigned: Unassigned » drunken monkey
Status: Active » Needs review
Issue tags: -API change
StatusFileSize
new22.78 KB

Tackling this now.
Attached is a first-step patch which reproduces the current functionality in a slightly different way, to make it easier to build on.
Next steps: define new kind of data definitions that show which processor they belong to, make it possible for them to have configuration, have a separate method on the processor for providing data for one of its properties, etc.

drunken monkey’s picture

StatusFileSize
new38.21 KB
new55.18 KB

Step two: Properties are explicitly linked to the processor that defines them and addFieldValues($item) can/should be used to provide associated field values.

Not completely sure about that method, though – this version keeps the code working more or less exactly the same as before, and especially allows processors like "Aggregated fields" to use synergies across multiple fields – on the other hand, though, it's much more awkward if you just want a single property's values for some other reason than indexing (since you have to construct a dummy object for that).
So, that might still change slightly. (Suggestions also welcome.)

Next step: make it possible for fields to have settings (based on their property).
Final step afterwards: integrate this into the UI as smoothly as possible. (Luckily, I'm a renowned UI expert.)

Status: Needs review » Needs work

The last submitted patch, 8: 2575003-8--processor_properties.patch, failed testing.

The last submitted patch, 8: 2575003-8--processor_properties.patch, failed testing.

drunken monkey’s picture

Status: Needs work » Needs review
StatusFileSize
new2.7 KB
new57.88 KB

Next try.

Status: Needs review » Needs work

The last submitted patch, 11: 2575003-11--processor_properties.patch, failed testing.

drunken monkey’s picture

Status: Needs work » Needs review
StatusFileSize
new7.13 KB
new62.27 KB

OK, container rebuilds apparently cause more problems than they solve. (Couldn't reproduce the test fail locally, though, and it's also weird that the other environment passes – but random fails would be just as bad as normal ones, if not worse.)
So, second try, circumventing the Field UI entirely.

borisson_’s picture

I quickly went trough the latest interdiff - I did see one thing. I like that there are less random fails now that we removed the container rebuilds.

+++ b/src/Entity/Index.php
@@ -912,6 +912,7 @@ public function indexItems($limit = '-1', $datasource_id = NULL) {
+          debug($e->getMessage());

We should remove this debug statement.

I have more time to look at this later today/tomorrow.

borisson_’s picture

Status: Needs review » Needs work

Overall, I think this patch looks great, this makes the processors that add fields much simpler.

  1. +++ b/src/Plugin/search_api/processor/ContentAccess.php
    @@ -129,40 +120,54 @@ public function preprocessIndexItems(array &$items) {
    +    if (!$node) {
    +      // Apparently we were active for a wrong item.
    +      return;
    +    }
    

    Can this happen? I know it was in the previous processor as well but I still don't understand it.

  2. +++ b/src/Plugin/search_api/processor/ContentAccess.php
    @@ -129,40 +120,54 @@ public function preprocessIndexItems(array &$items) {
    +        $result = Database::getConnection()
    +          ->query('SELECT * FROM {node_access} WHERE (nid = 0 OR nid = :nid) AND grant_view = 1', array(':nid' => $node->id()));
    

    Since we're refactoring this processor anyway, can we inject the database service instead? We could open a followup as well I guess.

  3. +++ b/src/Plugin/search_api/processor/ContentAccess.php
    @@ -197,7 +202,7 @@ public function preprocessSearchQuery(QueryInterface $query) {
    -      if (is_object($account)) {
    +      if ($account instanceof AccountInterface) {
    

    Nice, this makes sense!

drunken monkey’s picture

Can this happen? I know it was in the previous processor as well but I still don't understand it.

Yes, it can – e.g., with a buggy datasource, or with someone overriding the processor.
Not very realistic, true, but if the method can return NULL (just code-wise, not logic-wise), I'd say it's good practice to check for it. And it doesn't really cost us anything.

Nice, this makes sense!

And avoids yellow squiggly lines in PhpStorm!
(Or, rather, it should. But no, PhpStorm is still positive that $account is a QueryInterface object. Huh.)

joachim’s picture

> And, as said, incorporating the "Aggregated fields" processor into this plan would of course be quite a challenge

I was thinking that you could have a new plugin type for index fields, and OOTB have 'normal' and 'aggregated'.

drunken monkey’s picture

Status: Needs work » Needs review
StatusFileSize
new99.69 KB
new141.19 KB

Next step, passes all tests locally (and fixes Joris' comments).
Now only the UI to be done, as far as I can see.

borisson_’s picture

Status: Needs review » Needs work

This patch has become really hard to review, does it make the patch smaller to use git diff -M?

I did find 2 things in the tests, but overall from what I can see, this is looking great. Thanks for the answers in #16, I understand that now.

  1. +++ b/src/Plugin/search_api/processor/RenderedItem.php
    @@ -315,6 +248,7 @@ public function calculateDependencies() {
    +    # @todo
    

    What exactly still needs to happen here? Can we link to an issue or should this be done in this patch?

  2. +++ b/src/Tests/IntegrationTest.php
    @@ -618,9 +621,10 @@ protected function checkFieldLabels() {
    +    # @todo add "rendered item" field and test there
    +//    $this->drupalGet($this->getIndexPath('processors'));
    +//    $this->assertHtmlEscaped($content_type_name);
    +//    $this->assertHtmlEscaped($field_name);
    

    We should fix this in this issue as well?

borisson_’s picture

I spent some time sitting in the car waiting, so I looked trough the patch again.

  1. +++ b/config/schema/search_api.processor.schema.yml
    @@ -15,46 +22,17 @@ plugin.plugin_configuration.search_api_processor.add_url:
    +# Default for any processor without specific configuration
    +
    +plugin.plugin_configuration.search_api_processor.*:
    +  type: search_api.default_processor_configuration
    

    Should we remove this blank line? That makes it clearer that the comment belongs to the definition.

  2. +++ b/config/schema/search_api.processor.schema.yml
    @@ -15,46 +22,17 @@ plugin.plugin_configuration.search_api_processor.add_url:
     plugin.plugin_configuration.search_api_processor.highlight:
    -  type: mapping
    +  type: search_api.default_processor_configuration
    

    I like this, makes it clearer!

  3. +++ b/config/schema/search_api.processor.schema.yml
    @@ -72,15 +50,9 @@ plugin.plugin_configuration.search_api_processor.highlight:
     plugin.plugin_configuration.search_api_processor.html_filter:
    -  type: mapping
    +  type: search_api.default_processor_configuration
    

    can we make this extend field_processors_configuration instead? Less lines of code!

  4. +++ b/config/schema/search_api.processor.schema.yml
    @@ -280,35 +137,46 @@ plugin.plugin_configuration.search_api_processor.tokenizer:
    +# Definitions for property configuration
    +
    +search_api.property_configuration.*:
    

    Let's remove this blank line as well.

  5. +++ b/src/Entity/Index.php
    @@ -1126,12 +1137,33 @@ public function query(array $options = array()) {
    +    // Merge in default options.
    +    $config = \Drupal::config('search_api.settings');
    ...
    +    // Merge in default options.
    +    $config = \Drupal::config('search_api.settings');
    

    Do we really need to do this twice? I'd think that this is only needed in the preSave? Not sure though.

drunken monkey’s picture

Status: Needs work » Needs review
StatusFileSize
new26.07 KB
new159.44 KB

This patch has become really hard to review, does it make the patch smaller to use git diff -M?

No, doesn't change anything, unfortunately. The problem is that the code from one class got split into two in several cases – no good way of expressing that in a diff.

Both cases you point out were just temporary notes for me, for what still needs to be done. I'd never use # for a "real" comment. But thanks for reminding me!

Most of your other finds are spot-on, though. As always: thanks a lot!

+++ b/config/schema/search_api.processor.schema.yml
@@ -280,35 +137,46 @@ plugin.plugin_configuration.search_api_processor.tokenizer:
+# Definitions for property configuration
+
+search_api.property_configuration.*:

Let's remove this blank line as well.

I disagree in this case, since the comment is a "heading" for all three definitions underneath.

Do we really need to do this twice? I'd think that this is only needed in the preSave? Not sure though.

That could lead to problems if someone was accessing a newly created index's configuration before it is saved. Unlikely, but not completely implausible.
I'd think just doing it in postCreate() might be the better option, if we're gonna drop one – after creation, there should be no point at which those settings get removed again. We can do that, if you want.
I just opted for extra safety here, but possibly went a bit too far.

The attached patch would be RTBC in my opinion, unless I've overlooked something.

Status: Needs review » Needs work

The last submitted patch, 21: 2575003-21--processor_properties.patch, failed testing.

The last submitted patch, 21: 2575003-21--processor_properties.patch, failed testing.

drunken monkey’s picture

Status: Needs work » Needs review
StatusFileSize
new441 bytes
new159.4 KB

checkFieldLabels
fail: [Other] Line 627 of modules/search_api/src/Tests/IntegrationTest.php:
Raw "^6%{[*>.<"field" found

I have not the smallest trace of a clue why, but for some reason in this one instance the double quote character (") doesn't get escaped. What the heck is going on …

Just took the easy way out now, like the Beatles recommended.

borisson_’s picture

Status: Needs review » Reviewed & tested by the community

woo.

  • drunken monkey committed 8294f6c on 8.x-1.x
    Issue #2575003 by drunken monkey: Improved system for processor-added...
drunken monkey’s picture

Status: Reviewed & tested by the community » Fixed

Excellent, thanks for reviewing!
Committed.

joachim’s picture

This breaks existing search indexes -- or at least I assume it's this:

> Drupal\search_api\SearchApiException: Could not retrieve data definition for field 'Foo - aggregated' on index 'My index'. in Drupal\search_api\Item\Field->getDataDefinition() (line 369 of modules/search_api/src/Item/Field.php).

I know this is still in alpha, so it's my problem :) but tips on upgrading would be useful.

joachim’s picture

Big improvement to the UI for aggregated fields BTW :)

joachim’s picture

Ok so got it working. Your existing search index configuration will need hacking by hand in the exported YML file then reimporting:

1. Find the settings for the aggregated_field processor, eg:

processor_settings:
  aggregated_field:
    plugin_id: aggregated_field
    settings:
      fields:
        search_api_aggregation_1:
          label: 'Topics - aggregated'
          type: union
          fields:
            - 'entity:node/field_topics'
            - 'entity:resource/field_topics'

2. Remove the whole of the 'fields' property from this, but pasting it to another file.
3. For each aggregated field, find it in the main fields list:

field_settings:
  search_api_aggregation_1:
    label: 'Topics - aggregated'
    datasource_id: null
    property_path: search_api_aggregation_1
    type: integer
    indexed_locked: true

4. Change the property_path to be 'aggregated_field'
5. Remove the indexed_locked property
6. Add a configuration property
7. Paste into the configuration property the type and fields properties from the stuff you removed from the processor in step 2.

End result should be like:

  search_api_aggregation_1:
    label: 'Topics - aggregated'
    datasource_id: null
    property_path: aggregated_field
    type: integer
    configuration:
      type: union
      fields:
        - 'entity:node/field_topics'
        - 'entity:resource/field_topics'

There are a few other changes to the config array, but saving the Processors form will take care of those.

audriusb’s picture

thanks @joachim!

Status: Fixed » Closed (fixed)

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

mpp’s picture

Could someone update the documentation on https://www.drupal.org/node/2715959?

csedax90’s picture

Sorry, someone can update the documentation or explain here what the new procedure to add custom fields?