Graphql 初体验 第五章 | #7 Dynamic Relations

对应内容:#7 Dynamic Relations | Build a Complete App with GraphQL, Node.js, MongoDB and React.js

工作内容

  • 解决嵌套查询问题
  • promise全部async/await化
  • 优化项目结构,拆分schema和rootValue

这一部分主要解决event和user的嵌套查询。

user对象是可以查询到他所涉及的订单的,而订单也可以查到对应的用户。那么如果我想要嵌套查询该怎么办呢?

这里创建两个 返回函数的函数以供互相调用。

1 想要查询event的同时获得对应的用户Id?

query {
  events {
    title
    creator {
      _id
      email
    }
  }
}

先修改Schema内容,添加最后两行使得可以在获取一方的同时获取对应的另外一方:

type Event {
    _id: ID!
    title: String!
    description: String!
    price: Float!
    date: String!
    +>creator: User
}

type User {
    _id: ID!
    email: String!
    password: String
    +>createEvents: [Event!]
}

但是这里要注意,以 EventModel 举例,数据库里存的一直是外键——user的Id,从来没有存储整个User。

所以这样是可以的,可以查询到user的id
query {
  events {
    title
    creator {
      _id
    }
  }
}
这样是不行的,只能查询到id,email是查不到的
query {
  events {
    title
    creator {
      email
    }
  }
}

所以要靠一些方法来进行join。比如 populate 方法。

events: () => {
    return Event.find()
        .populate("creator")
        .then(events => {
        return events.map(event => {
            return {
                ...event._doc,
                date: event.date.toISOString(),
                creator: {
                    // 省略一些系统内建字段,比如_v
                    ...event.creator._doc,
                    password: null
                }
            };
        })
    }).catch(err => {
        throw err
    });
},

这里采用的是populate(Web 填入;填充)方法。上面采用event的"creator"字段作为populate的参数作为字符串传入,通过"creator"字段中的ref属性,来获取对应的user对象,并自动加入到event对象中。最终结果如下,成功。

在新版的graphQL中,不需要再对_id 进行重写覆盖了,但是date还是需要

{
  "data": {
    "events": [
      {
        "title": "test title",
        "creator": {
          "_id": "604482754c6bd70d2453f3e0",
          "email": "787552171@qq.com"
        }
      }
    ]
  }
}

2 如果想继续嵌套获得呢?甚至是无限嵌套

query {
  events {
    title
    creator {
      email
      createEvents {
        title
        creator {
			email
			createEvents {
				...
			}
		}
      }
    }
  }
}

先创建两个函数,分别通过user的id,和event的id,获得另外一方。注意,这里是返回的不是直接的数据,而是返回的一个返回期望数据的函数。理由后面会揭晓。

const getUserById = userId => {
    return async function () {
        try {
            const user = await User.findById(userId);
            return {
                ...user._doc,
                password: null,
                createEvents: getEventsById(user.createEvents)
            };
        } catch (error) {
            throw error;
        }
    }
}

// 这里返回的是数组,因为一个用户可能创建多个事件。所以这里要用 $in 指令
const getEventsById = eventIds => {
    return async function () {
        try {
            const events = await Event.find({ _id: { $in: eventIds } });
            return events.map(event => {
                return {
                    ...event._doc,
                    creator: getUserById(event.creator)
                }
            })
        } catch (error) {
            throw error;
        }
    }
}

在这里调用上面创建的getUserById 函数,这里是直接调用的,如果上面不是一个返回数据的函数,而是直接返回数据,那么就会无限循环互相调用。

这里主要的思路是,先返回函数,当我们在前端发送查询字符串时,graphql会根据我们的查询字符串,动态调用函数,当需要执行的时候才触发函数。不需要的时候,就停留在返回的函数处,不用触发。

这样就解决了互相嵌套的问题。

events: () => {
    return Event.find()
        .then(events => {
        return events.map(event => {
            return {
                ...event._doc,
                date: event.date.toISOString(),
                creator: getUserById(event.creator)
            };
        })
    }).catch(err => {
        throw err
    });
},

3 修改项目结构

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值