Closed (fixed)
Project:
Search API
Version:
7.x-1.x-dev
Component:
Framework
Priority:
Normal
Category:
Feature request
Assigned:
Unassigned
Reporter:
Created:
18 May 2011 at 18:12 UTC
Updated:
11 Jun 2011 at 11:21 UTC
Jump to comment: Most recent file
Comments
Comment #1
awolfey commentedHere's the patch.
Comment #2
drunken monkeyThanks! I think this will make a great addition, so will add it to the main module.
However, there are a few issues that will have to be addressed first (although the processor works quite well already, so great job!):
Oh, and I corrected
postprocessSearchResults()to usearray_merge()instead of the + operator, as the latter won't work with numeric indexes.preg_split()to get the individual words. (Quick test shows this shouldn't be a performance issue, taking 130ms for a 1MB string.)configurationFormValidate(), you can probably just set#requiredon the form element. This of course would be moot when you add another way of specifying stop words.$keysisn't empty. It could also be a string, which will have to also be checked. (You could maybe use apreg_replace()for this case, as well as for dealing with untokenized text at index time.)is_array($stopwords)will always beTRUE(barring acts of God), you should check forempty($stopwords)instead.$valuein the loop can be an array, which should be processed recursively to be completely correct. Removing the stopwords inside of phrase queries withpreg_replace()would probably also be a good idea.in_array($v, $stopword), you could, e.g., flip the array and useisset($stopwords[$v]).preprocessIndexItems(), as well as the rest of this system, is necessary. Just overridingprocessFieldValue()should suffice, and would even free you from explicitly handling tokenized data. Which you don't do completely correctly at the moment, anyways. (I admit it is pretty hard to do, had also a number of bugs in there. But that's exactly why I provided a system that can be overridden in many places to free implementers from most of the work.)preg_replace()instead ofprocessWords()to deal with words inside of text should also be looked at, in my opinion. Don't know how well that would perform for large numbers of stop words, though. In any case, this should be the code to do it:Based on a quick test, it doesn't perform too shabby, either. Maybe we can provide a checkbox to disable this, if users run into performance problems with it.
Comment #3
drunken monkeyOh, you should also check whether
$this->ignoredand$response['ignored']are even set inpostprocessSearchResults().Comment #4
awolfey commentedThanks for the thorough review. I'm learning a lot working with search_api.
The D6 fuzzysearch uses files for stopwords. I considered that for D7, but after an IRC discussion with Gabor decided that storing stopwords files with the modules wasn't the way to go. That doesn't mean that they can't be used here even if we don't store them. But, with the idea of providing localization support at some future point, if you have ideas on how to build a repository of files for any language people want to contribute, that would be good to work in.
Comment #5
drunken monkeyI agree, maintaining stopword lists inside the module wouldn't be a good idea.
For this use case, you could later just provide some syntax to specify those files. E.g., specify "stopword.*.txt" and the asterisk will automatically be replaced with the language code (or "und"). I don't think that's much of a problem.
Comment #7
awolfey commentedWhat configuration will give me an array in $value? Is that coming from a facet, or from some other module?
Is phrase searching supported by anything yet?
Comment #8
awolfey commentedThis takes care of most of the issues from #2. I ran some very simple tests and found that using preg_replace() was quite a bit slower than processing the words with arrays. I left the test code in the patch so you can confirm.
When you get a chance to look at #7 I work the recursion in. Your style seems to be to leave comments out of the processors so I'll stick with that, or maybe I should comment the new functions.
Attaching the stopwords file I used for the test.
Comment #9
awolfey commentedThis adds static caching in getStopWords() and fixes the stopwords textarea default value.
Comment #10
drunken monkeyThose can be used for more complex queries, e.g. with negated terms. I don't think it's currently used, but it's in the specification and therefore should be minded.
But there is also a
processKey()method which will be called for all keywords in the default processor class, and which you can use so you don't have to take care of all of this yourself. You should probably just use that.It's supported by the Solr backend, and I think by the Xapian backend, too.
Other comments:
static, you should use an object property for caching ingetStopwords(), asstaticwill cache the same version for all objects. In the (admittedly unlikely, but still possible) event that two searches on different indexes (with both using differently configured stopwords processors) are executed on the same page request, this would filter out the wrong term for all searches but the first.getStopwords(), use anfilterStopwords($word)method right away. This would help you not duplicate code for fields and search keys. (Or, you can just override theprocess()method, if you'd do the same things inprocessFieldValue()andprocessKey()anyways. This would, by the way, also take care of search filters set on fulltext fields, which you are currently ignoring.)$query->keys()to set the processed search keys, as this will mess up the "original keys" property. Instead, save the return value ofgetKeys()as a reference and operate on$keysdirectly.Please always wrap the body of if/else/… in braces.
/…/Sflag, and by saving the regex so it hasn't got to be constructed anew for each single value. The major benefit however isn't speed, but correctness: as far as I can see, your code currently will only find words separated by spaces on both sides, but not those separated by commas, periods, other special characters, newlines, etc.If you think the difference in performance is too large, we should maybe just ignore untokenized inout altogether and specify that people should tokenize the input (i.e., use the "Tokenizer" processor) before using the stopwords processor. Or we could implement the regex variant for untokenized input (or tokens that still contain whitespace, which we should then also check) and note in the processor description (or the configuration form) that performance will improve when tokenizing the input before filtering out stopwords.
However, as I see it, both run times are too low to really care about the performance, even if the difference is a factor of about 100. In the end, in most cases this will still be a rather insignificant addition to the overall runtime.
is_file()/file_exists()(in contrast to, e.g.,file_get_contents()) only work for local files. Better just try to read the file and see if this works, instead of usingis_file().Powered by Dreditor.
Comment #11
drunken monkeyThere's also a typo ("ignore" instead of "ignored"), resulting in ignored words not showing properly.
Powered by Dreditor.
Comment #12
awolfey commentedWe must be getting there because there's not much code left. :)
I think process() will work for keys and content. This really should run after tokenizer in my opinion, so I don't think we need to worry about more than spaces in $value, right?
This message "The following search keys are too short or too common and were therefore ignored:" probably won't apply to all stopwords use cases, but maybe that's a separate issue.
Comment #13
drunken monkeyI fit should run after the tokenizer, this should be clearly stated.
This also had a bug that if a keyword was removed, there would be an empty string as the key. The abstract default processor should take care of unsetting such keys.
Lastly, we still cannot just use
explode(), as that just ignores too many cases for my taste. We should at least usepreg_split()to split on any whitespace.The attached patch should fix all of this.
Comment #14
awolfey commentedThis is looking good and ready to me. Marking rtbc but maybe we can get some others to test also.
I'm going to create a new issue related to the default tokenizer configuration, which I think creates a couple of problems. #1168684: Problems with tokenizer defaults in English
Comment #15
drunken monkeyOK, then let's just wait a couple of days whether someone else wants to review, and then add this.
In any case, thanks again for your contribution!
Comment #16
drunken monkeyJust committed this, thanks again!