diff --git a/src/bonsai/bonsai/tool/search.py b/src/bonsai/bonsai/tool/search.py index f0cebb708b..b328016a9c 100644 --- a/src/bonsai/bonsai/tool/search.py +++ b/src/bonsai/bonsai/tool/search.py @@ -64,19 +64,16 @@ class Search(bonsai.core.tool.Search): query = [] for filter_group in filter_groups: filter_group_query = [] - has_instance_or_entity_filter = False for ifc_filter in filter_group.filters: if not ifc_filter.value: continue if ifc_filter.type == "instance": - has_instance_or_entity_filter = True if "bpy.data.texts" in ifc_filter.value: data_name = ifc_filter.value.split("bpy.data.texts")[1][2:-2] filter_group_query.append(bpy.data.texts[data_name].as_string()) else: filter_group_query.append(ifc_filter.value) elif ifc_filter.type == "entity": - has_instance_or_entity_filter = True filter_group_query.append(ifc_filter.value) elif ifc_filter.type == "attribute": if not ifc_filter.name: @@ -113,9 +110,6 @@ class Search(bonsai.core.tool.Search): keys = cls.wrap_value(ifc_filter, ifc_filter.name) comparison, value = cls.get_comparison_and_value(ifc_filter) filter_group_query.append(f"query:{keys}{comparison}{value}") - if not has_instance_or_entity_filter: - filter_group_query.insert(0, "IfcProduct") - filter_group_query.insert(0, "IfcTypeProduct") query.append(", ".join(filter_group_query)) return " + ".join(query) diff --git a/src/ifcopenshell-python/docs/ifcopenshell-python/selector_syntax.rst b/src/ifcopenshell-python/docs/ifcopenshell-python/selector_syntax.rst index 2cd3557ad6..a5eeb47c1b 100644 --- a/src/ifcopenshell-python/docs/ifcopenshell-python/selector_syntax.rst +++ b/src/ifcopenshell-python/docs/ifcopenshell-python/selector_syntax.rst @@ -87,9 +87,15 @@ The filters are chained and apply from left to right. filter[, filter]* -There are nine types of filters to choose from. Some of these filters will add -new elements to your filter group, and some will filter previously added -elements in your filter group based on their criteria. +Below is the table of filters to choose from. Most of these filters will filter +previously added elements in your filter group based on their criteria. + +There are two exceptions - if ``elements`` are not provided to ``filter_elements`` +*Class* and *GlobalId* filters (without ``[!]``) will add new elements to the filter group , +otherwise they'll also filter elements based on criteria. + +If neither *Class* and *GlobalId* and ``elements`` are not provided then filter +will search through all IfcTypeProducts and IfcProducts in the IFC project. .. csv-table:: :header: "Filter", "Type", "Usage", "Example" diff --git a/src/ifcopenshell-python/ifcopenshell/util/selector.py b/src/ifcopenshell-python/ifcopenshell/util/selector.py index 5fa6a16f3e..1ac8809d11 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/selector.py +++ b/src/ifcopenshell-python/ifcopenshell/util/selector.py @@ -771,6 +771,23 @@ class FacetTransformer(lark.Transformer): results |= r return results + def transform(self, tree: lark.ParseTree): + def has_elements_token(tree: lark.tree.ParseTree) -> bool: + for child in tree.iter_subtrees_topdown(): + data = child.data + # Tokens that add elements to filter. + if data in ("entity", "instance"): + return True + return False + + # Only elements tokens can add elements to the filter. + # Fallback to default elements if they are not provided. + if self.base_elements is None and not has_elements_token(tree): + self.elements.update(self.file.by_type("IfcProduct")) + self.elements.update(self.file.by_type("IfcTypeProduct")) + + return super().transform(tree) + def facet_list(self, args): if self.elements: self.results.append(self.elements) diff --git a/src/ifcopenshell-python/test/util/test_selector.py b/src/ifcopenshell-python/test/util/test_selector.py index 56f52b2077..50b032fe8e 100644 --- a/src/ifcopenshell-python/test/util/test_selector.py +++ b/src/ifcopenshell-python/test/util/test_selector.py @@ -159,6 +159,14 @@ class TestFilterElements(test.bootstrap.IFC4): assert subject.filter_elements(self.file, "IfcWall") == {element} assert subject.filter_elements(self.file, "IfcElement, ! IfcWall") == {element2} + def test_select_without_elements_token(self): + element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall") + element.Name = "Foo" + element2 = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall") + element2.Name = "Bar" + ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcSlab") + assert subject.filter_elements(self.file, "Name=Foo") == {element} + def test_selecting_by_attribute(self): element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall") element.Name = "Foo"