数据库#

高级查询#

关于使用 QueryBuilder 查找您感兴趣的数据的基础知识,请参阅 finding and querying how-to 。本节将介绍一些查询数据库和 QueryBuilder dictionary 的高级方法。

处理边缘#

过滤器和投影既可以应用于查询路径的顶点,也可以应用于连接这些顶点的边。对边应用过滤器或投影的方式与对顶点应用过滤器或投影的方式相同,但现在相关关键字前面加上了 edge_ 。以 ArithmeticAddCalculation 计算作业为例,假设我们要查询加法运算的第一个输入,即 Int node,该输入的标签为 x

from aiida.orm import QueryBuilder
qb = QueryBuilder()
qb.append(CalcJobNode, tag='calcjob')
qb.append(Int, with_outgoing='calcjob', edge_filters={'label': 'x'})

通过使用关键字参数 edge_filters ,我们可以只查询标签为 x 的输入。请注意,任何可用于过滤顶点的操作符也可用于边。假设我们要查找所有通过标有 x 的边与 CalcJobNode 不***连接的输入 Int node:

qb = QueryBuilder()
qb.append(CalcJobNode, tag='calcjob')
qb.append(Int, with_outgoing='calcjob', edge_filters={'label': {'!==': 'x'}})

在这里,相等运算符 == 通过加上感叹号 ! 被否定。请参阅 reference table below 以获取包含所有运算符的表格。与筛选器类似,我们可以使用 edge_project 关键字参数来 投影 边缘信息:

qb = QueryBuilder()
qb.append(CalcJobNode, tag='calcjob')
qb.append(Int, with_outgoing='calcjob', edge_project='label')

在上面的示例中,我们查询的是所有 CalcJobNodeInt node 边标签。

排序和限制结果#

您可以根据实体的属性对查询结果进行排序。例如,您想返回所有 CalcJobNodeInt 输出列表,并按创建时间降序排序,即最近的先返回:

qb = QueryBuilder()
qb.append(CalcJobNode, tag='calcjob')
qb.append(Int, with_incoming='calcjob')
qb.order_by({Int: {'ctime': 'desc'}})

这也可用于根据(嵌套)字典中的值对结果进行排序,例如 attributes 列。不过,在这种情况下, QueryBuilder 无法推断值的类型,因此您必须 cast 类型:

qb = QueryBuilder()
qb.append(CalcJobNode, tag='calcjob')
qb.append(Int, with_incoming='calcjob')
qb.order_by({Int: {'attributes.value': {'order': 'asc', 'cast': 'i'}}})

上述查询将返回所有 CalcJobNode 的输出的所有 Int node,按其值的*升序排列,即从小到大。请注意,在这种情况下,您必须使用字典指定排序操作,其中 order 键详细说明了您希望如何对查询结果进行排序, cast 键告知 QueryBuilder 属性类型。下表列出了可用的铸模类型及其别名:

蟒蛇类型

** 别名**

SQLAlchemy 类型

int

i

整数

浮动

f

浮动

bool

b

布尔型

字符串

t

字符串

口述

j

JSONB

datetime.datetime

d

日期时间

您还可以提供一个字典列表,每个字典指定一种排序操作,从而使用多个属性进行排序:

qb = QueryBuilder()
qb.append(CalcJobNode, tag='calcjob')
qb.append(Int, with_incoming='calcjob')
qb.order_by({Int: [{'attributes.value': {'order': 'asc', 'cast': 'f'}}, {'ctime': 'desc'}]})

在这里, Int node 将首先按其值升序排序。值相等的 Node 随后按修改时间降序排序。

最后,还可以使用 limit() 方法限制返回的查询结果数量。假设您只想要查询的前三个结果:

qb = QueryBuilder()
qb.append(CalcJobNode)
qb.limit(3)

这种方法可以很容易地与 order_by 方法结合起来,以获得数据库中创建的最后三个 CalcJobNode

qb = QueryBuilder()
qb.append(CalcJobNode)
qb.limit(3)
qb.order_by({CalcJobNode: {'ctime': 'desc'}})

参考表格#

所有操作员列表:

操作员

数据类型

举例

说明

==

一应俱全

'id': {'==': 123}

筛选平等

in

一应俱全

'name': {'in': ['foo', 'bar']}

筛选给定列表中的值。

>,<,<=,>=

浮点数、整数、日期

'ctime': {'<': datetime(2016, 03, 03)}

筛选大于或小于特定值的数值

like

字符串

'name': {'like': 'label%'}

筛选匹配子串,其中 %_ 为通配符。要匹配字面意义上的 %_,可在其前缀 \\ 中转义。

ilike

字符串

'name': {'ilike': 'lAbEl%'}

不区分大小写的 like 版本。

or

表达式清单

'id': {'or': [{'<': 12}, {'==': 199}]}

