[英]Parsing Apollo GraphQL query into Sequelize query with include and attributes fields
我正在與 Apollo 合作,為我的 GraphQL 請求構建解析器。
為了提高效率,我想獲取請求的模型列表(帶有相應的嵌套)以及從這些模型中的每一個請求的字段。 這樣我就可以將這些信息傳遞給sequelize
以僅在需要時加入模型 - 並且只提取必要的字段。
解析器確實在info
對象中傳遞此信息。
(obj, args, { models }, info) => ...
從info
對象中,字段、嵌套模型及其各自選定的字段通過以下路徑公開:
info.fieldNodes[0].selectionSet.selections
我的問題是將這個結構(以我想象的某種遞歸方式)解析為一個合理的結構,以便我傳遞給sequelize
查詢。
GraphQL 查詢示例:
{
getCompany(id: 1) {
id
name
companyOffices {
id
users {
id
title
userLinks {
id
linkUrl
}
}
}
}
}
它在info.fieldNodes[0].selectionSet.selections
上生成以下內容(為了簡潔起見,修剪一些字段):
[
{
"kind":"Field",
"name":{
"kind":"Name",
"value":"id"
}
},
{
"kind":"Field",
"name":{
"kind":"Name",
"value":"name"
}
},
{
"kind":"Field",
"name":{
"kind":"Name",
"value":"companyOffices"
},
"selectionSet":{
"kind":"SelectionSet",
"selections":[
{
"kind":"Field",
"name":{
"kind":"Name",
"value":"id"
}
},
{
"kind":"Field",
"name":{
"kind":"Name",
"value":"users"
},
"selectionSet":{
"kind":"SelectionSet",
"selections":[
{
"kind":"Field",
"name":{
"kind":"Name",
"value":"id"
}
},
{
"kind":"Field",
"name":{
"kind":"Name",
"value":"title"
}
},
{
"kind":"Field",
"name":{
"kind":"Name",
"value":"userLinks"
},
"selectionSet":{
"kind":"SelectionSet",
"selections":[
{
"kind":"Field",
"name":{
"kind":"Name",
"value":"id"
}
},
{
"kind":"Field",
"name":{
"kind":"Name",
"value":"linkUrl"
}
}
]
}
}
]
}
}
]
}
}
]
使用此信息,我想生成如下查詢:
const company = await models.Company.findOne({
where: { id: args.id },
attributes: // DYNAMIC BASED ON QUERY
include: // DYNAMIC BASED ON QUERY
});
因此,我需要將上面的 GraphQL 查詢解析為類似於上面info
對象中的這種結構:
{
attributes: ["id", "name"],
include: [
{
model: "companyOffices",
attributes: ["id"],
include: [
{
model: users,
attributes: ["id", "title"],
include: [{ model: "userLinks", attributes: ["id", "linkUrl"] }]
}
]
}
]
};
但我不清楚如何通過遞歸實現這一點,而不會讓事情變得混亂。 如果有更簡單的方法來實現這種動態include
/ attributes
我也願意。
文藝青年最愛的-我怎么能阿波羅GraphQL查詢的模型和領域轉移到include
和attributes
上sequelize
查詢?
它可能會繞過這個問題,但我想知道像graphql-sequelize這樣的東西是否可以幫助解決這樣的問題。 如果沒有,我已經使用此策略來完成您問題的屬性部分。
const mapAttributes = (model, { fieldNodes }) => {
// get the fields of the Model (columns of the table)
const columns = new Set(Object.keys(model.rawAttributes));
const requested_attributes = fieldNodes[0].selectionSet.selections
.map(({ name: { value } }) => value);
// filter the attributes against the columns
return requested_attributes.filter(attribute => columns.has(attribute));
};
User: async (
_, // instance (not used in Type resolver)
{ username, ... }, // arguments
{ models: { User }, ... }, // context
info,
) => {
if (username) {
// (only requested columns queried)
return User.findOne({
where: { username },
attributes: mapAttributes(User, info),
});
} ...
}
我終於找到了這個問題的解決方案(有點)。
const mapAttributes = (projectors, models) => {
const map = {};
const set = [];
Object.keys(projectors).forEach((projector) => {
if (typeof projectors[projector] === 'object') {
map[projector] = mapAttributes(projectors[projector], models.slice(1));
} else {
set.push(projector);
map[models[0]] = set;
}
});
return map;
};
這里的projectors
是 graphql 模式的對象符號,就像我們從info.fieldNodes[0].selectionSet.selections
得到的對象一樣
和模型,是迭代的有序方式。 所以在上面的例子中它會像
['company', 'companyOffices', 'users']
等。
從最終的地圖中,我們進入了一個整潔的結構,從中我們可以很好地獲取屬性。
最后,當您返回參數時,您可能需要將 sequelize 輸出轉換為 graphql 類型
const sequelizeToGraphql = (results = [], vouchers = [], postings = []) => {
const final = { particulars: [] };
results.forEach((result) => {
vouchers.forEach((voucher) => {
final[voucher] = result[voucher];
});
const obj = {};
postings.forEach((posting) => {
obj[posting] = result[`posting.${posting}`];
});
final.particulars.push(obj);
});
return final;
};
這里的vouchers
和postings
是我sequqlize中給我的表名,同理自己修改
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.