Problem/Motivation

It would be useful to be able to perform a full text search in a conditionGroup, but currently, that is not supported.

Proposed resolution

Pass the query settings (which includes fuzziness) from QueryParamBuilder (where the data is available) to FilterBuilder.

Add a LIKE operator to the supported query conditions, which runs an Elasticsearch match query on the condition's field, with the given search-term-value and query's fuzziness (or the default fuzziness if not specified).

Add a NOT LIKE operator to the supported query conditions, which wraps an Elasticsearch must_not boolean query around a match query on the condition's field, with the given search-term-value and query's fuzziness (or the default fuzziness if not specified).

Adds an EXACT operator to the supported query conditions, which runs an Elasticsearch match_phrase query on the condition's field with the given search-term-value.

Remaining tasks

  1. Write a merge request - patch by @artemboiko in #2; converted to merge request !52 by @mparker17 in #4
  2. Review and feedback - reviewed by @mparker17 in #22 and @fathershawn in #30 and #31
  3. RTBC and feedback
  4. Commit to 9.0.x - committed by @mparker17 in #33
  5. Commit to 8.0.x - committed by @mparker17 in #35
  6. Release 9.0.x - released in 9.0.0-alpha3 by @mparker17
  7. Release 8.0.x - released in 8.0.0-alpha7 by @mparker17

User interface changes

None.

API changes

Adds arguments to FilterBuilder::buildFilters() and FilterBuilder::buildFilterTerm() (both public functions) but provide default values, so this is still backwards compatible.

Data model changes

None.

Original report by @artemboiko

Added LIKE and EXACT elastic operator like in parent issue but for 8.0.x-dev

If possible, it would be nice to have a NOT LIKE filter too

The parent issue says...

I was trying to create a custom query on Elastic and I needed to perform a full text search in a conditionGroup and the keys() method cannot support that.

To do that I've made two additional filters on FiltersFactory.php, LIKE, and EXACT to query the elasticsearch server with a full text.
Not sure if it's the best method, I'd love some feedback on this.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

artemboiko created an issue. See original summary.

artemboiko’s picture

mparker17 made their first commit to this issue’s fork.

mparker17’s picture

Status: Active » Needs review

