Comments

mark_fullmer created an issue. See original summary.

mark_fullmer’s picture

StatusFileSize
new1.31 KB
mark_fullmer’s picture

mark_fullmer’s picture

StatusFileSize
new5.25 KB
drunken monkey’s picture

Component: General code » Plugins
Status: Active » Needs work

Thanks a lot for proposing this addition!
You are right, having (English) stemming available for users of the database backend (or other "primitive" backends) would be great!
However, the extra step of downloading the external "library" is a bit of a drag. The algorithm is available publicly and easy enough to implement, so why not just add your own implementation in a processor? (Or ask Richard Heyes whether he wants to contribute his code to this project – might be even quicker.) Would be far better UX, and not too much additional effort.
Furthermore, the current patch introduces a dependency on the Libraries module, which I'd like to avoid. It also adds an undocumented new key for processors (but that wouldn't be as much of a problem – but also can be easily implemented differently).

So, would it be possible for you to just implement the algorithm directly in your processor (or find a GPL-compatible version which we can use)?

mark_fullmer’s picture

Agreed with all of your points about the additional overhead with a "3rd party" library. I'll seeing Heyes is game to contribute, and if not, write an original implementation of the algorithm with the same goal. Like you say, it's easy enough to implement.

drunken monkey’s picture

Awesome, thanks a lot!

mark_fullmer’s picture

Status: Needs work » Needs review
StatusFileSize
new17.77 KB

Okay! This revised patch makes a number of improvements:

  • Uses custom implementation of the stemming algorithm, thereby not requiring the Libraries dependency or a separate download
  • Custom implementation is of the Porter 2 algorithm, instead of the original (this one goes to 11)
  • Allows for user-inputted exceptions list via configuration form
  • Processing optimization by memoizing already stemmed results

Unit tests for this implementation are available at https://github.com/markfullmer/porter2

drunken monkey’s picture

StatusFileSize
new18.16 KB
new22.7 KB
new20.02 KB

Great job, thanks a lot!
However, while the implementation looks great, the code doesn't really follow Drupal's coding standards. I fixed that and also some code that looked more like a direct C port than actual PHP code.
I also attach a patch for your Github repository. I ran the tests there, and one of my changes seems to have fixed one of the three failures the test script reported for me. (Does it report any for you?) So even if you don't like the code style changes, maybe you can use some of the other changes.

Anyways, please take a look at my changes and tell me if you're fine with them. Then I guess I can commit this, and we'll finally have a stemmer processor right in the module. (Should then port ASAP to D8, too.)

In any case, thanks a lot again!

mark_fullmer’s picture

Status: Needs review » Reviewed & tested by the community

I've adopted the code syntax changes in the original github repo, and run the tests & everything checks out.

I've also re-applied your re-roll to the dev branch & verified that the stemming is still working as expected within Search API.

Thumbs up on all counts! Let me know if I can help porting this to the D8 branch.

mark_fullmer’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new18.94 KB

.... and just to clarify (you probably already know this), 2779159-9--add_stemming_processor.patch is NOT the finished re-roll of what is in github. It does not include the charAt method, and changes to other methods that implement it, and generates notices occasionally due to a nonexistent position in $word[$position] -- https://www.evernote.com/l/AlW-t7DqmE9OWZ81ZN97dFMFo6WmnSkWk-0

The attached is a clean re-roll of your syntax changes.

Status: Needs review » Needs work

The last submitted patch, 11: 2779159-11--add_stemming_processor.patch, failed testing.

mark_fullmer’s picture

Status: Needs work » Needs review
StatusFileSize
new21.26 KB

One more patch -- with the SearchApiPorterStemmer class.

Status: Needs review » Needs work

The last submitted patch, 13: 2779159-13--add_stemming_processor.patch, failed testing.

drunken monkey’s picture

Status: Needs work » Needs review
Issue tags: +needs port to Drupal 8