表达式列表,其中至少有一个表达式需要匹配。

and

表达式清单

'id': {'and': [{'<': 12}, {'>': 1}]}

表达式列表,所有表达式都应匹配。

has_key

口述

'attributes': {'has_key': 'some_key'}

筛选包含特定键值的字典。

of_type

任何

'attributes.some_key': {'of_type': 'bool'}

筛选特定类型的值。

of_length

列表

'attributes.some_list': {'of_length': 4}

筛选一定长度的列表。

shorter

列表

'attributes.some_list': {'shorter': 4}

筛选短于特定长度的列表。

longer

列表

'attributes.some_list': {'longer': 4}

筛选长度超过特定长度的列表。

contains

列表

'attributes.some_key': {'contains': ['a', 'b']}

筛选应包含特定值的列表。

在 2.6 版本加入: Logical expression syntax

In the new logical expression syntax for filters and projections, the above operations are distributed by type. When using the autocompletion feature in constructing query expressions, only operations associated with the type of the queried field will be presented.

For example, Node.fields.uuid is a string type, and as such, Node.fields.uuid. will, in addition to == and in_, only suggest like and ilike.

As mentioned in the section about operator negations all operators can be turned into their associated negation (NOT operator) by adding a ! in front of the operator.

在 2.6 版本加入: Programmatic filter negation

In the new logical expression syntax, negation is defined with ~, such that ~(Node.fields.some_field < 1) is equivalent to Node.fields.some_field >= 1.

备注

呈现的 SQL 中(否定)操作符的形式可能与 QueryBuilder 实例中指定的不同。例如, QueryBuilder 中的 !== 运算符在 SQL 中将呈现为 !=

所有关系列表:

来自 的实体

实体到

关系

解释

Node

Node

with_outgoing

一个 node 作为另一个 node 的输入端

Node

Node

with_incoming

一个 node 作为另一个 node 的输出

Node

Node

with_descendants

一个 node 是另一个 node 的祖先(路径)

Node

Node

with_ancestors

一个 node 是另一个 node 的后代(路径)

Node

组别

with_node

node

组别

Node

with_group

node 是一个小组的成员

Node

计算机

with_node

node 的计算机

计算机

Node

with_computer

计算机的 node

Node

用户

with_node

node 的创建者是用户

用户

Node

with_user

node 由用户创建

用户

组别

with_user

node 由用户创建

组别

用户

with_group

node 由用户创建

Node

日志

with_node

node 的对数

日志 Node

Node 评论

with_log with_node

node 有一个日志 node 的注释

评论

Node

with_comment

node 有一个评论

用户

评论

with_user

该评论由用户创建

评论

用户

with_comment

评论的创建者是用户

将查询生成器转换成/转换成字典#

重要

在 aiida-core 第 1 版中,使用 QueryBuilder.queryhelp 访问该字典,该字典现已废弃。

The QueryBuilder class can be converted to a dictionary and also loaded from a dictionary, for easy serialisation and storage. Once you have built your query using the appender method explained in the finding and querying for data how-to and the advanced sections above, you can easily store your query by saving the QueryBuilder.as_dict() dictionary as a JSON file for later use:

import json
from aiida.orm import QueryBuilder

qb = QueryBuilder()
qb.append(CalcJobNode)

with open("querydict.json", "w") as file:
    file.write(json.dumps(qb.as_dict(), indent=4))

要使用该字典实例化 QueryBuilder ,可以使用 from_dict 类方法:

with open("querydict.json", "r") as file:
    query_dict = json.load(file)

qb = QueryBuilder.from_dict(query_dict)

