From c5d1801e1e3fc964eef151008f0b563a4e2e1f8b Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sun, 2 Jun 2024 17:39:23 +1000 Subject: [PATCH] Fix #4786. Speed up parent filter. --- .../ifcopenshell-python/selector_syntax.rst | 2 + .../ifcopenshell/util/selector.py | 52 ++++++++++++++----- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/ifcopenshell-python/docs/ifcopenshell-python/selector_syntax.rst b/src/ifcopenshell-python/docs/ifcopenshell-python/selector_syntax.rst index 5cea364c72..fcc4f7cd7d 100644 --- a/src/ifcopenshell-python/docs/ifcopenshell-python/selector_syntax.rst +++ b/src/ifcopenshell-python/docs/ifcopenshell-python/selector_syntax.rst @@ -102,6 +102,7 @@ elements in your filter group based on their criteria. "Material", "Filter", "``material{{=}}{{value}}``", "``material=Foo`` specifies the criteria that elements must have a IfcMaterial assigned directly or indirectly (such as within a layer set). That IfcMaterial must have either a ``Name`` or ``Category`` attribute with a value of ``Foo``." "Classification", "Filter", "``classification{{=}}{{value}}``", "``classification=Foo`` specifies the criteria that elements must have an IfcClassificationReference with an ``Identification`` attribute with a value of ``Foo``." "Location", "Filter", "``location{{=}}{{value}}``", "``location=Foo`` specifies the criteria that elements must be contained directly or indirectly in a spatial element with a ``Name`` attribute with a value of ``Foo``." + "Parent", "Filter", "``parent{{=}}{{value}}``", "``parent=Foo`` specifies the criteria that elements must be a direct or indirect child in the spatial hierarchy to an element with a ``Name`` attribute with a value of ``Foo``." "Query", "Filter", "``query:{{keys}}{{=}}{{value}}``", "``query:types.count=0`` specifies the criteria that elements must have zero type occurrences. The query keys corresponds to the syntax used in the `Getting element values`_ section" When you specify a filter with a ``{{=}}`` check, you can choose from one of @@ -183,6 +184,7 @@ Valid keys are: "``storey``", "Gets the first IfcBuildingStorey spatial element that an element is contained in." "``building``", "Gets the first IfcBuilding spatial element that an element is contained in." "``site``", "Gets the first IccSite spatial element that an element is contained in." + "``parent``", "Gets the parent element in the spatial hierarchy." "``material`` or ``mat``", "Gets the assigned material, which may be a material set." "``item`` or ``i``", "If the previous key returns a material set, gets the relevant material set items" "``materials`` or ``mats``", "Gets a list of IfcMaterials assigned directly or indirectly (such as via a material set) to the element" diff --git a/src/ifcopenshell-python/ifcopenshell/util/selector.py b/src/ifcopenshell-python/ifcopenshell/util/selector.py index a804ee406a..6097b9572c 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/selector.py +++ b/src/ifcopenshell-python/ifcopenshell/util/selector.py @@ -687,21 +687,45 @@ class FacetTransformer(lark.Transformer): def parent(self, args): comparison, value = args - def filter_function(element): - parents = [] - result = False - if parent := ifcopenshell.util.element.get_parent(element): - parents.append(parent) - while parents: - parent = parents.pop() - if self.compare(parent.Name, comparison, value): - result = True - break - if grandparent := ifcopenshell.util.element.get_parent(parent): - parents.append(grandparent) - return result if comparison == "=" else not result + parents = set() + for rel in self.file.by_type("IfcRelAggregates"): + parent = rel.RelatingObject + if parent and self.compare(parent.Name, comparison, value): + parents.add(parent) - self.elements = set(filter(filter_function, self.elements)) + for rel in self.file.by_type("IfcRelContainedInSpatialStructure"): + parent = rel.RelatingStructure + if parent and self.compare(parent.Name, comparison, value): + parents.add(parent) + + for rel in self.file.by_type("IfcRelNests"): + parent = rel.RelatingObject + if parent and self.compare(parent.Name, comparison, value): + parents.add(parent) + + for rel in self.file.by_type("IfcRelVoidsElement"): + parent = rel.RelatingBuildingElement + if parent and self.compare(parent.Name, comparison, value): + parents.add(parent) + + for rel in self.file.by_type("IfcRelVoidsElement"): + parent = rel.RelatingBuildingElement + if parent and self.compare(parent.Name, comparison, value): + parents.add(parent) + + for rel in self.file.by_type("IfcRelFillsElement"): + parent = rel.RelatingOpeningElement + if parent and self.compare(parent.Name, comparison, value): + parents.add(parent) + + children = set() + for parent in parents: + children |= set(ifcopenshell.util.element.get_decomposition(parent)) + + if comparison == "=": + self.elements = self.elements & children + else: + self.elements -= children def query(self, args): keys, comparison, value = args