# GraphQL API 使用介绍:以 Open Targets Platform 为例

GraphQL 是一种强大的 API 查询语言,它允许客户端精确地指定所需的数据,从而优化网络请求和提高数据获取的效率。本文将通过 Open Targets Platform API 的示例,介绍如何使用 GraphQL API 进行数据查询。

# 理解 GraphQL Schema

GraphQL API 的核心是其 Schema,Schema 定义了 API 中所有可用的查询(Query)、变更(Mutation)和订阅(Subscription),以及每个字段的类型和关系。Schema 可以被视为 API 的 “合同”,它规定了客户端可以请求的数据类型和结构。

为了具体说明,我们以 Open Targets Platform 的 GraphQL Schema 为例。这个平台为研究疾病和药物靶点的关联提供了丰富的数据,适用于生物信息学和药物开发领域。

# Open Targets Platform API 的 Schema 结构

在 Open Targets Platform 的 GraphQL Schema 中,核心查询类型包括 targetdiseasedrugsearch 等,这些类型对应不同的数据实体,例如基因、疾病、药物等。

type Query {
  target(ensemblId: String!): Target
  disease(id: String!): Disease
  drug(id: String!): Drug
  search(queryString: String!): SearchResults
}
  • target: 用于获取与特定靶点(基因)相关的信息,如该数据库使用的 Ensembl ID ( ensemblId )。
  • disease: 获取与特定疾病 / 表型的相关的信息,如疾病的 ID ( id ),该数据库使用了多个来源的疾病 ID 如 EFO ID 、MONDO ID 和 OTAR ID 等。
  • drug: 获取特定药物的信息,如药物 ID,该数据库使用 ChEMBL ID ( id )。
  • search: 进行关键词搜索,返回与查询字符串匹配的所有实体。

这些查询类型返回的数据结构可以是对象(例如 TargetDiseaseDrug )或包含多个对象的列表。

# 使用 GraphQL 查询 Open Targets 数据

# 查询基因靶点信息

假设我们要获取与特定基因(如 AR 基因,Ensembl ID 为 ENSG00000169083 )相关的详细信息,包括遗传约束(genetic constraint)和靶点可成药性(tractability)评估。我们可以通过以下 GraphQL 查询实现:

query targetInfo {
  target(ensemblId: "ENSG00000169083") {
    id
    approvedSymbol
    biotype
    geneticConstraint {
      constraintType
      exp
      obs
      score
      oe
      oeLower
      oeUpper
    }
    tractability {
      label
      modality
      value
    }
  }
}

这段查询代码会返回基因的基本信息以及相关的遗传约束和靶点可成药性数据。通过这种方式,客户端可以精确获取所需的数据字段,避免了过度获取或不足获取的问题。

# 查询疾病信息

类似地,我们可以通过以下查询获取与某种疾病(如透明细胞肾癌,EFO ID 为 EFO_0000349 )的相关信息,包括相关靶标(associatedTargets)和已知药物(knownDrugs)等:

query diseaseInfo {
  disease(efoId: "EFO_0000349") {
    id
    name
    associatedTargets {
      count
      rows {
        score
        target {
          id
          approvedName
          approvedSymbol
        }
      }
    }
    knownDrugs {
      uniqueDrugs
      uniqueTargets
      count
      rows {
        approvedName
        approvedSymbol
        targetId
        drugId
      }
    }
  }
}

这个查询会返回疾病的基本信息、与该疾病相关的基因靶点以及已知的药物信息。

# 执行搜索查询

如果我们不确定要查询的具体实体,可以使用 search 查询,通过关键词搜索平台中的所有相关实体:

query searchEntities {
  search(queryString: "breast cancer") {
    aggregations {
      total
      entities {
        name
        total
        categories {
          name
          total
        }
      }
    }
    hits {
      id
      entity
      category
      name
      score
    }
  }
}

这将返回与 “乳腺癌” 相关的所有实体,如基因、疾病或药物。

# 联合类型

