Graphql 初体验 第十八章 | #20 DataLoader | # 21 Graphql function

对应内容:#19 DataLoader & # 20 Graphql function

主要内容:

  • 使用DataLoader 来合并数据库请求,减少访问数据库的次数
  • 在前端使用函数代替写死的查询参数

问题思考

场景一

我们在之前的代码中应该明白这样一个流程:查询所有的n个活动,并返回一个列表,如果活动中需要用到创建者,那么就会再次访问n次数据库,来获得n个活动对应的创建者,这样就造成了1 + n次查询。但我们是不是其实可以把这n次查询合并成一次呢?考虑这个问题。

场景二
比如一个用户分别在五个订单买了两个商品,我们在获取订单列表的时候,就要获取这个用户id下的5个订单,但是如果我们继续获取这五个订单对应的商品,就要再访问五次数据库,但是这五次访问中,获得的商品id分别是,001 001 001 002 002,那么这五次查询其实就可以合并为两次,[001, 002],再把这次的访问结果分发给另外对应的三次查询。这样也能减少数据库访问的压力。

1 dataloader

视频中在处理嵌套查询的时候采用的是bind方法,返回这个函数,而不执行,我用的方法是直接构造一个返回函数的函数,再返回这个函数,还有一种方法是,直接简写成箭头函数,即用即写。这个dataloader的返回值比较严苛,我的方法可能需要小小的改动,所以我就直接使用第三种方法了。

const DataLoader = require('dataloader');
const Event = require("../../models/events");
const User = require("../../models/user");

const eventLoader = new DataLoader(eventIds => {
    return getEventsById(eventIds);
});

const userLoader = new DataLoader(userIds => {
    return User.find({ _id: { $in: userIds}});
})

const getEventsById = async eventIds => {
    try {
        const events = await Event.find({ _id: { $in: eventIds } });
        return events.map(event => {
            return transformEvent(event);
        })
    } catch (error) {
        throw error;
    }
};

const getSingleEvent = async eventId => {
    try {
        return await eventLoader.load(eventId.toString());
    } catch (error) {
        throw error;
    }
}

const getUserById = async userId => {
    try {
        const user = await userLoader.load(userId.toString());
        return {
            ...user._doc,
            password: null,
            createEvents: async () => await eventLoader.loadMany(user.createEvents)
        };
    } catch (error) {
        throw error;
    }
};

const transformEvent = event => {
    try {
        return {
            ...event._doc,
            date: event.date.toISOString(),
            creator: () => getUserById(event.creator)
        }
    }catch (err) {
        throw err;
    }
    
};

const transformBooking = booking => {
    return {
        ...booking._doc,
        user: () => getUserById(booking.user),
        event: async () => getSingleEvent(booking.event),
        createdAt: booking.createdAt.toISOString(),
        updatedAt: booking.updatedAt.toISOString(),
    }
};


exports.getUserById = getUserById;
exports.getEventsById = getEventsById;
exports.getSingleEvent = getSingleEvent;
exports.transformEvent = transformEvent;
exports.transformBooking = transformBooking;

这里要注意一个大问题,经常会报错,返回的值的长度与输入不相同。

这里可以理解为一个槽点了,因为在之前,想要返回一个id的时候需要对它进行toString一下的,以为数据库查出来的id是一个IdObject对象。但是后来graph对其进行了修改,当你查询的时候,不需要专门处理其id了,直接把这个对象丢进去,即可自动根据这个对象,转换成字符串类的id。

但是这里不行,是因为我之前提到了一个合并的问题,比如五个查询,id分别是 001 001 001 002 002,如果直接丢进去,比较的是五个id的引用,因为他们是IdObject类型,不是原始类型,所以认为他们五个是不一样的,但是返回的时候,发现其中三个都是一样的,最后只返回两个,所以就出现了上述的问题。所以这里要你对id进行toString一下,变成string这个原始类型,这时候五个就可以合并成两个了,最后输出也是两个。

2 在前端使用函数代替写死的查询参数

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值