Problem/Motivation
TaxonomyIndexDepthQueryTrait::addSubQueryJoin() builds one UNION branch for each configured depth level. Each branch adds another join to taxonomy_term__parent, and the pager count query executes the subquery a second time.
Query cost therefore grows with the configured depth, even when the actual taxonomy hierarchy is much shallower. Site builders commonly configure a higher maximum depth than their current hierarchy requires.
In a benchmark with 50,000 terms, 10,000 nodes, and a selected term with 119 descendants in a four-level hierarchy:
- Depth
4completed in~4 ms. - Depth
9completed in~102 ms.
Both configurations returned identical results.
This issue supersedes #1358412, where the benchmark results and scripts were first posted.
Steps to reproduce
- Generate the benchmark data using Devel Generate.
- Run the attached benchmark scripts at depths 4 and 9.
- Compare the execution times and confirm that both queries return the same results.
See #1358412-105: Content: Has taxonomy term ID (with depth) query performance and #1358412-106: Content: Has taxonomy term ID (with depth) query performance for the scripts and full benchmark details.
Proposed resolution
Expand the matching taxonomy term IDs in PHP before building the Views query:
- Query
taxonomy_term__parentonce per hierarchy level. - Stop when a level adds no new term IDs or the configured depth is reached.
- Join
taxonomy_indexagainst the resulting flat list of term IDs. - Use
DISTINCTto prevent duplicate results.
This makes the work depend on the actual hierarchy depth rather than the configured maximum depth. In the existing benchmark, depth 9 then performs the same work as depth 4 and completes in approximately 8 ms total, with an identical result set.
Remaining tasks
- Benchmark a subtree containing thousands of descendants to measure the effect of a large
INcondition. - Add kernel test coverage.
Issue fork drupal-3610700
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
joelpittetComment #4
joelpittetI currently have both plugins and for the end user that will be confusing (as @alexpott pointed out in the MR comment), I just have this like this for a quick side-by-side test, though a branch change would probably be just as quick during the test.
Setting to needs work, to remove that, and focus on the complexity of the change for the diff
Comment #5
sjpagan commentedAlternative to the proposed resolution: skip the
unionbranches that cannot match. A branch with N joins needs a chain of N parent-child relationships, so past the real hierarchy depth it returns nothing. Within the hierarchy the query is unchanged.On 50k terms over four levels, depth 9 goes from 54 branches to 9, and from 75.9 ms to 24.0 ms on the largest subtree, same rows. Cost is one cached lookup per query build, 0.3 ms.
Numbers from a local run: MariaDB 10.11, PHP 8.5, 50k terms, 10k nodes, four hierarchy levels.
No second handler, no schema change. Branch 3610700-real-hierarchy-depth on the issue fork. @joelpittet, separate MR or fold into !16315?