I am trying to use EntityQuery to select what nodes are matching a specific condition, in my query, I want to retrieve nodes what have a taxonomy term in a vocabulary A and a specific term in a Vocabulary B, below you can see content type definition of the field as entity reference to two different taxonomies.
This is the query I am trying to do
$query = $this->entityQuery->get('node');
$query->condition('status', 1);
$query->condition('type', 'study_program');
$query->condition('field_location.entity.tid', $location);
$query->condition('field_study_type.entity.tid', $study);
$results = $query->execute();
After validating my data I was totally sure that I have nodes that match with the condition, but I always get 0 results. using the module Webprofiler part of devel module, I found the query executed was wrong.
SELECT base_table.vid AS vid, base_table.nid AS nid
FROM node base_table
INNER JOIN node_field_data node_field_data ON node_field_data.nid = base_table.nid
INNER JOIN node__field_location node__field_location ON node__field_location.entity_id = base_table.nid
LEFT OUTER JOIN taxonomy_term_data taxonomy_term_data ON taxonomy_term_data.tid = node__field_location.field_location_target_id INNER JOIN taxonomy_term_field_data taxonomy_term_field_data ON taxonomy_term_field_data.tid = taxonomy_term_data.tid
INNER JOIN node__field_study_type node__field_study_type ON node__field_study_type.entity_id = base_table.nid
LEFT OUTER JOIN taxonomy_term_data taxonomy_term_data_2 ON taxonomy_term_data_2.tid = node__field_study_type.field_study_type_target_id
WHERE
(node_field_data.status = '1') AND
(node_field_data.type = 'study_program') AND
(taxonomy_term_field_data.tid = '14') AND
(taxonomy_term_field_data.tid = '28')
Inner joins and left joins are ok but the last condition is using the wrong table alias taxonomy_term_data instead of taxonomy_term_data_2
Comment | File | Size | Author |
---|---|---|---|
#30 | entityquery_wrong_sql-2759757-30.patch | 6.96 KB | gambry |
#30 | interdiff-28-30.txt | 2.34 KB | gambry |
Comments
Comment #2
TimRutherford CreditAttribution: TimRutherford at Acro Commerce commentedCan confirm this is an issue.
Looks like its an issue with the second term reference not creating a join with a second alias to the taxonomy_term_field_data table.
After testing it locally, the query worked when adding
INNER JOIN taxonomy_term_field_data taxonomy_term_field_data_2 ON taxonomy_term_field_data_2.tid = taxonomy_term_data_2.tid
and changing the second WHERE clause to taxonomy_term_field_data_2Looks like its probably coming from /web/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php in the addField() function.
Unfortunately, I haven't done much in D8 so I didnt get much further then that.
If someone with more expertise in this area could chime in, that would be great!
Comment #3
dawehnerLet's in general plan to make this method easier to read, I think that would help everyone: #2766745: Refactor \Drupal\Core\Entity\Query\Sql\Tables::addField to be better readable
I wrote a testcase for it, which shows an alternative syntax which actually works at the moment.
Comment #6
gambryThe issue seems to be on how \Drupal\Core\Entity\Query\Sql\Tables::ensureEntityTable() checks if the current table already exists on
$this->entityTables
array.The method use the
$table
value, which doesn't change if the same tables is joined (and aliased) multiple times.Changing occurrencies of
$this->entityTables[$index_prefix . $table]
with$this->entityTables[$index_prefix . $base_table]
fix the problem.However I'm not confident with this class, so I'm not sure if this is the best way of addressing the problem.
I'll run @dawehner tests + additional manual test to understand if it's at least a definitive fix, and if it is I'll provide a patch. In the meanwhile it would be good to have thoughts anyway.
Comment #7
gambry@dawehner I'm not sure what you mean, as your test - correctly - fails on my local env (+ throw an exception).
I'm pulling down your testcase, fixing the exception, and adding my changes.
Works locally, but I've ran only EntityQuery related tests. Let's see what testbot says.
Comment #9
gambryDrupal\KernelTests\Core\Entity::ensureEntityTable()
$base_table
can be either the aliased table name or 'base_table', which will be replaced with the right table later on in the process.This patch adds the check for the condition above and uses the $base_table instead of $table only if it is an alias.
If test is green (is green locally, but due my configuration I have to skip BrowserTests and The ones using sqlite) this change will probably need database manager review.
Comment #12
gambryWe can't overwrite
$table
, it's needed in the in the$this->addJoin()
second parameter. Handling the processed table name in a new variable.Comment #13
gambryLet's add a meaningful comment to the new test.
Tagging 'Needs subsystem maintainer review' as I'm not sure whether this is the best way to address the issue.
I have concerns also about the condition
$base != 'base_table'
and if there are other reserved table placeholders we would need to consider (for example I'm looking at\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema\getEntitySchemaTables()
keys).Comment #14
gambryComment #15
claudiu.cristeaThis is bug so it should be filled against 8.3.x.
What if we use only $base_table in the key. Basically this is only the static cache key. Let's see.
Added back the docs from #3, and made the test more compact.
I don't think this needs subsystem maintainer review. It's just a bug.
Comment #16
gambry@claudiu.cristea patch at #7 uses $base_table instead of $table as key, and it fails due 'base_table' placeholder used instead of the real name.
I've also remove additional docs from #3 due being out of issue topic, but happy if we include those!
The call for a maintainer was due the placeholder, as I don't know if there are othe possibilities other than 'base_table'.
But let's see testbot response!
Comment #18
claudiu.cristeaYour approach is correct.
Comment #19
claudiu.cristea@gambry
I think it's in the scope because we need to understand what we store in
$this->entityTables
.Also, in the test, the main entity MUST refer 2 distinct entities, as in the original patch, otherwise the "test only" patch will not fail. This patch restores the references to distinct entities.
Tidied-up a little bit the test.
Comment #21
gambryThanks for tidying up everything, looks really good now.
Just two questions:
Do you think it's the case we explain what's happening in here?
I see, and it does make sense. So shouldn't we - for the same reason - tidy up the docblock of ensureEntityTable()?
Comment #22
claudiu.cristeaComment #23
gambryWhy not documenting all the parameters?
Also there was a missing period on the addJoin() documentation.
Comment #24
gambryComment #25
claudiu.cristea+1 for RTBC... but I cannot do it because I've contributed with code :)
Comment #26
richard.c.allen2386 CreditAttribution: richard.c.allen2386 at Bixal commentedFYI I've applied this patch and it also seems to have fixed my issue while creating or groups. If folks didn't realize this was ALSO creating issues there. Patched worked great. Below is an example of what was breaking:
Thanks for the patch!
Comment #27
heldercor CreditAttribution: heldercor at morfose, lda for Azores Tourism Association commentedIt's failing to patch on 8.3.x for me, not sure why yet:
Comment #28
heldercor CreditAttribution: heldercor at morfose, lda for Azores Tourism Association commentedJust fixed an
array()
to short syntax inaddJoin()
(line 78 of the patch), which was causing failure to apply.Comment #29
joachim CreditAttribution: joachim as a volunteer commentedJust nitpicks:
Would help to say what the valid values are, or refer to some other docs that say that.
missing 'the'.
'for by' - missing word.
For maintainability, it would be nice to say here why we're doing this. Something like 'This ensures that if we join to the same entity table several times for different entity reference fields, each join gets a separate alias.'
Comment #30
gambryThis patch addresses feedback from #29.
I've also changed
\Drupal\Core\Entity\Query\Sql\Tables::$entityTables
doc to reflect changes to the array keys done fromensureEntityTable()
.Comment #32
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThis patch makes very much sense, I could only find minor documentation issues that could be fixed on commit:
Minor nitpick: return -> returns.
The join type...
The comment seems to wrap a bit early, I think 'the' could stay on the first line.
Tests that...
P.S. Note that there's a test-only patch in #19 which proves that this is a real problem.
Comment #35
catchCommitted/pushed to 8.5.x and cherry-picked to 8.4.x, thanks!
Made the docs fixes on commit.