예제 #1
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
예제 #2
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)
예제 #3
0
    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}}
예제 #4
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,
        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())
예제 #6
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())
예제 #7
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
예제 #8
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, [])
예제 #9
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"'))
예제 #10
0
    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
예제 #11
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))
예제 #12
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
            }},
        )
예제 #13
0
    def __init__(
        self,
        collection: orm.entities.Collection,
        resource_cls: EntryResource,
        resource_mapper: ResourceMapper,
    ):
        self.collection = collection
        self.parser = LarkParser()
        self.resource_cls = resource_cls
        self.resource_mapper = 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
예제 #14
0
    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
            }
        }
예제 #15
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
    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
    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))])
예제 #18
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
            }
        }
예제 #19
0
    def test_aliased_length_operator(self, mapper):
        from optimade.filtertransformers.mongo import MongoTransformer

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

        transformer = MongoTransformer(mapper=MyMapper())
        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("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("chemsys LENGTH 3")) == {"nelem": 3}
예제 #20
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())
예제 #21
0
def parser():
    return LarkParser()
예제 #22
0
    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
                    }
                },
            ]
        }
예제 #23
0
    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
                }
            }
예제 #24
0
    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"]
                        }
                    },
                ],
            }
예제 #25
0
    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))
예제 #26
0
 def setUp(self):
     p = LarkParser(version=self.version, variant=self.variant)
     t = MongoTransformer()
     self.transform = lambda inp: t.transform(p.parse(inp))
예제 #27
0
 def set_up(self):
     self.parser = LarkParser(version=self.version, variant=self.variant)
예제 #28
0
 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
예제 #29
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"
예제 #30
0
 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))