Falcor - 未缓存的深层嵌套引用
Falcor - Deep nested references not cached
当我请求包含嵌套引用的路由时,我发现 Falcor 客户端出现问题。
这是一个例子:
考虑以下来自 Falcor 服务器的 JsonGraph 响应 model.get
调用
{
"todos": {
"0": { "$type": "ref", "value": ["todosById", "id_0"] },
"1": { "$type": "ref", "value": ["todosById", "id_1"] },
"length": 2
},
"todosById": {
"id_0": {
"name": "get milk",
"label": { "$type": "ref", "value": ["labelsById", "lbl_0"] },
"completed": false
},
"id_1": {
"name": "do the laundry",
"label": { "$type": "ref", "value": ["labelsById", "lbl_1"] },
"completed": false
}
},
"labelsById": {
"lbl_0": { "name": "groceries" },
"lbl_1": { "name": "home" }
}
}
当我使用以下路径调用 model.get
时,以上所有 jsonGraph 结果应该在缓存中:
model.get(['todos', {from: 0, to: 1}, ['completed', 'label', 'name']])
但是,手动访问缓存,我可以看到 todos
和 todosById
在缓存中,但 labelsById
没有。
我不确定,但看起来 labelsById
不在缓存中,因为它是二级引用?
我是不是遗漏了什么,或者这是 Falcor 缓存的预期行为?
有没有办法强制 labelsById
进入缓存,这样就不会发出额外的数据源请求?
感谢任何帮助!
问题可以在这个小项目中重现:
https://github.com/ardeois/falcor-nested-references-cache-issue
更新
感谢@james-conkling 的回答 json 图可以通过执行以下操作来缓存 model.get
:
model.get(
['todos', {from: 0, to: 1}, ['completed', 'name']],
['todos', {from: 0, to: 1}, 'label', 'name']
);
然而,在服务器端 Falcor Router 会调用 todos[{integers:indices}]
路由两次。这可能会影响 API 或对 Falcor 服务器前端的任何数据库调用。
在路径集 ['todos', {from: 0, to: 1}, ['completed', 'label', 'name']]
中,以 completed
和 name
键结尾的路径在一个原子处终止。但是以 label
键结尾的路径在 ref 处终止。如果你想真正遵循该参考,你必须将它作为第二条路径包括在内:
[
['todos', {from: 0, to: 1}, ['completed', 'name']],
['todos', {from: 0, to: 1}, 'label', 'name']
]
一般而言,所有路径都应终止于原子,绝不能终止于引用。我不确定终止于 ref 的路径的预期行为是什么,或者即使它定义明确(如 所述,行为已从 v0 更改为 v1)。
model.get(...paths)
调用可以采用多个 pathSet 数组,因此重写查询应该像
一样简单
model.get(
['todos', {from: 0, to: 1}, ['completed', 'name']],
['todos', {from: 0, to: 1}, 'label', 'name']
);
编辑
如以下评论所述,由于路由器处理程序一次只能解析一个路径集,因此具有多个路径集的 GET 请求可能会导致对上游支持的多个请求 service/db。一些可能的解决方案:
使用单一路径
使用单个路径重写请求 ['todos', range, ['completed', 'name', 'label'], 'name']
。从技术上讲,除了 todos.n.label.name
(确实存在)之外,此请求还要求 todos.n.completed.name
和 todos.n.label.name
(不存在)。
但是,如果您的路由器处理程序 returns pathValues 的路径比匹配路径短,则应将较短的 pathValues 合并到您的 jsonGraph 缓存中。例如。当匹配 todos.0.completed.name
, return { path: ['todos', 0, 'completed'], value: true }
, 而当匹配 todos.0.label.name
return { path: ['todos', 0, 'label', 'name'], value: 'First TODO' }
.
这可能是最简单的方法,但这意味着您的查询在语义上并不是真正正确的(您是在故意询问不存在的路径)。
路由器发出的批量上行请求
在您的路由器中,将上游请求批处理到您的支持 service/db。这并不总是直截了当的。一种可能的方法是使用 facebook 的 data-loader, written to solve an equivalent problem w/ GraphQL routers, but not necessarily tied to GraphQL. Another approach could use a custom reducer function to combine requests issued w/i the same tick (e.g. here).
重写您的架构
这样所有需要同时请求的路径都是一样长的。不过,这并不总是可能的,所以 :shrug.
当我请求包含嵌套引用的路由时,我发现 Falcor 客户端出现问题。
这是一个例子:
考虑以下来自 Falcor 服务器的 JsonGraph 响应 model.get
调用
{
"todos": {
"0": { "$type": "ref", "value": ["todosById", "id_0"] },
"1": { "$type": "ref", "value": ["todosById", "id_1"] },
"length": 2
},
"todosById": {
"id_0": {
"name": "get milk",
"label": { "$type": "ref", "value": ["labelsById", "lbl_0"] },
"completed": false
},
"id_1": {
"name": "do the laundry",
"label": { "$type": "ref", "value": ["labelsById", "lbl_1"] },
"completed": false
}
},
"labelsById": {
"lbl_0": { "name": "groceries" },
"lbl_1": { "name": "home" }
}
}
当我使用以下路径调用 model.get
时,以上所有 jsonGraph 结果应该在缓存中:
model.get(['todos', {from: 0, to: 1}, ['completed', 'label', 'name']])
但是,手动访问缓存,我可以看到 todos
和 todosById
在缓存中,但 labelsById
没有。
我不确定,但看起来 labelsById
不在缓存中,因为它是二级引用?
我是不是遗漏了什么,或者这是 Falcor 缓存的预期行为?
有没有办法强制 labelsById
进入缓存,这样就不会发出额外的数据源请求?
感谢任何帮助!
问题可以在这个小项目中重现: https://github.com/ardeois/falcor-nested-references-cache-issue
更新
感谢@james-conkling 的回答 json 图可以通过执行以下操作来缓存 model.get
:
model.get(
['todos', {from: 0, to: 1}, ['completed', 'name']],
['todos', {from: 0, to: 1}, 'label', 'name']
);
然而,在服务器端 Falcor Router 会调用 todos[{integers:indices}]
路由两次。这可能会影响 API 或对 Falcor 服务器前端的任何数据库调用。
在路径集 ['todos', {from: 0, to: 1}, ['completed', 'label', 'name']]
中,以 completed
和 name
键结尾的路径在一个原子处终止。但是以 label
键结尾的路径在 ref 处终止。如果你想真正遵循该参考,你必须将它作为第二条路径包括在内:
[
['todos', {from: 0, to: 1}, ['completed', 'name']],
['todos', {from: 0, to: 1}, 'label', 'name']
]
一般而言,所有路径都应终止于原子,绝不能终止于引用。我不确定终止于 ref 的路径的预期行为是什么,或者即使它定义明确(如
model.get(...paths)
调用可以采用多个 pathSet 数组,因此重写查询应该像
model.get(
['todos', {from: 0, to: 1}, ['completed', 'name']],
['todos', {from: 0, to: 1}, 'label', 'name']
);
编辑
如以下评论所述,由于路由器处理程序一次只能解析一个路径集,因此具有多个路径集的 GET 请求可能会导致对上游支持的多个请求 service/db。一些可能的解决方案:
使用单一路径
使用单个路径重写请求 ['todos', range, ['completed', 'name', 'label'], 'name']
。从技术上讲,除了 todos.n.label.name
(确实存在)之外,此请求还要求 todos.n.completed.name
和 todos.n.label.name
(不存在)。
但是,如果您的路由器处理程序 returns pathValues 的路径比匹配路径短,则应将较短的 pathValues 合并到您的 jsonGraph 缓存中。例如。当匹配 todos.0.completed.name
, return { path: ['todos', 0, 'completed'], value: true }
, 而当匹配 todos.0.label.name
return { path: ['todos', 0, 'label', 'name'], value: 'First TODO' }
.
这可能是最简单的方法,但这意味着您的查询在语义上并不是真正正确的(您是在故意询问不存在的路径)。
路由器发出的批量上行请求
在您的路由器中,将上游请求批处理到您的支持 service/db。这并不总是直截了当的。一种可能的方法是使用 facebook 的 data-loader, written to solve an equivalent problem w/ GraphQL routers, but not necessarily tied to GraphQL. Another approach could use a custom reducer function to combine requests issued w/i the same tick (e.g. here).
重写您的架构
这样所有需要同时请求的路径都是一样长的。不过,这并不总是可能的,所以 :shrug.