【问题标题】:Resolve all fields in one resolver V.S. resolve field in each resolver在一个解析器 V.S. 中解析所有字段每个解析器中的解析字段
【发布时间】:2019-02-15 18:28:36
【问题描述】:

这是我的typeDefs

const typeDefs: string = `
  type Book {
    id: ID!
    title: String
    authorId: ID!
    author: User
  }

  type User {
    id: ID!
    name: String
  }

  type Query {
    books: [Book]!
  }
`;

export { typeDefs };

resolvers:

import { IResolvers } from 'graphql-tools';
import { IBook } from './db';

const resolvers1: IResolvers = {
  Query: {
    books: (_, args, { db }): Promise<IBook[]> => {
      return db.books.map(book => {
        book.author = db.users.find(user => book.authorId === user.id);
        return book;
      });
    }
  }
};

const resolvers2: IResolvers = {
  Query: {
    books: (_, args, { db }): Promise<IBook[]> => {
      return db.books;
    }
  },
  Book: {
    author: (book, args, { db }) => {
      return db.users.find(user => book.authorId === user.id);
    }
  }
};

export { resolvers1, resolvers2 };

resolvers1内,解析Book的所有字段。 (将author字段添加到book

resolvers2内,在独立解析器中解析Book的每个字段。

我发现 resovlers1resolvers2 都可以正常工作。我可以得到这样的正确响应:

{
  "data": {
    "books": [
      {
        "id": "02wDZbBuMi",
        "title": "Ea repellendus",
        "authorId": "hhP2TtobM",
        "author": {
          "id": "hhP2TtobM",
          "name": "Mrs. Destiney Kerluke"
        }
      },
      {
        "id": "tC3uPfKfUZ",
        "title": "Consectetur fugit",
        "authorId": "k9IHZAtld8",
        "author": {
          "id": "k9IHZAtld8",
          "name": "Mr. Rene Heidenreich"
        }
      }
    ]
  }
}

它们之间有什么区别?这两种方式正确吗?如果不是,为什么?

【问题讨论】:

  • 在 GraphQL 中,您希望使用 resolver2 方法并使用 dataloader 来使该方法高效。也许只是尝试一下,一旦你的服务器变得更大,你就会自己发现它是如何工作的。 Youtube 上还有多个关于 dataloader 的视频。
  • 是的。我知道dataloaderN+1 查询问题。但我认为使用resolver2 的最重要原因是@Gabriel Bleu 的回答。还是谢谢。

标签: graphql graphql-js


【解决方案1】:

resolver1 是在复杂性与性能之间的权衡。解析器一的主要论点是数据库通常具有resolver2 无法使用的连接。例如,书籍+作者查询可以用单个 SQL 语句表示。与第 2 版相比,即使使用 Dataloader,这也是一个巨大的性能优势。现在有人可能会争辩说,我们甚至不知道是否需要作者字段。但是我们可以使用resolveInfo 参数来了解这一点。可以编写一个函数来快速检查 resolveInfo 并告诉我们子选择中是否存在字段:

hasSelection(fieldName: string, resolveInfo: GraphQLResolveInfo): boolean

然后我们可以使用这个函数来检查我们是否应该进行连接。

books: (_, args, { db }, resolveInfo): Promise<IBook[]> => {
  if (hasSelection('author', resolveInfo)) {
    // Just an example, there is some more transformation needed
    return db.query('SELECT ... FROM book JOIN author ON book.authorId = author.id');
  }
  return db.query('SELECT ... FROM book');
}

这可能会提高两倍的性能。实际上有很多公司这样做,因为有时性能是关键。如果示例变得更复杂,那么复杂性会显着增加,如果不首先将其确定为瓶颈,我不会进行此类优化。另一方面,有很多项目致力于“原生 GraphQL”,这意味着他们正在将 GraphQL 查询直接转换为数据库查询。

【讨论】:

  • 昨晚,我还想用resolver1 + arguments 来决定响应数据结构是什么样的。这意味着是否包括author。但是如果有很多领域。这个解析器将充满if...else... 语句。这很糟糕。
  • 如果相关数据只能从另一个服务中检索,比如通过 http 调用,那么这种方法不会有任何好处,因为除了额外的自定义代码,通常由 graphql 引擎为您处理。
【解决方案2】:

resolver2 更好,因为如果您的请求中不包含author,它不会对数据库进行无用的调用。

【讨论】:

  • 是的。我同意你的看法。这是一个最重要的原因。 resolver1的其他原因或缺点?
猜你喜欢
  • 2021-07-26
  • 2013-12-15
  • 2017-05-26
  • 2013-07-31
  • 2020-10-07
  • 2020-08-25
  • 2020-08-02
  • 2019-04-16
  • 2020-08-26
相关资源
最近更新 更多