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
Implement live query on @orbit/record-cache #718
Conversation
ffda213
to
dcd4c47
Compare
I changed interface QueryObserverSubscription {
unsubscribe(): void;
}
interface QueryObserver {
subscribe(listener: Listener): QueryObserverSubscription
} to interface LiveQuery {
on(listener: (result: QueryResult) => void): () => void
} so it is more in line with orbit vocabulary and less prompt to create confusion with an actual observable |
I am not sure about |
Right now I added bits all around code base. Maybe it all should be just part of a new |
Also maybe |
@selvagsz it is a way to observe a query result. Example: memory.cache.liveQuery(q => q.findRecords('planet')).on((planets => {
console.log('>', planets);
});
memory.update(t => t.addRecord({ type: 'planet', id: 'earth' }));
memory.update(t => t.addRecord({ type: 'planet', id: 'mars' }));
// will print:
// > []
// > [{ type: 'planet', id: 'earth' }]
// > [{ type: 'planet', id: 'earth' }, { type: 'planet', id: 'mars' }] |
I went ahead and moved everything to |
513e8af
to
9e9414c
Compare
I am getting quite happy with this. The main concern I have is error handling. I am dealing with I guess I am just a bit unhappy that we are basically reinventing |
@tchak where can I find the motivation or use case for this feature ? |
For prior art you can check:
If we want to implement any reactive functionality on top of orbit (like a react hook) we would need this. |
@selvagsz I can see you have implemented some react hooks: https://github.com/selvagsz/react-orbit-example/blob/master/src/hooks/useQuery.js |
0d3c72b
to
e1c7928
Compare
added proper error handling |
27a1fef
to
e3ed0fc
Compare
@tchak I was re-implementing the react orbit hooks on top of this PR. I hit 2 issues,
|
remove: boolean; | ||
} | ||
|
||
export function recordOperationChange( |
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.
@tchak this impl. is 🔥🔥🔥 Was able to get an optimal implementation of react-orbit hook with this record change detection on the source's transform
event instead of cache's patch
event.
Thank you for giving it a go! I definitely remember @dgeb explaining in some issue why listening to
Yeah, this is going to be a problem... I think we can fix this with some sort of async coalescing. I am also pretty sure that the whole "dirty check" logic in
Not sure what's going on here |
Ideally I think a |
97a5dc4
to
e29d57a
Compare
@selvagsz I think I fixed the over fetching issue. I will add more tests tomorrow to be sure :) |
@tchak Can confirm. Over-firing of the listener is fixed with the tick change. The other issue related to |
executeQuery(); | ||
}); | ||
|
||
executeQuery(); |
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.
@tchak any reason to immediately execute the listener ? Does it make sense to add an api for this similar to ember debounce method debounce (target, method, args*, wait, immediate)
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 made another change to the interface. So first execution needs to be explicit now.
const subscription = store.cache
.liveQuery(q => q.findRecords('planet'))
.subscribe(fn);
subscription.execute();
subscribe(): LiveQuerySubscription;
interface LiveQuerySubscription {
unsubscribe(): void;
execute(): void;
}
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'm curious about the store.cache
usage here, does this mean that liveQuery is only available on the local cache, not the actual store? And would this mean that a coordinator strategy that auto-pulls data from a remote would not be part of this liveQuery process?
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.
liveQuery
as defined in this PR is a cache
feature not a source
feature. I would like to have a conversation about remote subscriptions, but it is a quite different topic. Regarding a setup with a remote source – if your remote source is syncing back to memory source 'liveQuery' will pick up changes.
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.
Ah, I see, thanks for explaining! Our team is currently looking at orbitjs as a backing store for our json:api connected react application. One thing I'd like to solve is auto-pull of missing data, together with a reactive connection to the query that triggered the pull. It seems like this is not quite there yet - We'll continue to watch this space :)
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.
so would it help you if in the query
/pull
flow there was a subscribe
/unsubscribe
event on the source
? Or something similar. What is your server implementation like? Is your server able to push diff operations for a registered query?
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.
For example in one app I am trying to use something like this : https://gist.github.com/tchak/2a23843c35617a265e75c136afc99b77
dd7ccfe
to
9c2551e
Compare
1251e92
to
18a4b56
Compare
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.
@tchak great work! I really like the new interfaces we settled on.
IMO this just needs some meta
and links
handling, but that doesn't have to happen in this PR (your choice).
RecordOperation | ||
} from '@orbit/data'; | ||
|
||
export interface RecordChange extends RecordIdentity { |
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 think we'll also need to track changes to meta
and links
(which are currently only possible via addRecord
/ updateRecord
ops).
|
||
switch (operation.op) { | ||
case 'addRecord': | ||
case 'updateRecord': |
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.
Check for record.meta
and record.links
.
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 am realizing that current implementation of change matching is not using attributes
or keys
(nor meta
or links
). Right now the algorithm is just to check if the identity
or relationship identity
is the same, we consider change relevant. So in practice we could only expose identity
, relationships
, and removed
in the RecordChange
interface. But we also may improuve the algorith in the future and use extra information.
ae57ed6
to
21918a2
Compare
This a new proposition to introduce query observers. This one is not relying on any known interface such as
RX
orasync iterator
. It uses orbit events. It does expose anLiveQuery
interface, but it is simply an object with asubscribe
method with returns a subscription with anunsubscribe
method.The current one has a downside of looking like an
RX
/Zen
Observable but not being one. I am worried it might confuse folks. One option I considered is to pass the callback directly toliveQuery
method, but it makes it for a complex signature because of optionaloptions
andid
arguments...What do you think @dgeb?