@@ -165,128 +165,6 @@ a list of user ids in one call.
165165
166166That said, with key caching turn on (the default), it will still be more efficient using ` dataloader ` than without it.
167167
168- # Using dataloader in graphql for maximum efficiency
169-
170-
171- If you are using ` graphql ` , you are likely to making queries on a graph of data (surprise surprise). ` dataloader ` will help
172- you to make this a more efficient process by both caching and batching requests for that graph of data items. If ` dataloader `
173- has previously see a data item before, it will cached the value and will return it without having to ask for it again.
174-
175- Imagine we have the StarWars query outlined below. It asks us to find a hero and their friend's names and their friend's friend's
176- names. It is likely that many of these people will be friends in common.
177-
178-
179-
180- {
181- hero {
182- name
183- friends {
184- name
185- friends {
186- name
187- }
188- }
189- }
190- }
191-
192- The result of this query is displayed below. You can see that Han, Leia, Luke and R2-D2 are tight knit bunch of friends and
193- share many friends in common.
194-
195-
196- [hero: [name: 'R2-D2', friends: [
197- [name: 'Luke Skywalker', friends: [
198- [name: 'Han Solo'], [name: 'Leia Organa'], [name: 'C-3PO'], [name: 'R2-D2']]],
199- [name: 'Han Solo', friends: [
200- [name: 'Luke Skywalker'], [name: 'Leia Organa'], [name: 'R2-D2']]],
201- [name: 'Leia Organa', friends: [
202- [name: 'Luke Skywalker'], [name: 'Han Solo'], [name: 'C-3PO'], [name: 'R2-D2']]]]]
203- ]
204-
205- A naive implementation would called a ` DataFetcher ` to retrieved a person object every time it was invoked.
206-
207- In this case it would be * 15* calls over the network. Even though the group of people have a lot of common friends.
208- With ` dataloader ` you can make the ` graphql ` query much more efficient.
209-
210- As ` graphql ` descends each level of the query ( eg as it processes ` hero ` and then ` friends ` and then for each their ` friends ` ),
211- the data loader is called to "promise" to deliver a person object. At each level ` dataloader.dispatch() ` will be
212- called to fire off the batch requests for that part of the query. With caching turned on (the default) then
213- any previously returned person will be returned as is for no cost.
214-
215- In the above example there are only * 5* unique people mentioned but with caching and batching retrieval in place their will be only
216- * 3* calls to the batch loader function. * 3* calls over the network or to a database is much better than * 15* calls you will agree.
217-
218- If you use capabilities like ` java.util.concurrent.CompletableFuture.supplyAsync() ` then you can make it even more efficient by making the
219- the remote calls asynchronous to the rest of the query. This will make it even more timely since multiple calls can happen at once
220- if need be.
221-
222- Here is how you might put this in place:
223-
224-
225- ``` java
226-
227- // a batch loader function that will be called with N or more keys for batch loading
228- BatchLoader<String , Object > characterBatchLoader = new BatchLoader<String , Object > () {
229- @Override
230- public CompletionStage<List<Object > > load (List<String > keys ) {
231- //
232- // we use supplyAsync() of values here for maximum parellisation
233- //
234- return CompletableFuture . supplyAsync(() - > getCharacterDataViaBatchHTTPApi(keys));
235- }
236- };
237-
238- // a data loader for characters that points to the character batch loader
239- DataLoader characterDataLoader = new DataLoader<String , Object > (characterBatchLoader);
240-
241- //
242- // use this data loader in the data fetchers associated with characters and put them into
243- // the graphql schema (not shown)
244- //
245- DataFetcher heroDataFetcher = new DataFetcher () {
246- @Override
247- public Object get (DataFetchingEnvironment environment ) {
248- return characterDataLoader. load(" 2001" ); // R2D2
249- }
250- };
251-
252- DataFetcher friendsDataFetcher = new DataFetcher () {
253- @Override
254- public Object get (DataFetchingEnvironment environment ) {
255- StarWarsCharacter starWarsCharacter = environment. getSource();
256- List<String > friendIds = starWarsCharacter. getFriendIds();
257- return characterDataLoader. loadMany(friendIds);
258- }
259- };
260-
261- //
262- // DataLoaderRegistry is a place to register all data loaders in that needs to be dispatched together
263- // in this case there is 1 but you can have many
264- //
265- DataLoaderRegistry registry = new DataLoaderRegistry ();
266- registry. register(" character" , characterDataLoader);
267-
268- //
269- // this instrumentation implementation will dispatched all the dataloaders
270- // as each level fo the graphql query is executed and hence make batched objects
271- // available to the query and the associated DataFetchers
272- //
273- DataLoaderDispatcherInstrumentation dispatcherInstrumentation
274- = new DataLoaderDispatcherInstrumentation (registry);
275-
276- //
277- // now build your graphql object and execute queries on it.
278- // the data loader will be invoked via the data fetchers on the
279- // schema fields
280- //
281- GraphQL graphQL = GraphQL . newGraphQL(buildSchema())
282- .instrumentation(dispatcherInstrumentation)
283- .build();
284- ```
285-
286- One thing to note is the above only works if you use ` DataLoaderDispatcherInstrumentation ` which makes sure ` dataLoader.dispatch() ` is called. If
287- this was not in place, then all the promises to data will never be dispatched ot the batch loader function and hence nothing would ever resolve.
288-
289- See below for more details on ` dataLoader.dispatch() `
290168
291169### Error object is not a thing in a type safe Java world
292170
0 commit comments