Problem/Motivation
On non-simple views searches using either the search_keywords filter or argument plugins, an SQL Syntax error occurs. Non-simple searches are when more than one word is searched for (eg, 'xxxx xx'). The relevant part of the broken query:
(.data LIKE '% xxxx %' ESCAPE '\\') AND (.data LIKE '% xx %' ESCAPE '\\') ))
Note the empty table in front of .data. This appears to be due to views being unable to find a path to the search_dataset table, and thus the $search_dataset variable is empty in Drupal\search\Plugin\views\filter\Search::query():
if (!$this->searchQuery->simple()) {
$search_dataset = $this->query->addTable('search_dataset');
$conditions = $this->searchQuery->conditions();
$condition_conditions =& $conditions->conditions();
To reproduce, edit the content view that ships with core, add an exposed 'search keywords' filter, and enter xxxx xx into the search box.
Proposed resolution
Fix the views relationship data for the search_dataset table.
Remaining tasks
User interface changes
API changes
Beta phase evaluation
Comments
Comment #1
jhedstromComment #2
jhedstromComment #4
dawehnerOh wow, you work on the dark side of things :) Thank you!
As written below, it would be not a bad idea to have some actual test coverage
You want to have the node_search_dataset handlers available on node views, so this change makes sense!
Its great to have some test!
Do you think we want to have some actual test that the query results in a result we expect it to be?
Comment #5
jhodgdonHm... I don't understand the NodeViewsData part of this patch in the context of the existing stuff in NodeViewsData. NodeViewsData is currently doing the following:
- Setting up an auto-join between node_search_index (an alias of search_index) to node_field_data.
- Setting up an auto-join between node_search_total and node_search_index
- Setting up an auto-join between node_search_dataset (an alias of search_dataset) to node_search_index
==> the patch modifies this join as follows:
The Search Keywords filter/argument are set up on the "node_search_index" table. So, I see why the modifications in the filter/argument plugins are needed:
because the table to be added is definitely node_search_dataset, not search_dataset. But it should be fine for it to join to the node_search_index table as it was, because this is the table that is already part of the view if you are using the keywords filter?
Comment #6
jhodgdonI also do not understand why the SearchIntegrationTest in views/src/Tests didn't catch this? Oh, yeah, it's because of the keywords being entered. So, maybe we should add to that test as well, instead of or in addition to the tests here? Things we should probably be testing:
- And/Or/Multiple:
a)
pizza OR sandwichb)
pizza sandwich OR colac) code>cola good
- Phrases:
d)
"cola is good"To do these, we should add another node to the test with title "cola is good" to the "pizza" and "sandwich" nodes in the current test.
Then:
a ==> returns pizza, sandwich nodes
b ==> returns all three
c) ==> returns cola
d) ==> returns cola
OK, here's a patch. Something like this anyway...
Comment #8
jhedstromThis change was needed, because even though
$this->ensureMyTable()addsnode_search_indexto the table queue, theensurePathcall for thenode_search_datasetfails to find a path to the table, since the base table of the query isnode_field_data.Comment #9
jhedstromI tried the tests from #7 with the fix, and there are many fewer fails. The 5 assertions that do fail revolve around the arguments with
ORin them.Comment #10
jhodgdonHm on #9... perhaps there is another bug we need to investigate then, or maybe the test is failing in some other way. I don't have time to look into it today, sorry. What I would do to investigate is try a view with a search query/filter and try an OR query there and see if it works right. Perhaps the test has a bug; perhaps the code. Unsure which.
Regarding #8, I thought Views could do a join from table B to table C even if table A is the base table of the View? It must know how to do that in some cases, but maybe it has to be a relationship rather than an auto-join for that to work. Anyway. Onwards!
Comment #11
jhedstromHere's current behavior (with the patch in #2).
AND( (node_search_index.word = 'pizza') OR (node_search_index.word = 'cola') ))AND (node_search_dataset.data LIKE '% pizza %' ESCAPE '\\') AND (node_search_dataset.data LIKE '% co %' ESCAPE '\\')AND( (node_search_index.word = 'pizza') OR (node_search_index.word = 'cola') )AND( (node_search_index.word = 'pizza') OR (node_search_index.word = 'cola') ))AND (node_search_dataset.data LIKE '% pizza cola %' ESCAPE '\\') ))The only conditions that trigger the sql syntax error (eg, without the patch in #2) appear to be 3LETTER-WORD-OR-MORE + ONE-OR-TWO-LETTER-WORD:
xxx xorxxx xx, or quoted strings:"xxxx xxxxx".Comment #12
jhodgdonOK. I looked into this some more.
The logic in the Search filter/argument plugins for Views was flawed. It was basically saying "If this is a simple query, do it my way instead of how Search does it by default". But the queries didn't actually work, so anything but one keyword wasn't really working right. So I took this out of these two plugins: now it just defers to the SearchQuery class to figure out what the query should be, no matter what.
Then I was getting duplicates in the results. This turned out to be a problem in how the search score was added to the views query. So I fixed that, and also updated the documentation on the Views Sql query class for addField so hopefully no one else will need to make that mistake.
Then the test was also not right. It was basically double-url-encoding the argument page URLs. So I fixed that. Also added to the test so that it makes sure there is only 1 result for each node and no dups.
So... now it is working. Attached is a patch, containing the code fixes and my test additions. There is also an interdiff, containing changes from both my test patch in #6 and the code patch in #2. The tests from #2 are not included... wasn't sure we needed them? If we do they are easy to add back, but I think the new test coverage is sufficient. I also made a patch with just my test additions so we can look again and verify the tests fail without the code changes.
Whew!
Comment #14
jhedstromThis is at NR. Test was expected to fail.
Here's my table from #11 with the patch from #12:
AND (node_search_dataset.data LIKE '% pizza %' ESCAPE '\\') AND (node_search_dataset.data LIKE '% cola %' ESCAPE '\\')AND (node_search_dataset.data LIKE '% pizza %' ESCAPE '\\') AND (node_search_dataset.data LIKE '% co %' ESCAPE '\\')AND (node_search_dataset.data LIKE '% pizza %' ESCAPE '\\') AND (node_search_dataset.data LIKE '% cola %' ESCAPE '\\')AND( (node_search_dataset.data LIKE '% pizza %' ESCAPE '\\') OR (node_search_dataset.data LIKE '% cola %' ESCAPE '\\')AND (node_search_dataset.data LIKE '% pizza cola %' ESCAPE '\\') )This fix resolves the originally reported issue, and then some!
Comment #15
jhedstromHowever, when using the argument instead of the filter:
pizza+OR+colaresults in
AND (node_search_dataset.data LIKE '% pizza %' ESCAPE '\\') AND (node_search_dataset.data LIKE '% or %' ESCAPE '\\') AND (node_search_dataset.data LIKE '% cola %' ESCAPE '\\')Comment #16
jhodgdonHm. The test does:
and the correct search results are returned. So it was testing the argument... and the word "or" is not part of any of the nodes.
Are you sure about what you were testing, and are you sure you had the code patch applied?
Comment #17
jhedstromVery odd. I do have the patch in place, but am just testing directly through the views ui where it displays the query.
Comment #18
jhodgdonLet me take a look...
Hm. On my test site from yesterday, which is where I developed the patch in #12, in the Views preview for the Arg page on that test view, when I do
pizza OR cola
I get this WHERE clause in the view:
This is correct (and working)... not sure where you're getting the other one?
I also got this when I edited the Filter page's search keywords filter and put "pizza OR cola" in that field (without the quotes).
Comment #19
jhedstromMy mistake--I hadn't capitalized the 'OR' in my testing of the arguments.
Comment #20
jhedstromThis does indeed resolve the issue.
I've added a beta phase evaluation to the issue summary.
Comment #21
alexpottCommitted bc23d01 and pushed to 8.0.x. Thanks!
This is deprecated - need to use SafeMarkup::format() - fixed on commit.