No, I didn't know that, must have forgot about writing those changes back to the module, too. Thanks a lot for catching that!
However, why did you also revert all my changes to the processor class?
Also, please create patches relative to the module directory, not to the Drupal installation – otherwise the test bot can't apply them.

Anyways, this should now be the final patch (interdiff compared to #9). Would you say this is RTBC now?

Getting your help for porting this to Drupal 8 would of course be awesome, thanks a lot!
For D8, though, we should definitely also add tests, we have some for all processors there. (But you can leave that part for someone else, too, no worries.)

mark_fullmer’s picture

StatusFileSize
new20.82 KB

My mistake on removing the changes to the processor class -- they're valid, I'd just overlooked them. I don't see a patch on the last comment, so here it is. I'll have a colleague RTBC it tomorrow.

brayfe’s picture

Hey all,

Just applied this patch to my Search API config and it seems to be working as advertised. I tested this locally with the following:

Mac OSX 10.10.5 (Yosemite)
MAMP 4.0.5
PHP 5.6.25
MySQL 5.6.28

After configuring Search API to use the "Stem Words" processor, and re-indexing all my nodes, I ran a search for "teach" and several results with teach, teacher, teaching were at the top. I would suggest, as a feature request or enhancement, that Search API could highlight the word AND the stemmed results. That would make it more apparent to the user why certain results are appearing.

I'll leave it up to @drunken-monkey to determine if this issue should be considered RTBC.

drunken monkey’s picture

Version: 7.x-1.x-dev » 8.x-1.x-dev
Status: Needs review » Patch (to be ported)
Issue tags: -needs port to Drupal 8 +release target

Yes, looks good. Thanks for testing!
Regarding highlighting: Unfortunately, that's not really possible to do without a completely different framework (like Apache Solr has).

Anyways: committed.
Again, thank you very much for contributing this! Great job!

mark_fullmer’s picture

Assigned: Unassigned » mark_fullmer
Status: Patch (to be ported) » Needs review
StatusFileSize
new20.4 KB

Attached is an 8.x port of the Porter 2 Stemmer processor. I've manually verified that it replicates all behavior of the 7.x branch, including custom exceptions. Automated tests, however, are still a to-do.

drunken monkey’s picture

Status: Needs review » Needs work
StatusFileSize
new36.9 KB
new21.57 KB

Great job, thanks a lot!
Didn't test it, but from looking at the code, I have a few smaller notes:

  • In Drupal 8, every class has to go into its own file. Also, we use namespaces there for avoiding conflicts, not class name prefixes.
  • I prefer porter_stemmer for the plugin ID, to reflect the class name. (Or change both to just "stemmer", since that's also the label? Or change the label?)
  • The weights for the preprocessing stages were too low; this should definitely run after the tokenizer.
  • The processor also needs a config schema.
  • Why go to the trouble of splitting the "Exceptions" string and calling parse_ini_string() for each line individually? Why not just call it on the whole?
  • More importantly (don't know how I missed that for D7): Why not parse this once, when submitting the config form, instead of every time the processor is called? The way it currently is, you can't even report a syntax error to the user and end up with invalid config, that leads to warnings on every search or indexing operation. (Didn't implement this change in my patch yet.)
  • We should also clearly note in the description (or maybe even in the label?) that this stemmer only works for English.
  • Relatedly, we might want to ignore any items that have a different language set. (Also not implemented.)

Except for the two noted exceptions, all of these are fixed in the attached patch.
Also, the tests are still missing. (But pretty easy to add: see, for instance, \Drupal\Tests\search_api\Unit\Plugin\Processor\IgnoreCaseTest. You just need a sufficiently long list of correct stems – but you already have those in your test on Github, it seems. I guess you can just copy the full.ini from there to our module. Or, maybe just a subset, since that file is pretty large.)

mark_fullmer’s picture

Status: Needs work » Needs review
StatusFileSize
new9.3 KB
new26.52 KB

All good points, above, drunken_monkey. I've addressed all outstanding items:

borisson_’s picture

Status: Needs review » Needs work

Thanks @mark_fullmer for that test, it's great! I now have a better understanding of how a stemmer is supposed to work, great work!

I did find some nitpicks, some docs issues, a question and a request for additional test.

  1. +++ b/src/Plugin/search_api/processor/Resources/Porter2.php
    @@ -0,0 +1,652 @@
    +/**
    + * Implements the Porter2 stemming algorithm.
    + *
    + * @see https://github.com/markfullmer/porter2
    + */
    +class Porter2 {
    

    I wanted to ask to actually use that library as a dependency, but that doesn't seem like a good idea, we wouldn't get any of their tests (as there's only one) and we'd need to contribute at least a composer.json and namespacing. So let's not.

    We should probably do a // @codingStandardsIgnoreFile line at the top of this file so phpcs doesn't complain about it though.

  2. +++ b/src/Plugin/search_api/processor/Stemmer.php
    @@ -0,0 +1,147 @@
    + *   description = @Translation("Stems search terms (e.g., <em>talking</em> to <em>talk</em>). Currently, this only acts on English language content. It uses the Porter 2 stemmer algorithm (<a href=""https://wikipedia.org/wiki/Stemming"">More information</a>). For best results, use after tokenizing."),
    

    The "" here doesn't make much sense, let's use proper escaping \" or swap the quotes for the entire @Translation to use '

  3. +++ b/src/Plugin/search_api/processor/Stemmer.php
    @@ -0,0 +1,147 @@
    +   * @var array
    +   */
    
    @var string[]
      An array of stemmed words, keyed by original word.
  4. +++ b/src/Plugin/search_api/processor/Stemmer.php
    @@ -0,0 +1,147 @@
    +   * @var string[]
    

    Same comment here?

  5. +++ b/src/Plugin/search_api/processor/Stemmer.php
    @@ -0,0 +1,147 @@
    +      // Limit this processor to English language data.
    +      if ($item->getLanguage() == 'en') {
    +        foreach ($item->getFields() as $name => $field) {
    +          if ($this->testField($name, $field)) {
    +            $this->processField($field);
    

    This is simple code, but nested a bit too deep for my taste. Can we do something like this to reduce nesting?

    // Skip items that are not in English.
    if ($item->getLanguage() !== 'en') {
    continue;
    }
    
    foreach (...
    

    This is just cosmetics, so feel free to disagree.

  6. +++ b/tests/src/Unit/Plugin/Processor/StemmerTest.php
    @@ -0,0 +1,137 @@
    +  public function testProcess($passed_value, $expected_value) {
    

    This test is great! It'd be lovely to also test the preprocessIndexItems method with at least two items (english / not english) to see that that also works.

drunken monkey’s picture

Status: Needs work » Needs review
StatusFileSize
new6.9 KB
new27.61 KB

Wow, great job, Mark, thanks a lot!

We should probably do a // @codingStandardsIgnoreFile line at the top of this file so phpcs doesn't complain about it though.

I disagree. If we include the code in the project, we should also format it according to the Drupal coding standards.
Also, do you see any violations? I went through the file and fixed all I could find.

The "" here doesn't make much sense, let's use proper escaping \" or swap the quotes for the entire @Translation to use '

"" is the proper escaping for annotations. As far as I can see, there is no other way. (Otherwise, I'd prefer either of your mentioned alternatives, too.)

@var string[]
  An array of stemmed words, keyed by original word.

That's the wrong format for properties.

I totally agree with your items 5 and 6, though. Should be fixed in the attached patch.

borisson_’s picture

Status: Needs review » Reviewed & tested by the community

Thanks for correcting me on those points, changes look good!

drunken monkey’s picture

Status: Reviewed & tested by the community » Fixed

Great to hear, thanks again for reviewing.
And of course thanks a lot again to Mark for all his work here!
Committed.

Status: Fixed » Closed (fixed)

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