New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement internal "GraphMode" response writer #967
Comments
In your example I think you have |
An important aspect of this is that we don't have redundant data. Imagine this query:
The result tree will have lots of duplicates, since my cousins have me as a cousin, and most have each other as cousins. In Graph Mode, we'll only have a single instance of each User. |
Surprisingly perhaps not quite as important as you may think, because gzip ends up eating up the redundancy for breakfast. |
Sorry if I'm missing this part but would GraphMode require MutationConfig for adding edges or that information could be captured in GraphMode itself? |
@eyston Great question - the idea is GraphMode could describe mutations w/o any additional config. For example a range add might be described with: {
...
nodes: {
123: {
friends: {
$type: 'connection',
$data: [
{calls: 'append', value: {$ref: 'addedID1'}},
{calls: 'append', value: {$ref: 'addedID2'}}, // <-- append multiple edges at once
]
}
},
addedID1: {
...
},
addedID2: {
...
},
} |
What is this...Falcor?!?!? |
Another question -- would non-node objects be embedded? e.g.
kind of like no |
@eyston yes, id-less records are inline |
After discussion with @leebyron I started looking for ways to avoid the special Here's an example query that demonstrates these challenges and an updated proposal for the data format:
The results could be described using operations similar to those in JavaScript Object Notation (JSON) Patch but with semantics specifically tailored to describing GraphQL/Relay object graphs: [
{
// The `root` operation describes how a root field maps to an id.
// This concept may not be necessary once Relay supports arbitrary
// root fields.
// In this case, `me` corresponds to id `123`:
op: 'root'
data: {
field: 'me',
arguments: null,
id: '123',
},
{
// The `add` operation denotes new data that should be added/merged into the object graph.
// This describes scalar fields, plain lists, references (linked records), and lists of references.
// Other field types such as pagination cannot be represented inline.
op: 'add',
data: {
123: {
name: '...',
address: {
city: '...', // no cache identifier (`id`), so value is inline
},
bestFriend {$ref: '456'}, // single key `$ref` indicates a reference to another record
},
456: {
name: '...',
},
friend1: {
... // first friend in the connection - note that it isn't linked to within this operation, that's ok
},
friend2: {
... // second friend in the connection - note that it isn't linked to within this operation, that's ok
},
},
},
{
// The `connection` operation describes portions of a list that should be merged into
// the existing list. It may be necessary to change the `id` key to a "path" in order to
// allow updating connections on records without an `id`.
op: 'connection',
data: {
id: '123',
field: 'friends',
arguments: {first: 2, after: 'fooCursor'},
edges: [...], // includes `$ref`s to friends 1 and 2
pageInfo: {...},
},
},
] Note that the EDIT: I updated the issue description with a modified version of this proposal. |
sorry, more questions...
thanks! |
For the foreseeable future this transform would happen on the client, possibly on another thread.
Yes, inserting data w/o a query could lead to stale data with the current approach to diffing and mutations. To prevent this, initially only Relay internals will use GraphMode, and we will use a pre/post traversal to update tracked queries along with every payload. We're also exploring an alternate approach to constructing mutation queries that avoids the need to store tracked queries. |
I'm a bit worried about the potential confusion caused by making something that is similar-but-still-different. What's the value of getting rid of |
I'm interested in this if you get to the point of something to share. |
Reviewed By: wincent Differential Revision: D3068427 fb-gh-sync-id: 0cd95a14fc0c180f0808607faae4a47c8588935c fbshipit-source-id: 0cd95a14fc0c180f0808607faae4a47c8588935c
Summary: Builds on D3068427 to implement a transform function that accepts as input a query and "tree" payload and outputs a GraphMode payload. Part of #967. Reviewed By: wincent Differential Revision: D2987074 fb-gh-sync-id: 4607921d997af5ec0cbb66b41336fd2ab27d9d52 fbshipit-source-id: 4607921d997af5ec0cbb66b41336fd2ab27d9d52
Summary: Preparation diff for GraphMode. The current writer traverses over query results with `RelayNodeInterface.getResultsFromPayload`; this method has the side effect of generating a new `dataID` for id-less root records. In the new GraphMode writer root ids aren't generated until a `putRoot` record is encountered and the dataID does not need to be generated in `getResutlsFromPayload`. This diff will reduce the changes when we actually switch on the GraphMode writer. Reviewed By: wincent Differential Revision: D3139655 fb-gh-sync-id: f4d579b24a23fda732e90063a965d09487b0fb20 fbshipit-source-id: f4d579b24a23fda732e90063a965d09487b0fb20
Summary:Preparation diff for GraphMode. This simplifies a few aspects of the current writer implementation and tests in order to reduce the number of changes when switching to GraphMode: - Change `RelayRecordStore#hasDeferredFragmentData` (and associated setters) to `hasFragmentData`, and use this for both deferred fragment tracking *and* edge fragment tracking. This means `RelayFragmentTracker` is no longer needed. This simplifies the handling of fragment/data tracking in GraphMode, allowing GraphMode to deal with one interface for tracking instead of two. - Update various callers of `hasDeferredFragmentData` - Update writer call sites to not pass a fragment tracker - Update the writer to not record tracked queries for non-root client records: RelayQueryTracker ignores these calls anyway, so this doesn't change the outward behavior (however the tests were expecting the *calls* to the tracker, not what was actually tracked). Reviewed By: wincent Differential Revision: D3140133 fb-gh-sync-id: 13bb235633d30f2dbb055c045724e4e88a0a0a32 fbshipit-source-id: 13bb235633d30f2dbb055c045724e4e88a0a0a32
Relay currently applies the results of queries, mutations, and subscriptions by traversing the query and payload in parallel. The payload cannot be processed in isolation because it lacks sufficient context - for example, consider the following payload:
What does
friends.edges
signify? It could be a plainList[Friend]
, it could be thefirst: 10
friends in a connection, or it could be thefirst: 10, after: foo
- a pagination result that should be merged with any existing edges. Currently, a payload can only be interpreted correctly in the context of a query. This process isn't optimal: a given field such asid
may be queried multiple times by sibling fragments, and therefore has to be repeatedly processed. Further, the same object may appear multiple times in the response payload (e.g. the same person authored multiplecomments
), again causing duplicate processing.Goals
The primary goal of this proposal is to define a data format that can be efficiently applied to a normalized client-side cache. The format should be capable of describing any change that could be applied to a normalized object graph: i.e. both the results of queries as well as mutations and subscriptions.
Specifically, we have found the following characteristics to be important to ensure efficient processing of query/mutation results:
Non-goals include:
Specification Strawman
We're still figuring this out, but we'd prefer to develop this specification in the open and with input from the community. We'll continue to update this as we iterate, but here's a commented example with Flow annotations:
Example Query:
Standard "Tree" Response:
GraphMode Response:
Where the shape of the response is:
Next Steps
RelayEnvironment
for applying GraphMode payloads to the store (as part of [meta] Relay Core API #559).The text was updated successfully, but these errors were encountered: