Skip to content

Commit 5ff6f86

Browse files
committed
Add a RethinkDb example, avoiding pitfalls
1 parent da2fb5f commit 5ff6f86

File tree

1 file changed

+81
-0
lines changed

1 file changed

+81
-0
lines changed

README.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,86 @@ Promise.all([ promise1, promise2 ]).then(([ user1, user2]) => {
452452
});
453453
```
454454

455+
#### RethinkDb
456+
457+
RethinkDb offers a batching method called `getAll` but there are a few caveats :
458+
* Order of results is not guaranteed ([rethinkdb/rethinkdb#5187][rethinkdb_5187])
459+
* Non-existent keys will not return and empty record
460+
Assuming a table `example_table` with those records :
461+
```js
462+
[
463+
{"id": 1, "name": "Document 1"},
464+
{"id": 2, "name": "Document 2"}
465+
]
466+
```
467+
A query `r.getAll(1, 2, 3)` could return such an array :
468+
```js
469+
[
470+
{"id": 2, "name": "Document 2"},
471+
{"id": 1, "name": "Document 1"}
472+
]
473+
```
474+
475+
In essence, this naive implementation won't work :
476+
```js
477+
var r = require('rethinkdb');
478+
var db = await r.connect();
479+
480+
var batchLoadFn = keys => db.table('example_table').getAll(...keys).then(res => res.toArray());
481+
var exampleLoader = new DataLoader(batchLoadFn);
482+
483+
await exampleLoader.loadMany([1, 2, 3]); // Throws, values length !== keys length
484+
485+
await exampleLoader.loadMany([1, 2]);
486+
await exampleLoader.load(1); // {"id": 2, "name": "Document 2"}
487+
```
488+
489+
A solution is to normalize results returned by `getAll` to match the structure of supplied `keys`.
490+
491+
To achieve this efficiently, we first write an indexing function. This function will return a Map indexing results.
492+
Parameters :
493+
* `results` : Array of RethinkDb results
494+
* `indexField` : String indicating which field was used as index for this batch query
495+
* `cacheKeyFn` : Optional function used to serialize non-scalar index field values
496+
```js
497+
function indexResults(results, indexField, cacheKeyFn = key => key) {
498+
var indexedResults = new Map();
499+
results.forEach(res => {
500+
indexedResults.set(cacheKeyFn(res[indexField]), res);
501+
});
502+
return indexedResults;
503+
}
504+
```
505+
Then, we can leverage our Map to normalize RethinkDb results with another utility function which will produce a normalizing function.
506+
```js
507+
function normalizeRethinkDbResults(keys, indexField, cacheKeyFn = key => key) {
508+
return results => {
509+
var indexedResults = indexResults(results, indexField, cacheKeyFn);
510+
return keys.map(val => indexedResults.get(cacheKeyFn(val)) || new Error(`Key not found : ${val}`));
511+
}
512+
}
513+
```
514+
515+
Full dataloader implementation :
516+
```js
517+
var r = require('rethinkdb');
518+
var db = await r.connect();
519+
520+
var batchLoadFn = keys => db.table('example_table')
521+
.getAll(...keys)
522+
.then(res => res.toArray())
523+
.then(normalizeRethinkDbResults(keys, 'id'));
524+
525+
var exampleLoader = new DataLoader(batchLoadFn);
526+
527+
await exampleLoader.loadMany([1, 2, 3]); // [{"id": 1, "name": "Document 1"}, {"id": 2, "name": "Document 2"}, Error];
528+
529+
await exampleLoader.load(1); // {"id": 1, "name": "Document 1"}
530+
```
531+
532+
533+
534+
455535
## Video Source Code Walkthrough
456536

457537
**DataLoader Source Code Walkthrough (YouTube):**
@@ -468,3 +548,4 @@ Promise.all([ promise1, promise2 ]).then(([ user1, user2]) => {
468548
[node_redis]: https://github.com/NodeRedis/node_redis
469549
[nano]: https://github.com/dscape/nano
470550
[sqlite3]: https://github.com/mapbox/node-sqlite3
551+
[rethinkdb_5187]: https://github.com/rethinkdb/rethinkdb/issues/5187

0 commit comments

Comments
 (0)