将复杂的graphql查询转换为优化的数据库查询。

graphql-compiler的Python项目详细描述


graphql编译器

生成状态覆盖状态licensepypi pythonpypi versionpypi statuspypi wheel端到端示例)。

在不久的将来,我们还计划添加来自sqlalchemy元数据的模式自动生成。

有关更详细的概述和入门指南,请参见 我们的博客文章

目录

功能

端到端示例

尽管这个示例专门针对一个orientdb数据库,但它是泛型的 如何使用GraphQL编译器的端到端示例。

fromgraphql.utils.schema_printerimportprint_schemafromgraphql_compilerimport(get_graphql_schema_from_orientdb_schema_data,graphql_to_match)fromgraphql_compiler.schema_generation.orientdb.utilsimportORIENTDB_SCHEMA_RECORDS_QUERY# Step 1: Get schema metadata from hypothetical Animals database.client=your_function_that_returns_an_orientdb_client()schema_records=client.command(ORIENTDB_SCHEMA_RECORDS_QUERY)schema_data=[record.oRecordDataforrecordinschema_records]# Step 2: Generate GraphQL schema from metadata.schema,type_equivalence_hints=get_graphql_schema_from_orientdb_schema_data(schema_data)print(print_schema(schema))# schema {#    query: RootSchemaQuery# }## directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT## directive @tag(tag_name: String!) on FIELD## directive @output(out_name: String!) on FIELD## directive @output_source on FIELD## directive @optional on FIELD## directive @recurse(depth: Int!) on FIELD## directive @fold on FIELD## type Animal {#     name: String#     net_worth: Int#     limbs: Int# }## type RootSchemaQuery{#     Animal: [Animal]# }# Step 3: Write GraphQL query that returns the names of all animals with a certain net worth.# Note that we prefix net_worth with '$' and surround it with quotes to indicate it's a parameter.graphql_query='''{    Animal {        name @output(out_name: "animal_name")        net_worth @filter(op_name: "=", value: ["$net_worth"])    }}'''parameters={'net_worth':'100',}# Step 4: Use autogenerated GraphQL schema to compile query into the target database language.compilation_result=graphql_to_match(schema,graphql_query,parameters,type_equivalence_hints)print(compilation_result.query)# SELECT Animal___1.name AS `animal_name` # FROM  ( MATCH  { class: Animal, where: ((net_worth = decimal("100"))), as: Animal___1 } # RETURN $matches)

定义

{
    Animal {
        name @output(out_name: "name")
        out_Entity_Related {
            ... on Species {
                description @output(out_name: "description")
            }
        }
    }
}

指令

@可选

如果没有此指令,当查询包含顶点字段时,任何与该查询匹配的结果 必须能够生成该顶点场的值。应用于顶点场, 此指令防止无法为该字段生成值的结果集 被丢弃,并允许继续处理查询的其余部分。

示例使用

{
    Animal {
        name @output(out_name: "name")
        out_Animal_ParentOf @optional {
            name @output(out_name: "child_name")
        }
    }
}

对于每种动物

约束和规则
  • @可选只能应用于顶点字段,根顶点字段除外。
  • 允许在可选范围内展开顶点字段。 但是,这样做目前与match中的性能惩罚相关。 有关详细信息,请参见:展开可选顶点字段。
  • @recurse@fold@output_source不能与@optional在同一个顶点字段中使用
  • @output\u source@fold不能在作用域内的任何位置使用 标记为@可选

如果给定的结果集无法为标记为"可选"的顶点字段生成值, 在该顶点字段中,任何标有@output的字段都返回空值。

当过滤(通过@filter)或类型强制(通过.)时…适用于动物 在标记为@optional的顶点字段处或其内,优先使用@optional

  • 如果给定的结果集无法生成可选顶点字段的值,则将保留该值: 首先应用@可选的指令,不进行筛选或类型强制开可能发生。
  • 如果给定的结果集能够生成可选顶点场的值, @可选的不适用,然后根据筛选或类型检查该值 强迫。这些后续操作可能会导致结果集被丢弃 不匹配。

例如,假设我们有两个名为albertbetty顶点,这样就有一个albertbetty的边

然后执行以下查询:

{
  Person {
    out_Person_Knows @optional {
      name @filter(op_name: "=", value: ["$name"])
    }
    name @output(out_name: "person_name")
  }
}

使用运行时参数

{"name":"Charles"}

会输出一个空列表,因为您知道的人albertbetty的边满足了@可选的指令,但是betty与对名为charles的节点的筛选器检查不匹配

然而,如果没有这样的人知道从albert存在边缘,那么输出将是

{name:'Albert'}

因为没有这样的边可以满足@可选的指令,并且不会进行过滤。

@输出

表示属性字段的值应包含在输出中。 其out_name参数指定 应返回输出值。

示例使用

{
    Animal {
        name @output(out_name: "animal_name")
    }
}

此查询返回图形中每个动物的名称,在名为动物名称的列中。

约束和规则
  • @输出只能应用于属性字段。
  • out_name提供的值只能由大写或小写字母组成 (a-za-z)或下划线(/code>)。
  • out_name提供的值不能以\uu作为前缀(三个下划线)。这个 命名空间保留给编译器内部使用。
  • 对于任何给定的查询,所有out_name值都必须是唯一的。换句话说,输出列必须 有唯一的名字。

如果标记为@output的属性字段存在于标记为@optional的范围内,则结果设置为 无法将值赋给可选作用域返回值null作为输出 属性字段的值。

@折叠

@fold应用于范围"折叠"该范围内的所有输出:而不是显示 在查询结果中的单独行上,折叠的输出被合并成列表,开始 在标有@fold的范围内

示例使用

{
    Animal {
        name @output(out_name: "animal_name")
        out_Animal_ParentOf @fold {
            name @output(out_name: "child_names")
        }
    }
}

每个返回的行有两列:animal_name在图中每个animal的名称, 以及子动物名以及动物名的所有子动物名列表。 如果给定的动物没有子项,则其子名称列表为空。

约束和规则

示例

下面的graphql是不允许的,它将生成一个graphqlcompilationerror。 此查询无效,原因有两个:

  • 它在@output指令(outputtinganimal\u name)之后展开顶点字段
  • 动物体内的范围,位于标记为@fold的范围内, 扩展两个顶点场而不是最多一个。
pip install graphql-compiler
0

以下是@fold的有效用法:

pip install graphql-compiler
1

@标签

@tag指令允许根据同一查询中其他地方遇到的值进行筛选。 应用于属性字段,它为该属性字段的值指定一个名称,允许 值,然后用作@filter指令的一部分。

要向@filter指令提供标记值,请放置标记名(前缀为%符号) 在@filter'svalue数组中。请参见传递参数 有关详细信息。

示例使用

pip install graphql-compiler
2

此查询返回的每一行在子名称列中包含动物的名称 它是另一种动物的后代,其名称在词汇上小于 其母公司的名称。

约束和规则
  • @标记只能应用于属性字段。
  • 标记名提供的值只能由大写或小写字母组成 (a-za-z)或下划线(/code>)。
  • 对于任何给定的查询,所有标记名的值都必须是唯一的。
  • 不能应用于标记为@fold的范围内的属性字段
  • 允许使用引用同一顶点内标记的@tag@filter, 只要两者不出现在完全相同的属性字段中。

@过滤器

允许根据一组筛选操作中的任何一个来筛选要返回的数据。 从概念上讲,它相当于sqlwhere关键字的graphql。

请参见支持的筛选操作 有关编译器当前支持的各种类型筛选的详细信息。 这些操作目前是在编译器中硬编码的;将来, 我们可以通过编译器插件添加自定义筛选操作。

