Problem/Motivation
When creating a normal search view with just an exposed "Search: Fulltext search" filter (like the default search view we provide), results (all indexed items) will be listed even when no keywords are specified, which might be confusing to visitors since it's not how searches normally work.
If you therefore want to set the filter to "Required", though, you run into two problems, which we should try to fix in this issue:
- When a visitor comes to the search page at first, there will already be an error message and the filter will have a red border, since it has no value. While this is a Views problem in general, it would be nice to be able to resolve it for this filter in particular, since a lot of people will want that "Required".
- Furthermore, it appears that, with specific setups, this error message actually leads to a session cookie being set, which breaks caching in lots of ways.
Proposed resolution
- Make the "Required" setting for the fulltext filter (no other filter was changed yet) fail without an error message.
- Set the filter to "Required" in our default view, and also sets its exposed form to "Input required".
- Add tests verifying both the filter's behavior and the settings in the default view. (Since the setting of the session cookie obviously has some further dependencies, this is currently not tested. If setting a message leads to the cookie (sometimes), verifying that there is no message should be good enough.)
Remaining tasks
Test and review the patch, both regarding the session cookie problem and the error message UX problem.
User interface changes
No (empty) error message when first visiting such a search view.
API changes
None.
Data model changes
None.
Original report by dakku
It seems that a cookie gets set when:
- solr backend is used
- there is exposed filter
- field exposed is Search: Fulltext
I tested the above with non solr backend (database search) and no cookie gets set.
Can anyone confirm this?
Comments
Comment #2
dakku CreditAttribution: dakku as a volunteer commentedAdditionally, un-exposing the Fulltext filter results in this error:
Comment #3
dakku CreditAttribution: dakku as a volunteer commentedhere is the example cookie that gets set..
Comment #4
dakku CreditAttribution: dakku as a volunteer commentedand here is what my view looks like.
Comment #5
eporama CreditAttribution: eporama at Acquia commentedI wonder if this could be from
src/ParamConverter/SearchApiConverter.php
Comment #6
mkalkbrennerThe fatal error you mentioned in #2 is already fixed in search_api_solr 8.x-1.x-dev.
The cookie is obviously a standard session cookie. I don't think that the solr backend forces opening a session. Therefore I move this issue to the Search API queue.
Comment #7
dakku CreditAttribution: dakku as a volunteer commentedThanks Erik and Markus.
I will try the latest dev version of search_api. And it looks like an xdebug session tomorrow can confirm if the session is indeed coming from the above line.
Comment #8
Wim LeersI also looked into this, together with Acquia colleague @wouter.adem.
The cookie is set because the exposed filter form (a
GET
form) is automatically submitted (because it is aGET
form). Then, during form validation, this runs:Why does this validation logic run? Because the exposed filter is required by default. And if you make it un-required, the error cited in #2 happens.
So… the problem is somewhere in the fact that a Views-powered Search API block is making a search string required, which makes no sense — because searching is an optional activity on any site.
Then if you make the search optional, the validation doesn't happen, hence the error doesn't occur, hence the cookie doesn't get set … but because there is no validation error, the view is executed, which in this case means the search is executed (and note that the search was only not executed previously because of the form validation error) … and that is the real bug here, which was being masked.
Tricky stuff!
Comment #9
Wim LeersComment #10
Wim LeersComment #11
kylebrowning CreditAttribution: kylebrowning commentedUpdated title a little bit.
This is definitely worth the critical because it 100% breaks varnish.
Comment #12
Wim LeersCapturing the exact root cause described in #8 in the title.
Comment #13
geerlingguy CreditAttribution: geerlingguy at Acquia commented@kylebrowning - If I'm reading this correctly, it would also break all of Drupal's page-level caches (Dynamic page cache, Page cache) in addition to any upstream caches (Varnish, Fastly, etc.).
If it were just upstream caches, I don't think that'd be enough to warrant a critical.
Comment #14
borisson_I think this is what's going on? Is what I'm doing in the test correct? Because if it is, there might be something going on, there's no errors now.
It is however 1000% possible that I'm doing something wrong in the test.
Comment #15
borisson_Improved comments in test + use BTB methods. Still no clue why the test is not failing.
Comment #16
drunken monkeySorry, but I'm gonna need much more information than that to do anything here.
First off, @ dakku, I can't reproduce your problem, sorry. (Which is supported by Joris' test not failing, I'd say.) When I set the "Fulltext search" filter to be "Required" (see attached view export), it does give me a validation error on first going to the page (as expected). However, it doesn't set any kind of cookie – neither with the DB backend nor with Solr. So that needs further explanation/investigation, in my opinion. E.g., what other modules are you using that might be involved here? Are you using normal Solr, or with the Acquia Search module?
If just setting a form error breaks caching, and there needn't be a cookie involved, the issue summary (or at least any of the comments) should reflect this.
Then, @ Wim: nothing in #8 describes a bug, as far as I can see.
When you set the filter to "Required", we validate that it's actually there. Yes – what else? If you don't want that, don't set the filter to "Required", or set a default value.
When you don't set it to "Required" then the view will get executed even if it's not there. Once again: what else should we do?
All of this is not a bug, it's completely normal Views behavior. If you do this with a standard view, it will be exactly the same. The problem is not the code, it's your configuration.
For avoiding both these problems, Views already has a solution: the "Input required" exposed form style. Just use that, it should do exactly what you want (though, of course, it also has some problems). But that's got nothing to do with this module. (One reasonable feature request here would be to make the default search view included in the DB Defaults module use that exposed form style – might make sense. But hardly a critical bug report.)
So, please, explain again how this is any kind of bug, or I'll have to mark this as "works as expected".
If, of course, we find out that Search API (or Solr) code is indeed to blame for setting a cookie in some such cases, that would indeed be a bug to be investigated – that definitely shouldn't happen. But, as said, I can't reproduce that part.
No, that's only active on a few admin pages – if you get to that code, you definitely have some session cookies anyways.
Since the issue reporter clearly states it only occurs in combination with the Solr backend, and that they've tested that, it does seem somehow dependent on it, though maybe indirectly. I'd also not be aware how the Search API would set any session cookie, so that's not really an argument.
That being said, though, I can reproduce this with neither DB nor Solr backend, so we might as well discuss this here. For me, it currently just seems like a misconfigured view.
Comment #17
Wim Leers#13: I could not disagree more.
#16:
It's not Search API that sets a cookie. A form validation error calls
drupal_set_message()
which sets$_SESSION['messages']
which triggers a session cookie.Suggested solution: Search API should make it impossible to make such an exposed filter required.
Setting to required is a no-go as per the above, because that triggers a validation error. How can you set a default value to not trigger a search? Why is this always executed? If there's nothing to search, then Search API should not search (or perhaps return the empty result set?).
I don't know anything about Search API's architecture/execution model, nor do I know enough about Views. But what I do know, is that this configuration is extremely misleading. So I can find myself in:
Especially that second quote. It is completely beyond me why you would ever want to release a 1.0 stable with such a massive usability problem. Because it'll cause many support requests. And once a site uses a Search API-powered view, it's in configuration, and updating configuration retroactively may be impossible.
I urge those who are working on sites with exactly this problem to report more details ASAP. It surprises me that that hasn't already happened. Without further information from you, the module maintainers can't help.
Comment #18
Wim LeersFinally, to be clear: I wholly defer to the maintainers of this module to determine whether should block the long-awaited 1.0 stable release or not.
Again, I don't know the code well enough. I just know the reported behavior will be a showstopper for many sites: I marked this critical for that reported behavior, which as far as I can tell has its origin in Search API's Views integration. No matter if Views is to blame or not, people will come and report this here, especially if it's a default, which it indeed seems to be:
modules/search_api_db/search_api_db_defaults/config/optional/views.view.search_content.yml
.Comment #19
drunken monkeyNot when I tested it. (Nor for the test bot, as you can see above.) As far as I know,
$_SESSION['messages']
is removed again if the message is displayed in the same page request (seedrupal_get_messages()
). It's only used to "remember" messages if they can't be displayed right away.Of course, this might still break caching, I guess, maybe in conjunction with BigPipe, or some other setups. You obviously know more about caching than I do. I'm not gonna argue against this being a problem for caching, and it's certainly clear that having a page where the user sees an error by default is a bad idea.
Whatever the consequence, though, I maintain that this is just a configuration problem. It might be confusing to you, but it's exactly like other views work, too. So I daresay that changing this in any major way (maybe even breaking existing views) would confuse a lot more users than this currently does.
The fact that we already have 12k (reported) installations in D8, with no-one complaining about this until now, supports this claim, I think.
I'm certainly not happy with all aspects of how Views work, and this specifically also doesn't look very sensible, but it's been like this for a decade or so, I guess, and I'm certainly not gonna change it just in this one module.
Making the "Required" checkbox function the same basically, but maybe not setting a status message when it triggers, might be an improvement to look into. Or maybe having a separate option that does that, or modifies "Required" that way – we can certainly discuss that, and would have to take a closer look.
But that's just a feature request, possibly major, but hardly a "critical bug". As said, Views has been working like this for around a decade, and the same goes for the Search API Views integration since it started working almost seven years ago. This shouldn't block a stable release a lot of people are waiting for.
@ Joris: What's your opinion on the switch of the "Exposed form style" to "Input required"? I guess that does make sense, but would you agree with Wim that this is important enough to go in before 1.0? As you know, I'd rather release ASAP. (Same for my suggestion in the above paragraph, though that would take a bit longer to implement, so should be considered even more carefully.)
Apart from anything else, though, the test fails in #15 regarding 8.2 are very good to know: #2870782: Fix tests for Drupal 8.2.
Comment #20
wouter.adem CreditAttribution: wouter.adem commentedThese are the steps so the error can be reproduced locally, plus some files and screenshots.
Some general info about Drupal core and contributed modules that were used.
The config file views.view.search contains with the following relevant extracts:
As you can see the fulltext search is set to required.
Steps:
COOKIES
(expect my XDebug session) using Developer Tools.cache_render
andcache_dynamic_page_cache
tablescurl -s -D
Output:
Some debugging info:
Checking the
$_SESSION
andform_state
in XDebug:FormState.php
and looking into the form_state variableFinishResponseSubscriber.php
and looking into the$S_SESSION
Gives the following info:
Then given the comment from
bootstrap.inc
, this explains why the session cookie is set.I hope given the info from above this could help.
Wouter
Comment #21
borisson_For me, this is a showstopper. If Wim, with all his drupal-knowledge is confused with the current behavior, what are us normal humans supposed to do?
I know we'd like to release sooner rather than later, but I'd very much love to see this bug squashed before release. Not sure if changing the default will help, I'd try to to take a look at:
\Drupal\search_api\Plugin\views\filter\SearchApiFulltext::validateExposed
to see if we can resolve the error there.@Wim: I don't see the error in the test I uploaded in #15, is there anything we're missing there to help reproduce the bug? Maybe we can change the behavior there from setting the validation error to just returning?
In any case. I'm convinced that we need to write a test that fails before and passes after the fix is applied.
Comment #22
Wim LeersYou're giving too much credit to me ;)
I'm heavily time-constrained this week, so I'm concerned about holding up this issue and that possibly being unnecessary.
@wouter.adem: Why is the field set to required? The default search view doesn't have the exposed filter set to required. But if you make it optional, there's an error deep in Search API. Can you make it optional, and then post a comment describing the consequences? I think that will e very helpful.
Comment #23
wouter.adem CreditAttribution: wouter.adem commentedSo when:
curl -s -D
Then I get the following output:
On first curl request:
Next curl request:
with error trace:
Let me know if an XDebug output could help here.
Actually, this seems to be a problem of not being able to show any results, so this is probably not related.
Also, had to update a file in Lightning "drupal/lightning" so it uses search_api_time.
Will test this on a stable environment and let the outcome know.
Comment #24
drunken monkeyHe himself said, "I don't know […] enough about Views." While I wouldn't doubt his prowess regarding, e.g., development in the caching system, it's perfectly possible that he doesn't have much experience creating views. (It's similar for me: while I think it's safe to say I'm an expert for programming search functionality, give me a lot of pretty basic site builder tasks and I'd be stumped.)
And as, again, no-one else has really complained about this before, at least definitely not in as strong a way, I'd say it's likely that most people have not such a problem with this. Especially: We had this running like this in Drupal 7 for ages, with currently 80k reported installations, and no major complaints about this. So, improving usability: definitely, why not? But holding up the stable release for it and having to implement and test this under a lot of time pressure: I'd vote against that.
Anyways, attached is a patch that should implement all this:
If a few people here could test this and confirm that this has a better UX, and also solves the cookie problem for them, that would be awesome! If we get a few reviews quick enough, then maybe we can commit this before the stable release after all. But usually, UI changes and feature requests are a big no-go during RC phase, for pretty good reasons. So maybe we should then at least do another RC release before stable.
In any case, this currently still has a problem, in my opinion, namely that it now doesn't communicate the required search keywords at all. (Though the empty message we got previously was also not optimal, of course.) This is mitigated if you use the "Input required" exposed form style, since that will enable you to display a message, but if you use "Basic", or the user just submits the form without entering any keywords (as we know, "Input required" isn't smart enough to detect that case), you get a page with just the form and no results, without any indication what's going on. (Or, probably even worse, the "No results behavior" message, telling you there were no results for your search.)
So maybe we should separate the cases of "no input yet" and "input is there, but empty" and still set a form error in the latter case. Would that be OK, in your opinion?
@ Wim:
I thought having this "Required" is the whole point of this issue? Now I'm really confused. What "error deep in Search API" is there if you don't have it "Required"?
Comment #25
drunken monkey@ #23: Yes, that looks like an entirely different issue, at least as far as I understand. But, as said above, I thought this issue was about "Required" being enabled and making problems in the first place.
For the issue you describe in #23, see #2871030: Cached Search API results in Views are NULL.
Comment #26
drunken monkeyComment #27
drunken monkeyOh, forgot the tests-only patch in #24.
Comment #28
wouter.adem CreditAttribution: wouter.adem commentedMaybe there is also some mis-understanding which is totally my fault because I dragged Wim into this and he doesn't have the full background.
Wim mentioned:
This is not true (I think), but we had to set it to required since that broke our site because of an error related to search_api_solr.
Updating to search_api_solr: 1.0-beta2 did solve that locally. So that's something that Wim was not aware off and what he saw, was the required field being checked in our view assuming it was set to required per default.
After updating that module we could avoid setting it to required, as expected.
But the error in #23 occored and (see #2871030: Cached Search API results in Views are NULL) does not solve this.
Comment #30
drunken monkeyYeah, you're right, that part is definitely wrong. (Same for the sentence immediately following it.) This issue is getting pretty chaotic, I think, with multiple different problems discussed, some bugs, some UX issues, and most of them unrelated to the original issue reported.
I've edited the issue summary to reflect the problem that I think is being discussed here, and which my patch attempts to solve. If there are other problems (except #2871030: Cached Search API results in Views are NULL – please comment there if you tested that patch), we should probably open new issues for those, with clear descriptions.
Comment #31
Wim Leers#24:
Exactly. Nobody knows all the things in Drupal.
+1 — that's what I said (or tried to) in my last comment.
Yay!
#28:
Great!
#30:
Agreed.
Patch review:
This makes sense!
Also makes sense!
Does not make sense. Why is this not simply plain text?
I looked at
\Drupal\views\Plugin\views\exposed_form\InputRequired
to understand why — apparently this is tightly coupled to formatted text for some reason… I wonder why.Oh… apparently this text format is not applied to the required input, but:
Wow this is confusingly named, but of course the fault of Views, not Search API!
This suggests that this default view was created years ago, long before the D8 release. So it's been wrong all this time, which might have caused edge case-y errors itself. Glad to see this resolved :)
Excellent!
Where is this custom validation? Can we
@see
it?Hm, does this comment really match the logic here?
If there is no input, and the "required" config option is set to "true", then we abort the query, followed by an early return.
We abort the query, because the query is what would cause an error, because there are no keywords to search? And without this explicit abortion, the return statement below would cause the validation of the input to succeed, and then during the query execution we'd get an error?
I think that's what's happening?
Perhaps we can get a slightly longer comment explaining this? This is not obvious to me — but if it's obvious to the Search API maintainers, then please ignore this request for a more detailed comment :)
Excellent!
Comment #32
drunken monkeyThanks for your detailed comment! Good to see the issue getting back on track. Now I'd just like Joris' OK for releasing 1.0 – if he still thinks this is too much of a usability problem than I guess we should still try to fix it.
The attached addresses your comments 6 and 7, and also fixes a problem I just discovered that the "Required" validation was dependent on a "Minimum keyword length" being said – hardly ideal. (Such things are exactly why I'd need several reviews and tests before feeling confident in committing this between the last RC and the first stable release.)
No, not at all. The Search API has no problem executing a search without keywords (or even without any other filters/conditions). A lot of people, e.g., use it to replace admin listings (like
admin/content
), which of course shouldn't require keywords. Maybe this is where part of your confusion originated?We abort the query just so it doesn't return any results. Without this
abort()
, it would just return all items (except for those excluded by other filters, of course) – which is not what people want when they set a filter to "Required".I think the comment should have been clear enough for most people familiar with Search API development, but I guess there's no harm in being more verbose. Is the new phrasing better?
Comment #33
Wim LeersI expected to see:
Thanks for the explanation in #32, much clearer now! Comment is a big improvement, but I think this would be even clearer:
I propose:
This
< 2
condition seems like an unnecessarily confusing. Surely,min_length
can never be 0 or 1, because those minimum lengths simply don't make sense? So surely, we validate those numbers when the user is configuring this through the UI?I had to read all the subsequent code to understand this!
Improving this is probably out of scope though.
Comment #34
borisson_I agree with everything #33 said except .4. That is definitly out of scope if we'd want to fix that.
I think we should commit this and release 1.0 after that.
Comment #35
Wim Leers#34: agreed with #33.4 being out of scope. I already said that, actually :)
I have no strong opinion whether this should be committed before 1.0. Although the fact that this is changing
modules/search_api_db/search_api_db_defaults/config/optional/views.view.search_content.yml
is a strong indication that this is preferable: otherwise all sites that start using Search API because it reached 1.0 will start with incorrect default configuration!Furthermore, the changes here seem very low-risk to me, and come with test coverage.
As far as I'm concerned, this is RTBC. #33 only has comment nitpicks. So, being bold here in order to signal to @drunken monkey that this is ready.
Comment #36
wouter.adem CreditAttribution: wouter.adem commented@drunken monkey Related to the patch in #32 and to confirm that it solves the following tasks:
Thanks, this was really useful to follow.
Wouter
Comment #37
borisson_Ah, I disagreed with the initial point and agreed with it being out of scope.
Communication over text is hard. :-)
Thanks for coming back here and confirming that the patch works @wouter.adem.
Comment #39
drunken monkeySince
@see
doesn't work in inline comments, I usually don't use it there. (Also, the correct syntax is debated – according to the current standards, I think we'd have to include the FQN of the class, too.) Also,validateExposed()
is just the next method.Anyways, since Joris agreed, I've now applied all your suggestions. Thanks again for the detailed review!
And if you also, more or less strongly, agree that this should go in before stable, then let's do that!
However, I think I'm still gonna create a last RC for a few days before finally making the module stable. Would be really good to have more than one person verifying that this works well for them.
So: committed. Thanks again, everyone!
Comment #40
wouter.adem CreditAttribution: wouter.adem commented@drunken monkey Sorry, I was a little to fast when confirming that all code from the patch worked.
cfr. #31.7 the comments from Wim
This raises a fatal error:
Fatal error: Call to a member function abort() on null in modules/contrib/search_api/src/Plugin/views/filter/SearchApiFulltext.php on line 237
Comment #41
holist CreditAttribution: holist at Siili Solutions commentedI also got fatal error on all pages after updating, because there is a site search box in the header. Looking at what the error comes from though, I tweaked the config yaml to change the "required" settting to false, and after that no more error. However it would be useful to be able to set the search text field as required...
Comment #42
wouter.adem CreditAttribution: wouter.adem commentedComment #43
Wim LeersGood thing @drunken monkey did another RC!
Comment #44
wouter.adem CreditAttribution: wouter.adem commentedFor what it's worth I can tell that on line 272 in
search_api/src/Plugin/views/filter/SearchApiFulltext.php
the early return causes the$query
not being set cfr.This causes the fatal error on the
$this-getQuery()
method, which returnsNULL
cfr.We probably need to set 'some' query object so we can abort properly.
Comment #45
borisson_Instead of setting some query object, we should probably do
if ($this->getQuery === NULL) { return; }
at the top of the validation method? No query object means no query should be aborted because no query will be executed.I think, not sure. Letting @drunken monkey confirm.
Comment #46
drunken monkeyWoah, yeah, thank god for that … ! I know why I didn't want to commit this this late in the process …
Anyways, Joris is right, we just shouldn't abort the query when there is none. Just didn't realize this was possible – but apparently, this is the case when the exposed form for a page display is displayed in a block (potentially on other pages on the site). Man, Views is fucked up …
Please see/test/review the attached, simple patch!
After this, I really need a vacation.
@ wouter.adem/#44: Please stop making suggestions!
Comment #47
Wim Leers:P
:) I'll let @borisson_ review this, that's probably safer.
@wouter.adem is just trying to help. He was himself very much doubting whether to post this. I encouraged him to post it anyway, in case it might help. In my experience, those who don't know a particular piece of code inside-out often come up with suggestions/ideas that end up not being the solution, but do make the solution better.
Comment #48
borisson_I looked at the patch and that does exactly what I figured it should do, great work @drunken monkey.
Thanks for the suggestion @wouter.adem: you pinpointed the problem perfectly! The suggestion was also good and I really appreciate it, it allowed us to come up with the solution that ended up in the patch, great work!
Comment #49
drunken monkeyYou're right, of course, I shouldn't have written that. I apologize, Wouter!
This issue just has me pretty annoyed by now (as evidenced by the recent influx of expletives into my comments), but of coruse I understand that people with incomplete knowledge of the code base can still come up with good suggestions and remarks, and should be encouraged to try, not deterred.
So, again, I'm sorry! It won't happen again.
Anyways, back to the patch, it's good to hear you think it's fine like this, but I'm shocked none of you tagged this "Needs tests"! :P I went ahead anyways, the attached should do it. (Interdiff == tests-only.)
Comment #50
drunken monkeyComment #53
borisson_Test looks sufficient.
Comment #55
drunken monkeyGood to hear, thanks for reviewing again!
Committed. Onwards, to RC 4!
Thanks again, everyone!
Comment #56
Wim Leers#49:
Thank you for taking a step back and apologizing :) It again shows how constructive and well-intentioned this community is! <3
Also: you already added tests! :D That's why I didn't add the
issue tag! You're right that we should've asked for a failing test-only patch though!Glad to see this resolved!
Comment #57
drunken monkeyAdded #2872280: Set error message when required fulltext filter is submitted empty as a follow-up.
Comment #58
drunken monkeyHm, also, I guess a back-port to D7 wouldn't hurt?