This issue seems to relate to an incredibly old one #1105704: Exposed sorts always sort on 'top' sort, but the behaviour is similar.
I have a view with exposed sorts:
- Relevance
- Date ascending
- Date descending
- Title ascending
- Title descending
And default sorts applied:
- Relevance
- Date descending
- Another date field descending
Using the exposed sort on the Date field just does not apply to the query. Inspecting the views query, the default shows:
Index: default
Keys: NULL
Sorting: search_api_relevance DESC, field_date_published DESC, field_date_released DESC
Options: array (
'search_api_view' => 'object (Drupal\\views\\ViewExecutable)',
)
With the exposed Date (either DESC or ASC) it is exactly the same.
With the Title descending exposed sort:
Index: default
Keys: NULL
Sorting: title DESC, search_api_relevance DESC, field_date_published DESC, field_date_released DESC
Options: array (
'search_api_view' => 'object (Drupal\\views\\ViewExecutable)',
)
This is the correct behaviour.
If I remove the default Date published sort then apply the exposed sort:
Query
Index: default
Keys: NULL
Sorting: field_date_published DESC, search_api_relevance DESC, field_date_released DESC
Options: array (
'search_api_view' => 'object (Drupal\\views\\ViewExecutable)',
)
This is also the correct behaviour.
I am going to dig more into this tomorrow, but it seems like maybe the fix from the above issue doesn't quite work any more. If I debug into that function (SearchApiSort::query()) it is hit a large number of times, sometimes $this->query->orderby is not set, sometimes it is.
Comment | File | Size | Author |
---|---|---|---|
#17 | 2880239-17--fix_multiple_sorts_on_one_field.patch | 11.81 KB | drunken monkey |
|
Comments
Comment #2
acbramley CreditAttribution: acbramley at PreviousNext for Transport for NSW commentedComment #3
acbramley CreditAttribution: acbramley at PreviousNext for Transport for NSW commentedLooks like the issue stems from this if statement in Drupal\search_api\Query\Query::sort where it unsets the existing sort (which would be the exposed sort) and adds the default sort effectively reordering them back to the defaults.
Comment #4
acbramley CreditAttribution: acbramley at PreviousNext for Transport for NSW commentedHere's a failing test to show the issue.
Comment #5
acbramley CreditAttribution: acbramley at PreviousNext for Transport for NSW commentedComment #7
jibranYou are missing the 'identifier' key here.
Comment #8
acbramley CreditAttribution: acbramley at PreviousNext for Transport for NSW commentedDebugging into it, the issue is actually with the field we are adding at ViewsTest.php line 656 (the entity_test name field).
Comment #9
drunken monkeyCould you please provide a screenshot of your Views sort configuration, so I have an easier time reproducing the issue?
Comment #10
drunken monkeyNevermind, I see the problem now. Very good analysis, thanks!
After thinking about it, that code in
\Drupal\search_api\Query\Query::sort()
just seems wrong, or at least counter-intuitive. If I add a sort for a field, and later another one for the same field, the second should not delete the first, but on the contrary be ignored. At least, that's how it works in SQL queries, and also for multiple sorts on different fields: sorts specified later only take effect for those results that are considered "equal" regarding all previous sorts. Which means a second sort on the same field will never have any effect.The attached patch should fix this. Let's see whether it also fixes the (first) test fail.
Luckily, it seems we never documented the old, broken behavior anywhere, so changing this now isn't exactly an API break. Still, we should probably create a change record for it if we decide to go with this solution.
Also, since you already have the sorts set up, could you maybe check the (first)
@todo
comment in\Drupal\search_api\Plugin\views\sort\SearchApiSort::query()
? (I guess the second one can be deleted with this issue – sorry, should have done that in my patch but realized too late.) I.e., do we still need that weirdif
block or can we just make the method a simpler one-liner? Could you test whether it works the same (or maybe even better) without that block?Of course, if we can get those tests to work, that would be even better, since we could then check with them whether or not this block is needed.
Comment #12
acbramley CreditAttribution: acbramley at PreviousNext for Transport for NSW commented@drunken monkey, thanks that does seem to make sense and fix the issue for me.
As for the first @todo: That if block does get entered, $this->query->getSort() contains the default sorts from the view, so it essentially resets that to apply the sorts again. I'm not too sure if it's relevant or not, but maybe you know more about that?
I'll have another crack at fixing the failure.
Comment #13
acbramley CreditAttribution: acbramley at PreviousNext for Transport for NSW commentedI think I've found out what is causing that undefined index issue, but it's a core bug. So I've moved the sorts testing into a separate view which seems to have cleared up the issue in this module.
Comment #14
borisson_I think we can slightly improve this.
That's the only remark I have, but because it reduces complexity, I'm setting this back to needs work for it.
Comment #15
acbramley CreditAttribution: acbramley at PreviousNext for Transport for NSW commentedFixed #14
Comment #16
borisson_Looks great.
Comment #17
drunken monkeyThanks a lot, great job!
Thanks also for your review, Joris!
However, I still have a few nitpicks:
$expected_results
optional makes no sense forcheckResultsOrder()
. Likewise$arguments
, if we then never use it.checkResultsOrder()
should also clearly document it uses a different view, and it should check that really only those results are returned (i.e., also check the count).testView()
instead of adding a new test method just for them. This new test method isn't really needed at all, and unnecessarily increases test run time. However, I already know most others don't share my opinion there, so it's also fine to leave it as a separate method, I guess.Please see/review the attached revision! (Unfortunately, the interdiff is a bit chaotic due to the moved method. However, I don't think I changed anything in that method, so just ignore that part.)
Comment #18
borisson_Was easier to just go trough the patch again. Looks good.
Is the change record for this one ready? Can we have a look at that one so we publish that at the same time as pushing the commit? (That's the way core does this, not sure if we'd want to also use that standard but I guess it's a good idea)
Comment #19
drunken monkeyI was planning to write and publish it right when committing the issue, but letting you review it first of course also makes sense, sure: here is my suggestion. Any remarks?
Writing this I realized, though, that subsequent calls could, in some cases, have some meaning: if some code would later fo and remove the (first) sort again from the query, for any reason. However, that wasn't possible before either, and to fix it we'd have to change the (documented) return value of
\Drupal\search_api\Query\QueryInterface::getSorts()
, so this is a no-go anyways.Comment #20
borisson_That looks super solid! Great job.
Comment #22
drunken monkeyGood to hear, thanks again!
Committed and published the CR.
Thanks again also to acbramley for the initial patch!