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