Source code for aiida.orm.implementation.querybuilder

# -*- coding: utf-8 -*-
###########################################################################
# Copyright (c), The AiiDA team. All rights reserved.                     #
# This file is part of the AiiDA code.                                    #
#                                                                         #
# The code is hosted on GitHub at https://github.com/aiidateam/aiida_core #
# For further information on the license, see the LICENSE.txt file        #
# For further information please visit http://www.aiida.net               #
###########################################################################
"""Backend query implementation classes"""
from __future__ import division
from __future__ import print_function
from __future__ import absolute_import
import abc
import six

from aiida.common import exceptions
from aiida.common.lang import abstractclassmethod, type_check
from aiida.common.exceptions import InputValidationError

__all__ = ('BackendQueryBuilder',)


[docs]@six.add_metaclass(abc.ABCMeta) class BackendQueryBuilder(object): """Backend query builder interface""" # pylint: disable=invalid-name,too-many-public-methods,useless-object-inheritance
[docs] def __init__(self, backend): """ :param backend: the backend """ from . import backends type_check(backend, backends.Backend) self._backend = backend
[docs] @abc.abstractmethod def Node(self): """ Decorated as a property, returns the implementation for DbNode. It needs to return a subclass of sqlalchemy.Base, which means that for different ORM's a corresponding dummy-model must be written. """
[docs] @abc.abstractmethod def Computer(self): """ A property, decorated with @property. Returns the implementation for the Computer """
[docs] @abc.abstractmethod def User(self): """ A property, decorated with @property. Returns the implementation for the User """
[docs] @abc.abstractmethod def Group(self): """ A property, decorated with @property. Returns the implementation for the Group """
[docs] @abc.abstractmethod def AuthInfo(self): """ A property, decorated with @property. Returns the implementation for the Group """
[docs] @abc.abstractmethod def Comment(self): """ A property, decorated with @property. Returns the implementation for the Comment """
[docs] @abc.abstractmethod def Log(self): """ A property, decorated with @property. Returns the implementation for the Log """
[docs] @abc.abstractmethod def table_groups_nodes(self): """ A property, decorated with @property. Returns the implementation for the many-to-many relationship between group and nodes. """
@property def AiidaNode(self): """ A property, decorated with @property. Returns the implementation for the AiiDA-class for Node """ from aiida.orm import Node return Node
[docs] @abc.abstractmethod def get_session(self): """ :returns: a valid session, an instance of sqlalchemy.orm.session.Session """
[docs] @abc.abstractmethod def modify_expansions(self, alias, expansions): """ Modify names of projections if ** was specified. This is important for the schema having attributes in a different table. """
[docs] @abstractclassmethod def get_filter_expr_from_attributes(cls, operator, value, attr_key, column=None, column_name=None, alias=None): # pylint: disable=too-many-arguments """ Returns an valid SQLAlchemy expression. :param operator: The operator provided by the user ('==', '>', ...) :param value: The value to compare with, e.g. (5.0, 'foo', ['a','b']) :param str attr_key: The path to that attribute as a tuple of values. I.e. if that attribute I want to filter by is the 2nd element in a list stored under the key 'mylist', this is ('mylist', '2'). :param column: Optional, an instance of sqlalchemy.orm.attributes.InstrumentedAttribute or :param str column_name: The name of the column, and the backend should get the InstrumentedAttribute. :param alias: The aliased class. :returns: An instance of sqlalchemy.sql.elements.BinaryExpression """
[docs] @classmethod def get_filter_expr_from_column(cls, operator, value, column): """ A method that returns an valid SQLAlchemy expression. :param operator: The operator provided by the user ('==', '>', ...) :param value: The value to compare with, e.g. (5.0, 'foo', ['a','b']) :param column: an instance of sqlalchemy.orm.attributes.InstrumentedAttribute or :returns: An instance of sqlalchemy.sql.elements.BinaryExpression """ # Label is used because it is what is returned for the # 'state' column by the hybrid_column construct # Remove when https://github.com/PyCQA/pylint/issues/1931 is fixed # pylint: disable=no-name-in-module,import-error from sqlalchemy.sql.elements import Cast, Label from sqlalchemy.orm.attributes import InstrumentedAttribute, QueryableAttribute from sqlalchemy.sql.expression import ColumnClause from sqlalchemy.types import String if not isinstance(column, (Cast, InstrumentedAttribute, QueryableAttribute, Label, ColumnClause)): raise TypeError('column ({}) {} is not a valid column'.format(type(column), column)) database_entity = column if operator == '==': expr = database_entity == value elif operator == '>': expr = database_entity > value elif operator == '<': expr = database_entity < value elif operator == '>=': expr = database_entity >= value elif operator == '<=': expr = database_entity <= value elif operator == 'like': # the like operator expects a string, so we cast to avoid problems # with fields like UUID, which don't support the like operator expr = database_entity.cast(String).like(value) elif operator == 'ilike': expr = database_entity.ilike(value) elif operator == 'in': expr = database_entity.in_(value) else: raise InputValidationError('Unknown operator {} for filters on columns'.format(operator)) return expr
[docs] @abc.abstractmethod def get_projectable_attribute(self, alias, column_name, attrpath, cast=None, **kwargs): pass
[docs] @abc.abstractmethod def get_aiida_res(self, key, res): """ Some instance returned by ORM (django or SA) need to be converted to Aiida instances (eg nodes) :param key: the key that this entry would be returned with :param res: the result returned by the query :returns: an aiida-compatible instance """
[docs] @abc.abstractmethod def yield_per(self, query, batch_size): """ :param int batch_size: Number of rows to yield per step Yields *count* rows at a time :returns: a generator """
[docs] @abc.abstractmethod def count(self, query): """ :returns: the number of results """
[docs] @abc.abstractmethod def first(self, query): """ Executes query in the backend asking for one instance. :returns: One row of aiida results """
[docs] @abc.abstractmethod def iterall(self, query, batch_size, tag_to_index_dict): """ :return: An iterator over all the results of a list of lists. """
[docs] @abc.abstractmethod def iterdict(self, query, batch_size, tag_to_projected_entity_dict): """ :returns: An iterator over all the results of a list of dictionaries. """
[docs] def get_column(self, colname, alias): # pylint: disable=no-self-use """ Return the column for a given projection. """ try: return getattr(alias, colname) except AttributeError: raise exceptions.InputValidationError("{} is not a column of {}\n" "Valid columns are:\n" "{}".format( colname, alias, '\n'.join(alias._sa_class_manager.mapper.c.keys()) # pylint: disable=protected-access ))