或者,也可以使用字典来设置查询,方法是指定路径、过滤器和投影,然后手工构建字典。为此,您必须指定

  • path :在这里,用户以字典列表的形式指定连接表的路径,其中每个列表项都标识路径中的一个顶点。您可以使用 cls 键定义顶点类::

    query_dict = {
        'path':[
            {'cls': Data}
        ]
    }
    

    查询中的每个实体都必须有一个唯一的标记。如果没有提供标签,则会将其设置为类的名称。但是,如果您在查询中两次选择相同的类,这将不起作用。在这种情况下,您必须使用 tag 键提供标签::

    query_dict = {
        'path':[
            {
                'cls':Node,
                'tag':'node_1'
            },
            {
                'cls':Node,
                'tag':'node_2'
            }
        ]
    }
    

    您还必须详细说明顶点边的一些信息,以便正确连接它们。有几种多余的方法可以做到这一点:

    • 您可以指定该 node 是列表中当前 node 之前另一个 node 的输入或输出。另一个 node 可以用整数或类或类型来指定。下面的示例都是有效的连接指令,假设在路径索引 2 中定义了一个结构体,其标记为 ``struc1``::

      edge_specification = query_dict['path'][3]
      edge_specification['with_incoming'] = 2
      edge_specification['with_incoming'] = StructureData
      edge_specification['with_incoming'] = 'struc1'
      edge_specification['with_outgoing']  = 2
      edge_specification['with_outgoing']  = StructureData
      edge_specification['with_outgoing']  = 'struc1'
      
    • query_dict['path'][<i>]['direction'] = integer

      如果没有指定上述任何规格(``with_outgoing``、``with_incoming``),则会查找键 ``direction``。方向被定义为树上的距离。1 被定义为沿着链接在树中向下移动一步。这意味着 1 将本字典中指定的 node 连接到**之前的 list-item 中指定的 node 作为输出**。方向默认为 1,这就是为什么如果不指定任何内容,这个 node 默认会连接到前一个作为输出。负数将反转链接方向。方向的绝对值定义了相对于自己在列表中的位置要连接到哪个表。绝对值为 1 时,连接到上面的一个表,绝对值为 2 时,连接到上面 2 个索引定义的表。以下两个字典的查询结果相同::

      from aiida.orm import TrajectoryData
      from aiida_quantumespresso.calculations.pw import PwCalculation
      from aiida.orm import Dict
      query_dict_1 = {
          'path': [
              {
                  'cls':PwCalculation
              },
              {
                  'cls':TrajectoryData
              },
              {
                  'cls':Dict,
                  'direction':-2
              }
          ]
      }
      
      # returns same query as:
      
      query_dict_2 = {
          'path':[
              {
                  'cls':PwCalculation
              },
              {
                  'cls':TrajectoryData
              },
              {
                  'cls':Dict,
                  'with_outgoing':PwCalculation
              }
          ]
      }
      
      # Shorter version:
      
      query_dict_3 = {
          'path':[
              Dict,
              PwCalculation,
              TrajectoryData,
          ]
      }
      
  • 什么到 project :确定查询将返回哪些列::

    query_dict = {
        'path':[PwCalculation],
        'project':{
            PwCalculation:['user_id', 'id'],
        }
    }
    

    如果使用的是 JSONB 列,也可以投射存储在 json: 内的值:

    query_dict = {
        'path':[
            PwCalculation,
            StructureData,
        ],
        'project':{
            PwCalculation:['state', 'id'],
        }
    }
    

    返回 PwCalculation 的所有实例的状态和 id,其中结构作为放松计算的输出被链接。输入的字符串必须是列名。如果输入星号(‘*’),查询将返回 AiidaClass 的实例。

  • filters :过滤器可让您进一步指定查询。下面是一个示例,用于查询在特定时间(如过去 4 天)后添加且 id 大于 50 的结构::

    from aiida.common import timezone
    from datetime import timedelta
    
    query_dict = {
        'path':[
            {'cls':PwCalculation}, # PwCalculation with structure as output
            {'cls':StructureData}
        ],
        'filters':{
            StructureData:{
                'ctime':{'>':  timezone.now() - timedelta(days=4)},
                'id':{'>': 50}
            }
        }
    }
    

如果要在 node 之间的链接上添加筛选器和投影,则必须在查询字典中的筛选器和投影中添加这些筛选器和投影。让我们以之前的示例为例,在链接上添加几个过滤器::

query_dict = {
    'path':[
        {'cls':PwCalculation, 'tag':'relax'}, # PwCalculation with structure as output
        {'cls':StructureData, 'tag':'structure'}
    ],
    'filters':{
        'structure':{
            'id':{'>': 50}
        },
        'relax--structure':{
            'label':{'like':'output_%'},
        }
    },
    'project':{
        'relax--structure':['label'],
        'structure':['label'],
        'relax':['label', 'uuid'],
    }
}

请注意,默认情况下,链接的标记是以两个dashes ‘–’ 分隔的两个连接的 node 的标记,而且顺序也很重要。

或者,也可以在定义要连接的实体时,使用 edge_tag : 为路径中的边缘选择标记:

query_dict = {
    'path':[
        {'cls':PwCalculation, 'tag':'relax'},         # Relaxation with structure as output
        {
            'cls':StructureData,
            'tag':'structure',
            'edge_tag':'ThisIsMyLinkTag'     # Definining the link tag
        }
    ],
    'filters':{
        'structure':{
            'id':{'>': 50}
        },
        'ThisIsMyLinkTag':{                  # Using this link tag
            'label':{'like':'output_%'},
        }
    },
    'project':{
        'ThisIsMyLinkTag':['label'],
        'structure':['label'],
        'relax':['label', 'uuid'],
    }
}

可以像这样直接设置限值和偏移量:…..:

query_dict = {
    'path':[Node],
    'limit':10,
    'offset':20
}

query_dict 将告诉查询生成器在跳过前 20 行后返回 10 行。