可以同时对同一字段应用多个@filter指令。在概念上, 这就好像不同的@filter指令是由sql关键字连接起来的。

允许使用引用同一顶点内标记的@tag@filter, 只要两者不出现在完全相同的属性字段中。

传递参数

@filter指令接受两种类型的参数:运行时参数和标记的参数。

运行时参数用前缀(例如$foo)表示,并表示参数 其值将在运行时已知。编译器将编译graphql查询,留下 在运行时填充值的点。编译后,用户必须为 所有运行时参数及其值将被插入到最终查询中,然后 对检察官执行死刑表<<P>

考虑以下问题:

pip install graphql-compiler
3

它为每个animal顶点返回一行,该顶点的颜色等于$animal\u color。每行 在名为"动物名"的列中包含动物名。参数$animal_color是 一个运行时参数——用户必须传入一个值(例如{"animal\u color":"blue"}) 将在查询数据库之前插入查询。

标记的参数用前缀(例如%foo)表示,并表示参数 其值派生自查询中其他地方遇到的属性字段。 如果用户使用@tag指令和适当的名称标记属性字段, 该值可用作所有后续@filter指令中的标记参数。

考虑以下问题:

pip install graphql-compiler
4

它返回包含父母名字的动物的名字作为自己的子串。 数据库将父动物名称的值捕获为父动物名称标记,然后 然后将值用作子动物的@filter中的%parent_name标记参数

我们考虑并拒绝了允许文字值的想法(例如123) 作为@filter参数,有几个原因:

  • @filter指令的值字段的graphql类型不能合理地包含 人们可能提供的所有不同类型的论据。即使只计算标量类型, 已经有了id,int,float,boolean,string,date,datetime…--太多了,无法包含。
  • 当参数的值已知是固定的时,将使用文本值。我们可以 通过使用具有固定值的运行时参数轻松完成相同的任务。这种方法 有可能减少必须 已编译:两个具有不同文字值的查询必须编译两次,而 使用两组不同的运行时参数只需要编译一个查询。
  • 我们担心可能会意外误用文字值。SQL系统有 几十年来支持的存储过程和参数化查询,以及即席sql查询 通过简单的字符串插值构造仍然是一个严重的问题,并且是 许多SQL注入漏洞。我们认为在查询中不允许文本值将 大大减少不安全字符串插值的使用和风险, 以可接受的成本。

