Skip to content

Commit 8a26727

Browse files
committed
Set makes more sense than List with Mapped batch loaders
1 parent ca62226 commit 8a26727

8 files changed

+31
-22
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,15 +217,17 @@ For example, let's assume you want to load users from a database, you could prob
217217
create the list of results, with nulls filling in for missing values.
218218

219219
```java
220-
MapBatchLoader<Long, User> mapBatchLoader = new MapBatchLoader<Long, User>() {
220+
MappedBatchLoaderWithContext<Long, User> mapBatchLoader = new MappedBatchLoaderWithContext<Long, User>() {
221221
@Override
222-
public CompletionStage<Map<Long, User>> load(List<Long> userIds, BatchLoaderEnvironment environment) {
222+
public CompletionStage<Map<Long, User>> load(Set<Long> userIds, BatchLoaderEnvironment environment) {
223223
SecurityCtx callCtx = environment.getContext();
224224
return CompletableFuture.supplyAsync(() -> userManager.loadMapOfUsersById(callCtx, userIds));
225225
}
226226
};
227227

228-
DataLoader<Long, User> userLoader = DataLoader.newDataLoader(mapBatchLoader);
228+
DataLoader<Long, User> userLoader = DataLoader.newMappedDataLoader(mapBatchLoader);
229+
230+
// ...
229231

230232
// ...
231233
```

