优雅而有效的方法来解决GraphQL中的相关数据

时间:2017-02-03 05:29:52

标签: graphql graphql-js

什么是 resolve GraphQL数据的最佳方法

此处我有一个SeekerTypeJobTypeJobsType嵌套在SeekerType

搜索者可以申请许多工作。在查询搜索者时,可以查询搜索者的数据,也可以查询嵌套的JobType,也可以获取作业类型数据。

但问题是如果One不查询嵌套JobType 他不会获得Jobs数据,但我Seekerresolver viewerType也会获取该数据。

因此,在向搜索者查询提供数据时,我该如何处理,他要么只想要搜索者的详细信息,要么也可能想要这些工作细节。

我应该使用每个nestedType的resolver并获取父对象,并使用父对象中的字段获取相关数据

The code below is just for illustration and clarification, the question is about the best way to resolve data

ViewerType.js

const Viewer = new GraphQLObjectType({
    name: 'Viewer',
    fields: () => ({
        Seeker: {
            type: SeekerConnection,
            args: _.assign({
                seekerId: { type: GraphQLID },
                status: { type: GraphQLString },
                shortlisted: { type: GraphQLInt },
            }, connectionArgs),
            resolve: (obj, args, auth, rootValue) => {
                const filterArgs = getFilters(args) || {};
                return connectionFromPromisedArray(getSeekers(filterArgs), args)
                    .then((data) => {

      // getSeekers() provides all the data required for SeekerType fields and it's
          JobsType fields

                    data.args = filterArgs;
                    return data;
                }).catch(err => new Error(err));
            },
        },
    }),
});

SeekerType.js

const SeekerType = new GraphQLObjectType({
    name: 'SeekerType',
    fields: () => ({
        id: globalIdField('SeekerType', obj => obj._id),
        userId: {
            type: GraphQLID,
            resolve: obj => obj._id,
        },
        email: { type: GraphQLString },
        password: { type: GraphQLString },
        firstName: { type: GraphQLString },
        lastName: { type: GraphQLString },
        imageLink: { type: GraphQLString },
        education: { type: GraphQLString },
        address: { type: GraphQLString },
        jobs: {
            type: new GraphQLList(JobType),
        },
    }),
    interfaces: [nodeInterface],
});

getSeekers()使用嵌套提供完整数据作为graphql字段格式 jobs字段数据

const getSeekers = filterArgs => new Promise((resolve, reject) => {
    if (Object.keys(filterArgs).length === 0) {
        Seeker.find(filterArgs, { password: 0 }, (err, d) => {
            if (err) return reject(err);
            return resolve(d);
        });
    } else {
        async.parallel([
            (callback) => {
                filterArgs._id = filterArgs.seekerId;
                delete filterArgs.seekerId;
                Seeker.find(filterArgs).lean()
                       .exec((err, d) => {
                    if (err) return callback(err);
                    if (err === null && d === null) return callback(null);
                    callback(null, d);
                });
            },
            (callback) => {
                filterArgs.seekerId = filterArgs._id;
                delete filterArgs._id;
                Applicant.find(filterArgs).populate('jobId').lean()
                    .exec((err, resp) => {
                    if (err) return callback(err);
                    callback(null, resp);
                });
            },
        ], (err, data) => {
            const cleanedData = {
                userData: data[0],
                userJobMap: data[1],
            };
            const result = _.reduce(cleanedData.userData, (p, c) => {
                if (c.isSeeker) {
                    const job = _.filter(cleanedData.userJobMap, 
                                 v => _.isEqual(v.seekerId, c._id));
                    const arr = [];
                    _.forEach(job, (i) => {
                        arr.push(i.jobId);
                    });
                    const t = _.assign({}, c, { jobs: arr });
                    p.push(t);
                    return p;
                }
                return reject('Not a Seekr');
            }, []);
            if (err) reject(err);
            resolve(result);

            // result have both SeekerType data and nested type 
               JobType data too.


        });
    }
});

2 个答案:

答案 0 :(得分:1)

我认为这是一个关于如何防止过度获取相关数据的问题......即。查询导引头时如何不必请求作业数据。

这可能有多种优化和安全性的动机。

考虑:

  1. 如果消费者(例如Web应用程序)在您的控制之下,您可以简单地避免在查询搜索者时请求作业字段。您可能知道,这是graphql的既定目标之一,只能通过线路向消费者返回所需内容,以最大限度地减少网络流量并在一次旅行中执行操作。在后端我会想象graphql引擎足够聪明,如果没有请求,也不会过度获取作业数据。

  2. 例如,如果您的担忧更多是安全性或消费者应用程序无意中的过度获取,那么您可以通过创建单独的查询并限制对它们的访问来解决这个问题。例如。寻求者的一个查询和seekerWithJobsData的另一个查询。

  3. 另一种需要考虑的技术是graphql指令,它提供了一个include开关,可用于有条件地为特定字段提供服务。在您的场景中使用此技术的一个优点可能是允许一种方便的方法在多个查询中有条件地显示多个字段,具体取决于单个布尔值的值,例如: JobSearchFlag =假。请阅读此处以获取指令概述:http://graphql.org/learn/queries/

答案 1 :(得分:0)

我不确定我是否完全理解这个问题,但在我看来,您在一个级别上加载了搜索者和工作类型信息。你应该按需加载它们。

在搜索者级别,您只能获取搜索者信息,并且您可以获取与该搜索者相关的任何记录的ID。例如,作业类型ID(如果搜索者有许多作业类型)

在作业类型级别,当用作一个搜索者的嵌套级别时,您可以使用这些ID来获取实际记录。这将使得在查询请求时按需提取作业类型的记录。

可以使用dataloader

等库来缓存和批量记录提取的ID