Skip to content

Commit 25047ce

Browse files
committed
Removing graphql dependency from dataloader
1 parent fbb35ef commit 25047ce

File tree

5 files changed

+0
-319
lines changed

5 files changed

+0
-319
lines changed

README.md

-122
Original file line numberDiff line numberDiff line change
@@ -165,128 +165,6 @@ a list of user ids in one call.
165165

166166
That 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

build.gradle

-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ task myJavadocs(type: Javadoc) {
5454
}
5555

5656
dependencies {
57-
compile "com.graphql-java:graphql-java:4.0"
5857
testCompile "junit:junit:$junitVersion"
5958
testCompile 'org.awaitility:awaitility:2.0.0'
6059
}

src/main/java/org/dataloader/graphql/DataLoaderDispatcherInstrumentation.java

-33
This file was deleted.

src/test/java/ReadmeExamples.java

-82
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
1-
import graphql.GraphQL;
2-
import graphql.schema.DataFetcher;
3-
import graphql.schema.DataFetchingEnvironment;
4-
import graphql.schema.GraphQLSchema;
51
import org.dataloader.BatchLoader;
62
import org.dataloader.CacheMap;
73
import org.dataloader.DataLoader;
84
import org.dataloader.DataLoaderOptions;
9-
import org.dataloader.DataLoaderRegistry;
105
import org.dataloader.Try;
116
import org.dataloader.fixtures.User;
127
import org.dataloader.fixtures.UserManager;
13-
import org.dataloader.graphql.DataLoaderDispatcherInstrumentation;
148

159
import java.util.ArrayList;
1610
import java.util.List;
@@ -81,82 +75,6 @@ public CompletionStage<List<User>> load(List<Long> userIds) {
8175
}
8276

8377

84-
class StarWarsCharacter {
85-
List<String> getFriendIds() {
86-
return null;
87-
}
88-
}
89-
90-
void starWarsExample() {
91-
92-
// a batch loader function that will be called with N or more keys for batch loading
93-
BatchLoader<String, Object> characterBatchLoader = new BatchLoader<String, Object>() {
94-
@Override
95-
public CompletionStage<List<Object>> load(List<String> keys) {
96-
//
97-
// we use supplyAsync() of values here for maximum parellisation
98-
//
99-
return CompletableFuture.supplyAsync(() -> getCharacterDataViaBatchHTTPApi(keys));
100-
}
101-
};
102-
103-
// a data loader for characters that points to the character batch loader
104-
DataLoader characterDataLoader = new DataLoader<String, Object>(characterBatchLoader);
105-
106-
//
107-
// use this data loader in the data fetchers associated with characters and put them into
108-
// the graphql schema (not shown)
109-
//
110-
DataFetcher heroDataFetcher = new DataFetcher() {
111-
@Override
112-
public Object get(DataFetchingEnvironment environment) {
113-
return characterDataLoader.load("2001"); // R2D2
114-
}
115-
};
116-
117-
DataFetcher friendsDataFetcher = new DataFetcher() {
118-
@Override
119-
public Object get(DataFetchingEnvironment environment) {
120-
StarWarsCharacter starWarsCharacter = environment.getSource();
121-
List<String> friendIds = starWarsCharacter.getFriendIds();
122-
return characterDataLoader.loadMany(friendIds);
123-
}
124-
};
125-
126-
//
127-
// DataLoaderRegistry is a place to register all data loaders in that needs to be dispatched together
128-
// in this case there is 1 but you can have many
129-
//
130-
DataLoaderRegistry registry = new DataLoaderRegistry();
131-
registry.register("character", characterDataLoader);
132-
133-
//
134-
// this instrumentation implementation will dispatched all the dataloaders
135-
// as each level fo the graphql query is executed and hence make batched objects
136-
// available to the query and the associated DataFetchers
137-
//
138-
DataLoaderDispatcherInstrumentation dispatcherInstrumentation
139-
= new DataLoaderDispatcherInstrumentation(registry);
140-
141-
//
142-
// now build your graphql object and execute queries on it.
143-
// the data loader will be invoked via the data fetchers on the
144-
// schema fields
145-
//
146-
GraphQL graphQL = GraphQL.newGraphQL(buildSchema())
147-
.instrumentation(dispatcherInstrumentation)
148-
.build();
149-
150-
}
151-
152-
private GraphQLSchema buildSchema() {
153-
return null;
154-
}
155-
156-
private List<Object> getCharacterDataViaBatchHTTPApi(List<String> keys) {
157-
return null;
158-
}
159-
16078

16179
private void tryExample() {
16280
Try<String> tryS = Try.tryCall(() -> {

src/test/java/org/dataloader/graphql/DataLoaderDispatcherInstrumentationTest.java

-81
This file was deleted.

0 commit comments

Comments
 (0)