GraphQL 查询操作 #

查询基础 #

Query 是 GraphQL 中用于获取数据的操作类型,类似于 REST API 中的 GET 请求。

text
┌─────────────────────────────────────────────────────────────┐
│                    Query 操作特点                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ✅ 只读操作:不修改数据                                      │
│  ✅ 并行执行:多个字段可并行获取                              │
│  ✅ 精确获取:只返回请求的字段                                │
│  ✅ 嵌套查询:一次请求获取关联数据                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

基本查询 #

单条记录查询 #

graphql
query {
  user(id: "1") {
    id
    name
    email
  }
}

响应:

json
{
  "data": {
    "user": {
      "id": "1",
      "name": "Alice",
      "email": "alice@example.com"
    }
  }
}

列表查询 #

graphql
query {
  users {
    id
    name
    email
  }
}

响应:

json
{
  "data": {
    "users": [
      {
        "id": "1",
        "name": "Alice",
        "email": "alice@example.com"
      },
      {
        "id": "2",
        "name": "Bob",
        "email": "bob@example.com"
      }
    ]
  }
}

嵌套查询 #

GraphQL 的强大之处在于可以一次请求获取多层嵌套的关联数据:

graphql
query {
  user(id: "1") {
    name
    email
    posts {
      id
      title
      comments {
        id
        content
        author {
          name
        }
      }
    }
  }
}

分页查询 #

分页是处理大量数据的关键技术。GraphQL 通常使用两种分页方式。

偏移分页(Offset-based) #

graphql
query {
  users(limit: 10, offset: 0) {
    id
    name
    email
  }
  usersCount
}

游标分页(Cursor-based) #

游标分页更适合实时数据和高性能场景:

graphql
query {
  users(first: 10, after: "cursor-value") {
    edges {
      node {
        id
        name
        email
      }
      cursor
    }
    pageInfo {
      hasNextPage
      hasPreviousPage
      startCursor
      endCursor
    }
    totalCount
  }
}

响应:

json
{
  "data": {
    "users": {
      "edges": [
        {
          "node": {
            "id": "1",
            "name": "Alice",
            "email": "alice@example.com"
          },
          "cursor": "YXJyYXljb25uZWN0aW9uOjA="
        }
      ],
      "pageInfo": {
        "hasNextPage": true,
        "hasPreviousPage": false,
        "startCursor": "YXJyYXljb25uZWN0aW9uOjA=",
        "endCursor": "YXJyYXljb25uZWN0aW9uOjk="
      },
      "totalCount": 100
    }
  }
}

分页类型对比 #

text
┌─────────────────────────────────────────────────────────────┐
│                    分页方式对比                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  偏移分页(Offset):                                        │
│  ✅ 简单直观,易于理解                                       │
│  ✅ 支持跳页                                                 │
│  ❌ 大偏移量性能差                                           │
│  ❌ 数据变化时可能遗漏/重复                                  │
│                                                             │
│  游标分页(Cursor):                                        │
│  ✅ 性能稳定,不受数据量影响                                 │
│  ✅ 数据一致性更好                                           │
│  ❌ 不支持跳页                                               │
│  ❌ 实现稍复杂                                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

连接规范(Relay Style) #

Relay 风格的分页是业界标准:

graphql
type PageInfo {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}

type UserEdge {
  node: User!
  cursor: String!
}

type UserConnection {
  edges: [UserEdge!]!
  pageInfo: PageInfo!
  totalCount: Int
}

使用示例:

graphql
query GetUsers($first: Int, $after: String) {
  users(first: $first, after: $after) {
    edges {
      node {
        id
        name
      }
      cursor
    }
    pageInfo {
      hasNextPage
      endCursor
    }
    totalCount
  }
}

过滤查询 #

过滤用于筛选符合条件的数据。

简单过滤 #

graphql
query {
  users(status: "active") {
    id
    name
    status
  }
}

多条件过滤 #

graphql
query {
  users(
    status: "active"
    role: "admin"
    country: "US"
  ) {
    id
    name
    role
    country
  }
}

过滤对象 #

复杂过滤使用输入对象:

graphql
query {
  users(filter: {
    status: "active",
    age: { gte: 18, lte: 65 },
    name: { contains: "Alice" }
  }) {
    id
    name
    age
    status
  }
}

过滤操作符 #

graphql
query {
  products(filter: {
    price: { gte: 10, lte: 100 },
    category: { in: ["electronics", "books"] },
    name: { contains: "phone", mode: INSENSITIVE },
    stock: { gt: 0 },
    tags: { has: "featured" }
  }) {
    id
    name
    price
    category
  }
}

常用操作符:

