Example #1
0
class ParserTestV0_9_5(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.test_filters = []
        for fn in sorted(glob(os.path.join(testfile_dir, "*.inp"))):
            with open(fn) as f:
                cls.test_filters.append(f.read().strip())

    def setUp(self):
        self.parser = LarkParser(version=(0, 9, 5))

    def test_inputs(self):
        for tf in self.test_filters:
            if tf == "filter=number=0.0.1":
                self.assertRaises(ParserError, self.parser.parse, tf)
            else:
                tree = self.parser.parse(tf)
                self.assertTrue(tree, Tree)

    def test_parser_version(self):
        v = (0, 9, 5)
        p = LarkParser(version=v)
        self.assertIsInstance(p.parse(self.test_filters[0]), Tree)
        self.assertEqual(p.version, v)

    def test_repr(self):
        self.assertIsNotNone(repr(self.parser))
        self.parser.parse(self.test_filters[0])
        self.assertIsNotNone(repr(self.parser))
Example #2
0
class TestParserV0_9_5:
    @pytest.fixture(autouse=True)
    def set_up(self):
        self.test_filters = []
        for fn in sorted(glob(os.path.join(testfile_dir, "*.inp"))):
            with open(fn) as f:
                self.test_filters.append(f.read().strip())
        self.parser = LarkParser(version=(0, 9, 5))

    def test_inputs(self):
        for tf in self.test_filters:
            if tf == "filter=number=0.0.1":
                with pytest.raises(ParserError):
                    self.parser.parse(tf)
            else:
                tree = self.parser.parse(tf)
                assert isinstance(tree, Tree)

    def test_parser_version(self):
        v = (0, 9, 5)
        p = LarkParser(version=v)
        assert isinstance(p.parse(self.test_filters[0]), Tree)
        assert p.version == v

    def test_repr(self):
        assert repr(self.parser) is not None
        self.parser.parse(self.test_filters[0])
        assert repr(self.parser) is not None
Example #3
0
    def test_mongo_special_id(self, mapper):

        from optimade.filtertransformers.mongo import MongoTransformer
        from bson import ObjectId

        class MyMapper(mapper("StructureMapper")):
            ALIASES = (("immutable_id", "_id"), )

        transformer = MongoTransformer(mapper=MyMapper())
        parser = LarkParser(version=self.version, variant=self.variant)

        assert transformer.transform(
            parser.parse('immutable_id = "5cfb441f053b174410700d02"')) == {
                "_id": {
                    "$eq": ObjectId("5cfb441f053b174410700d02")
                }
            }

        assert transformer.transform(
            parser.parse('immutable_id != "5cfb441f053b174410700d02"')) == {
                "_id": {
                    "$ne": ObjectId("5cfb441f053b174410700d02")
                }
            }

        for op in ("CONTAINS", "STARTS WITH", "ENDS WITH", "HAS"):
            with pytest.raises(
                    BadRequest,
                    match=
                    r".*not supported for query on field 'immutable_id', can only test for equality.*",
            ):
                transformer.transform(
                    parser.parse(f'immutable_id {op} "abcdef"'))
Example #4
0
    def __init__(
        self,
        resource_cls: EntryResource,
        resource_mapper: BaseResourceMapper,
        transformer: Transformer,
    ):
        """Initialize the collection for the given parameters.

        Parameters:
            resource_cls (EntryResource): The `EntryResource` model
                that is stored by the collection.
            resource_mapper (BaseResourceMapper): A resource mapper
                object that handles aliases and format changes between
                deserialization and response.
            transformer (Transformer): The Lark `Transformer` used to
                interpret the filter.

        """
        self.parser = LarkParser()
        self.resource_cls = resource_cls
        self.resource_mapper = resource_mapper
        self.transformer = transformer

        self.provider_prefix = CONFIG.provider.prefix
        self.provider_fields = [
            field if isinstance(field, str) else field["name"] for field in
            CONFIG.provider_fields.get(resource_mapper.ENDPOINT, [])
        ]

        self._all_fields: Set[str] = None
Example #5
0
    def set_up(self):
        from optimade.filtertransformers.elasticsearch import Transformer, Quantity

        self.parser = LarkParser(version=(0, 10, 0), variant="elastic")

        nelements = Quantity(name="nelements")
        elements_only = Quantity(name="elements_only")
        elements_ratios = Quantity(name="elements_ratios")
        elements_ratios.nested_quantity = elements_ratios
        elements = Quantity(
            name="elements",
            length_quantity=nelements,
            has_only_quantity=elements_only,
            nested_quantity=elements_ratios,
        )
        dimension_types = Quantity(name="dimension_types")
        dimension_types.length_quantity = dimension_types

        quantities = [
            nelements,
            elements_only,
            elements_ratios,
            elements,
            dimension_types,
            Quantity(name="chemical_formula_reduced"),
        ]

        self.transformer = Transformer(quantities=quantities)
Example #6
0
    def __init__(
        self,
        collection,
        resource_cls: EntryResource,
        resource_mapper: BaseResourceMapper,
        transformer: Transformer,
    ):
        """Initialize the collection for the given parameters.

        Parameters:
            collection: The backend-specific collection.
            resource_cls (EntryResource): The `EntryResource` model
                that is stored by the collection.
            resource_mapper (BaseResourceMapper): A resource mapper
                object that handles aliases and format changes between
                deserialization and response.
            transformer (Transformer): The Lark `Transformer` used to
                interpret the filter.

        """
        self.collection = collection
        self.parser = LarkParser()
        self.resource_cls = resource_cls
        self.resource_mapper = resource_mapper
        self.transformer = transformer

        self.provider_prefix = CONFIG.provider.prefix
        self.provider_fields = CONFIG.provider_fields.get(
            resource_mapper.ENDPOINT, [])
    def test_list_length_aliases(self, mapper):
        from optimade.filtertransformers.mongo import MongoTransformer

        transformer = MongoTransformer(mapper=mapper("StructureMapper")())
        parser = LarkParser(version=self.version, variant=self.variant)

        assert transformer.transform(parser.parse("elements LENGTH 3")) == {
            "nelements": 3
        }

        assert transformer.transform(
            parser.parse('elements HAS "Li" AND elements LENGTH = 3')
        ) == {"$and": [{"elements": {"$in": ["Li"]}}, {"nelements": 3}]}

        assert transformer.transform(parser.parse("elements LENGTH > 3")) == {
            "nelements": {"$gt": 3}
        }
        assert transformer.transform(parser.parse("elements LENGTH < 3")) == {
            "nelements": {"$lt": 3}
        }
        assert transformer.transform(parser.parse("elements LENGTH = 3")) == {
            "nelements": 3
        }
        assert transformer.transform(
            parser.parse("cartesian_site_positions LENGTH <= 3")
        ) == {"nsites": {"$lte": 3}}
        assert transformer.transform(
            parser.parse("cartesian_site_positions LENGTH >= 3")
        ) == {"nsites": {"$gte": 3}}
Example #8
0
 def __init__(self):
     self.opers = {
         "=": self.eq,
         ">": self.gt,
         ">=": self.ge,
         "<": self.lt,
         "<=": self.le,
         "!=": self.ne,
         "OR": self.or_,
         "AND": self.and_,
         "NOT": self.not_,
     }
     self.parser = LarkParser(version=(0, 9, 7))
    def test_other_provider_fields(self, mapper):
        """Test that fields from other providers generate
        queries that treat the value of the field as `null`.

        """
        from optimade.filtertransformers.mongo import MongoTransformer

        t = MongoTransformer(mapper=mapper("StructureMapper"))
        p = LarkParser(version=self.version, variant=self.variant)
        assert t.transform(p.parse("_other_provider_field > 1")) == {
            "_other_provider_field": {
                "$gt": 1
            }
        }
Example #10
0
    def __init__(self):
        p = LarkParser(version=(1, 0, 0), variant="default")
        t = MongoTransformer()
        self.transform = lambda inp: t.transform(p.parse(inp))

        client = MongoClient('mongodb://{}:{}@{}:{}/?authSource={}'.format(
            "admin", "admin", "localhost", "27017", "admin"))
        db = client["MaterialsDB"]
        self.cl = db["Data.Calculation.StaticCalculation"]

        self.lu = Lower2Upper()

        self.data = info1
        self.info = info2
Example #11
0
    def __init__(
        self,
        name: str,
        resource_cls: EntryResource,
        resource_mapper: BaseResourceMapper,
        database: str = CONFIG.mongo_database,
    ):
        """Initialize the MongoCollection for the given parameters.

        Parameters:
            name: The name of the collection.
            resource_cls: The type of entry resource that is stored by the collection.
            resource_mapper: A resource mapper object that handles aliases and
                format changes between deserialization and response.
            database: The name of the underlying MongoDB database to connect to.

        """
        super().__init__(
            resource_cls,
            resource_mapper,
            MongoTransformer(mapper=resource_mapper),
        )

        self.parser = LarkParser(version=(1, 0, 0), variant="default")
        self.collection = CLIENT[database][name]

        # check aliases do not clash with mongo operators
        self._check_aliases(self.resource_mapper.all_aliases())
        self._check_aliases(self.resource_mapper.all_length_aliases())
    def __init__(
        self,
        name: str,
        resource_cls: "EntryResource",
        resource_mapper: "BaseResourceMapper",
    ):
        """Initialize the AsyncMongoCollection for the given parameters.

        Parameters:
            name: The name of the collection.
            resource_cls: The `EntryResource` model that is stored by the collection.
            resource_mapper: A resource mapper object that handles aliases and format
                changes between deserialization and response.

        """
        from optimade_gateway.mongo.database import (  # pylint: disable=import-outside-toplevel
            MONGO_DB, )

        super().__init__(
            resource_cls=resource_cls,
            resource_mapper=resource_mapper,
            transformer=MongoTransformer(mapper=resource_mapper),
        )

        self.parser = LarkParser(version=(1, 0, 0), variant="default")
        self.collection: MongoCollection = MONGO_DB[name]

        # Check aliases do not clash with mongo operators
        self._check_aliases(self.resource_mapper.all_aliases())
        self._check_aliases(self.resource_mapper.all_length_aliases())
Example #13
0
    def __init__(
        self,
        collection: Union[pymongo.collection.Collection,
                          mongomock.collection.Collection],
        resource_cls: EntryResource,
        resource_mapper: BaseResourceMapper,
    ):
        """Initialize the MongoCollection for the given parameters.

        Parameters:
            collection (Union[pymongo.collection.Collection, mongomock.collection.Collection]):
                The backend-specific collection.
            resource_cls (EntryResource): The `EntryResource` model
                that is stored by the collection.
            resource_mapper (BaseResourceMapper): A resource mapper
                object that handles aliases and format changes between
                deserialization and response.

        """
        super().__init__(
            collection,
            resource_cls,
            resource_mapper,
            MongoTransformer(mapper=resource_mapper),
        )

        self.parser = LarkParser(
            version=(0, 10, 1), variant="default"
        )  # The MongoTransformer only supports v0.10.1 as the latest grammar

        # check aliases do not clash with mongo operators
        self._check_aliases(self.resource_mapper.all_aliases())
        self._check_aliases(self.resource_mapper.all_length_aliases())
    def __init__(
        self,
        collection: Union[pymongo.collection.Collection,
                          mongomock.collection.Collection],
        resource_cls: EntryResource,
        resource_mapper: ResourceMapper,
    ):
        super().__init__(collection, resource_cls, resource_mapper)
        self.transformer = MongoTransformer()

        self.provider = CONFIG.provider["prefix"]
        self.provider_fields = CONFIG.provider_fields.get(
            resource_mapper.ENDPOINT, [])
        self.parser = LarkParser(
            version=(0, 10, 1), variant="default"
        )  # The MongoTransformer only supports v0.10.1 as the latest grammar
Example #15
0
 def __init__(
     self, collection, resource_cls: EntryResource, resource_mapper: ResourceMapper
 ):
     self.collection = collection
     self.parser = LarkParser()
     self.resource_cls = resource_cls
     self.resource_mapper = resource_mapper
Example #16
0
    def test_list_length_aliases(self):
        from optimade.server.mappers import StructureMapper

        transformer = MongoTransformer(mapper=StructureMapper())
        parser = LarkParser(version=self.version, variant=self.variant)

        self.assertEqual(
            transformer.transform(parser.parse("elements LENGTH 3")),
            {"nelements": 3})

        self.assertEqual(
            transformer.transform(
                parser.parse('elements HAS "Li" AND elements LENGTH = 3')),
            {"$and": [{
                "elements": {
                    "$in": ["Li"]
                }
            }, {
                "nelements": 3
            }]},
        )

        self.assertEqual(
            transformer.transform(parser.parse("elements LENGTH > 3")),
            {"nelements": {
                "$gt": 3
            }},
        )
        self.assertEqual(
            transformer.transform(parser.parse("elements LENGTH < 3")),
            {"nelements": {
                "$lt": 3
            }},
        )
        self.assertEqual(
            transformer.transform(parser.parse("elements LENGTH = 3")),
            {"nelements": 3})
        self.assertEqual(
            transformer.transform(
                parser.parse("cartesian_site_positions LENGTH <= 3")),
            {"nsites": {
                "$lte": 3
            }},
        )
        self.assertEqual(
            transformer.transform(
                parser.parse("cartesian_site_positions LENGTH >= 3")),
            {"nsites": {
                "$gte": 3
            }},
        )
Example #17
0
def test_list_length_aliases():
    """Check LENGTH aliases for lists"""
    from optimade.server.mappers import StructureMapper

    transformer = AiidaTransformer(mapper=StructureMapper())
    parser = LarkParser(version=VERSION, variant=VARIANT)

    assert transformer.transform(parser.parse("elements LENGTH 3")) == {
        "nelements": 3
    }

    assert transformer.transform(
        parser.parse('elements HAS "Li" AND elements LENGTH = 3')) == {
            "and": [{
                "elements": {
                    "contains": ["Li"]
                }
            }, {
                "nelements": 3
            }]
        }

    assert transformer.transform(parser.parse("elements LENGTH > 3")) == ({
        "nelements": {
            ">": 3
        }
    })
    assert transformer.transform(parser.parse("elements LENGTH < 3")) == ({
        "nelements": {
            "<": 3
        }
    })
    assert transformer.transform(parser.parse("elements LENGTH = 3")) == {
        "nelements": 3
    }
    assert transformer.transform(
        parser.parse("cartesian_site_positions LENGTH <= 3")) == {
            "nsites": {
                "<=": 3
            }
        }
    assert transformer.transform(
        parser.parse("cartesian_site_positions LENGTH >= 3")) == {
            "nsites": {
                ">=": 3
            }
        }
Example #18
0
    def __init__(
        self,
        collection: orm.entities.Collection,
        resource_cls: EntryResource,
        resource_mapper: ResourceMapper,
    ):
        super().__init__(collection, resource_cls, resource_mapper)

        self.transformer = AiidaTransformerV0_10_1()
        self.provider = CONFIG.provider["prefix"]
        self.provider_fields = CONFIG.provider_fields[resource_mapper.ENDPOINT]
        self.page_limit = CONFIG.page_limit
        self.db_page_limit = CONFIG.db_page_limit
        self.parser = LarkParser(version=(0, 10, 0))

        # "Cache"
        self._data_available: int = None
        self._data_returned: int = None
        self._filter_fields: set = None
        self._latest_filter: dict = None
Example #19
0
    def __init__(
        self,
        collection: Union[pymongo.collection.Collection,
                          mongomock.collection.Collection],
        resource_cls: EntryResource,
        resource_mapper: BaseResourceMapper,
    ):
        super().__init__(collection, resource_cls, resource_mapper)
        self.transformer = MongoTransformer(mapper=resource_mapper)

        self.provider_prefix = CONFIG.provider.prefix
        self.provider_fields = CONFIG.provider_fields.get(
            resource_mapper.ENDPOINT, [])
        self.parser = LarkParser(
            version=(0, 10, 1), variant="default"
        )  # The MongoTransformer only supports v0.10.1 as the latest grammar

        # check aliases do not clash with mongo operators
        self._check_aliases(self.resource_mapper.all_aliases())
        self._check_aliases(self.resource_mapper.all_length_aliases())
    def __init__(
        self,
        entity: Entity,
        resource_cls: EntryResource,
        resource_mapper: ResourceMapper,
    ):
        self.entity = entity
        self.parser = LarkParser()
        self.resource_cls = resource_cls
        self.resource_mapper = resource_mapper

        self.transformer = AiidaTransformer()
        self.provider = CONFIG.provider.prefix
        self.provider_fields = CONFIG.provider_fields[resource_mapper.ENDPOINT]
        self.parser = LarkParser()

        # "Cache"
        self._data_available: int = None
        self._data_returned: int = None
        self._filter_fields: set = None
        self._latest_filter: dict = None
    def setUpClass(cls):
        parser = LarkParser(version=(0, 9, 7))
        transformer = MongoTransformer()

        def convert(_, q):
            parsed = parser.parse(q)
            return transformer.transform(parsed)

        cls.convert = convert
        cls.client = MongoClient()
        cls.db = cls.client[f"test_db_{uuid.uuid4()}"]
        cls.coll = cls.db.data
        cls.coll.insert_many([{
            "a": a,
            "b": b
        } for a, b in itertools.product(range(10), range(10))])
class BaseTestFilterParser(abc.ABC):
    """Base class for parsing different versions of the grammar using `LarkParser`."""

    version: Tuple[int, int, int]
    variant: str = "default"

    @pytest.fixture(autouse=True)
    def set_up(self):
        self.parser = LarkParser(version=self.version, variant=self.variant)

    def test_repr(self):
        assert repr(self.parser) is not None
        self.parse("band_gap = 1")
        assert repr(self.parser) is not None

    def parse(self, inp):
        return self.parser.parse(inp)

    def test_parser_version(self):
        assert self.parser.version == self.version
        assert self.parser.variant == self.variant
Example #23
0
        return "string"

    def number(self, arg):
        # number: SIGNED_INT | SIGNED_FLOAT
        print("Node: ", "number", arg)
        return "number"

    def __default__(self, data, children, meta):
        print("Node: ", data, children)
        return data


if __name__ == "__main__":  # pragma: no cover
    from optimade.filterparser import LarkParser

    p = LarkParser(version=(0, 10, 0))
    # t = DebugTransformer()
    t = TransformerSkeleton()

    # f = 'a.a = "text" OR a<a AND NOT b>=8'

    # single list

    f = "list HAS < 3"

    f = "list HAS < 3, > 4"  # -> error
    f = "list HAS ALL < 3, > 4"

    # multiple lists

    f = "list1:list2 HAS < 3 : > 4"
    def test_filtering_on_relationships(self, mapper):
        """Test the nested properties with special names
        like "structures", "references" etc. are applied
        to the relationships field.

        """
        from optimade.filtertransformers.mongo import MongoTransformer

        t = MongoTransformer(mapper=mapper("StructureMapper"))
        p = LarkParser(version=self.version, variant=self.variant)

        assert t.transform(p.parse('references.id HAS "dummy/2019"')) == {
            "relationships.references.data.id": {
                "$in": ["dummy/2019"]
            }
        }

        assert t.transform(
            p.parse('structures.id HAS ANY "dummy/2019", "dijkstra1968"')) == {
                "relationships.structures.data.id": {
                    "$in": ["dummy/2019", "dijkstra1968"]
                }
            }

        assert t.transform(
            p.parse('structures.id HAS ALL "dummy/2019", "dijkstra1968"')) == {
                "relationships.structures.data.id": {
                    "$all": ["dummy/2019", "dijkstra1968"]
                }
            }

        assert t.transform(p.parse('structures.id HAS ONLY "dummy/2019"')) == {
            "$and": [
                {
                    "relationships.structures.data": {
                        "$size": 1
                    }
                },
                {
                    "relationships.structures.data.id": {
                        "$all": ["dummy/2019"]
                    }
                },
            ]
        }

        assert t.transform(
            p.parse(
                'structures.id HAS ONLY "dummy/2019" AND structures.id HAS "dummy/2019"'
            )) == {
                "$and": [
                    {
                        "$and": [
                            {
                                "relationships.structures.data": {
                                    "$size": 1,
                                }
                            },
                            {
                                "relationships.structures.data.id": {
                                    "$all": ["dummy/2019"]
                                }
                            },
                        ]
                    },
                    {
                        "relationships.structures.data.id": {
                            "$in": ["dummy/2019"]
                        }
                    },
                ],
            }
    def set_up(self):
        from optimade.filtertransformers.mongo import MongoTransformer

        p = LarkParser(version=self.version, variant=self.variant)
        t = MongoTransformer()
        self.transform = lambda inp: t.transform(p.parse(inp))
Example #26
0
class DjangoTransformer:
    """Filter transformer for implementations using Django.

    !!! warning "Warning"
        This transformer is deprecated as it only supports
        the 0.9.7 grammar version, and works different to other
        filter transformers in this package.

    """
    def __init__(self):
        self.opers = {
            "=": self.eq,
            ">": self.gt,
            ">=": self.ge,
            "<": self.lt,
            "<=": self.le,
            "!=": self.ne,
            "OR": self.or_,
            "AND": self.and_,
            "NOT": self.not_,
        }
        self.parser = LarkParser(version=(0, 9, 7))

    def parse_raw_q(self, raw_query):
        return self.parser.parse(raw_query)

    def eq(self, a, b):
        return Q(**{a: b})

    def gt(self, a, b):
        return Q(**{a + "__gt": b})

    def ge(self, a, b):
        return Q(**{a + "__gte": b})

    def lt(self, a, b):
        return Q(**{a + "__lt": b})

    def le(self, a, b):
        return Q(**{a + "__lte": b})

    def ne(self, a, b):
        return ~Q(**{a: b})

    def not_(self, a):
        return ~a

    def and_(self, a, b):
        return operator.and_(a, b)

    def or_(self, a, b):
        return operator.or_(a, b)

    def evaluate(self, parse_Tree):
        if isinstance(parse_Tree, Tree):
            children = parse_Tree.children
            if len(children) == 1:
                return self.evaluate(children[0])
            elif len(children) == 2:
                op_fn = self.evaluate(children[0])
                return op_fn(self.evaluate(children[1]))
            elif len(children) == 3:
                if parse_Tree.data == "comparison":
                    db_prop = self.evaluate(children[0])
                    op_fn = self.evaluate(children[1])

                    if db_prop in django_db_keys.keys():
                        return op_fn(django_db_keys[db_prop],
                                     self.evaluate(children[2]))
                    else:
                        raise DjangoQueryError(
                            "Unknown property is queried : " + (db_prop))

                else:
                    op_fn = self.evaluate(children[1])
                    return op_fn(self.evaluate(children[0]),
                                 self.evaluate(children[2]))
            else:
                raise DjangoQueryError(
                    "Not compatible format. Tree has >3 children")

        elif isinstance(parse_Tree, Token):
            if parse_Tree.type == "VALUE":
                return parse_Tree.value
            elif parse_Tree.type in ["NOT", "CONJUNCTION", "OPERATOR"]:
                return self.opers[parse_Tree.value]
        else:
            raise DjangoQueryError("Not a Lark Tree or Token")
    def test_suspected_timestamp_fields(self, mapper):
        import datetime
        import bson.tz_util
        from optimade.filtertransformers.mongo import MongoTransformer
        from optimade.server.warnings import TimestampNotRFCCompliant

        example_RFC3339_date = "2019-06-08T04:13:37Z"
        example_RFC3339_date_2 = "2019-06-08T04:13:37"
        example_non_RFC3339_date = "2019-06-08T04:13:37.123Z"

        expected_datetime = datetime.datetime(
            year=2019,
            month=6,
            day=8,
            hour=4,
            minute=13,
            second=37,
            microsecond=0,
            tzinfo=bson.tz_util.utc,
        )

        assert self.transform(f'last_modified > "{example_RFC3339_date}"') == {
            "last_modified": {
                "$gt": expected_datetime
            }
        }
        assert self.transform(
            f'last_modified > "{example_RFC3339_date_2}"') == {
                "last_modified": {
                    "$gt": expected_datetime
                }
            }

        non_rfc_datetime = expected_datetime.replace(microsecond=123000)

        with pytest.warns(TimestampNotRFCCompliant):
            assert self.transform(
                f'last_modified > "{example_non_RFC3339_date}"') == {
                    "last_modified": {
                        "$gt": non_rfc_datetime
                    }
                }

        class MyMapper(mapper("StructureMapper")):
            ALIASES = (("last_modified", "ctime"), )

        transformer = MongoTransformer(mapper=MyMapper)
        parser = LarkParser(version=self.version, variant=self.variant)

        assert transformer.transform(
            parser.parse(f'last_modified > "{example_RFC3339_date}"')) == {
                "ctime": {
                    "$gt": expected_datetime
                }
            }
        assert transformer.transform(
            parser.parse(f'last_modified > "{example_RFC3339_date_2}"')) == {
                "ctime": {
                    "$gt": expected_datetime
                }
            }
Example #28
0
def parser():
    return LarkParser()
    def test_aliases(self, mapper):
        """Test that valid aliases are allowed, but do not affect r-values."""
        from optimade.filtertransformers.mongo import MongoTransformer

        class MyStructureMapper(mapper("StructureMapper")):
            ALIASES = (
                ("elements", "my_elements"),
                ("A", "D"),
                ("property_a", "D"),
                ("B", "E"),
                ("C", "F"),
                ("_exmpl_nested_field", "nested_field"),
            )

            PROVIDER_FIELDS = ("D", "E", "F", "nested_field")

        mapper = MyStructureMapper
        t = MongoTransformer(mapper=mapper)
        p = LarkParser(version=self.version, variant=self.variant)

        assert mapper.get_backend_field("elements") == "my_elements"
        test_filter = 'elements HAS "A"'
        assert t.transform(p.parse(test_filter)) == {
            "my_elements": {
                "$in": ["A"]
            }
        }
        test_filter = 'elements HAS ANY "A","B","C" AND elements HAS "D"'
        assert t.transform(p.parse(test_filter)) == {
            "$and": [
                {
                    "my_elements": {
                        "$in": ["A", "B", "C"]
                    }
                },
                {
                    "my_elements": {
                        "$in": ["D"]
                    }
                },
            ]
        }
        test_filter = 'elements = "A"'
        assert t.transform(p.parse(test_filter)) == {
            "my_elements": {
                "$eq": "A"
            }
        }
        test_filter = 'property_a HAS "B"'
        assert t.transform(p.parse(test_filter)) == {"D": {"$in": ["B"]}}

        test_filter = "_exmpl_nested_field.sub_property > 1234.5"
        assert t.transform(p.parse(test_filter)) == {
            "nested_field.sub_property": {
                "$gt": 1234.5
            }
        }

        test_filter = "_exmpl_nested_field.sub_property.x IS UNKNOWN"
        assert t.transform(p.parse(test_filter)) == {
            "$or": [
                {
                    "nested_field.sub_property.x": {
                        "$exists": False
                    }
                },
                {
                    "nested_field.sub_property.x": {
                        "$eq": None
                    }
                },
            ]
        }
    def test_aliased_length_operator(self, mapper):
        from optimade.filtertransformers.mongo import MongoTransformer

        class MyMapper(mapper("StructureMapper")):
            ALIASES = (
                ("elements", "my_elements"),
                ("nelements", "nelem"),
                ("elements_ratios", "ratios"),
            )
            LENGTH_ALIASES = (
                ("chemsys", "nelements"),
                ("cartesian_site_positions", "nsites"),
                ("elements", "nelements"),
            )
            PROVIDER_FIELDS = ("chemsys", )

        m = MyMapper
        transformer = MongoTransformer(mapper=m)
        parser = LarkParser(version=self.version, variant=self.variant)

        assert transformer.transform(
            parser.parse("cartesian_site_positions LENGTH <= 3")) == {
                "nsites": {
                    "$lte": 3
                }
            }
        assert transformer.transform(
            parser.parse("cartesian_site_positions LENGTH < 3")) == {
                "nsites": {
                    "$lt": 3
                }
            }
        assert transformer.transform(
            parser.parse("cartesian_site_positions LENGTH = 3")) == {
                "nsites": 3
            }
        assert transformer.transform(
            parser.parse("cartesian_site_positions LENGTH 3")) == {
                "nsites": 3
            }

        assert transformer.transform(
            parser.parse("elements_ratios LENGTH 3")) == {
                "ratios": {
                    "$size": 3
                }
            }

        assert transformer.transform(
            parser.parse("cartesian_site_positions LENGTH >= 10")) == {
                "nsites": {
                    "$gte": 10
                }
            }

        assert transformer.transform(
            parser.parse("structure_features LENGTH > 10")) == {
                "structure_features.11": {
                    "$exists": True
                }
            }

        assert transformer.transform(parser.parse("nsites LENGTH > 10")) == {
            "nsites.11": {
                "$exists": True
            }
        }

        assert transformer.transform(parser.parse("elements LENGTH 3")) == {
            "nelem": 3
        }

        assert transformer.transform(parser.parse('elements HAS "Ag"')) == {
            "my_elements": {
                "$in": ["Ag"]
            }
        }

        assert transformer.transform(
            parser.parse("_exmpl_chemsys LENGTH 3")) == {
                "nelem": 3
            }