约束和规则
  • op_name提供的值只能由大写或小写字母组成 (a-za-z)或下划线(/code>)。
  • 列表中提供的值必须以$开头 (表示运行时参数)或%(表示标记参数)。 后跟大写或小写字母(a-za-z)或下划线(/code>)。
  • 与给定@filter查询中的任何标记参数对应的@tag指令 必须应用于与带有@filter的字段位于同一顶点的字段, 或者严格在带有@filter指令的字段之前。
  • "无法比较苹果和桔子"--提供给@filter的参数的graphql类型 必须与编译器根据应用@filter的字段推断的GraphQL类型匹配。
  • 如果与标记参数对应的@tag来自顶点字段中 标记为@optional,发出的@filter代码检查@optional字段是否 分配了一个值。如果没有值是assi登录到@optional字段,与 从该字段中标记的参数返回true
    • 例如,假设%from_optional源于@optional作用域,当没有值时 分配给@可选的字段:
      • 使用@filter(op_name:"=",值:[%from_optional"])等同于not 有过滤器;
      • 使用@filter(op_name:"between",value:["$lower","%from_optional"])相当于 @filter(操作名:">;=",值:["$lower"])
  • 允许使用引用同一顶点内标记的@tag@filter, 只要两者不出现在完全相同的属性字段中。

@递归

应用于顶点场,指定将该顶点场连接到当前 顶点应该被重复访问,直到depth次。递归总是开始的 在深度=0时,即当前顶点--请参见以下各节以获得更详细的解释。

示例使用

假设用户想要获取每个动物的子孙的名字。 这可以通过运行以下两个查询并连接它们的结果来完成:

pip install graphql-compiler
5
pip install graphql-compiler
6

如果用户还想将曾孙添加到子代输出中,则 需要另一个查询,依此类推。而不是连接多个查询的结果, 用户只需使用@recurse指令即可。下面的查询返回 孙辈:

pip install graphql-compiler
7

此查询返回的每一行都包含祖先列中动物的名称 以及其子代或孙代的名称。 标记为"递归"的顶点字段的"输出动物"父字段已包含在 另一个out_animal_parentof顶点字段,因此递归从 "child"级别(未用@recurse标记的输出动物的父级)。 因此,后代列包含祖先的名称 子级(从递归的depth=0开始)及其子级的名称(从depth=1开始)。

使用此指令的递归是可能的,因为封闭作用域和递归的类型 作用域计算:recurse指令应用于animal类型的顶点字段 它的顶点域被封装在animal类型的范围内。 下面将详细介绍允许递归的其他情况。

由于@递归,因此后代列不能有祖先动物的名称 已经在一个out_animal_parentof中,而不是在根animal顶点字段中。 同样,它不能删除超过两个步骤的子代 (例如,曾孙),因为@recursedepth参数设置为1

现在,让我们看看当我们消除外部out_animal_parentof顶点字段时会发生什么 只需将@recurse应用于根顶点字段作用域中的out\u animal父对象

pip install graphql-compiler
8

在这种情况下,当递归开始于depth=0时,递归范围内的动物 在根顶点字段的animal将相同,因此,在 递归,self_或_后代的值将等于 "祖先"字段。

约束和规则
  • "类型必须解决"--当在类型a的范围内应用时, 到b,必须至少满足以下条件之一:
    • a是一个graphql联合体;
    • b是一个graphql接口,a是实现该接口的类型;
    • ab是同一类型。
  • @recurse只能应用于查询的根顶点字段以外的顶点字段。
  • 不能在标记为@可选@折叠的范围内使用
  • 递归的depth参数的值必须始终大于或等于1。 使用depth=1沿 指定边缘。
  • 在标记为recurse的作用域中,类型强制和@filter指令不限制 递归深度。概念上,先递归到指定深度, 然后类型强制和@filter指令消除一些到达的位置 通过递归。
  • 如上面的例子所示,递归总是从深度0开始, 所以递归作用域总是包含 标记为@recurse的顶点字段

@输出源

请参阅返回结果的完整性部分 有关指令和示例的说明。

约束和规则
  • 在任何给定的graphql查询中最多只能存在一次。
  • 只能存在于顶点字段上,并且只能存在于查询中使用的最后一个顶点字段上。
  • 不能在标记为@可选@折叠的范围内使用

支持的筛选操作

比较运算符

支持的比较运算符:

  • 等于:=
  • 不等于:!=
  • 大于:>;
  • 小于:<;
  • 大于或等于:>;=
  • 小于或等于:<;=

示例使用

等于(=):
pip install graphql-compiler
9

这将为每个物种返回一行,其名称等于$物种名称的值 参数。每一行在名为species_uid的列中包含uuidspecies_uid

大于或等于(>;=):
fromgraphql.utils.schema_printerimportprint_schemafromgraphql_compilerimport(get_graphql_schema_from_orientdb_schema_data,graphql_to_match)fromgraphql_compiler.schema_generation.orientdb.utilsimportORIENTDB_SCHEMA_RECORDS_QUERY# Step 1: Get schema metadata from hypothetical Animals database.client=your_function_that_returns_an_orientdb_client()schema_records=client.command(ORIENTDB_SCHEMA_RECORDS_QUERY)schema_data=[record.oRecordDataforrecordinschema_records]# Step 2: Generate GraphQL schema from metadata.schema,type_equivalence_hints=get_graphql_schema_from_orientdb_schema_data(schema_data)print(print_schema(schema))# schema {#    query: RootSchemaQuery# }## directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT## directive @tag(tag_name: String!) on FIELD## directive @output(out_name: String!) on FIELD## directive @output_source on FIELD## directive @optional on FIELD## directive @recurse(depth: Int!) on FIELD## directive @fold on FIELD## type Animal {#     name: String#     net_worth: Int#     limbs: Int# }## type RootSchemaQuery{#     Animal: [Animal]# }# Step 3: Write GraphQL query that returns the names of all animals with a certain net worth.# Note that we prefix net_worth with '$' and surround it with quotes to indicate it's a parameter.graphql_query='''{    Animal {        name @output(out_name: "animal_name")        net_worth @filter(op_name: "=", value: ["$net_worth"])    }}'''parameters={'net_worth':'100',}# Step 4: Use autogenerated GraphQL schema to compile query into the target database language.compilation_result=graphql_to_match(schema,graphql_query,parameters,type_equivalence_hints)print(compilation_result.query)# SELECT Animal___1.name AS `animal_name` # FROM  ( MATCH  { class: Animal, where: ((net_worth = decimal("100"))), as: Animal___1 } # RETURN $matches)
0

这将为每个动物顶点返回一行,该顶点是在$point_in_time之后或之上出生的。 每行分别在名为namebirthday的列中包含动物的名称和生日。

约束和规则
  • 所有比较运算符都必须位于属性字段上。

姓名或别名

允许您在包含精确字符串$wanted_name_或_alias的顶点上进行筛选 名称别名字段。

示例使用

fromgraphql.utils.schema_printerimportprint_schemafromgraphql_compilerimport(get_graphql_schema_from_orientdb_schema_data,graphql_to_match)fromgraphql_compiler.schema_generation.orientdb.utilsimportORIENTDB_SCHEMA_RECORDS_QUERY# Step 1: Get schema metadata from hypothetical Animals database.client=your_function_that_returns_an_orientdb_client()schema_records=client.command(ORIENTDB_SCHEMA_RECORDS_QUERY)schema_data=[record.oRecordDataforrecordinschema_records]# Step 2: Generate GraphQL schema from metadata.schema,type_equivalence_hints=get_graphql_schema_from_orientdb_schema_data(schema_data)print(print_schema(schema))# schema {#    query: RootSchemaQuery# }## directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT## directive @tag(tag_name: String!) on FIELD## directive @output(out_name: String!) on FIELD## directive @output_source on FIELD## directive @optional on FIELD## directive @recurse(depth: Int!) on FIELD## directive @fold on FIELD## type Animal {#     name: String#     net_worth: Int#     limbs: Int# }## type RootSchemaQuery{#     Animal: [Animal]# }# Step 3: Write GraphQL query that returns the names of all animals with a certain net worth.# Note that we prefix net_worth with '$' and surround it with quotes to indicate it's a parameter.graphql_query='''{    Animal {        name @output(out_name: "animal_name")        net_worth @filter(op_name: "=", value: ["$net_worth"])    }}'''parameters={'net_worth':'100',}# Step 4: Use autogenerated GraphQL schema to compile query into the target database language.compilation_result=graphql_to_match(schema,graphql_query,parameters,type_equivalence_hints)print(compilation_result.query)# SELECT Animal___1.name AS `animal_name` # FROM  ( MATCH  { class: Animal, where: ((net_worth = decimal("100"))), as: Animal___1 } # RETURN $matches)
1

这将为名称和/或别名等于$所需名称或别名的每个动物顶点返回一行。 每一行在名为name的列中包含动物的名称。

$wanted_name_或_alias提供的值必须是动物的全名和/或别名。 子字符串将不匹配。

约束和规则
  • 必须位于具有名称别名属性的顶点字段上。

介于

示例使用

fromgraphql.utils.schema_printerimportprint_schemafromgraphql_compilerimport(get_graphql_schema_from_orientdb_schema_data,graphql_to_match)fromgraphql_compiler.schema_generation.orientdb.utilsimportORIENTDB_SCHEMA_RECORDS_QUERY# Step 1: Get schema metadata from hypothetical Animals database.client=your_function_that_returns_an_orientdb_client()schema_records=client.command(ORIENTDB_SCHEMA_RECORDS_QUERY)schema_data=[record.oRecordDataforrecordinschema_records]# Step 2: Generate GraphQL schema from metadata.schema,type_equivalence_hints=get_graphql_schema_from_orientdb_schema_data(schema_data)print(print_schema(schema))# schema {#    query: RootSchemaQuery# }## directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT## directive @tag(tag_name: String!) on FIELD## directive @output(out_name: String!) on FIELD## directive @output_source on FIELD## directive @optional on FIELD## directive @recurse(depth: Int!) on FIELD## directive @fold on FIELD## type Animal {#     name: String#     net_worth: Int#     limbs: Int# }## type RootSchemaQuery{#     Animal: [Animal]# }# Step 3: Write GraphQL query that returns the names of all animals with a certain net worth.# Note that we prefix net_worth with '$' and surround it with quotes to indicate it's a parameter.graphql_query='''{    Animal {        name @output(out_name: "animal_name")        net_worth @filter(op_name: "=", value: ["$net_worth"])    }}'''parameters={'net_worth':'100',}# Step 4: Use autogenerated GraphQL schema to compile query into the target database language.compilation_result=graphql_to_match(schema,graphql_query,parameters,type_equivalence_hints)print(compilation_result.query)# SELECT Animal___1.name AS `animal_name` # FROM  ( MATCH  { class: Animal, where: ((net_worth = decimal("100"))), as: Animal___1 } # RETURN $matches)
2

返回:

  • 对于生日介于$lower$upper日期(含)之间的每个动物顶点,一行。 每一行在名为name的列中包含动物的名称。

约束和规则
  • 必须在属性字段上。
  • 上下界表示terval,这意味着输出可以 包含完全匹配的值。

收藏中

示例使用

fromgraphql.utils.schema_printerimportprint_schemafromgraphql_compilerimport(get_graphql_schema_from_orientdb_schema_data,graphql_to_match)fromgraphql_compiler.schema_generation.orientdb.utilsimportORIENTDB_SCHEMA_RECORDS_QUERY# Step 1: Get schema metadata from hypothetical Animals database.client=your_function_that_returns_an_orientdb_client()schema_records=client.command(ORIENTDB_SCHEMA_RECORDS_QUERY)schema_data=[record.oRecordDataforrecordinschema_records]# Step 2: Generate GraphQL schema from metadata.schema,type_equivalence_hints=get_graphql_schema_from_orientdb_schema_data(schema_data)print(print_schema(schema))# schema {#    query: RootSchemaQuery# }## directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT## directive @tag(tag_name: String!) on FIELD## directive @output(out_name: String!) on FIELD## directive @output_source on FIELD## directive @optional on FIELD## directive @recurse(depth: Int!) on FIELD## directive @fold on FIELD## type Animal {#     name: String#     net_worth: Int#     limbs: Int# }## type RootSchemaQuery{#     Animal: [Animal]# }# Step 3: Write GraphQL query that returns the names of all animals with a certain net worth.# Note that we prefix net_worth with '$' and surround it with quotes to indicate it's a parameter.graphql_query='''{    Animal {        name @output(out_name: "animal_name")        net_worth @filter(op_name: "=", value: ["$net_worth"])    }}'''parameters={'net_worth':'100',}# Step 4: Use autogenerated GraphQL schema to compile query into the target database language.compilation_result=graphql_to_match(schema,graphql_query,parameters,type_equivalence_hints)print(compilation_result.query)# SELECT Animal___1.name AS `animal_name` # FROM  ( MATCH  { class: Animal, where: ((net_worth = decimal("100"))), as: Animal___1 } # RETURN $matches)
3

这将为每个动物顶点返回一行,该顶点的颜色包含在颜色列表中。 每一行分别在名为动物名颜色的列中包含动物的名称和颜色。

约束和规则
  • 必须位于非列表类型的属性字段上。

不在集合中

示例使用

fromgraphql.utils.schema_printerimportprint_schemafromgraphql_compilerimport(get_graphql_schema_from_orientdb_schema_data,graphql_to_match)fromgraphql_compiler.schema_generation.orientdb.utilsimportORIENTDB_SCHEMA_RECORDS_QUERY# Step 1: Get schema metadata from hypothetical Animals database.client=your_function_that_returns_an_orientdb_client()schema_records=client.command(ORIENTDB_SCHEMA_RECORDS_QUERY)schema_data=[record.oRecordDataforrecordinschema_records]# Step 2: Generate GraphQL schema from metadata.schema,type_equivalence_hints=get_graphql_schema_from_orientdb_schema_data(schema_data)print(print_schema(schema))# schema {#    query: RootSchemaQuery# }## directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT## directive @tag(tag_name: String!) on FIELD## directive @output(out_name: String!) on FIELD## directive @output_source on FIELD## directive @optional on FIELD## directive @recurse(depth: Int!) on FIELD## directive @fold on FIELD## type Animal {#     name: String#     net_worth: Int#     limbs: Int# }## type RootSchemaQuery{#     Animal: [Animal]# }# Step 3: Write GraphQL query that returns the names of all animals with a certain net worth.# Note that we prefix net_worth with '$' and surround it with quotes to indicate it's a parameter.graphql_query='''{    Animal {        name @output(out_name: "animal_name")        net_worth @filter(op_name: "=", value: ["$net_worth"])    }}'''parameters={'net_worth':'100',}# Step 4: Use autogenerated GraphQL schema to compile query into the target database language.compilation_result=graphql_to_match(schema,graphql_query,parameters,type_equivalence_hints)print(compilation_result.query)# SELECT Animal___1.name AS `animal_name` # FROM  ( MATCH  { class: Animal, where: ((net_worth = decimal("100"))), as: Animal___1 } # RETURN $matches)
4

这将为每个动物顶点返回一行,该顶点的颜色不包含在颜色列表中。 每一行分别在名为动物名颜色的列中包含动物的名称和颜色。

约束和规则
  • 必须位于非列表类型的属性字段上。

有_子串

示例使用

fromgraphql.utils.schema_printerimportprint_schemafromgraphql_compilerimport(get_graphql_schema_from_orientdb_schema_data,graphql_to_match)fromgraphql_compiler.schema_generation.orientdb.utilsimportORIENTDB_SCHEMA_RECORDS_QUERY# Step 1: Get schema metadata from hypothetical Animals database.client=your_function_that_returns_an_orientdb_client()schema_records=client.command(ORIENTDB_SCHEMA_RECORDS_QUERY)schema_data=[record.oRecordDataforrecordinschema_records]# Step 2: Generate GraphQL schema from metadata.schema,type_equivalence_hints=get_graphql_schema_from_orientdb_schema_data(schema_data)print(print_schema(schema))# schema {#    query: RootSchemaQuery# }## directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT## directive @tag(tag_name: String!) on FIELD## directive @output(out_name: String!) on FIELD## directive @output_source on FIELD## directive @optional on FIELD## directive @recurse(depth: Int!) on FIELD## directive @fold on FIELD## type Animal {#     name: String#     net_worth: Int#     limbs: Int# }## type RootSchemaQuery{#     Animal: [Animal]# }# Step 3: Write GraphQL query that returns the names of all animals with a certain net worth.# Note that we prefix net_worth with '$' and surround it with quotes to indicate it's a parameter.graphql_query='''{    Animal {        name @output(out_name: "animal_name")        net_worth @filter(op_name: "=", value: ["$net_worth"])    }}'''parameters={'net_worth':'100',}# Step 4: Use autogenerated GraphQL schema to compile query into the target database language.compilation_result=graphql_to_match(schema,graphql_query,parameters,type_equivalence_hints)print(compilation_result.query)# SELECT Animal___1.name AS `animal_name` # FROM  ( MATCH  { class: Animal, where: ((net_worth = decimal("100"))), as: Animal___1 } # RETURN $matches)
5

这将为其名称包含所提供值的每个动物顶点返回一行 对于$substring参数。每行包含匹配的动物名 在名为"动物名"的列中

约束和规则
  • 必须在字符串类型的属性字段上。

包含

示例使用

fromgraphql.utils.schema_printerimportprint_schemafromgraphql_compilerimport(get_graphql_schema_from_orientdb_schema_data,graphql_to_match)fromgraphql_compiler.schema_generation.orientdb.utilsimportORIENTDB_SCHEMA_RECORDS_QUERY# Step 1: Get schema metadata from hypothetical Animals database.client=your_function_that_returns_an_orientdb_client()schema_records=client.command(ORIENTDB_SCHEMA_RECORDS_QUERY)schema_data=[record.oRecordDataforrecordinschema_records]# Step 2: Generate GraphQL schema from metadata.schema,type_equivalence_hints=get_graphql_schema_from_orientdb_schema_data(schema_data)print(print_schema(schema))# schema {#    query: RootSchemaQuery# }## directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT## directive @tag(tag_name: String!) on FIELD## directive @output(out_name: String!) on FIELD## directive @output_source on FIELD## directive @optional on FIELD## directive @recurse(depth: Int!) on FIELD## directive @fold on FIELD## type Animal {#     name: String#     net_worth: Int#     limbs: Int# }## type RootSchemaQuery{#     Animal: [Animal]# }# Step 3: Write GraphQL query that returns the names of all animals with a certain net worth.# Note that we prefix net_worth with '$' and surround it with quotes to indicate it's a parameter.graphql_query='''{    Animal {        name @output(out_name: "animal_name")        net_worth @filter(op_name: "=", value: ["$net_worth"])    }}'''parameters={'net_worth':'100',}# Step 4: Use autogenerated GraphQL schema to compile query into the target database language.compilation_result=graphql_to_match(schema,graphql_query,parameters,type_equivalence_hints)print(compilation_result.query)# SELECT Animal___1.name AS `animal_name` # FROM  ( MATCH  { class: Animal, where: ((net_worth = decimal("100"))), as: Animal___1 } # RETURN $matches)
6

这将为其别名列表包含所提供值的每个动物顶点返回一行 对于$想要的参数。每行包含匹配的动物名 在名为"动物名"的列中

约束和规则
  • 必须在列表类型的属性字段中。

不包含

示例使用

fromgraphql.utils.schema_printerimportprint_schemafromgraphql_compilerimport(get_graphql_schema_from_orientdb_schema_data,graphql_to_match)fromgraphql_compiler.schema_generation.orientdb.utilsimportORIENTDB_SCHEMA_RECORDS_QUERY# Step 1: Get schema metadata from hypothetical Animals database.client=your_function_that_returns_an_orientdb_client()schema_records=client.command(ORIENTDB_SCHEMA_RECORDS_QUERY)schema_data=[record.oRecordDataforrecordinschema_records]# Step 2: Generate GraphQL schema from metadata.schema,type_equivalence_hints=get_graphql_schema_from_orientdb_schema_data(schema_data)print(print_schema(schema))# schema {#    query: RootSchemaQuery# }## directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT## directive @tag(tag_name: String!) on FIELD## directive @output(out_name: String!) on FIELD## directive @output_source on FIELD## directive @optional on FIELD## directive @recurse(depth: Int!) on FIELD## directive @fold on FIELD## type Animal {#     name: String#     net_worth: Int#     limbs: Int# }## type RootSchemaQuery{#     Animal: [Animal]# }# Step 3: Write GraphQL query that returns the names of all animals with a certain net worth.# Note that we prefix net_worth with '$' and surround it with quotes to indicate it's a parameter.graphql_query='''{    Animal {        name @output(out_name: "animal_name")        net_worth @filter(op_name: "=", value: ["$net_worth"])    }}'''parameters={'net_worth':'100',}# Step 4: Use autogenerated GraphQL schema to compile query into the target database language.compilation_result=graphql_to_match(schema,graphql_query,parameters,type_equivalence_hints)print(compilation_result.query)# SELECT Animal___1.name AS `animal_name` # FROM  ( MATCH  { class: Animal, where: ((net_worth = decimal("100"))), as: Animal___1 } # RETURN $matches)
7

这将为其别名列表不包含所提供值的每个动物顶点返回一行 对于$想要的参数。每行包含匹配的动物名 在名为"动物名"的列中

约束和规则
  • 必须在列表类型的属性字段中。

相交

示例使用

fromgraphql.utils.schema_printerimportprint_schemafromgraphql_compilerimport(get_graphql_schema_from_orientdb_schema_data,graphql_to_match)fromgraphql_compiler.schema_generation.orientdb.utilsimportORIENTDB_SCHEMA_RECORDS_QUERY# Step 1: Get schema metadata from hypothetical Animals database.client=your_function_that_returns_an_orientdb_client()schema_records=client.command(ORIENTDB_SCHEMA_RECORDS_QUERY)schema_data=[record.oRecordDataforrecordinschema_records]# Step 2: Generate GraphQL schema from metadata.schema,type_equivalence_hints=get_graphql_schema_from_orientdb_schema_data(schema_data)print(print_schema(schema))# schema {#    query: RootSchemaQuery# }## directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT## directive @tag(tag_name: String!) on FIELD## directive @output(out_name: String!) on FIELD## directive @output_source on FIELD## directive @optional on FIELD## directive @recurse(depth: Int!) on FIELD## directive @fold on FIELD## type Animal {#     name: String#     net_worth: Int#     limbs: Int# }## type RootSchemaQuery{#     Animal: [Animal]# }# Step 3: Write GraphQL query that returns the names of all animals with a certain net worth.# Note that we prefix net_worth with '$' and surround it with quotes to indicate it's a parameter.graphql_query='''{    Animal {        name @output(out_name: "animal_name")        net_worth @filter(op_name: "=", value: ["$net_worth"])    }}'''parameters={'net_worth':'100',}# Step 4: Use autogenerated GraphQL schema to compile query into the target database language.compilation_result=graphql_to_match(schema,graphql_query,parameters,type_equivalence_hints)print(compilation_result.query)# SELECT Animal___1.name AS `animal_name` # FROM  ( MATCH  { class: Animal, where: ((net_worth = decimal("100"))), as: Animal___1 } # RETURN $matches)
8

这将为其别名列表具有非空交集的每个动物顶点返回一行 以及为$wanted参数提供的值列表。 每一行在名为"动物名"的列中都包含匹配的动物名。

约束和规则
  • 必须在列表类型的属性字段中。

有边缘度

示例使用

fromgraphql.utils.schema_printerimportprint_schemafromgraphql_compilerimport(get_graphql_schema_from_orientdb_schema_data,graphql_to_match)fromgraphql_compiler.schema_generation.orientdb.utilsimportORIENTDB_SCHEMA_RECORDS_QUERY# Step 1: Get schema metadata from hypothetical Animals database.client=your_function_that_returns_an_orientdb_client()schema_records=client.command(ORIENTDB_SCHEMA_RECORDS_QUERY)schema_data=[record.oRecordDataforrecordinschema_records]# Step 2: Generate GraphQL schema from metadata.schema,type_equivalence_hints=get_graphql_schema_from_orientdb_schema_data(schema_data)print(print_schema(schema))# schema {#    query: RootSchemaQuery# }## directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT## directive @tag(tag_name: String!) on FIELD## directive @output(out_name: String!) on FIELD## directive @output_source on FIELD## directive @optional on FIELD## directive @recurse(depth: Int!) on FIELD## directive @fold on FIELD## type Animal {#     name: String#     net_worth: Int#     limbs: Int# }## type RootSchemaQuery{#     Animal: [Animal]# }# Step 3: Write GraphQL query that returns the names of all animals with a certain net worth.# Note that we prefix net_worth with '$' and surround it with quotes to indicate it's a parameter.graphql_query='''{    Animal {        name @output(out_name: "animal_name")        net_worth @filter(op_name: "=", value: ["$net_worth"])    }}'''parameters={'net_worth':'100',}# Step 4: Use autogenerated GraphQL schema to compile query into the target database language.compilation_result=graphql_to_match(schema,graphql_query,parameters,type_equivalence_hints)print(compilation_result.query)# SELECT Animal___1.name AS `animal_name` # FROM  ( MATCH  { class: Animal, where: ((net_worth = decimal("100"))), as: Animal___1 } # RETURN $matches)
9

这将为每个具有$child\u count子节点的动物顶点返回一行 (即,当out_animal_parentof边正好出现$child_count次)。 每一行都包含匹配的动物名,在名为动物名的列中。

顶点字段中的uuid字段被添加以满足 graphql语法规则,要求任何{}中至少存在一个字段。 由于此字段未标记任何指令,因此对查询没有影响。

注意:请注意上面正在过滤的顶点字段上的@optional指令。 如果在您的用例中希望将$child\u count设置为0,则还必须将其标记为 顶点字段@可选。回想一下,没有@optional意味着至少有一个 这样的边缘一定存在。如果将参数设置为0时使用has_edge_degree过滤器, 这就要求边缘不存在。因此,如果在此情况下不存在可选的, 无法生成有效的结果集,结果查询将不返回任何结果。

约束和规则
  • 必须位于不是查询根顶点的顶点字段上。
  • 标记值不支持为此过滤器的参数。
  • 如果此运算符的运行时参数可以是0,则强烈建议也应用 @可选到要过滤的顶点字段(有关详细信息,请参见上面的N.B.)。

类型强制

类型强制是创建类型不同于 强制的封闭范围——它们将封闭范围强制为不同的类型。 类型强制用graphql内联片段表示。

示例使用

{
    Animal {
        name @output(out_name: "name")
        out_Entity_Related {
            ... on Species {
                description @output(out_name: "description")
            }
        }
    }
}
0

在这里,out-species\u eats顶点字段是union-foodorspecies\u-speciesunion类型。继续进行 使用查询时,用户必须选择要使用的union\u foodorspecies\u speciesunion中的哪些类型。 在本例中,。on food表示选择了food类型,以及任何顶点 在该范围内,不属于类型的食物被过滤掉并丢弃。

{
    Animal {
        name @output(out_name: "name")
        out_Entity_Related {
            ... on Species {
                description @output(out_name: "description")
            }
        }
    }
}
1

在此查询中,与输出实体相关的属于实体类型。但是,查询只想 返回结果,其中相关实体是物种,该物种。关于物种确保是这样。

元字段

_类型名

编译器支持标准的graphql元字段\u typename,它返回运行时类型 字段所在范围的。假设graphql模式与数据库的模式匹配, 运行时类型将始终是作用域的静态类型的子类型(或完全等于) 由graphql类型系统确定。下面,我们提供一个示例查询,其中 运行时类型是静态类型的子类型,但不等于静态类型。

\u typename字段被视为字符串类型的属性字段,并支持 可应用于任何其他属性字段的所有指令。

示例使用

{
    Animal {
        name @output(out_name: "name")
        out_Entity_Related {
            ... on Species {
                description @output(out_name: "description")
            }
        }
    }
}
2

此查询为每个实体顶点返回一行。出现\u typename的范围是 静态类型实体。然而,动物是一种实体,物种食物, 以及其他。因此,将返回实体的所有子类型的顶点,并且实体类型 输出\u typename字段的列将显示它们的运行时类型:动物物种食物

x_计数

元字段是由graphql编译器定义的非标准元字段,它使 可以与标记为@fold的作用域中元素的个数进行交互。通过应用指令 与@output@filter一样,查询可以输出捕获的元素数 在@fold中,向下筛选结果以仅选择具有所需折叠大小的折叠。

我们使用前缀来表示这是编译器引入的扩展元字段, 不是由graphql规范定义的graphql元字段规范集的一部分。 我们不对元字段使用graphql标准双下划线(\uu)前缀, 因为所有带那个前缀的名字 显式保留,禁止使用 在指令、字段或任何其他工件中。

\u x\u count元字段添加到架构中

由于\u x\u count元字段当前不是graphql标准的一部分,因此必须 显式添加到架构中的所有接口和类型。有两种方法可以做到这一点。

这样做的首选方法是使用extended_meta_field_definitions常量 构建接口和类型字段描述的起点注:

{
    Animal {
        name @output(out_name: "name")
        out_Entity_Related {
            ... on Species {
                description @output(out_name: "description")
            }
        }
    }
}
3

如果您不能以编程方式定义模式,而只是预先创建一个 graphql schema对象,您可以对其进行变异,另一种方法是通过 将元字段插入现有模式中编译器定义的帮助函数:

{
    Animal {
        name @output(out_name: "name")
        out_Entity_Related {
            ... on Species {
                description @output(out_name: "description")
            }
        }
    }
}
4

示例使用

{
    Animal {
        name @output(out_name: "name")
        out_Entity_Related {
            ... on Species {
                description @output(out_name: "description")
            }
        }
    }
}
5

此查询为每个动物顶点返回一行。每行包含其名称、编号和名称 它的孩子们。虽然子名称选择的输出类型是字符串列表, number_of_children选项的输出类型是一个整数。

{
    Animal {
        name @output(out_name: "name")
        out_Entity_Related {
            ... on Species {
                description @output(out_name: "description")
            }
        }
    }
}
6

在这里,我们修改了上面的查询,为返回的行添加了另外两个过滤约束:

  • childanimal顶点必须包含$substr的值作为其名称中的子字符串,并且
  • 动物顶点必须至少有满足上述过滤器的$min_子节点子节点。

重要的是,在任何其他过滤器和类型强制之后,对\u x\u count的任何过滤都将应用。 出现在问题中的@fold中。这种操作顺序非常重要:选择 动物有3个以上子节点的顶点,然后根据其名称筛选子节点不相同 先过滤子节点,然后选择有3个以上子节点的动物节点 与先前的过滤器匹配。

约束和规则
  • 仅允许在标记为@fold的顶点字段中出现@count字段
  • 在存在任何其他筛选器和类型强制之后,始终在\u x\u count上应用筛选 在那里面@fold
  • 过滤或输出\u x\u count字段的值必须始终在最里面进行 折叠的范围。在过滤后展开@fold中的顶点字段是无效的 或输出"元"字段的值。

\u x_count上过滤与@filter具有的has_edge_degree有何不同?

has_edge_degree过滤器允许根据特定类型的边数进行过滤。 在某些情况下,使用进行过滤具有边缘度,使用=进行过滤时,使用\u x\u count 生成等效查询。这里有一对这样的查询:

{
    Animal {
        name @output(out_name: "name")
        out_Entity_Related {
            ... on Species {
                description @output(out_name: "description")
            }
        }
    }
}
7

{
    Animal {
        name @output(out_name: "name")
        out_Entity_Related {
            ... on Species {
                description @output(out_name: "description")
            }
        }
    }
}
8

在这两个查询中,我们都会要求具有 $num_anims成员。然而,我们用两种不同的方式表达了这个问题:一次 作为物种顶点的一个属性("物种的程度是$num动物", 一次作为@fold生成的动物顶点列表的属性 @fold中的元素是$num_anims"。

当我们在物种的顶点的内添加额外的过滤时 场,这个区别变得非常重要。比较以下两个查询:

{
    Animal {
        name @output(out_name: "name")
        out_Entity_Related {
            ... on Species {
                description @output(out_name: "description")
            }
        }
    }
}
9

{
    Animal {
        name @output(out_name: "name")
        out_Animal_ParentOf @optional {
            name @output(out_name: "child_name")
        }
    }
}
0

在第一种情况下,为了进行has_edge_degree过滤,动物的位置 live是不相关的:具有边缘度只确保物种顶点具有 正确的类型的边的数目,就这样。相反,第二个查询 确保只有物种顶点有$num_动物生活在选定区域中的动物 返回位置——位置很重要,因为@filter\u x\u count字段中适用 到@fold范围中的元素数。

graphql模式

本节假设读者熟悉模式在 Graphql的参考实现

与编译器一起使用的graphql模式必须包含自定义指令和自定义日期。 以及编译器定义的标量类型

{
    Animal {
        name @output(out_name: "name")
        out_Animal_ParentOf @optional {
            name @output(out_name: "child_name")
        }
    }
}
1

如果以编程方式构造模式,可以简单地导入python对象 自定义指令和自定义类型的表示:

{
    Animal {
        name @output(out_name: "name")
        out_Animal_ParentOf @optional {
            name @output(out_name: "child_name")
        }
    }
}
2

因为graphql和orientdb类型的系统有不同的规则,所以没有一个适合所有类型的系统 为给定数据库模式编写graphql模式的解决方案。 但是,请记住以下经验法则:

  • 通常,将orientdb抽象类表示为graphql接口。在graphql的类型系统中, graphql接口不能从其他graphql接口继承。
  • 通常,将orientdb非抽象类表示为graphql类型, 列出它们实现的graphql接口。在graphql的类型系统中,graphql类型 无法从其他GraphQL类型继承。
  • 两个OrientDB非抽象类之间的继承关系, 或者在两个orientdb抽象类之间,引入graphql中的一些困难。 在OrientDB中建模数据时,最好尽可能避免这种继承。
  • 如果无法避免两个非抽象的orientdb类ab这样 b继承自a,您有两个选项:
    • 您可以选择将aOrientDB类表示为GraphQL接口, 与b相对应的graphql类型可以实现的。 在本例中,graphql模式保留继承关系 在ab之间,但牺牲了任何继承关系的表示 a可以与任何orientdb超类一起使用。
    • 您可以选择将ab都表示为graphql类型。在这种情况下的权衡是 与前面的情况正好相反:graphql模式 牺牲ab之间的继承关系,但保留 a与其超类的继承关系。 在这种情况下,建议创建graphql联合类型a b, 并将该graphql联合类型用于 在OrientDB中,类型为A
  • 如果无法避免有两个抽象的orientdb类abb继承自a,您同样有两个选项:
    • 您可以选择将b表示为可以实现graphql接口的graphql类型 对应于a。这使得graphql模式保留继承关系 介于ab之间,但牺牲了其他graphql类型从b继承的能力。
    • 您可以选择将ab都表示为graphql接口,牺牲模式的 表示ab之间的继承,但允许graphql类型 从ab继承。如果需要,可以创建一个graphql union typea b并将其用于orientdb中类型为a的任何顶点字段
  • 完全省略在graphql中不可表示的类和字段是合法的。编译程序 当前不支持orientdb的embeddedmap类型或嵌入的非基元类型字段, 因此,在类的graphql表示中可以简单地省略这些字段。 或者,可以省略整个orientdb类和可能指向它的所有边 完全来自graphql模式。

执行模型

因为graphql编译器可以针对多个不同的查询语言,每个语言都有自己的 行为和限制,执行模型还必须定义为 编译目标语言。当我们努力缩小 编译目标,有些差异是不可避免的。

编译器遵循以下原则:

  • 当使用已编译的查询字符串查询数据库时,其响应必须始终位于 结果列表的形式。
  • 每个这样的结果的精确格式由每个编译目标分别定义。
    • gremlinmatchsql以表格格式返回数据,其中每个结果都是 表中的一行和标记为输出的字段是列。
    • 但是,未来的编译目标可能有不同的格式。例如,每个结果 可能以标准GraphQL规范使用的嵌套树格式出现。
  • 每个这样的结果都必须满足其相应graphql查询中的所有指令和类型。
  • 返回的结果列表不保证完整!
    • 换句话说,可能还有满足所有指令和 相应GraphQL查询中的类型,但数据库未返回这些类型。
    • 但是,我们鼓励编译目标实现返回完整的结果 如果可行的话。匹配编译目标保证生成完整的结果。

返回结果的完整性

为了更详细地解释返回结果的完整性,假设数据库包含 下面的示例图:

{
    Animal {
        name @output(out_name: "name")
        out_Animal_ParentOf @optional {
            name @output(out_name: "child_name")
        }
    }
}
3

a、b、x、y为四个顶点的name属性字段的值。 让名为ab的顶点为s类型,让xyt类型。 让顶点a通过e类型的有向边连接到xy上。 类似地,让vertexb也通过e类型的有向边连接到xy

考虑graphql查询:

{
    Animal {
        name @output(out_name: "name")
        out_Animal_ParentOf @optional {
            name @output(out_name: "child_name")
        }
    }
}
4

在数据库中的数据和查询的结构之间,很明显 ab使用xy中的任何一个都会产生有效的结果。因此, 这里以json格式显示的完整结果列表将是:

{
    Animal {
        name @output(out_name: "name")
        out_Animal_ParentOf @optional {
            name @output(out_name: "child_name")
        }
    }
}
5

这正是match编译目标保证产生的结果。 本节的其余部分仅适用于gremlin编译目标。如果使用 匹配,本节其余部分中列出的所有查询都将生成相同的完整查询 结果列表。

由于gremlin编译目标不能保证完整的结果列表, 使用由gremlin编译目标生成的查询字符串查询数据库 将只生成类似以下内容的部分结果列表:

{
    Animal {
        name @output(out_name: "name")
        out_Animal_ParentOf @optional {
            name @output(out_name: "child_name")
        }
    }
}
6

由于底层查询语言的限制,gremlin将默认最多生成一个 查询中每个起始位置的结果。上面的graphql查询开始于 类型s,因此返回结果列表中的每个s\u name都是不同的。此外, 无法保证(也无法提前知道)是否xy将作为 每个结果中的t_name值,因为它们都是有效的结果。

用户可以对查询的最后一个作用域应用@output\u source指令 要改变此行为:

{
    Animal {
        name @output(out_name: "name")
        out_Animal_ParentOf @optional {
            name @output(out_name: "child_name")
        }
    }
}
7

查询现在不会为每个s最多生成一个结果,而是生成 最多只能有一个结果对于每个可以在out\u e中找到的不同值,其中 已应用:

{
    Animal {
        name @output(out_name: "name")
        out_Animal_ParentOf @optional {
            name @output(out_name: "child_name")
        }
    }
}
8

从概念上讲,应用@output\u source指令会使查询看起来是在 相反的顺序:

{
    Animal {
        name @output(out_name: "name")
        out_Animal_ParentOf @optional {
            name @output(out_name: "child_name")
        }
    }
}
9

SQL

下表概述了graphql编译器的特性,以及它们对各种 关系数据库风格:

<表><广告>特色/方言 所需边@过滤器 @输出 @递归 @折叠 @可选 @输出源 < /广告><正文>PostgreSQL<不< < > >受限制,
相交有懔edge懔degree,并且不支持筛选器有限,不支持输出元字段 <不< < > ><不< < > ><不< < > ><不< < > >sqlite<不< < > >受限制,相交有懔edge懔degree,并且不支持筛选器有限,不支持输出元字段 <不< < > ><不< < > ><不< < > ><不< < > >Microsoft SQL Server<不< < > >受限制,相交有懔edge懔degree,并且不支持筛选器有限,不支持输出元字段 <不< < > ><不< < > ><不< < > ><不< < > >mysql<不< < > >受限制,相交有懔edge懔degree,并且不支持筛选器有限,不支持输出元字段 <不< < > ><不< < > ><不< < > ><不< < > >马里亚行<不< < > >受限制,相交有懔edge懔degree,并且不支持筛选器有限,不支持输出元字段 <不< < > ><不< < > ><不< < > ><不< < > >

配置sqlalchemy

通过编译到sqlalchemy core作为中介支持关系数据库 语言,然后依赖于sqlalchemy编译的特定于方言的sql字符串来查询 目标数据库。

对于sql后端,假定graphql类型有一个同名的sql表,并且 相同的属性。例如,模式类型

{
  Person {
    out_Person_Knows @optional {
      name @filter(op_name: "=", value: ["$name"])
    }
    name @output(out_name: "person_name")
  }
}
0

应与同名的SqlAlchemy表对象相对应,不区分大小写。为此 架构类型可能如下:

{
  Person {
    out_Person_Knows @optional {
      name @filter(op_name: "=", value: ["$name"])
    }
    name @output(out_name: "person_name")
  }
}
1

如果架构类型名的表不存在,则在编译时将引发异常。见 配置SQL数据库以匹配GraphQL架构 以获得解决此类命名差异的可能选项。

端到端SQL示例

下面是一个端到端的示例,包括相关的graphql模式和sqlalchemy引擎准备。

这旨在显示graphql编译器的sql后端的设置步骤,以及 不代表在生产系统中配置和运行sqlalchemy的最佳实践。

{
  Person {
    out_Person_Knows @optional {
      name @filter(op_name: "=", value: ["$name"])
    }
    name @output(out_name: "person_name")
  }
}
2

配置SQL数据库以匹配GraphQL架构

为了简单起见,sql后端要求sqlalchemy表和grap之间完全匹配HQL类型, 在sqlalchemy列和graphql字段之间。如果表名或列名在 数据库不符合这些规则?最终的计划是 SQL后端更可配置。在短期内,解决这一问题的一种可能方法是使用 SQL视图。

例如,假设数据库中有一个名为animal_table的表,它有一个列 称为动物名。如果所需的模式具有类型

{
  Person {
    out_Person_Knows @optional {
      name @filter(op_name: "=", value: ["$name"])
    }
    name @output(out_name: "person_name")
  }
}
0

然后可以通过如下视图将其曝光:

{
  Person {
    out_Person_Knows @optional {
      name @filter(op_name: "=", value: ["$name"])
    }
    name @output(out_name: "person_name")
  }
}
4

此时,可以在sqlalchemy表中使用animal视图进行编译。

其他

漂亮的图形打印查询

要进行漂亮的打印图形查询,请使用附带的漂亮打印机:

{
  Person {
    out_Person_Knows @optional {
      name @filter(op_name: "=", value: ["$name"])
    }
    name @output(out_name: "person_name")
  }
}
5

它以python的json.tool为模型,从stdin读取并写入stdout。

展开顶点字段

在graphql中包含可选语句本身没有性能问题, 但是如果你继续在一个可选范围内扩展顶点场, 可能会对性能产生重大影响。

接下来,我们将介绍两种不同的可选指令。

  • 可选的"simple"是一个顶点,其@可选的指令不展开 任何顶点域。 例如:
{
  Person {
    out_Person_Knows @optional {
      name @filter(op_name: "=", value: ["$name"])
    }
    name @output(out_name: "person_name")
  }
}
6

orientdbmatch当前允许任意遍历的最后一步是可选的。 因此,上述graphql的等效match遍历如下:

{
  Person {
    out_Person_Knows @optional {
      name @filter(op_name: "=", value: ["$name"])
    }
    name @output(out_name: "person_name")
  }
}
7
  • 可选的"compound"是具有@可选的指令的顶点,该指令确实展开 其中的顶点场。 例如:
{
  Person {
    out_Person_Knows @optional {
      name @filter(op_name: "=", value: ["$name"])
    }
    name @output(out_name: "person_name")
  }
}
8

目前,这不能用一个简单的match查询来表示。 具体来说,以下是一个有效的match语句, 因为可选的遍历遵循另一条边:

{
  Person {
    out_Person_Knows @optional {
      name @filter(op_name: "=", value: ["$name"])
    }
    name @output(out_name: "person_name")
  }
}
9

相反,我们用两个不同的union(unionall)表示一个可选的复合 匹配查询。例如,上面的graphql查询可以表示如下:

{"name":"Charles"}
0

在第一种情况下,如果不遵循可选边, 我们必须显式地过滤掉边可能被跟踪的所有顶点。 这是为了消除两个"匹配"选项之间的重复。

前面的例子并不是我们如何实现复合选项 (我们还有$match1$match2中的select语句, 但它说明了总体思路。

性能惩罚

如果在给定的图形中有许多复合选项, 上述过程导致大量匹配查询的并集。 具体来说,对于n复合选项,我们生成2个n不同的匹配查询。 对于n可选边的2个子集s中的每一个子集:

  • 我们删除了s中每个遍历的@可选限制。
  • 对于s的补码中的每个遍历t,我们完全放弃t 以及其中的所有顶点和指令,我们添加了一个过滤器 在上一次遍历中,以确保与t相对应的边不存在。

因此,我们得到的性能惩罚是指数增长的 具有个复合边个可选边。 在使用许多可选指令编写查询时,记住这一点非常重要。

如果其中一些复合选项包含自己的顶点字段, 由于我们必须把所有的P可选@的可能子集 可以同时满足的语句。

可选的类型等效提示参数

此编译参数是解决graphql和gremlin限制的一种方法。 键入系统:

  • graphql不允许类型从另一个类型继承,只允许实现接口
  • gremlin对继承根本没有一流的支持。

假设以下graphql模式:

{"name":"Charles"}
1

这里适当的类型等价提示值应该是{animal:animalcatdog}。 这使编译器知道animalcatdogunion类型隐式等价于 animal类型,因为在数据库模式中没有其他类型从animal继承。 这允许编译器在gremlin中执行精确的类型强制,并优化 如果强制转换为 工会的同等类型。

在编译过程中设置类型等效提示{animal:animalcatdog}。 将允许在foo的相邻动物的顶点字段上使用@fold

{"name":"Charles"}
2

图表

当从数据库元数据构建graphql模式时,我们首先从 元数据,然后从schemagraph构建graphql模式。架构图 底层数据库模式的表示,但它有三个主要优点,使其成为 更强大的模式内省工具:

  1. 它能够存储和公开模式的索引信息。索引访问接口 不过,这些信息是临时性的,可能会在不久的将来发生变化。
  2. 它的类可以从非抽象类继承。
  3. 它公开了许多实用函数,例如get_subclass_set,这些函数使探索变得更容易 架构。
  4. < > >

    有关如何构建和使用schemagraph的模拟示例,请参见下面的内容:

    {"name":"Charles"}
    3

    在将来,我们计划添加从sqlalchemy元数据生成的schemagraphgeneration。我们还计划 添加一种机制,可以在其中使用graphql查询schemagraph

    cypher查询参数

    redisgraph不支持查询参数,因此我们在 graphql_to_redisgraph_cypher函数。但是,对于neo4j,我们可以使用neo4j的客户机 参数插值本身,这样我们就不会重新发明轮子。

    函数在查询中插入参数 这里足够细粒度——对于cypher后端,我们只希望在后端 是重新发现,但如果是Neo4J就不行。

    相反,neo4j cypher的正确方法如下:给定一个名为neo4j_client的neo4j python客户机

    {"name":"Charles"}
    4

    修改已解析的自定义标量类型

    有关自定义标量类型的描述、序列化和解析的信息 从字符串解析graphql架构时,对象将丢失。当 使用自定义标量类型对象。为了避免这些问题,可以使用代码 下面的代码片段用于修改编译器使用的自定义标量类型的定义。

    {"name":"Charles"}
    5

    FAQ

    q:您是真的使用graphql,还是只使用类似graphql的语法?

    A:我们真的用GraphQL。编译器将接受的任何查询都是完全有效的graphql, 实际上,我们使用graphql核心库的python端口进行解析和类型检查。 但是,由于编译graphql生成的数据库查询受到限制 的它们运行在数据库系统上,我们的执行模型与 标准graphql规范中描述的那个。见 执行模型部分了解更多详细信息。

    q:这个项目是否附带了graphql服务器实现?

    A:不——有很多现有的框架可以用来运行Web服务器。我们只是建立了一个工具 它接受graphql查询字符串(及其参数)并返回一个查询字符串 与数据库一起使用。编译器不针对数据库执行查询字符串, 它也不会反序列化结果。因此,选择 使用服务器框架和数据库客户端库。

    Q:您是否计划在未来支持其他数据库/更多GraphQL功能?

    A:我们很乐意,我们真的需要你的帮助!请考虑参与这个项目 通过打开问题、打开拉取请求或参与讨论。

    问:我想我发现了一个虫子,我该怎么办?

    A:请检查是否已经为该bug创建了问题,如果没有,请打开一个新的问题。 确保尽可能详细地描述错误,包括任何堆栈跟踪或 您可能看到的错误消息、正在使用的数据库以及编译的查询。

    问:我想我发现了一个安全漏洞,该怎么办?

    A:请联系我们 graphql compiler mainter@kensho.com 因此,我们可以对问题进行分类并采取适当的措施。

    许可证

    根据Apache2.0许可证授权。除非适用法律要求或书面同意, 根据许可证分发的软件按"原样"分发,无需保证或 任何形式的条件,无论是明示的还是默示的。请参阅特定语言的许可证 管理许可下的权限和限制。

    版权所有2017-至今Kensho Technologies,LLC。当前日期由时间戳确定 存储库中最新提交的。

    欢迎加入QQ群-->: 979659372 Python中文网_新手群

    推荐PyPI第三方库


热门话题
junit有没有办法在Java中重新初始化静态类?   在浏览器中点击应用程序时java Play框架挂起   文件Java错误中的NullPointerException   使用Java中的SNMP查找网络中计算机的登录名   java包装服务器引导程序已弃用,有什么替代方案?   当客户在等待理发时,java信号量值是否存在问题?   java如何使用JavaMail仅下载特定类型的附件   如何在java中将十进制转换为十六进制   java Slick2D粒子系统不会生成粒子   java检测更改事件来自何处   将Java集合类型参数类设置为数组   java如何从eclipse导出为可运行JAR文件?   java EntityManager对象未注入Glassfish和Spring   swing从actionPerformed和actionListener Java返回字符串   java在给定另一个等价键对象的情况下获取映射项的当前键   无论输入如何,java网络都会产生相同的输出