GraphQL 从 MySQL 连接查询返回 null

GraphQL returning null from MySQL join query

我有一个小型 Node 项目(基于位置的简单猜谜游戏)来为 React 前端提供服务。它使用 Apollo 服务器从 AWS RDS MySQL 数据库中检索数据,并使用 knex 作为查询构建器。我可以成功地从单个 table 中检索数据,但是当查询包含一个连接时,graphql 中的子对象始终为 null。

当我在本地访问节点服务器并查询 guesses 的数据时,我有 guess 数据,但关联的 userquestion 数据始终为空,如图所示。

graphql_issue

这是代码 - 我已经删除了一些工作正常的查询的多余代码。

数据库

CREATE TABLE `question` (
  `id` int NOT NULL AUTO_INCREMENT,
  `lat` float NOT NULL,
  `lng` float NOT NULL,
  `question` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `guess` (
  `id` int NOT NULL AUTO_INCREMENT,
  `lat` float NOT NULL,
  `lng` float NOT NULL,
  `question_id` int NOT NULL,
  `user_id` int NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `question_id` (`question_id`),
  CONSTRAINT `guess_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
  CONSTRAINT `guess_ibfk_2` FOREIGN KEY (`question_id`) REFERENCES `question` (`id`)
);

CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(11) NOT NULL,
  `icon` varchar(11) NOT NULL,
  `score` int NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
);

Apollo 服务器应用程序

index.js

const { ApolloServer } = require("apollo-server");
const typeDefs = require("./schema");
const mapDatabase = require("./datasources/map");
const resolvers = require("./resolver");

const knexConfig = {
    client: "mysql",
    connection: {
        host: "my-server",
        user: "my-username",
        password: "my-password",
        database: "db-name",
    },
};

const server = new ApolloServer({
    typeDefs,
    resolvers,
    dataSources: () => ({
        mapDatabase: new mapDatabase(knexConfig),
    }),
});

// The `listen` method launches a web server.
server.listen().then(({ url }) => {
    console.log(`Server ready at ${url}`);
});

schema.js

const { gql } = require("apollo-server");

const typeDefs = gql`
    type User {
        id: ID!
        username: String
        icon: String
        score: Int
    }
    type Question {
        id: ID!
        lat: Float
        lng: Float
        question: String
    }
    type Guess {
        id: ID!
        lat: Float
        lng: Float
        question: Question
        user: User
    }
    type Query {
        guesses(question: Int!): [Guess]
    }
`;

module.exports = typeDefs;

resolver.js

module.exports = {
    Query: {
        guesses: (_, { question }, { dataSources }) =>
            dataSources.mapDatabase.getGuessesByQuestion(question),
    },
};

datasources/map.js

const { SQLDataSource } = require("datasource-sql");

class MapDatabase extends SQLDataSource {
    getGuessesByQuestion(question) {
        return this.knex
            .select("*")
            .from("guess AS g")
            .leftJoin("user AS u", "u.id", "g.user_id")
            .leftJoin("question AS q", "q.id", "g.question_id")
            .where("g.question_id", "=", question);
    }
}

就像完整性检查一样,我 运行 对数据库进行查询以确保我得到相关结果并且它按预期返回所有内容。

SELECT * FROM guess g
LEFT JOIN user u ON u.id = g.user_id
LEFT JOIN question q ON a.id = g.question_id
WHERE g.question_id = 1;

问题是您的查询 return 是平面数据,而您的 GraphQL 隐式解析器需要嵌套对象结构。例如。 knext 会 return 像这样:

{
  id: 1,
  lat: 89.4,
  lon: -12.6,
  user_id: 2,
  username: "user",
  icon: "x",
  score: 3
}

但是您需要这样的东西才能使解析器“正常工作”。

{
  id: 1,
  lat: 89.4,
  lon: -12.6,
  user: {
    id: 2,
    username: "user",
    icon: "x",
    score: 3
  }
}

记住:如果您不将解析器传递到字段中,它将使用默认解析器来尝试访问对象的字段名称 属性。因此 user 字段将具有以下默认解析器:

parent => parent.user

在平面结构中,没有字段,因此 returning undefined -> 查询 returns null 字段。 在嵌套结构中,它将 return 一个有效的用户对象。

您可以使用 SQL JSON 函数或通过为 Guess.user:

实现自定义解析器来构建结果
module.exports = {
  Query: {
    guesses: (_, { question }, { dataSources }) =>
      dataSources.mapDatabase.getGuessesByQuestion(question),
  },
  Guess: {
    user: (parent) => ({
      id: parent.user_id,
      username: parent.username,
      icon: parent.icon,
      score: parent.score
    }),
  }
};