递归地尝试 运行 GraphQL 查询以及查询结果时出错
Error while trying to run a GraphQL query recursively, along with queried results
这与我上一个问题密切相关。简而言之,我有 2 个模式,dbPosts 和 dbAuthors。它们看起来有点像这样(为了简洁起见,我在这里省略了一些字段):
dbPosts
id: mongoose.Schema.Types.ObjectId,
title: { type: String },
content: { type: String },
excerpt: { type: String },
slug: { type: String },
author: {
id: { type: String },
fname: { type: String },
lname: { type: String },
}
dbAuthors
id: mongoose.Schema.Types.ObjectId,
fname: { type: String },
lname: { type: String },
posts: [
id: { type: String },
title: { type: String }
]
我正在这样解决我的 post 查询:
const mongoose = require('mongoose');
const graphqlFields = require('graphql-fields');
const fawn = require('fawn');
const dbPost = require('../../../models/dbPost');
const dbUser = require('../../../models/dbUser');
fawn.init(mongoose);
module.exports = {
// Queries
Query: {
posts: (root, args, context) => {
return dbPost.find({});
},
post: (root, args, context) => {
return dbPost.findById(args.id);
},
},
Post: {
author: (parent, args, context, ast) => {
// Retrieve fields being queried
const queriedFields = Object.keys(graphqlFields(ast));
console.log('-------------------------------------------------------------');
console.log('from Post:author resolver');
console.log('queriedFields', queriedFields);
// Retrieve fields returned by parent, if any
const fieldsInParent = Object.keys(parent.author);
console.log('fieldsInParent', fieldsInParent);
// Check if queried fields already exist in parent
const available = queriedFields.every((field) => fieldsInParent.includes(field));
console.log('available', available);
if(parent.author && available) {
return parent.author;
} else {
return dbUser.findOne({'posts.id': parent.id});
}
},
},
};
我正在解决所有 author 这样的查询:
const mongoose = require('mongoose');
const graphqlFields = require('graphql-fields');
const dbUser = require('../../../models/dbUser');
const dbPost = require('../../../models/dbPost');
module.exports = {
// Queries
Query: {
authors: (parent, root, args, context) => {
return dbUser.find({});
},
author: (root, args, context) => {
return dbUser.findById(args.id);
},
},
Author: {
posts: (parent, args, context, ast) => {
// Retrieve fields being queried
const queriedFields = Object.keys(graphqlFields(ast));
console.log('-------------------------------------------------------------');
console.log('from Author:posts resolver');
console.log('queriedFields', queriedFields);
// Retrieve fields returned by parent, if any
const fieldsInParent = Object.keys(parent.posts[0]._doc);
console.log('fieldsInParent', fieldsInParent);
// Check if queried fields already exist in parent
const available = queriedFields.every((field) => fieldsInParent.includes(field));
console.log('available', available);
if(parent.posts && available) {
// If parent data is available and includes queried fields, no need to query db
return parent.posts;
} else {
// Otherwise, query db and retrieve data
return dbPost.find({'author.id': parent.id, 'published': true});
}
},
},
};
同样,为了简洁起见,我省略了与这个问题无关的部分,例如突变。我的 objective 是让所有查询递归工作,同时优化数据库查找。但不知何故我无法做到这一点。这是我 运行ning 的一个查询,例如:
{
posts{
id
title
author{
first_name
last_name
id
posts{
id
title
}
}
}
}
它 return 是这样的:
{
"errors": [
{
"message": "Cannot return null for non-nullable field Post.author.",
"locations": [
{
"line": 5,
"column": 5
}
],
"path": [
"posts",
1,
"author"
]
}
],
"data": {
"posts": [
{
"id": "5ba1f3e7cc546723422e62a4",
"title": "A Title!",
"author": {
"first_name": "Bill",
"last_name": "Erby",
"id": "5ba130271c9d440000ac8fc4",
"posts": [
{
"id": "5ba1f3e7cc546723422e62a4",
"title": "A Title!"
}
]
}
},
null
]
}
}
如果您注意到,此查询会 return 所有请求的值,但还会针对 post.author 查询添加错误消息!可能是什么原因造成的?
为了不让事情变得混乱,我没有包含整个代码库,但如果您想看一下,Github and a GraphiQL interface is up at https://graph.schandillia.com 如果您希望自己看到结果,它就在上面了。
非常感谢您抽出宝贵时间,如果您能走到这一步。非常感谢任何指向正确方向的指示!"
P.S.:如果你注意到了,我在每个解析器中记录了 3 个变量的值以用于调试目的:
- queriedFields: 查询所有字段的数组
- fieldsInParent:在解析器的 parent 属性[= 中 returned 的所有字段的数组80=]
- available:一个布尔值,显示 queriedFields 成员是否存在于 fieldsInParent
当我 运行 一个像这样的简单查询时:
{
posts{
id
author{
id
posts{
id
}
}
}
}
这是记录的内容:
-------------------------------------------------------------
from Post:author resolver
queriedFields [ 'id', 'posts' ]
fieldsInParent [ '$init', 'id', 'first_name', 'last_name' ]
available false
-------------------------------------------------------------
from Post:author resolver
queriedFields [ 'id', 'posts' ]
fieldsInParent [ '$init', 'id', 'first_name', 'last_name' ]
available false
-------------------------------------------------------------
from Author:posts resolver
queriedFields [ 'id' ]
fieldsInParent [ 'id', 'title' ]
available true
post:author 解析器不应该只执行一次吗?此外,有趣的是,在前 2 个日志中,fieldsInParent 缺少 posts 字段,即使 [=49] 的架构也是如此=]author包含这样一个字段。
您的查询结果实际上不包括所有请求的数据。 posts
查询解析为包含一个 Post
对象和一个空值的数组。 null 在那里是因为 GraphQL 试图完全解析另一个 Post
对象但不能——它遇到了验证错误,即 post 的作者解析为 null。
您可以更改架构以使 author
字段可为空,这样可以消除错误,但仍会留下空值 post。据推测,如果 post 存在,它应该有一个作者(尽管 MongoDB 我猜你很可能只是有一些坏数据)。如果您查看解析器内部,有两个 return
语句——其中一个(可能是 db 调用)在第二个 post.
返回 null
顺便说一句,作为客户,您可能不想处理数组中的空值,而是想要一个空数组而不是整个字段的空值。使用列表(数组)时,您可能希望使它们都不可为空,并使该列表中的每个项目也不可为空。你这样做:
posts: [Post!]!
您仍然需要确保您的解析器逻辑防止这些空值发生,但添加验证可以帮助您更轻松地捕获此类行为。
这与我上一个问题密切相关
dbPosts
id: mongoose.Schema.Types.ObjectId,
title: { type: String },
content: { type: String },
excerpt: { type: String },
slug: { type: String },
author: {
id: { type: String },
fname: { type: String },
lname: { type: String },
}
dbAuthors
id: mongoose.Schema.Types.ObjectId,
fname: { type: String },
lname: { type: String },
posts: [
id: { type: String },
title: { type: String }
]
我正在这样解决我的 post 查询:
const mongoose = require('mongoose');
const graphqlFields = require('graphql-fields');
const fawn = require('fawn');
const dbPost = require('../../../models/dbPost');
const dbUser = require('../../../models/dbUser');
fawn.init(mongoose);
module.exports = {
// Queries
Query: {
posts: (root, args, context) => {
return dbPost.find({});
},
post: (root, args, context) => {
return dbPost.findById(args.id);
},
},
Post: {
author: (parent, args, context, ast) => {
// Retrieve fields being queried
const queriedFields = Object.keys(graphqlFields(ast));
console.log('-------------------------------------------------------------');
console.log('from Post:author resolver');
console.log('queriedFields', queriedFields);
// Retrieve fields returned by parent, if any
const fieldsInParent = Object.keys(parent.author);
console.log('fieldsInParent', fieldsInParent);
// Check if queried fields already exist in parent
const available = queriedFields.every((field) => fieldsInParent.includes(field));
console.log('available', available);
if(parent.author && available) {
return parent.author;
} else {
return dbUser.findOne({'posts.id': parent.id});
}
},
},
};
我正在解决所有 author 这样的查询:
const mongoose = require('mongoose');
const graphqlFields = require('graphql-fields');
const dbUser = require('../../../models/dbUser');
const dbPost = require('../../../models/dbPost');
module.exports = {
// Queries
Query: {
authors: (parent, root, args, context) => {
return dbUser.find({});
},
author: (root, args, context) => {
return dbUser.findById(args.id);
},
},
Author: {
posts: (parent, args, context, ast) => {
// Retrieve fields being queried
const queriedFields = Object.keys(graphqlFields(ast));
console.log('-------------------------------------------------------------');
console.log('from Author:posts resolver');
console.log('queriedFields', queriedFields);
// Retrieve fields returned by parent, if any
const fieldsInParent = Object.keys(parent.posts[0]._doc);
console.log('fieldsInParent', fieldsInParent);
// Check if queried fields already exist in parent
const available = queriedFields.every((field) => fieldsInParent.includes(field));
console.log('available', available);
if(parent.posts && available) {
// If parent data is available and includes queried fields, no need to query db
return parent.posts;
} else {
// Otherwise, query db and retrieve data
return dbPost.find({'author.id': parent.id, 'published': true});
}
},
},
};
同样,为了简洁起见,我省略了与这个问题无关的部分,例如突变。我的 objective 是让所有查询递归工作,同时优化数据库查找。但不知何故我无法做到这一点。这是我 运行ning 的一个查询,例如:
{
posts{
id
title
author{
first_name
last_name
id
posts{
id
title
}
}
}
}
它 return 是这样的:
{
"errors": [
{
"message": "Cannot return null for non-nullable field Post.author.",
"locations": [
{
"line": 5,
"column": 5
}
],
"path": [
"posts",
1,
"author"
]
}
],
"data": {
"posts": [
{
"id": "5ba1f3e7cc546723422e62a4",
"title": "A Title!",
"author": {
"first_name": "Bill",
"last_name": "Erby",
"id": "5ba130271c9d440000ac8fc4",
"posts": [
{
"id": "5ba1f3e7cc546723422e62a4",
"title": "A Title!"
}
]
}
},
null
]
}
}
如果您注意到,此查询会 return 所有请求的值,但还会针对 post.author 查询添加错误消息!可能是什么原因造成的?
为了不让事情变得混乱,我没有包含整个代码库,但如果您想看一下,Github and a GraphiQL interface is up at https://graph.schandillia.com 如果您希望自己看到结果,它就在上面了。
非常感谢您抽出宝贵时间,如果您能走到这一步。非常感谢任何指向正确方向的指示!"
P.S.:如果你注意到了,我在每个解析器中记录了 3 个变量的值以用于调试目的:
- queriedFields: 查询所有字段的数组
- fieldsInParent:在解析器的 parent 属性[= 中 returned 的所有字段的数组80=]
- available:一个布尔值,显示 queriedFields 成员是否存在于 fieldsInParent
当我 运行 一个像这样的简单查询时:
{
posts{
id
author{
id
posts{
id
}
}
}
}
这是记录的内容:
-------------------------------------------------------------
from Post:author resolver
queriedFields [ 'id', 'posts' ]
fieldsInParent [ '$init', 'id', 'first_name', 'last_name' ]
available false
-------------------------------------------------------------
from Post:author resolver
queriedFields [ 'id', 'posts' ]
fieldsInParent [ '$init', 'id', 'first_name', 'last_name' ]
available false
-------------------------------------------------------------
from Author:posts resolver
queriedFields [ 'id' ]
fieldsInParent [ 'id', 'title' ]
available true
post:author 解析器不应该只执行一次吗?此外,有趣的是,在前 2 个日志中,fieldsInParent 缺少 posts 字段,即使 [=49] 的架构也是如此=]author包含这样一个字段。
您的查询结果实际上不包括所有请求的数据。 posts
查询解析为包含一个 Post
对象和一个空值的数组。 null 在那里是因为 GraphQL 试图完全解析另一个 Post
对象但不能——它遇到了验证错误,即 post 的作者解析为 null。
您可以更改架构以使 author
字段可为空,这样可以消除错误,但仍会留下空值 post。据推测,如果 post 存在,它应该有一个作者(尽管 MongoDB 我猜你很可能只是有一些坏数据)。如果您查看解析器内部,有两个 return
语句——其中一个(可能是 db 调用)在第二个 post.
顺便说一句,作为客户,您可能不想处理数组中的空值,而是想要一个空数组而不是整个字段的空值。使用列表(数组)时,您可能希望使它们都不可为空,并使该列表中的每个项目也不可为空。你这样做:
posts: [Post!]!
您仍然需要确保您的解析器逻辑防止这些空值发生,但添加验证可以帮助您更轻松地捕获此类行为。