Implementing FUZZY Search in Drupal 7
Customers don't always remember the exact name of the product they are looking for, so they typically want to have FUZZY search capabilities, which means they enter a partial search term to get a list of candidate answers to then pick from.
A good example of this is a product we have that is very popular, but has a bit of a strange name: OCU-GLO. This name has a variant spelling and also a hypen in the middle, which leads to a lot of confusion if you've only heard the name, but not seen it.
Unfortunately, the default search behaviour of Drupal is exact search. This is really stupid in an age where everyone is using Natural Language Processing (NLP) in their search, including Google - who has offered it for 20 years!
This is what happens on a stock Drupal 7 site when an incomplete term like "ocu" is used to initiate a search:
Stupid, stupid, stupid.
This feature has been requested by Drupal 7 users since 2009, yet it has not made itself into the stock Drupal 7 distribution, even as Drupal 7 is being "sunsetted" by its maintainers, who in my opinion are doing a terrible job supporting their user community.
Rant
Several of the features we need appear to still require CORE modifications, even seven years after the releast of Drupal 7. Of course, this is very silly in our customer-centered era - but to be expected with such a disorganized setup as the Drupal organization, which features an achingly slow, authoritarian central planning committee and a fanatical but glaringly incomplete engineering and product focus to the point where its output is actually detrimental to the customer organization (i.e. putting Drupal in and getting it working wastes a lot of time). So, ordinary users are forced to hack the source code of Drupal, even as Drupal high priests ominously intone that "hacking Drupal Core is forbidden behaviour" - while taking years to fix critical things! Of course, this is incredibly frustrating for someone who has a business to run on Drupal, like us, mostly because we stupidly chose Drupal in 2010 and are now desperately trying to avoid the massive migration costs of moving across eCommerce platforms, even as they are forced to re-implement their entire back-end within the Drupal eCommerce platform because its upgrade model is basically broken. Drupal really is the gift that keeps on taking.
How to Patch Drupal
1. Document the PatchExtensively document the patch because you may need to re-implement it if you upgrade Drupal Core, and things will stop working after an upgrade so in the heat of your WTF you will want to go somewhere you wrote stuff with a cool head, like here on Blogger(tm), thus helping along others who are also struggling to make Drupal what it could be, rather than what it is.
2. Determine Where to Apply the Patch
You can figure out where the patch is to be applied by looking at the header of the patch file. Here's the location of the patch file we are going to use as an example:
https://www.drupal.org/files/issues/search-partial_words_hack-498752-41_0.patch
Here's the entire source code of the above file, just in case it disappears one day:
OK, the file in question is search.extender.inc, which lives in the /modules/search/ directory
You can put the patch in the same directory as the module to be patched using wget:
8. Apply the Patch (In Verbose Mode)
https://www.drupal.org/files/issues/search-partial_words_hack-498752-41_0.patch
diff --git a/modules/search/search.extender.inc b/modules/search/search.extender.inc
index 4074256..827ca3a 100644
--- a/modules/search/search.extender.inc
+++ b/modules/search/search.extender.inc
@@ -285,7 +285,7 @@ class SearchQuery extends SelectQueryExtender {
foreach ($key as $or) {
list($num_new_scores) = $this->parseWord($or);
$any |= $num_new_scores;
- $queryor->condition('d.data', "% $or %", 'LIKE');
+ $queryor->condition('d.data', "%$or%", 'LIKE');
}
if (count($queryor)) {
$this->conditions->condition($queryor);
@@ -297,7 +297,7 @@ class SearchQuery extends SelectQueryExtender {
else {
$simple_and = TRUE;
list($num_new_scores, $num_valid_words) = $this->parseWord($key);
- $this->conditions->condition('d.data', "% $key %", 'LIKE');
+ $this->conditions->condition('d.data', "%$key%", 'LIKE');
if (!$num_valid_words) {
$this->simple = FALSE;
}
@@ -310,7 +310,7 @@ class SearchQuery extends SelectQueryExtender {
}
// Negative matches.
foreach ($this->keys['negative'] as $key) {
- $this->conditions->condition('d.data', "% $key %", 'NOT LIKE');
+ $this->conditions->condition('d.data', "%$key%", 'NOT LIKE');
$this->simple = FALSE;
}
@@ -366,7 +366,7 @@ class SearchQuery extends SelectQueryExtender {
if (!empty($this->words)) {
$or = db_or();
foreach ($this->words as $word) {
- $or->condition('i.word', $word);
+ $or->condition('i.word', '%' . $word . '%', 'LIKE');
}
$this->condition($or);
}
Examine the header of the patch file, which shows the location of the file to patch:diff --git a/modules/search/search.extender.inc b/modules/search/search.extender.inc
index 4074256..827ca3a 100644
--- a/modules/search/search.extender.inc
+++ b/modules/search/search.extender.inc
3. Verify Where to Put the Patch
Don't blindly believe the patch file paths. Things change and, remember, there are TWO /modules directories in Drupal (yes, very stupid), so verifying where to put the patch file is necessary.
Figuring out where exactly to put the patch is pretty easy using the find command:
# pwd
/var/www/vhosts/holisticpethelp.com/www/mobile
# find . -name "search.extender.inc"
./modules/search/search.extender.inc
4. Go to Where the Target File Is
Next, physically go to where the file to be patched is:
# cd modules/search/
# ls
search.admin.inc search.extender.inc search.pages.inc search.test
search.api.php search.info search-results.tpl.php tests
search-block-form.tpl.php search.install search-result.tpl.php
search.css search.module search-rtl.css
5. Back up the Target File
Create a copy of the file to be changed, because who knows if the patch will be successful, or even solve your problem - Remember, you are in "wildcatting" programmer mode, with no guarantee of help from the incredibly slow formal Drupal team
# cp search.extender.inc search.extender.inc.backup-2019-11-25
# ls
search.admin.inc search.module
search.api.php search.pages.inc
search-block-form.tpl.php search-partial_words_hack-498752-41_0.patch
search.css search-results.tpl.php
search.extender.inc search-result.tpl.php
search.extender.inc.backup-2019-11-25 search-rtl.css
search.info search.test
search.install tests
6. Install the Patch
You can put the patch in the same directory as the module to be patched using wget:
wget https://www.drupal.org/files/issues/search-partial_words_hack-498752-41_0.patch
--2019-10-25 10:05:31-- https://www.drupal.org/files/issues/search-partial_words_hack-498752-41_0.patch
Resolving www.drupal.org (www.drupal.org)... 151.101.10.217
Connecting to www.drupal.org (www.drupal.org)|151.101.10.217|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1650 (1.6K) [text/plain]
Saving to: ‘search-partial_words_hack-498752-41_0.patch’
100%[====================================================>] 1,650 --.-K/s in 0s
2019-10-25 10:05:32 (182 MB/s) - ‘search-partial_words_hack-498752-41_0.patch’ saved [1650/1650]
7. Verify Patch Installation
# ls
search.admin.inc search.module
search.api.php search.pages.inc
search-block-form.tpl.php search-partial_words_hack-498752-41_0.patch
search.css search-results.tpl.php
search.extender.inc search-result.tpl.php
search.extender.inc.backup-2019-11-25 search-rtl.css
search.info search.test
search.install tests
8. Apply the Patch (In Verbose Mode)
You can apply the patch using the patch command as described above, which will below, replacing hello.patch with the name of your particular patch file. For those who are a bit paranoid and sceptical when it comes to Drupal (me), you might want to verify that the changes were actually made in code using the --verbose option, which tells you what patch is up to as it does its thing:
# patch --verbose < search-partial_words_hack-498752-41_0.patch
Hmm... Looks like a unified diff to me...
The text leading up to this was:
--------------------------
|diff --git a/modules/search/search.extender.inc b/modules/search/search.extender.inc
|index 4074256..827ca3a 100644
|--- a/modules/search/search.extender.inc
|+++ b/modules/search/search.extender.inc
--------------------------
patching file search.extender.inc
Using Plan A...
Hunk #1 succeeded at 285.
Hunk #2 succeeded at 297.
Hunk #3 succeeded at 310.
Hunk #4 succeeded at 366.
done
9. Validate the Patch Was Applied
Considering that
10. Validate Desired Website Behaviour
REFERENCES:
https://www.thegeekstuff.com/2014/12/patch-command-examples/
https://www.drupal.org/project/drupal/issues/498752
Hmm... Looks like a unified diff to me...
The text leading up to this was:
--------------------------
|diff --git a/modules/search/search.extender.inc b/modules/search/search.extender.inc
|index 4074256..827ca3a 100644
|--- a/modules/search/search.extender.inc
|+++ b/modules/search/search.extender.inc
--------------------------
patching file search.extender.inc
Using Plan A...
Hunk #1 succeeded at 285.
Hunk #2 succeeded at 297.
Hunk #3 succeeded at 310.
Hunk #4 succeeded at 366.
done
9. Validate the Patch Was Applied
Considering that
- There were four changes detailed in the above diff file (+/-)
- Four Hunks were successful in the patch
10. Validate Desired Website Behaviour
REFERENCES:
https://www.thegeekstuff.com/2014/12/patch-command-examples/
https://www.drupal.org/project/drupal/issues/498752
No comments:
Post a Comment