Пример #1
0
    def test_aliases(self):
        """ Test that valid aliases are allowed, but do not effect
        r-values.

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

        mapper = MyStructureMapper()
        t = MongoTransformer(mapper=mapper)

        self.assertEqual(mapper.alias_for("elements"), "my_elements")

        test_filter = {"elements": {"$in": ["A", "B", "C"]}}
        self.assertEqual(
            t.postprocess(test_filter),
            {"my_elements": {
                "$in": ["A", "B", "C"]
            }},
        )
        test_filter = {"$and": [{"elements": {"$in": ["A", "B", "C"]}}]}
        self.assertEqual(
            t.postprocess(test_filter),
            {"$and": [{
                "my_elements": {
                    "$in": ["A", "B", "C"]
                }
            }]},
        )
        test_filter = {"elements": "A"}
        self.assertEqual(t.postprocess(test_filter), {"my_elements": "A"})
        test_filter = ["A", "B", "C"]
        self.assertEqual(t.postprocess(test_filter), ["A", "B", "C"])

        test_filter = ["A", "elements", "C"]
        self.assertEqual(t.postprocess(test_filter), ["A", "elements", "C"])

        test_filter = {"_exmpl_nested_field.sub_property": {"$gt": 1234.5}}
        self.assertEqual(t.postprocess(test_filter),
                         {"nested_field.sub_property": {
                             "$gt": 1234.5
                         }})

        test_filter = {
            "_exmpl_nested_field.sub_property.x": {
                "$exists": False
            }
        }
        self.assertEqual(
            t.postprocess(test_filter),
            {"nested_field.sub_property.x": {
                "$exists": False
            }},
        )
Пример #2
0
 def __init__(
         self,
         collection: Union[pymongo.collection.Collection, mongomock.collection.Collection],
         resource_cls: Resource
 ):
     super().__init__(collection, resource_cls)
     self.transformer = MongoTransformer()
Пример #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,
        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())
Пример #6
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())
Пример #7
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"'))
Пример #8
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("BaseResourceMapper")):
            ALIASES = (
                ("elements", "my_elements"),
                ("A", "D"),
                ("B", "E"),
                ("C", "F"),
                ("_exmpl_nested_field", "nested_field"),
            )

        mapper = MyStructureMapper()
        t = MongoTransformer(mapper=mapper)

        assert mapper.alias_for("elements") == "my_elements"

        test_filter = {"elements": {"$in": ["A", "B", "C"]}}
        assert t.postprocess(test_filter) == {
            "my_elements": {
                "$in": ["A", "B", "C"]
            }
        }
        test_filter = {"$and": [{"elements": {"$in": ["A", "B", "C"]}}]}
        assert t.postprocess(test_filter) == {
            "$and": [{
                "my_elements": {
                    "$in": ["A", "B", "C"]
                }
            }]
        }
        test_filter = {"elements": "A"}
        assert t.postprocess(test_filter) == {"my_elements": "A"}
        test_filter = ["A", "B", "C"]
        assert t.postprocess(test_filter) == ["A", "B", "C"]

        test_filter = ["A", "elements", "C"]
        assert t.postprocess(test_filter) == ["A", "elements", "C"]

        test_filter = {"_exmpl_nested_field.sub_property": {"$gt": 1234.5}}
        assert t.postprocess(test_filter) == {
            "nested_field.sub_property": {
                "$gt": 1234.5
            }
        }

        test_filter = {
            "_exmpl_nested_field.sub_property.x": {
                "$exists": False
            }
        }
        assert t.postprocess(test_filter) == {
            "nested_field.sub_property.x": {
                "$exists": False
            }
        }
Пример #9
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
            }},
        )
Пример #10
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
            }
        }
Пример #11
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))])
Пример #14
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}
Пример #15
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())
Пример #16
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": {
                        "$not": {
                            "$elemMatch": {
                                "id": {
                                    "$nin": ["dummy/2019"]
                                }
                            }
                        }
                    }
                },
                {
                    "relationships.structures.data.0": {
                        "$exists": True
                    }
                },
            ]
        }

        assert t.transform(
            p.parse(
                'structures.id HAS ONLY "dummy/2019" AND structures.id HAS "dummy/2019"'
            )) == {
                "$and": [
                    {
                        "$and": [
                            {
                                "relationships.structures.data": {
                                    "$not": {
                                        "$elemMatch": {
                                            "id": {
                                                "$nin": ["dummy/2019"]
                                            }
                                        }
                                    }
                                }
                            },
                            {
                                "relationships.structures.data.0": {
                                    "$exists": True
                                }
                            },
                        ]
                    },
                    {
                        "relationships.structures.data.id": {
                            "$in": ["dummy/2019"]
                        }
                    },
                ]
            }

        with pytest.raises(
                NotImplementedError,
                match=
                'Cannot filter relationships by field "doi", only "id" is supported.',
        ):
            assert t.transform(
                p.parse(
                    'references.doi HAS ONLY "10.123/12345" AND structures.id HAS "dummy/2019"'
                )) == {
                    "$and": [
                        {
                            "$and": [
                                {
                                    "relationships.references.data": {
                                        "$not": {
                                            "$elemMatch": {
                                                "doi": {
                                                    "$nin": ["10.123/12345"]
                                                }
                                            }
                                        }
                                    }
                                },
                                {
                                    "relationships.references.data.0": {
                                        "$exists": True
                                    }
                                },
                            ]
                        },
                        {
                            "relationships.structures.data.id": {
                                "$in": ["dummy/2019"]
                            }
                        },
                    ]
                }
Пример #17
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
                    }
                },
            ]
        }
Пример #18
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
                }
            }
Пример #19
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"]
                        }
                    },
                ],
            }
Пример #20
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))
Пример #21
0
 def setUp(self):
     p = LarkParser(version=self.version, variant=self.variant)
     t = MongoTransformer()
     self.transform = lambda inp: t.transform(p.parse(inp))