操作符 说明 示例
eq 等于 { status: { eq: "active" } }
ne 不等于 { status: { ne: "deleted" } }
gt 大于 { age: { gt: 18 } }
gte 大于等于 { age: { gte: 18 } }
lt 小于 { age: { lt: 65 } }
lte 小于等于 { age: { lte: 65 } }
in 包含于 { status: { in: ["active", "pending"] } }
nin 不包含于 { status: { nin: ["deleted"] } }
contains 包含 { name: { contains: "Alice" } }
startsWith 开头匹配 { name: { startsWith: "Al" } }
endsWith 结尾匹配 { email: { endsWith: "@example.com" } }

逻辑操作符 #

graphql
query {
  users(filter: {
    AND: [
      { status: { eq: "active" } },
      { OR: [
        { role: { eq: "admin" } },
        { role: { eq: "moderator" } }
      ]}
    ]
  }) {
    id
    name
    role
    status
  }
}

排序查询 #

排序用于控制返回数据的顺序。

单字段排序 #

graphql
query {
  users(orderBy: { field: "createdAt", direction: DESC }) {
    id
    name
    createdAt
  }
}

多字段排序 #

graphql
query {
  users(orderBy: [
    { field: "status", direction: ASC },
    { field: "createdAt", direction: DESC }
  ]) {
    id
    name
    status
    createdAt
  }
}

枚举排序 #

使用枚举更简洁:

graphql
query {
  users(orderBy: createdAt_DESC) {
    id
    name
    createdAt
  }
}

多枚举排序 #

graphql
query {
  users(orderBy: [status_ASC, createdAt_DESC]) {
    id
    name
    status
    createdAt
  }
}

搜索查询 #

全文搜索 #

graphql
query {
  search(query: "GraphQL tutorial", limit: 10) {
    id
    title
    excerpt
    score
    type
  }
}

高亮搜索结果 #

graphql
query {
  search(query: "GraphQL", highlight: true) {
    id
    title
    highlightedTitle
    content
    highlightedContent
  }
}

聚合搜索 #

graphql
query {
  search(query: "GraphQL") {
    total
    items {
      id
      title
    }
    aggregations {
      category {
        key
        count
      }
      author {
        key
        count
      }
    }
  }
}

批量查询 #

GraphQL 允许在一个请求中查询多个资源。

多个独立查询 #

graphql
query {
  users {
    id
    name
  }
  posts {
    id
    title
  }
  comments {
    id
    content
  }
}

使用别名区分 #

graphql
query {
  activeUsers: users(status: "active") {
    id
    name
  }
  inactiveUsers: users(status: "inactive") {
    id
    name
  }
  pendingUsers: users(status: "pending") {
    id
    name
  }
}

关联数据批量查询 #

graphql
query {
  user(id: "1") {
    name
    posts {
      title
    }
  }
  user(id: "2") {
    name
    posts {
      title
    }
  }
}

使用别名:

graphql
query {
  alice: user(id: "1") {
    name
    posts {
      title
    }
  }
  bob: user(id: "2") {
    name
    posts {
      title
    }
  }
}

条件查询 #

使用指令 #

graphql
query GetDashboard($showPosts: Boolean!, $showComments: Boolean!) {
  user(id: "1") {
    name
    posts @include(if: $showPosts) {
      title
    }
    comments @include(if: $showComments) {
      content
    }
  }
}

使用 @skip #

graphql
query GetUser($skipDetails: Boolean!) {
  user(id: "1") {
    name
    email
    details @skip(if: $skipDetails) {
      bio
      location
    }
  }
}

聚合查询 #

聚合查询用于统计数据。

计数 #

graphql
query {
  usersCount
  postsCount(status: "published")
  commentsCount(userId: "1")
}

分组统计 #

graphql
query {
  userStats {
    status
    count
  }
}

响应:

json
{
  "data": {
    "userStats": [
      { "status": "active", "count": 150 },
      { "status": "inactive", "count": 30 },
      { "status": "pending", "count": 20 }
    ]
  }
}

数值聚合 #

graphql
query {
  orderStats {
    totalOrders
    totalRevenue
    averageOrderValue
    maxOrderValue
    minOrderValue
  }
}

自省查询 #

GraphQL 提供自省系统,可以查询 API 的类型信息。

查询所有类型 #

graphql
query {
  __schema {
    types {
      name
      kind
      description
    }
  }
}

查询特定类型 #

graphql
query {
  __type(name: "User") {
    name
    kind
    fields {
      name
      type {
        name
        kind
      }
      args {
        name
        type {
          name
        }
      }
    }
  }
}

查询查询操作 #

graphql
query {
  __schema {
    queryType {
      fields {
        name
        description
        args {
          name
          type {
            name
          }
        }
      }
    }
  }
}