src/main/java/org/dataloader/DataLoaderHelper.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.ArrayList;
77
import java.util.Collection;
88
import java.util.HashMap;
9+
import java.util.LinkedHashSet;
910
import java.util.List;
1011
import java.util.Map;
1112
import java.util.concurrent.CompletableFuture;
@@ -275,9 +276,9 @@ private CompletionStage<List<V>> invokeListBatchLoader(List<K> keys, BatchLoader
275276
private CompletionStage<List<V>> invokeMapBatchLoader(List<K> keys, BatchLoaderEnvironment environment) {
276277
CompletionStage<Map<K, V>> loadResult;
277278
if (batchLoadFunction instanceof MappedBatchLoaderWithContext) {
278-
loadResult = ((MappedBatchLoaderWithContext<K, V>) batchLoadFunction).load(keys, environment);
279+
loadResult = ((MappedBatchLoaderWithContext<K, V>) batchLoadFunction).load(new LinkedHashSet<>(keys), environment);
279280
} else {
280-
loadResult = ((MappedBatchLoader<K, V>) batchLoadFunction).load(keys);
281+
loadResult = ((MappedBatchLoader<K, V>) batchLoadFunction).load(new LinkedHashSet<>(keys));
281282
}
282283
CompletionStage<Map<K, V>> mapBatchLoad = nonNull(loadResult, "Your batch loader function MUST return a non null CompletionStage promise");
283284
return mapBatchLoad.thenApply(map -> {

src/main/java/org/dataloader/MappedBatchLoader.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,19 @@
1818

1919
import java.util.List;
2020
import java.util.Map;
21+
import java.util.Set;
2122
import java.util.concurrent.CompletionStage;
2223

2324
/**
24-
* A function that is invoked for batch loading a map of of data values indicated by the provided list of keys. The
25+
* A function that is invoked for batch loading a map of of data values indicated by the provided set of keys. The
2526
* function returns a promise of a map of results of individual load requests.
2627
* <p>
2728
* There are a few constraints that must be upheld:
2829
* <ul>
2930
* <li>The keys MUST be able to be first class keys in a Java map. Get your equals() and hashCode() methods in order</li>
30-
* <li>The caller of the {@link org.dataloader.DataLoader} that uses this batch loader function MUSt be able to cope with
31+
* <li>The caller of the {@link org.dataloader.DataLoader} that uses this batch loader function MUST be able to cope with
3132
* null values coming back as results
3233
* </li>
33-
* <li>The function MUST be resilient to the same key being presented twice.</li>
3434
* </ul>
3535
* <p>
3636
* This form is useful when you don't have a 1:1 mapping of keys to values or when null is an acceptable value for a missing value.
@@ -50,9 +50,6 @@
5050
* <p>
5151
* This means that if 10 keys are asked for then {@link DataLoader#dispatch()} will return a promise of 10 value results and each
5252
* of the {@link org.dataloader.DataLoader#load(Object)} will complete with a value, null or an exception.
53-
* <p>
54-
* When caching is disabled, its possible for the same key to be presented in the list of keys more than once. Your map
55-
* batch loader function needs to be resilient to this situation.
5653
*
5754
* @param <K> type parameter indicating the type of keys to use for data load requests.
5855
* @param <V> type parameter indicating the type of values returned
@@ -63,10 +60,10 @@ public interface MappedBatchLoader<K, V> {
6360
/**
6461
* Called to batch load the provided keys and return a promise to a map of values.
6562
*
66-
* @param keys the collection of keys to load
63+
* @param keys the set of keys to load
6764
*
6865
* @return a promise to a map of values for those keys
6966
*/
7067
@SuppressWarnings("unused")
71-
CompletionStage<Map<K, V>> load(List<K> keys);
68+
CompletionStage<Map<K, V>> load(Set<K> keys);
7269
}

src/main/java/org/dataloader/MappedBatchLoaderWithContext.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.List;
2020
import java.util.Map;
21+
import java.util.Set;
2122
import java.util.concurrent.CompletionStage;
2223

2324
/**
@@ -32,11 +33,11 @@ public interface MappedBatchLoaderWithContext<K, V> {
3233
/**
3334
* Called to batch load the provided keys and return a promise to a map of values.
3435
*
35-
* @param keys the collection of keys to load
36+
* @param keys the set of keys to load
3637
* @param environment the calling environment
3738
*
3839
* @return a promise to a map of values for those keys
3940
*/
4041
@SuppressWarnings("unused")
41-
CompletionStage<Map<K, V>> load(List<K> keys, BatchLoaderEnvironment environment);
42+
CompletionStage<Map<K, V>> load(Set<K> keys, BatchLoaderEnvironment environment);
4243
}

src/test/java/ReadmeExamples.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.ArrayList;
1616
import java.util.List;
1717
import java.util.Map;
18+
import java.util.Set;
1819
import java.util.concurrent.CompletableFuture;
1920
import java.util.concurrent.CompletionStage;
2021
import java.util.stream.Collectors;
@@ -105,7 +106,7 @@ private CompletionStage<List<String>> callDatabaseForResults(SecurityCtx callCtx
105106
private void mapBatchLoader() {
106107
MappedBatchLoaderWithContext<Long, User> mapBatchLoader = new MappedBatchLoaderWithContext<Long, User>() {
107108
@Override
108-
public CompletionStage<Map<Long, User>> load(List<Long> userIds, BatchLoaderEnvironment environment) {
109+
public CompletionStage<Map<Long, User>> load(Set<Long> userIds, BatchLoaderEnvironment environment) {
109110
SecurityCtx callCtx = environment.getContext();
110111
return CompletableFuture.supplyAsync(() -> userManager.loadMapOfUsersById(callCtx, userIds));
111112
}

src/test/java/org/dataloader/DataLoaderMapBatchLoaderTest.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.Map;
1010
import java.util.concurrent.CompletableFuture;
1111
import java.util.concurrent.ExecutionException;
12+
import java.util.concurrent.atomic.AtomicInteger;
1213

1314
import static java.util.Arrays.asList;
1415
import static java.util.Collections.singletonList;
@@ -33,12 +34,14 @@ public class DataLoaderMapBatchLoaderTest {
3334

3435
MappedBatchLoader<String, String> evensOnlyMappedBatchLoader = (keys) -> {
3536
Map<String, String> mapOfResults = new HashMap<>();
36-
for (int i = 0; i < keys.size(); i++) {
37-
String k = keys.get(i);
37+
38+
AtomicInteger index = new AtomicInteger();
39+
keys.forEach(k -> {
40+
int i = index.getAndIncrement();
3841
if (i % 2 == 0) {
3942
mapOfResults.put(k, k);
4043
}
41-
}
44+
});
4245
return CompletableFuture.completedFuture(mapOfResults);
4346
};
4447

@@ -153,7 +156,9 @@ public void should_work_with_duplicate_keys_when_caching_disabled() throws Execu
153156
assertThat(future1.get(), equalTo("A"));
154157
assertThat(future2.get(), equalTo("B"));
155158
assertThat(future3.get(), equalTo("A"));
156-
assertThat(loadCalls, equalTo(singletonList(asList("A", "B", "A"))));
159+
160+
// the map batch functions use a set of keys as input and hence remove duplicates unlike list variant
161+
assertThat(loadCalls, equalTo(singletonList(asList("A", "B"))));
157162
}
158163

159164
@Test

src/test/java/org/dataloader/DataLoaderWithTryTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.List;
99
import java.util.Map;
1010
import java.util.concurrent.CompletableFuture;
11+
import java.util.stream.Collectors;
1112

1213
import static java.util.Arrays.asList;
1314
import static java.util.Collections.singletonList;
@@ -45,7 +46,7 @@ public void should_handle_Trys_coming_back_from_mapped_batchLoader() throws Exce
4546

4647
List<List<String>> batchKeyCalls = new ArrayList<>();
4748
MappedBatchLoaderWithContext<String, Try<String>> batchLoader = (keys, environment) -> {
48-
batchKeyCalls.add(keys);
49+
batchKeyCalls.add(new ArrayList<>(keys));
4950

5051
Map<String, Try<String>> result = new HashMap<>();
5152
for (String key : keys) {

src/test/java/org/dataloader/fixtures/UserManager.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.LinkedHashMap;
55
import java.util.List;
66
import java.util.Map;
7+
import java.util.Set;
78
import java.util.stream.Collectors;
89

910
@SuppressWarnings({"unused", "SpellCheckingInspection", "NonAsciiCharacters"})
@@ -51,7 +52,7 @@ public List<User> loadUsersById(List<Long> userIds) {
5152
return userIds.stream().map(this::loadUserById).collect(Collectors.toList());
5253
}
5354

54-
public Map<Long, User> loadMapOfUsersById(SecurityCtx callCtx, List<Long> userIds) {
55+
public Map<Long, User> loadMapOfUsersById(SecurityCtx callCtx, Set<Long> userIds) {
5556
Map<Long, User> map = new HashMap<>();
5657
userIds.forEach(userId -> {
5758
User user = loadUserById(userId);

0 commit comments

Comments
 (0)