【问题标题】:Limit Depth on Recursive GraphQL Schema Query using graphql-sequelize resolver (Node.js, express-graphql)使用 graphql-sequelize 解析器(Node.js、express-graphql)限制递归 GraphQL 模式查询的深度
【发布时间】:2020-05-10 09:47:51
【问题描述】:

我有 2 个模型,用户和帖子。我希望在查询帖子时能够获取用户信息,并且在查询用户时能够获取用户的所有帖子。

它们的关联如下:

User.hasMany(Post, {
    foreignKey: 'user',
    as: 'posts'
});
Post.belongsTo(User, {
    foreignKey: 'id',
    sourceKey: 'user',
    as: 'userObject'
})

Post.addScope('defaultScope', {
    include: [{ model: User, as: 'userObject' }],
}, { override: true })

User.addScope('defaultScope', {
    include: [{ model: Post, as: 'posts' }],
}, { override: true })

这是我的模型

用户.js

module.exports.userType = new GQL.GraphQLObjectType({
    name: 'User',
    fields: () => {
        const { postType } = require('../Post/Post');
        return {
            id: {
                type: new GQL.GraphQLNonNull(GQL.GraphQLString),
                description: 'user unique id'
            },
            ci_username: {
                type: new GQL.GraphQLNonNull(GQL.GraphQLString),
                unique: true,
                description: 'case INSENSITIVE username of the user'
            },
            username: {
                type: new GQL.GraphQLNonNull(GQL.GraphQLString),
                description: 'case SENSITIVE username of the user'
            },
            password: {
                type: new GQL.GraphQLNonNull(GQL.GraphQLString),
                description: 'password for the user'
            },
            first_name: {
                type: new GQL.GraphQLNonNull(GQL.GraphQLString),
                description: 'first name of user'
            },
            last_name: {
                type: GQL.GraphQLString,
                description: 'last name of user (optional)'
            },
            profile_picture: {
                type: GQL.GraphQLString,
                description: 'profile picture for the user'
            },
            posts: {
                type: GQL.GraphQLList(postType),
                description: 'list of users posts'
            }
        }
    },
})

/** define User model for the database */
module.exports.User = db.define('user', {
    id: {
        type: Sequelize.UUID,
        primaryKey: true, 
        unique: true,
    },
    ci_username: {
        type: Sequelize.STRING,
        unique: true,
    },
    username: Sequelize.STRING,
    password: Sequelize.STRING,
    first_name: Sequelize.STRING,
    last_name: Sequelize.STRING,
    profile_picture: Sequelize.STRING,
}, {
    // Tells sequelize not to query the "CreatedAt" or "UpdatedAt" Columns
    timestamps: false
})

Post.js

module.exports.postType = new GQL.GraphQLObjectType({
    name: 'Post',
    fields: () => {
        const { userType } = require('../User/User');
        return {
            id: {
                type: new GQL.GraphQLNonNull(GQL.GraphQLString),
                description: 'post unique id'
            },
            name: {
                type: new GQL.GraphQLNonNull(GQL.GraphQLString),
                description: 'name of the post'
            },
            user: {
                type: userType,
                description: 'user object of who created the post'
            },
            created_at: {
                type: new GQL.GraphQLNonNull(GQL.GraphQLString),
                description: 'the datetime the post was created',
            }
        }
    },
})

/** define User model for the database */
module.exports.Post = db.define('post', {
    id: {
        type: DataTypes.UUID,
        primaryKey: true, 
        unique: true,
    },
    name: DataTypes.STRING,
    user: {
        type: DataTypes.STRING,
        references: {
            model: 'users',
            key: 'id'
        }
    },
    created_at: {
        type: DataTypes.TIME,
        defaultValue: DataTypes.NOW
    }
}, {
    // Tells sequelize not to query the "CreatedAt" or "UpdatedAt" Columns
    timestamps: false
})

这是我的查询:

allUsers.js

const allUsers = {
    type: new GQL.GraphQLList(userType),
    args: {
        username: {
            description: 'username of the user',
            type: GQL.GraphQLString,
        },
        // An arg with the key limit will automatically be converted to a limit on the target
        limit: {
            type: GQL.GraphQLInt,
            default: 10
        },
        // An arg with the key order will automatically be converted to a order on the target
        order: {
            type: GQL.GraphQLString
        }
    },
    // use graphql-sequelize resolver with the User model from database
    resolve: resolver(User)
}

allPosts.js

const allPosts = {
    type: new GQL.GraphQLList(postType),
    args: {
        username: {
            description: 'username of the user',
            type: GQL.GraphQLString,
        },
        // An arg with the key limit will automatically be converted to a limit on the target
        limit: {
            type: GQL.GraphQLInt,
            default: 10
        },
        // An arg with the key order will automatically be converted to a order on the target
        order: {
            type: GQL.GraphQLString
        }
    },
    // use graphql-sequelize resolver with the Post model from database
    resolve: resolver(Post)
}

我目前收到Maximum call stack size exceeded。我假设是因为查询中的resolver 无限递归地获取有关帖子和用户的详细信息。

有谁知道有什么方法可以限制解析器的深度?还是不可能有这样的递归查询?

【问题讨论】:

  • 您为allUsers.jsallPosts.js 发布的代码完全相同
  • 就是这样。谢谢你抓住那个。现在修复它。

标签: javascript node.js graphql express-graphql


【解决方案1】:

您必须从包含的模型中删除默认范围,如 here 所示,如下所示:

Post.addScope('defaultScope', {
    include: [{ model: User.scope(null), as: 'userObject' }],
}, { override: true })

User.addScope('defaultScope', {
    include: [{ model: Post.scope(null), as: 'posts' }],
}, { override: true })

为了支持更多深度,您需要为相关字段实现解析器,例如:

function resolve (user) {
  if (user.posts) {
    return user.posts
  }
  return user.getPosts()
}

【讨论】:

  • 谢谢,这似乎适用于 allUsers 查询,但由于某种原因,allPosts 查询的 JOIN 为 user 属性返回 null。虽然当我在 SQL 控制台中运行相同的查询时,它工作正常。知道可能是什么问题吗?
  • 查询结果为SELECT "post"."id", "post"."name", "post"."user", "post"."created_at", "userObject"."id" AS "userObject.id", "userObject"."ci_username" AS "userObject.ci_username", "userObject"."username" AS "userObject.username", "userObject"."password" AS "userObject.password", "userObject"."first_name" AS "userObject.first_name", "userObject"."last_name" AS "userObject.last_name", "userObject"."profile_picture" AS "userObject.profile_picture" FROM "posts" AS "post" LEFT OUTER JOIN "users" AS "userObject" ON "post"."user" = "userObject"."id" ORDER BY "post"."id" ASC;
  • 您将关联别名为userObject,但您的类型中没有具有该名称的字段。
  • 只要我使别名和字段匹配,我就会得到这个错误:Naming collision between attribute 'userObject' and association 'userObject' on model post. To remedy this, change either foreignKey or as in your association definition
  • 将关联定义中的as改成错误提示
猜你喜欢
  • 2019-02-08
  • 2020-03-31
  • 2020-09-11
  • 2020-05-30
  • 2018-11-10
  • 2017-08-25
  • 2019-10-14
  • 2019-07-14
  • 2021-07-25
相关资源
最近更新 更多