在 GraphQL 中,联合类型(union)允许一个字段可以返回多种不同类型的对象。Open Targets API 文档中定义了一个 EntityUnionType 联合类型,它可以是 TargetDrugDisease ,你可以在查询中使用 “内联片段”(inline fragments)来根据实际返回的类型提取对应的字段。下面是一个示例查询,展示如何处理联合类型:

query disease {
  disease(efoId: "EFO_0005952") {
    id
    name
    description
    dbXRefs
    similarEntities {
      category
      id
      score
      object {
        ... on Target {
          id
        }
        ... on Drug {
          id
        }
        ... on Disease {
          id
        }
      }
    }
  }
}

在上面的 GraphQL 查询中, object { ... } 使用了内联片段来处理联合类型,以获取相似实体的具体对象

因为 object 字段可能返回不同类型的对象(Target、Drug、Disease),因此我们需要根据实际类型使用内联片段来提取具体字段。

  • ... on Target { id, approvedSymbol } : 如果 object 是一个 Target 类型,提取其 idapprovedSymbol 字段。
  • ... on Drug { id, drugType } : 如果 object 是一个 Drug 类型,提取其 iddrugType 字段。
  • ... on Disease { id, name } : 如果 object 是一个 Disease 类型,提取其 idname 字段。

这种方式允许我们在处理联合类型时,根据返回的实际类型提取不同的字段。每个内联片段会对应一种可能的类型,你可以在每个片段中定义需要提取的字段。

# 在代码中使用 Open Targets Platform 的 GraphQL API

要在代码中使用这些查询,可以通过 Python 的 requests 库发送 HTTP POST 请求,以下是一个完整的 Python 示例:

import requests
import json
# GraphQL 查询字符串
query = """
query targetInfo {
  target(ensemblId: "ENSG00000169083") {
    id
    approvedSymbol
    biotype
    geneticConstraint {
      constraintType
      exp
      obs
      score
      oe
      oeLower
      oeUpper
    }
    tractability {
      label
      modality
      value
    }
  }
}
"""
# GraphQL API 的 URL
url = "https://api.platform.opentargets.org/api/v4/graphql"
# 发送 POST 请求
response = requests.post(url, json={'query': query})
# 打印返回的 JSON 数据
print(json.dumps(response.json(), indent=2))

这个脚本发送了一个 GraphQL 查询请求,并将返回的数据打印出来。你可以根据需要修改查询字符串,获取其他类型的数据。

# 其他

在上面的 GraphQL 查询字符串中,我们在前面都写了一个 query xxx 。在 GraphQL 中, query 关键字是可选的,可以用于明确表示这是一个查询操作。例如加上 query KnownDrugs 和不加的区别主要体现在以下几个方面:

  1. 可读性

    • 加上 query KnownDrugs :这种方式使得查询更具可读性和结构化,特别是在有多个查询、变更或订阅操作时。 KnownDrugs 是查询的操作名称(Operation Name),可以帮助开发者或工具在执行和调试时更容易识别和引用该查询。
    • 不加 query KnownDrugs :省略 query 关键字也可以正常执行查询,但可能在复杂场景下可读性略差。
  2. Operation Name

    • 加上 query KnownDrugs KnownDrugs 成为该查询的操作名称(Operation Name)。在某些情况下,例如日志记录、错误跟踪、缓存或在前端工具中,可以通过这个名称来标识和引用该查询。
    • 不加 query KnownDrugs :查询没有操作名称,直接开始查询内容。这在简单的查询中通常没什么影响,但在复杂的项目或应用程序中可能会失去一些管理查询的便利性。
  3. 调试与开发工具支持

    • 加上 query KnownDrugs :许多开发工具和 IDE 可以利用操作名称来显示查询的结构,并提供更好的调试和分析支持。
    • 不加 query KnownDrugs :省略操作名称后,工具的支持仍然存在,但会失去通过名称直接引用查询的能力。

在实际使用中,如果查询比较简单,可以省略操作名称;如果查询复杂或需要标识查询的特定用途,建议加上操作名称。