@artemboiko, thank you! (and apologies: I only have expertise in the 8.0.x branch of the module, so I'm unable to help move the 8.x-7.x branch forward!)

I've created a merge request: lets see if tests pass.

Offhand, the code looks fine thus far. May I request that you update the test at tests/src/Unit/SearchAPI/Query/FilterBuilderTest.php to test the new code? (this will ensure that future changes don't accidentally break the LIKE and EXACT functionality you need!)

Thank you very much!

mparker17’s picture

Issue summary: View changes

If possible, it would be nice to have a NOT LIKE filter too, as put forward in #3092486-10: LIKE filter missing. That issue (which is a lot older and uses the old, switch-based syntax) suggests the following code...

+        case 'LIKE':
+          $filter = [
+            'bool' => [
+              'should' => [
+                'wildcard' => [$condition->getField() => $condition->getValue()],
+              ],
+            ]
+          ];
+          break;
+
+        case 'NOT LIKE':
+            $filter = [
+              'bool' => [
+                'must_not' => [
+                  'wildcard' => [$condition->getField() => $condition->getValue()],
+                ],
+              ]
+            ];
+            break;
+

... I'd like to close that other issue in favor of this one, so I'm pasting it here so that the code isn't forgotten.

mparker17’s picture

Crediting @abrar_arshad because they proposed a patch in #3092486: LIKE filter missing

mparker17 credited sokru.

mparker17’s picture

Crediting @artemboiko because they were not credited automatically

Also crediting @sokru because they provided direction in #3092486: LIKE filter missing

mparker17 credited kevinn.

mparker17’s picture

Crediting @kevinn because they proposed a patch in #3092486: LIKE filter missing

mparker17’s picture

Assigned: Unassigned » mparker17

Assigning to myself to see if I can integrate the NOT LIKE filter, and add some tests.

Going to rebase onto the latest 8.0.x

mparker17’s picture

That seemed to work!

Although that being said, our tests (currently) only test that the queries get constructed in the way we expect, i.e.: we don't (yet) test that Elasticsearch 8 can parse them or that they return results that make sense for that kind of query.


I noticed that both the LIKE and NOT LIKE filters in #3092486: LIKE filter missing both specify wildcard queries, but the LIKE filter from this issue does not. However, I copied the NOT LIKE query construction from #3092486: LIKE filter missing for now.

We don't seem to use wildcard queries anywhere else in the 8.0.x version of this module. Our sibling project, Search API OpenSearch doesn't either (or at least, not in their 3.x branch).

I'm not exactly certain why we aren't using wildcard queries, but I daresay for now we should try to be internally consistent, i.e.: change the NOT LIKE query I just added from wildcard to something else; but I'm not particularly familiar with raw Elasticsearch query construction, so I'm not certain if — for example — changing wildcard to term would work without manual testing (which I will try shortly).

mparker17 credited karma86.

mparker17’s picture

Crediting @karma86 for the patch in #3268303: LIKE and EXACT filter for searching fulltext (8.x-7.x) (for this module's 8.x-7.x branch)

mparker17’s picture

Should probably add #3092486: LIKE filter missing as a related issue too, even though it's been closed as a duplicate of this one.

mparker17’s picture

Assigned: mparker17 » Unassigned

I grepped the code in Search API, and a number of modules providing search backends for it to see if I could find any other examples of LIKE and/or NOT LIKE filters... the only match was in search_api's search_api_db submodule (i.e.: an SQL-based search backend), which implemented LIKE and NOT LIKE by escaping any % and _ wildcard characters in the search term, then performing an SQL LIKE '%term%' query.

That says to me that end-users shouldn't be able to enter raw wildcards, meaning that we shouldn't be creating a wildcard query in our implementation. (but maybe I misread, please correct me if I am wrong). From a security perspective, I can imagine that allowing end-users to enter wildcards directly means that malicious end-users could create wildcard-filled queries to slow down the back-end, potentially causing problems for other users. But, I don't know if Elasticsearch has some built-in guardrails to prevent that.


It is also worth noting that all of our other filter term operators create term queries (which look for exact matches). However, @artemboiko's implementation for LIKE in #2 creates a match query (which fuzzy-matches the search term instead of looking for an exact match). Fuzzy-matching is not the same thing as an SQL LIKE '%term%', but I daresay that @artemboiko's implementation feels like the right solution here... it seems to me that LIKE should be more lenient than =; and NOT LIKE seems as if it should be more lenient than <>... and while we could implement LIKE and NOT LIKE exactly like search_api_db does, it seems to me like one of the reasons why someone might want to use ElasicSearch instead of SQL is because ElasticSearch has fuzzy-matching and SQL does not.

However, if you have a good counter-point, I'd be interested to hear it!


Acting on the above thoughts, I have left the LIKE implementation the way that @artemboiko wrote it, and changed the NOT LIKE implementation to avoid creating a wildcard query...

diff --git a/src/SearchAPI/Query/FilterBuilder.php b/src/SearchAPI/Query/FilterBuilder.php
index 96438b2..ed17010 100644
--- a/src/SearchAPI/Query/FilterBuilder.php
+++ b/src/SearchAPI/Query/FilterBuilder.php
@@ -190,7 +190,12 @@ class FilterBuilder {
       'NOT LIKE' => [
         'bool' => [
           'must_not' => [
-            'wildcard' => [$condition->getField() => $condition->getValue()],
+            'match' => [
+              $condition->getField() => [
+                'query' => $condition->getValue(),
+                'fuzziness' => 'AUTO',
+              ]
+            ],
           ],
         ],
       ],

I think this is ready for review now, but since I worked on the patch, I cannot RTBC it anymore. So I'll unassign myself, and leave this as "Needs review" for others to provide feedback.

Thank you in advance! (and sorry for the long comment)

sokru’s picture

Looks good to me, only thing I was wondering that we hardcode the fuzziness value here to "auto" and someone might expect to get the setting from here: https://git.drupalcode.org/project/elasticsearch_connector/-/blob/8.0.x/...

mparker17’s picture

Status: Needs review » Needs work

@sokru, good idea, I'll update the merge request!

mparker17’s picture

Status: Needs work » Needs review

Ready for re-review!

mparker17’s picture

Issue tags: +undefined

Briefly checking the status of this issue after releasing 8.0.0-alpha5...

  1. The existing tests are passing — so this merge request didn't cause any regressions
  2. The new code path has some tests, which is great
  3. The issue summary will need an update
  4. A maintainer will need to review the proposed changes
mparker17’s picture

Component: Elasticsearch Connector » Code
Issue summary: View changes
Issue tags: -undefined

I've updated the issue summary as best I can.

(going to remove one of the tags that has recently become "undefined" — I don't remember what it was though)

mparker17’s picture

Title: LIKE and EXACT filter for searching fulltext » LIKE and EXACT filter for searching fulltext (8.0.x)

(update the issue title to distinguish this from #3268303: LIKE and EXACT filter for searching fulltext (8.x-7.x))

mparker17’s picture

Re-reviewing this after a bit of time, it looks like I addressed @sokru's concern in #19. I'm also happy again with my code, so I'm rebasing onto the latest 8.0.x.

If tests pass, I'll create a branch to 9.0.x so we can review/merge there first.

mparker17’s picture

Version: 8.0.x-dev » 9.0.x-dev

Tests pass; changing version to 9.0.x then creating a branch.

mparker17 changed the visibility of the branch 9.0.x to hidden.

mparker17 changed the visibility of the branch 8.0.x to hidden.

fathershawn’s picture

MR181 is RTBC

fathershawn’s picture

Status: Needs review » Reviewed & tested by the community

Reading through MR51, it looks the same. No surprise as these two branches have not diverged much. Also RTBC

mparker17’s picture

Issue summary: View changes

Awesome, thanks for the review @fathershawn! Merging!

  • mparker17 committed 90b8c448 on 9.0.x
    feat: #3444888 LIKE and EXACT filter for searching fulltext (8.0.x)
    
    By...
mparker17’s picture

Title: LIKE and EXACT filter for searching fulltext (8.0.x) » LIKE and EXACT filter for searching fulltext (9.0.x, 8.0.x)
Version: 9.0.x-dev » 8.0.x-dev
Issue summary: View changes
Status: Reviewed & tested by the community » Patch (to be ported)

Merged to 9.0.x; will merge to 8.0.x shortly.

  • mparker17 committed 431a0bbb on 8.0.x
    feat: #3444888 LIKE and EXACT filter for searching fulltext (8.0.x)
    
    By...
mparker17’s picture

Issue summary: View changes
Status: Patch (to be ported) » Fixed

Merged to 8.0.x as well. I'll update this issue when this change is released. Thanks everyone!

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

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

mparker17’s picture

Issue summary: View changes

The changes in this issue were released in elasticsearch_connector-9.0.0-alpha3, and elasticsearch_connector-8.0.0-alpha7