-
Notifications
You must be signed in to change notification settings - Fork 64
Add findByFields method #50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
While the key didn't get cached in the new InMemoryLRUCache that's passed to createCachingMethods, it did get cached by the DataLoader's memoization cache since .load() is called twice with the same key. See https://github.com/graphql/dataloader#caching
Forgot to mention, this will also use Examples// 2 calls for completely different fields -> merged with $or
const pendingUsers1 = this.findByFields({ userName: 'ryan' })
const pendingUsers2 = this.findByFields({ tags: 'gaming' })
await Promise.all([pendingUsers1, pendingUsers2]);
// resulting query:
// - collection.find({
// $or: [
// { userName: { $in: ['ryan'] } },
// { tags: { $in: ['gaming'] } }
// ]
// })
// 2 calls for partially different fields -> merged with $or
const pendingUsers1 = this.findByFields({ userName: 'ryan' })
const pendingUsers2 = this.findByFields({ userName: 'ryan', tags: 'gaming' })
await Promise.all([pendingUsers1, pendingUsers2]);
// resulting query:
// - collection.find({
// $or: [
// { userName: { $in: ['ryan'] } },
// {
// userName: { $in: ['ryan'] },
// tags: { $in: ['gaming'] } }
// }
// ]
// })
// 2 calls for the same fields -> merged without $or
const pendingUsers1 = this.findByFields({ userName: 'ryan' })
const pendingUsers2 = this.findByFields({ userName: 'loren' })
await Promise.all([pendingUsers1, pendingUsers2]);
// resulting query:
// - collection.find({ userName: { $in: ['ryan', 'loren'] } })
// Mixed
const pendingUsers1 = this.findByFields({ userName: 'ryan', tags: 'games' })
const pendingUsers2 = this.findByFields({ userName: 'loren', tags: 'gaming' })
const pendingUsers3 = this.findByFields({ tags: ['games', 'gaming'] })
await Promise.all([pendingUsers1, pendingUsers2, pendingUsers3]);
// resulting query:
// - collection.find({
// $or: [
// {
// userName: { $in: ['ryan', 'loren'] },
// tags: { $in: ['games', 'gaming'] } }
// },
// { tags: { $in: ['games', 'gaming'] } }
// ]
// }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great, thanks! My efficiency brain wants to go straight to a #1 general solution instead of adding $in
queries, but this is a definite improvement over the current feature set :)
return loader.load(JSON.stringify(filter)) | ||
}) | ||
) | ||
docs = [].concat(...docsArray) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docs = [...docsArray]
clones array, but why necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docsArray ends up being an array of arrays in this case ([ [{}, {}], [{}, {}] ]
).
[].concat(...docsArray)
is just a succinct way of merging them all into a single array.
@@ -89,48 +132,114 @@ describe('createCachingMethods', () => { | |||
expect(collection.find.mock.calls.length).toBe(1) | |||
}) | |||
|
|||
// TODO why doesn't this pass? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like this to remain in the file until solved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.find()
is only called once because the dataloader saves the result from the first call. The second call doesn't find anything in the InMemoryLRUCache
since the ttl option wasn't passed in the first call, but it ends up calling the dataloader's .load()
again with the same key as the first call which returns from memoization cache rather than calling .find()
again.
it(`dataloader caches each value individually when finding by a single field`, async () => { | ||
await api.findByFields({ tags: ['foo', 'baz'] }, { ttl: 1 }) | ||
|
||
const fooBazCacheValue = await cache.get( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cache
is separate from dataloader. i suggest removing "dataloader" from the test title and creating other tests for dataloader (which should do memoization caching: https://github.com/graphql/dataloader#caching)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is testing the memoization caching. The desired behavior is that if findByFields
is used with a single field and multiple values, each individual value should get cached by the dataloaders memoization cache (tested by calling with 2 values, 'foo' and 'baz', on line 185, then calling with a single value, 'foo', on line 195, then expecting find
to have been called only once on line 196). However, the InMemoryLRUCache
cache should not be affected by this (tested by ensuring the cache has a result for both values on line 192, but not for a single value, on line 193)
Co-authored-by: Aditya Thakral <9at8@users.noreply.github.com>
Sorry for the delay! And thanks again for contributing. Published in |
Fixes #48
Added a findByFields method and refactored the DataLoader batch fn and tests to make it work.
This new method makes it possible to batch and cache calls for arbitrary fields on the collection.
example: