Reduce the clauses of child-of using common roots

For the path, if the longer path contains another searched path, we can ignore
it.
For MPTT, if a range is inside a larger range, we can also ignore it.

Closes #14064
This commit is contained in:
Cédric Krier
2025-06-10 23:22:01 +02:00
parent 6718a613cf
commit ea1393bb84
2 changed files with 40 additions and 4 deletions
+25 -4
View File
@@ -5,6 +5,7 @@ import warnings
from sql import As, Column, Expression, Literal, Null, Query, With
from sql.aggregate import Max
from sql.conditionals import Coalesce
from sql.functions import CharLength
from sql.operators import Or
from trytond.config import config
@@ -134,10 +135,20 @@ class Many2One(Field):
Target = self.get_target()
path_column = getattr(Target, self.path).sql_column(table)
path_column = Coalesce(path_column, '')
cursor.execute(*table.select(path_column, where=red_sql))
cursor.execute(*table.select(
path_column, where=red_sql,
order_by=[CharLength(path_column).desc, path_column.asc]))
if operator.endswith('child_of'):
where = Or()
paths = set()
for path, in cursor:
discard = set()
for opath in paths:
if path in opath:
discard.add(opath)
paths -= discard
paths.add(path)
where = Or()
for path in paths:
where.append(path_column.like(path + '%'))
else:
ids = [int(x) for path, in cursor for x in path.split('/')[:-1]]
@@ -156,9 +167,19 @@ class Many2One(Field):
Target = self.get_target()
left = getattr(Target, self.left).sql_column(table)
right = getattr(Target, self.right).sql_column(table)
cursor.execute(*table.select(left, right, where=red_sql))
where = Or()
cursor.execute(*table.select(
left, right, where=red_sql,
order_by=[(right - left).asc, left.asc]))
ranges = set()
for l, r in cursor:
discard = set()
for ol, or_ in ranges:
if l < ol and or_ < r:
discard.add((ol, or_))
ranges -= discard
ranges.add((l, r))
where = Or()
for l, r in ranges:
if operator.endswith('child_of'):
where.append((left >= l) & (right <= r))
else:
@@ -400,6 +400,21 @@ class FieldMany2OneTreeTestCase(TestCase):
self.first1, self.first2, self.first3, self.first4,
self.second2, self.second3, self.second4]))
@with_transaction()
def test_search_child_of_first1_second1(self):
"Test search many2one child of first1 and second1"
Many2One = Pool().get(self.model_name)
self.create_tree(Many2One)
result = Many2One.search([
('many2one', 'child_of',
[self.first1.id, self.second1.id]),
])
self.assertListEqual(
sorted(result),
sorted([self.first1, self.second1, self.second2]))
@with_transaction()
def test_search_child_of_empty(self):
"Test search many2one child of empty"