对应内容:#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!]
}
但是这里要注意,以 Event
的 Model
举例,数据库里存的一直是外键——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
});
},