While digging into #1400940: "IN" condition missing to create a filter query I discovered what appears to be a logic error in BackendTest:searchSuccess1(). The NOT IN test expects 4 results for NOT IN using 'grape' and 'apple', but looking at the sample data:
$this->entities[1] = $entity_test_storage->create(array(
'name' => 'foo bar baz foobaz föö smile' . $smiley,
'body' => 'test test case Case casE',
'type' => 'item',
'keywords' => array('Orange', 'orange', 'örange', 'Orange', $smiley),
'category' => 'item_category',
));
$this->entities[1]->save();
$this->entities[2] = $entity_test_storage->create(array(
'name' => 'foo test foobuz',
'body' => 'bar test casE',
'type' => 'item',
'keywords' => array('orange', 'apple', 'grape'),
'category' => 'item_category',
));
$this->entities[2]->save();
$this->entities[3] = $entity_test_storage->create(array(
'name' => 'bar',
'body' => 'test foobar Case',
'type' => 'item',
));
$this->entities[3]->save();
$this->entities[4] = $entity_test_storage->create(array(
'name' => 'foo baz',
'body' => 'test test test',
'type' => 'article',
'keywords' => array('apple', 'strawberry', 'grape'),
'category' => 'article_category',
'width' => '1.0',
));
$this->entities[4]->save();
$this->entities[5] = $entity_test_storage->create(array(
'name' => 'bar baz',
'body' => 'foo',
'type' => 'article',
'keywords' => array('orange', 'strawberry', 'grape', 'banana'),
'category' => 'article_category',
'width' => '2.0',
));
$this->entities[5]->save();
Only entities 1 and 3 have neither grape nor apple in their keywords.
Since this test passes for the database backend, I think there is a bug in that backend's NOT IN query logic too.
| Comment | File | Size | Author |
|---|---|---|---|
| #9 | 2697201-9--NOT_IN_multivalued--simple_solution.patch | 2.3 KB | drunken monkey |
| #9 | 2697201-9--NOT_IN_multivalued.patch | 6.62 KB | drunken monkey |
Comments
Comment #2
jhedstromConfirmed there's an issue with how the database backend handles
NOT INqueries:Since each keyword is stored in a separate row:
This query still returns results for entities that do have the values it is supposed to exclude.
Comment #3
jhedstromThis fixes just the test (which will fail for the db backend).
Comment #6
stborchertI created the test and also stumbled across this (at first sight) wrong behavior.
But if you look at the query, the resulting 4 items are totally correct. Using
LEFT OUTER JOINin combination withNOT INwill result in strange results if NULL values are involved (there are several post about this and how to avoid this, i.e. by usingNOT EXISTS).Unfortunately this will require a completely different query for the
NOT INoperator without using the join. I'm not sure, this is possible at all.Comment #7
jhedstromNot including the NULL (entity 3) may or may not work, but surely the other entities that have either of the values should not be included in the result set? Solr is returning entities 1 and 3 using a
NOT INquery. I may have some time today to look into how Views handles this (which IIRC would exclude the entities).Comment #8
jhedstromThis is how Views does a
NOT INquery:It does a join on the conditions (eg, the ones to exclude), and then filters by making sure the returned values have no rows in the joined table.
Comment #9
drunken monkeyThanks for reporting this issue!
I first had to think about this a bit, it seems to me like both interpretations could, on a technical level, be justified, so it's disputable whether our current code isn't already correct.
However, in the end I have to agree with you: your interpretation is pretty surely what most users would expect, too, so we should definitely fix this.
However, we already have this working correctly for normal
<>conditions, so all we have to do is treatNOT INthe same way – see my "simple_solution" patch above.Huh, that seems like a clever solution, avoiding the (in my estimate) more expensive nested query we're currently using.
So, I've used this opportunity to rewrite that part of the
createDbCondition()method entirely to, hopefully, make more sense, and also perform better in this scenario. (Seems we previously, in addition to the nested query, also did a JOIN we then weren't using, for negative conditions on multi-valued fields.)Please test/review!
Comment #10
jhedstromThanks! I've tested both approaches and each works. I haven't had time to run any sort of analysis on the performance implications, but since the non-simple solution is more in keeping with how Views does this, I'd say that approach should be used.
Comment #11
drunken monkeyOK, then let's use that. Thanks a lot for reviewing – and for writing the test, of course!
Committed.
Comment #13
drummCorrecting issue status, see #2698635: Issue statuses changing by themselves?.