查询枚举值 #

graphql
query {
  __type(name: "UserStatus") {
    enumValues {
      name
      description
    }
  }
}

查询最佳实践 #

1. 使用命名操作 #

graphql
query GetUserProfile($id: ID!) {
  user(id: $id) {
    name
    email
  }
}

2. 使用片段复用 #

graphql
query {
  user(id: "1") {
    ...userFields
  }
  users {
    ...userFields
  }
}

fragment userFields on User {
  id
  name
  email
  avatar
}

3. 合理使用变量 #

graphql
query SearchPosts($input: SearchInput!) {
  search(input: $input) {
    id
    title
  }
}

4. 控制查询深度 #

避免过深的嵌套查询:

graphql
query {
  user(id: "1") {
    posts {
      comments {
        author {
          posts {
            comments {
              author {
                name
              }
            }
          }
        }
      }
    }
  }
}

5. 使用分页 #

graphql
query {
  users(first: 20) {
    edges {
      node {
        id
        name
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

实战示例 #

示例一:用户列表页 #

graphql
query UserList(
  $filter: UserFilter
  $orderBy: UserOrderBy
  $first: Int
  $after: String
) {
  users(
    filter: $filter
    orderBy: $orderBy
    first: $first
    after: $after
  ) {
    edges {
      node {
        id
        name
        email
        avatar
        role
        status
        createdAt
      }
      cursor
    }
    pageInfo {
      hasNextPage
      hasPreviousPage
      startCursor
      endCursor
    }
    totalCount
  }
}

变量:

json
{
  "filter": {
    "status": { "eq": "active" }
  },
  "orderBy": { "field": "createdAt", "direction": "DESC" },
  "first": 20
}

示例二:文章详情页 #

graphql
query PostDetail($id: ID!) {
  post(id: $id) {
    id
    title
    content
    excerpt
    coverImage
    status
    publishedAt
    author {
      id
      name
      avatar
      bio
    }
    tags
    categories {
      id
      name
      slug
    }
    comments(first: 10) {
      edges {
        node {
          id
          content
          createdAt
          author {
            id
            name
            avatar
          }
        }
      }
      totalCount
    }
    relatedPosts(limit: 5) {
      id
      title
      excerpt
      coverImage
    }
  }
}

示例三:仪表板统计 #

graphql
query Dashboard {
  me {
    id
    name
    avatar
  }
  
  statistics {
    users {
      total
      active
      newThisMonth
    }
    posts {
      total
      published
      draft
    }
    comments {
      total
      pending
      approved
    }
  }
  
  recentActivity(limit: 10) {
    id
    type
    description
    createdAt
    user {
      name
      avatar
    }
  }
  
  notifications(limit: 5, unread: true) {
    id
    type
    message
    createdAt
  }
}

示例四:搜索页面 #

graphql
query Search(
  $query: String!
  $type: [SearchType!]
  $filters: SearchFilters
  $first: Int
  $after: String
) {
  search(
    query: $query
    type: $type
    filters: $filters
    first: $first
    after: $after
  ) {
    total
    items {
      ... on User {
        id
        name
        avatar
        email
      }
      ... on Post {
        id
        title
        excerpt
        coverImage
        author {
          name
        }
      }
      ... on Comment {
        id
        content
        post {
          title
        }
      }
    }
    facets {
      name
      values {
        key
        count
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

性能优化 #

查询复杂度 #

text
┌─────────────────────────────────────────────────────────────┐
│                    查询复杂度控制                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  复杂度计算示例:                                            │
│                                                             │
│  query {                                                    │
│    users(first: 10) {        # 10 users                    │
│      posts(first: 5) {       # 10 * 5 = 50 posts           │
│        comments(first: 3) {  # 50 * 3 = 150 comments       │
│          content                                            │
│        }                                                    │
│      }                                                      │
│    }                                                        │
│  }                                                          │
│                                                             │
│  总复杂度 = 10 * 5 * 3 = 150                                │
│                                                             │
│  建议:                                                     │
│  - 限制嵌套深度                                             │
│  - 限制每层数量                                             │
│  - 使用复杂度分析中间件                                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

批量加载(DataLoader) #

解决 N+1 查询问题:

javascript
const DataLoader = require('dataloader');

const userLoader = new DataLoader(async (ids) => {
  const users = await getUsersByIds(ids);
  return ids.map(id => users.find(user => user.id === id));
});

const resolvers = {
  Post: {
    author: (post) => userLoader.load(post.authorId)
  }
};

下一步 #

现在你已经掌握了 GraphQL 查询操作的各种技巧,接下来学习 变更操作详解,学习如何修改数据!

最后更新:2026-03-29